myexperiment-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[myexperiment-hackers] [3265] branches/wf4ever: Finn's folder stuff


From: noreply
Subject: [myexperiment-hackers] [3265] branches/wf4ever: Finn's folder stuff
Date: Fri, 14 Dec 2012 16:56:07 +0000 (UTC)

Revision
3265
Author
stain
Date
2012-12-14 16:56:06 +0000 (Fri, 14 Dec 2012)

Log Message

Finn's folder stuff

Added Paths

Diff

Added: branches/wf4ever/app/controllers/folders_controller.rb (0 => 3265)


--- branches/wf4ever/app/controllers/folders_controller.rb	                        (rev 0)
+++ branches/wf4ever/app/controllers/folders_controller.rb	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,40 @@
+require 'wf4ever/rosrs_client'
+
+class FoldersController < ApplicationController
+
+  BASE_URI = "http://sandbox.wf4ever-project.org/rodl/ROs/"
+  API_KEY = "32801fc0-1df1-4e34-b"
+  
+  def folder
+    @structure = {}
+    if params[:ro_uri].blank?
+      render :text => "Please supply an RO URI."
+    else
+      @session = ROSRS::Session.new(BASE_URI, API_KEY)
+      @ro = ROSRS::ResearchObject.new(@session, params[:ro_uri])
+      @structure = {:label => @ro.root_folder.name,
+                    :labelStyle => "root_folder",
+                    :uri => @ro.root_folder.uri}
+      @resources = {}
+      (@ro.resources + @ro.folders).each do |res|
+        type = 'folder'
+        leaf = false
+        unless res.is_a?(ROSRS::Folder)
+          type = 'resource'
+          leaf = true
+        end
+        @resources[res.uri] = {:labelStyle => type, :uri => res.uri, :isLeaf => leaf}
+      end
+    end
+    # Renders folder.html.erb
+  end
+
+  # Get a folder's contents when it is expanded in the UI
+  def folder_contents
+    @session = ROSRS::Session.new(BASE_URI, API_KEY)
+    @ro = ROSRS::ResearchObject.new(@session, params[:ro_uri])
+    folder = ROSRS::Folder.new(@ro, params[:folder_uri])
+    @contents = folder.contents.map {|fe| [fe.resource.uri, fe.name]}
+    # Renders folder_contents.js.erb
+   end
+

Added: branches/wf4ever/app/views/folders/_header.html.erb (0 => 3265)


--- branches/wf4ever/app/views/folders/_header.html.erb	                        (rev 0)
+++ branches/wf4ever/app/views/folders/_header.html.erb	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1 @@
+<h1>RO Mockup - Folder view</h1>
\ No newline at end of file

Added: branches/wf4ever/app/views/folders/_navigation.html.erb (0 => 3265)


--- branches/wf4ever/app/views/folders/_navigation.html.erb	                        (rev 0)
+++ branches/wf4ever/app/views/folders/_navigation.html.erb	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,59 @@
+<div id="nav-tabs" class="yui-navset">
+    <ul class="yui-nav">
+        <li class="selected"><a href="" View</em></a></li>
+        <!---<li><a href=""
+    </ul>
+    <div class="yui-content">
+        <div>
+          <!---<div id="tree-controls">
+            <a id="expand_all" href="" All</a> | <a id="collapse_all" href="" All</a>
+          </div> --->
+          <div id="tree"></div>
+        </div>
+        <!---<div id="relations">
+          <img src="" style="width:200px; height:200px"/>
+        </div>--->
+    </div>
+</div>
+
+<script type="text/_javascript_">
+  var tree = new YAHOO.widget.TreeView("tree", <%= @structure.to_json.html_safe -%> );
+  var resourceData = <%= @resources.to_json.html_safe -%>; // Hash of RO resource data
+
+  // Handle users clicking on nodes
+  tree.subscribe("labelClick", function(node) {
+    if(node.data.uri != null) {
+      $('#open-url').text(node.data.uri);
+    }
+  });
+
+  tree.setDynamicLoad(loadNodeData); //Trigger request when expanding a node
+  tree.render();
+
+  function loadNodeData(node, fnLoadComplete)  {
+    var nodeLabel = encodeURI(node.label);
+    var sUrl = "<%= get_folder_url -%>?folder_uri=" + node.data.uri + "&ro_uri=" + "<%= @ro.uri -%>";
+    var callback = {
+      success: function(oResponse) {
+        var oResults = eval("(" + oResponse.responseText + ")");
+        for(var i = 0; i < oResults.length; i++) {
+          var newNode = new YAHOO.widget.TextNode(resourceData[oResults[i][0]], node, false);
+          newNode.label = oResults[i][1];
+        }
+        tree.render();
+        oResponse.argument.fnLoadComplete();
+      },
+      failure: function(oResponse) {
+        oResponse.argument.fnLoadComplete();
+      },
+      argument: {
+        "node": node,
+        "fnLoadComplete": fnLoadComplete
+      },
+      timeout: 7000
+    };
+    YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
+  }
+
+
+</script>

Added: branches/wf4ever/app/views/folders/folder.html.erb (0 => 3265)


--- branches/wf4ever/app/views/folders/folder.html.erb	                        (rev 0)
+++ branches/wf4ever/app/views/folders/folder.html.erb	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,28 @@
+<div class="header">
+  <%= render "folders/header" %>
+</div>
+
+<div class="content">
+
+  <div class="navigation">
+    <%= render "folders/navigation" %>
+  </div>
+  <div class="main">
+    <div id="demo" class="yui-navset">
+      <div class="yui-content">
+        <h3><-- Click on a folder to expand it and see its contents.</h3>
+        <%= form_tag mockups_folder_url do %>
+          <p>
+            <strong>RO URI</strong>:<br/>
+            <%= text_field_tag(:ro_uri, params[:ro_uri], :size => 100) %> <%= submit_tag("Go") %>
+          </p>
+        <% end %>
+        <p>
+        <strong>Selected resource URI:</strong> <span id="open-url">-</span>
+        </p>
+      </div>
+    </div>
+  </div>
+
+</div>
+

Added: branches/wf4ever/app/views/folders/folder_contents.js.erb (0 => 3265)


--- branches/wf4ever/app/views/folders/folder_contents.js.erb	                        (rev 0)
+++ branches/wf4ever/app/views/folders/folder_contents.js.erb	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1 @@
+<%= @contents.to_json.html_safe -%>

Added: branches/wf4ever/public/images/resource.png (0 => 3265)


--- branches/wf4ever/public/images/resource.png	                        (rev 0)
+++ branches/wf4ever/public/images/resource.png	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,12 @@
+\x89PNG
+
+
+IHDR\xF3\xFFa	pHYs\x9A\x9CgAMA\xD8\xEB\xF5\xAA cHRMn's\xAF\xF67\x80\xA5p#\xDD0>\xB2\xCE]w
+\xA8IDATx\xDAb\xFC\xFF\xFF?L?\xFD\xFF\xFF\xA7\xEF\xBF>}\xFB\xC3\xF0\xFD\xD7_0\xCA\xFF\xF6\xEB\xEF\x82Å\xF2\x89H\xA6 \x80\xC0^~\xF1\xF7l\xC0z\xFA\xE5\xFF\xC9k_\xFEW\xAA\xBD>\xDD &\x98\xC0ן\xB0\x81_?~1\x98\xE8\x891\xF0
+\xF0&address@hidden<\xFA\xCC\xC0\xA0\xAC.\xCD  ʟ\xC0\x94r\xC5\x80b\x81\xBB\xE0\xD7\xEC.\xF8\xF2\x93\xE1\xD4ݟ\xE7n\xBCf\xF8\xF7\xE3ӿ?	1\xDBAR\xE00 \xB8\xDF~b7\xE0Ӌ\xEDd\xE0x\xF5\x8E\x81\xF9\xD7\xFE?\xBF\xBE\xB2'\xC0 \x84~`\x83U%\xF6\x8C\xC0\xC0\xFA\xFD\xF7ß?\x81\xF8?C\xD3\xF2kpy\x80\x82\xF0\xEB\xF60\xB0Ԓb\xF8\x94\x99\xA2i>\x8E\x9Bpy\x80B\x8Aa\x80\xA6D3\xFE\xFD	\x97 \xBC`\xD3\xF6\xE9_\x84Z\x80bB\xE2_\xA24\x83|address@hidden
address@hidden
+\xA5b4\x83\x941\xFE\xFD
+7 \x80\x98\x89\xFBQ\x9A\x80
address@hidden \x80\xE0\x80\x9CE\x8CfH b \x80\xE0\xE9\xE0\xDA\xFD\xD7\xA1mG\x81118\xAA\x80\xA1
+2\x98	\xCC\xFF
+\xB6\xA4\xF9ŽGp \xADB\x8An#\xAD}IEND\xAEB`\x82
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/check0.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/check0.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/check0.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1 @@
+GIF89a\xE6B\xE5\xE5\xE5\xE9\xE9\xE9\xF7\xF7\xF7\xE7\xE7\xE7\xFE\xFE\xFE\x81\x81\x81\xF6\xF6\xF6\xEA\xEA\xEA\xF3\xF3󑑑---\xF0\xF0\xF0\xDD\xDDݲ\xB2\xB2\x84\x84\x84\xFD\xFD\xFD\xE0\xE0ೳ\xB3\xE8\xE8蹹\xB9\xDE\xDE\xDE\xF1\xF1\xF1\xEF\xEF\xEF\xDC\xDCܤ\xA4\xA4\xD2\xD2\xD2\xF5\xF5\x{15E79E}\xE2\xE2\xE2\xD9\xD9\xD9\xDB\xDBۜ\x9C\x9C\xB0\xB0\xB0ccc\xAD\xAD\xAD\xBB\xBB\xBB\xD7\xD7ת\xAA\xAA\xD1\xD1\xD1...\x82\x82\x82\xBA\xBA\xBA\xE6\xE6惃\x83\xD3\xD3\xD3\xE3\xE3㦦\xA6\x85\x85\x85///\xBC\xBC\xBC\xF8\xF8\x{F7DF57}\x97\x97\xB7\xB7\xB7\xA8\xA8\xA8\xD5\xD5\xD5bbb111222\xD8\xD8\xD8\xDA\xDA\xDA\xEE\xEE\xEE\xDF\xDF\xDF\xF2\xF2\xF2\xFF\xFF\xFF\xFF\xFF\xFF!\xF9B,\xBD\x80B\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x83A!)\x950,A\x82	8A\xA1\xA1A	address@hidden@\xAA\x82	>\xB9\xBA\xB9	\x82\xC4\xC5<\x82-=\xD0\xD1\xD0'\x82?#=?\xDB\xDC?%&?\x826.+\xE8\xE8 \x824\xAF\xF4@>$\x82\xCC0 \xA0\xA0\x802(address@hidden@\x90\x81"t\xA0PC\x81\xC79n\xF4H\x86	R6X\xA9"\x90\x8Dbʜy(;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/check1.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/check1.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/check1.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,5 @@
+GIF89a\xE6^\xBD\xBD\xBD\xC1\xC1\xC1\xCD\xCD\xCD\xC6\xC6Ƹ\xB8\xB8\xB6\xB6\xB6```\xB2\xB2\xB2\x84\x84\x84\xC5\xC5ũ\xA9\xA9\xC2\xC2³\xB3\xB3\x81\x81\x81\xDD\xDD\xDD\xDC\xDCܾ\xBE\xBE\xBF\xBF\xBF\xF6\xF6\xF6mmm\xE5\xE5\xE5\xDE\xDE\xDE~~\x80\xFF\xFF\xFF\xEA\xEA\xEAPPP\xF2\xF2\xF2\xFD\xFD\xFD\xE0\xE0\xE0\xE7\xE7\xE7\xB9\xB9\xB9\xD9\xD9\xD9\xE2\xE2\xE2\xD2\xD2\xD2sss\xC0\xC0\xC0\xAA\xAA\xAA\xDF\xDF\xDF|||\x88\x88\x88\x86\x86\x869;?\xBA\xBA\xBA!&\xF7\xF7\x{1C20C4}\xA2\xA2\xA2\xB7\xB7\xB7yz|\xA0\xA0\xA0\x8A\x8A\x8A\x85\x85\x85\x91\x92\x93acffffuuukkk\x8B\x8B\x8B\xAF\xAF\xAF\xA4\xA4\xA4\xAC\xAC\xAC\x91\x91\x93\xCC\xCC\xCC222\xC7\xC7\xC7Z\^\xF5\xF5\xF5\x82\x82\x82~\x80\xB5\xB5\xB5\]`\xA7\xA7\xA7\xB4\xB4\xB4\x8C\x8C\x8C\xAE\xAE\xAEyyy\xA0\xA0\xA1\x83\x83\x83\xBB\xBB\xBB{|~\xC4\xC4Ħ\xA6\xA6www\x96\x96\x97opr111\xCE\xCE\xCE\xFF\xFF\xFF!\xF9^,ˀ^\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x83 G
+\x956Q \x82->\\xA1\xA1A\%E\x82\xAD\xAFS\x82\xBA\xBAJ]R\x82!%\xC73]]PK!\x82#/
+Z.W1XH
+4#\x82&'
+D],*Y7N=UO&\x82)I2]L0?\x82MH\x82C`\x84j<T\x84	address@hidden@uȑ\x80\x8BǏ\xA9\xEC\xE8 H\x82!\xCBR.3\xB2ŊA4\x88\xF8\xC0\xA0恛+Dh\xD8Ш\xA7ϟ\x87;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/check2.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/check2.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/check2.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,5 @@
+GIF89a\xE6M\xB8\xB8\xB8\xC6\xC6\xC6\xCD\xCDʹ\xB4\xB4```\xC2\xC2\xC2"0\xB2\xB2\xB2\x84\x84\x84\xB9\xB9\xB9\xB3\xB3\xB3\x81\x81\x81\xB6\xB6\xB6\xBF\xBF\xBF\xC5\xC5\xC5PPP\xB7\xB7\xB7mmm\xBE\xBE\xBE\xEA\xEA\xEA\xD2\xD2\xD2\xFD\xFD\xFD\xE7\xE7\xE7sss\xFF\xFF\xFF\xE2\xE2\xE2\xF2\xF2\xF2\xC0\xC0\xC0\xE5\xE5\xE5\xDC\xDC\xDC\xD9\xD9\xD9\xE0\xE0\xE0\xDE\xDEު\xAA\xAA\xDF\xDF\xDF\xF6\xF6\xF6\xDD\xDDݧ\xA7\xA7\xA2\xA2\xA2\xAE\xAE\xAE\x82\x82\x82fffyyy\xB5\xB5\xB5\x88\x88\x88\xBA\xBA\xBAwww\xAC\xAC\xAC\x8A\x8A\x8A\x8B\x8B\x8B|||uuu\xAF\xAF\xAFrtzkkk\xCC\xCC\xCC222\x85\x85\x85\xC4\xC4\xC4\xF5\xF5\x{146186}111\xF7\xF7\x{1CC30C}\xC7\xC7Ǥ\xA4\xA4\xA0\xA0\xA0\xA6\xA6\xA6\x94\x95\x98\x83\x83\x83\xC1\xC1\xC1\xA9\xA9\xA9\xBD\xBD\xBD\xCE\xCE\xCE\xFF\xFF\xFF!\xF9M,\xBE\x80M\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x83)\x95;address@hidden"=\x82\xADH\xAFH	\x82J\xBA\xBB\xBA\x82"\xC5\xC6	&\x82 'I\xD1\xD2\xD1D \x82#4I(7\xDEF6E+#\x82%>,\xEB\xEB0%\x82A\xAF
+\xF7
address@hidden(\x91\x82H\xD4\xD0\xC1\xA4\xA3G&2~\xBC !\xA8\x82
+	\xA8<\xC0\xD2
+ɜI\xF3P ;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/dt-arrow-dn.png (0 => 3265)


--- branches/wf4ever/public/images/yui/dt-arrow-dn.png	                        (rev 0)
+++ branches/wf4ever/public/images/yui/dt-arrow-dn.png	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,5 @@
+\x89PNG
+
+
+IHDR
+(\x8A]'	address@hidden
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/dt-arrow-up.png (0 => 3265)


--- branches/wf4ever/public/images/yui/dt-arrow-up.png	                        (rev 0)
+++ branches/wf4ever/public/images/yui/dt-arrow-up.png	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,5 @@
+\x89PNG
+
+
+IHDR
+(\x8A]'	PLTE\xFF\xFF\xFFVY_\x86\xCDQ'address@hidden@bj\xFBT*\x89P{IEND\xAEB`\x82
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/lm.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/lm.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/lm.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,7 @@
+GIF89a"address@hidden@\xB9\xB8\xC3\xC0\xB8\x90\xF0\xB00аp\xC0\xB0\x90\xF0\xA80ШP\xB0\xA8\x90\xE0\xA00\xE0\xA0 \xB0\xA0\x90\xB0\xA0\x80\x99\x99\x99̙А\xA0\x90\x80\x9A\x90\x8A\xC0\x90\xC0\x88\x90\x88\x80\x90\x88p\x92\x84k\xB0\x80\x90\x80`\x80\x80\x80\xA0x \x80x`\x80pP\x90p vgI\x80h address@hidden PH@@8000 0    \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF!\xF9,"߀\x82\x83\x84\x85\x86\x87\x88\x854\x89\x8C\x8D\x8E\x8F\x8E\x8B\x90\x93\x90#\x96\x96\x904\x9A\x9B\x9B\x85#&\x8F4\xA8\xA9\x92\x83&\xA2
+(+\xB5+0\x87\xA7\xAA\xA8\xAC\x82&\xA3	\xC4Ÿ\x86\xBA\xBB\xBD(\xB1  \x96''\xB6+/ǂ\xA7F\xDB۫\x8B\x8B+#\xE7\xE8
+
+",\x84ɪ\xCB\xE2#
+\xC5$9\x8A\xBB\xBC
address@hidden B	\x86P\xE1'N\x85^h0w1\xA1\x87%T\xE8\xE0A\x89R\xC8l\xE1B\x86\x8C8:\xB6c\x86\x8D;z\xFCDȐ"Dv*]ʴ\xA9ӧP\xA32
+;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/lmh.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/lmh.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/lmh.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,4 @@
address@hidden \xD0\xC0\x90`H address@hidden \xFF؀\xF0؀\xCAƷ0  \xFF\xD0p\xE0\xA00\x80x`\xC0\xB8\x90 \xC0\x88\xFF\xD0P\x90\x80`p`PШP\xE0\xA0 аp\x92\x84k\xF0Ѐ address@hidden@\xA0x address@hidden Kc\xAC\xD7\xDC\xE5\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF!\xF9,$\xE5\x80\x82\x83\x84\x85\x86\x87\x88\x87
+\x89\x8C\x8D\x8E\x8F\x90\x82\x8B\x91\x94\x90\x97\x97\x94F\x9B\x9C\x9C\x85<	\x91FG\xA9\xAAGF\x85	\xA3\xB6\x89\xA8\xAB\xA9\xAD\x84	\xA4$\xC5ƹ\x88\xBB\xBC\xBE\x83\xB2\x97\xB7Ȅ\xA8\xDCܬ\x8B\x8B\xE8\xE91:\x86ʫ̂\xE3A+\xC6.)?\x87⩚\xF7G@	sPC\xB04\xF2\xEAUH@
+<x\x80\xA0\x87\xC34$\xEBԩ\x85\xE782dpA\x86>**\x90`F\x83\x9B8ZD\x880\xA4\x88H\x99\x84
address@hidden;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/ln.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/ln.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/ln.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1 @@
+GIF89a\xB3\x80\x80\x80\xFF\xFF\xFF\xFF\xFF\xFF!\xF9!\xFEUlead GIF SmartSaver Ver 2.0,address@hidden(r\xC0Wzg\x97\x92O\xE9\xB6c,\xCF^;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/loading.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/loading.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/loading.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,27 @@
+GIF89a\xF7\xDC\xFA\xFA\xFA\xF9\xF9\xF9\xF7\xF7\xF7\xF6\xF6\xF6\xF8\xF8\xF8\xF5\xF5\xF5\xEF\xEF\xEF\xF2\xF2\xF2\xF3\xF3\xF3\xF0\xF0\xF0\xE8\xE8\xE8\xF1\xF1\xF1\xD1\xD1\xD1\xF4\xF4\xF4\xD3\xD3\xD3\xEC\xEC\xEC\xEB\xEB\xEB\xD9\xD9\xD9\xEE\xEE\xEE\xDF\xDF\xDF\xE2\xE2\xE2\xCE\xCE\xCE\xD7\xD7\xD7...---\xD2\xD2\xD2\xDE\xDE\xDE\xDA\xDA\xDA\xE1\xE1\xE1~~~\xA2\xA2\xA2___vvvoooVVVTTT^^^\x8C\x8C\x8C///<<<PPP333\xE0\xE0\xE0"""   \xD6\xD6\xD6\xE7\xE7\xE7\xCA\xCA\xCA\xDC\xDC\xDC\xE9\xE9\xE9\xD5\xD5՜\x9C\x9C\x97\x97\x97uuu\x8A\x8A\x8A\xCB\xCB˃\x83\x83\xAE\xAE\xAE\xA4\xA4\xA4\xC9\xC9\xC9+++\xAD\xAD\xADKKK,,,666aaammmSSS444;;;\xD8\xD8\xD8\x88\x88\x88777\xB6\xB6\xB6\xB4\xB4\xB4\x98\x98\x98\xC5\xC5\xC5xxxYYY\xB8\xB8\xB8sss\xAA\xAA\xAA(((\xDB\xDB۬\xAC\xAC\x9F\x9F\x9F\xA9\xA9\xA9###\x94\x94\x94\xB1\xB1\xB1yyy\xCD\xCD͓\x93\x93\x87\x87\x87!!!hhhRRR\xE5\xE5幹\xB9fffOOO]]]\x9B\x9B\x9B\xB3\xB3\xB3ttt\xA7\xA7\xA7888***)))CCCJJJ\xEA\xEA\xEA111iii\xAB\xAB\xAB555\xB7\xB7\xB7NNN\xC8\xC8\xC8999\xBC\xBC\xBC\\\ZZZ\xED\xED\xEDeee\xD4\xD4\xD4wwwqqq\x92\x92\x92GGG\xE6\xE6\xE6\xC7\xC7Ǖ\x95\x95\x8E\x8E\x8Eggg\x8F\x8F\x8F\xB5\xB5\xB5@@@222\x81\x81\x81\x8B\x8B\x8B\xA8\xA8\xA8\x84\x84\x84===\x86\x86\x86EEE\x80\x80\x80kkkWWWppp\xC6\xC6\xC6%%%UUUHHH\xCF\xCFϝ\x9D\x9D\xBF\xBF\xBF\xE4\xE4䑑\x91[[[\xE3\xE3㻻\xBB\x9E\x9E\x9E\xDD\xDDݲ\xB2\xB2|||\xA3\xA3\xA3:::$$$&&&\xC0\xC0\xC0\x82\x82\x82\xD0\xD0\xD0zzz\xBA\xBA\xBA\xB0\xB0\xB0\x96\x96\x96\xCC\xCC\xCCjjj'''\x8D\x8D\x8D}}}>>>lll\xAF\xAF\xAF\x9A\x9A\x9A\xC4\xC4\xC4LLL\xA0\xA0\xA0\xBE\xBE\xBEBBB\x90\x90\x90bbbQQQ{{{MMMrrr\xC3\xC3\xC3AAA\xC1\xC1\xC1\xA5\xA5\xA5\xC2\xC2\xC2000\xFB\xFB\xFB\xFC\xFC\xFC\xFD\xFD\xFD\xFE\xFE\xFE\xFF\xFF\xFF\xFF\xFF\xFF!\xFFNETSCAPE2.0!\xF9	\xDC,\xF9\xB9	address@hidden@address@hidden
+[ܰ8\x85'\x9F4,i\xEAl\xA0\xD9TJr\xC2\xC28\\xCC\xF9 \xC0\xB6>Lz\xB0M\xC05\x83\x8E\x95ڳ-\x80\xB6&address@hidden
+\xB4\x9D\x92\x88\xED\x9AE?:)X`\xA0\xC16\x89\xD7h\xBB\xB6\xEDS3V\x88\xE26\xD0!\x92\xA7&address@hidden@8\xB8\xC6
+$B2\xCBR`\xA8\xB2J\x84\xAF\xD9j?,address@hidden&\xAF\xD0&\xFC\xE3×:\xFF\xB8\xB09\xF5\xEA\xD6Eb\xAF!\xF9	\xDC,\xFB\xB9	\xB8\xAD\xE0\xC0\x83\xB7i+\xB8
address@hidden'
+Ć\xAD\x93\x88%a\xB4\xAC \xA2!fCl\xD9\xF4j\xA5쐩)>Jp\x88\xC9m\x80\x806j%ؖ\xED\xC0\x877J  \x80\xED
+ \xB0\xAD\x82\x8AFkr\xB8\xB6펊4, address@hidden	\x9B"\x84\xAC\x90H
address@hidden:(\xB3\xBDpt΅m
+,\xEADm\xD4ȅhB\xB6ml<2\xB0-\xC3m3\xB9\xE3dKM&@e\xC3\xC6\xED4zZ\xB4h\x83\xA9\x87O\xB0p\xE5\x87\xB3<](address@hidden	\xDC,\xF6\xB9	address@hidden<f\xBBf\xB0\xA4E\x85\xFA\x81\x85\xD4\x93\xB7	\xC8V\xC1ё.P\x98\xE9\xF3\x9A\x80m\xE8\\xF3\xE1\x8A"\xB65Ķ0\xD6
+
+.\xF8\xF0F\xD6\xA5g\xC5Q2a\xC5Hcp\x8D\xE2\xC6VPy\x80\xA06D\x92\xEC\xE2 @ۨP\xD6H\xC4Ȗ \x82<q\x98\x9D\x99\xF6[q
+\xC8T|-\x8B
+Un
+bK\xE3ƃ\xA6
+^$\x88\x80`[\x80\x92dF\xB1E\x89\xA8M9\xB6P\xB0dBZTY\xB5\xC6\xC15'M^\xCB\xE3\x8B˰h#h\xD2\xE05l\x87#4\xA9-$\xF2\x8B\xAD
+6\x87\xFD#\xF4\x92!\xF9	\xDC,\xF5\xB9	H\xB0\xA0\xC1\x83ܶ!$\xA8-\x9B6\x85\xB7a address@hidden<address@hidden )nh\xD8v\xF1#\x93.%v\xB8˄B9<b\xB0\x8E\x84)v\xB01\x87\x8E\x8C7C\xF1%\xC2\x97\x85R	\xE9\x81u\xDB,#'\xB0=0\xC0q5;\x94b+\x93\xA1ƶkL\xB8Vl\x97\x92HDa[B\xA18\x82D"\xC670\xE0\xE0p\xF7\x80	\xAB\xEA \xC0\x86[\x841Zƈ\xC0"\x80\x94	w(Ag"$\x80\xB1I\x8C\xD98܉\x80ol\xDAb\xD5\xC6y\x9B\xB2~\x8D\xC0\xB5\xE0\xB1.\x98q\xBA\xF5\x82!\xF9	\xDC,\xEE\xB9	address@hidden	\xB8\x96\xAD\xA1\xB6
+.(\xA0\x80\xB6m\\xA3\xC8\xC9;\xB20Q!b\xC36nCL\x82\x89@\x84\xE8#c6\x96D,!\x81C\x81\xB6:B\xDC\xC0\xC8\xD2\xC1KX<(\xF0T\x94,\xB71\xC1\xC0c\xDB5l3\xD5\xC9mٚd𴭀\x81'ٴm\x98\x8D\x81>\xB0X\xD0\xC1\x91\xAF:
+Qi\x816\x8F\xA2,Y\xC35\xAC\x95(I\xD0X80D\xA7p'R\xA4O\x93\xA9\x8C$\xA3\xDA!\xB6\x93X\xCC0ゅ
+\xD8:2\xCC\x80\x80k
+Ȧ
+'E\x84\xB5e#\xBEM\xA1\xF3\xE7\xD0!\xF9	\xDC,\xF6\xB9	\xC8m\xDB6\x82	\\x98P\xE1B\x83
+>|address@hidden
+\xA0\xC0\xA3\xA3"address@hidden>\xC1\x93BQ\x96"\x91\x90P\x88\xD1 6">\xB8\xCCؖi\xC36#`d\xBB\xA6m;$
+l# g\xDBX\xAD\xCA\+\x80\xF3\xC2L"(\x90\xAD\x86*\xD9<n\xBB\xD0&\x8A\x8Fls\xE2\x82\xD66\xC0(A\xC2ʶQ\xB6)\xF8\x93\xC1\x816\xDAXTk\x9B\x97\x9E|\xF1\xE5\xA3\xC5m\xD8`p\xABԥ!\xE8\xBC\xD8VU`\xDCm\xB1Rbˋ\x8C]\xE3\xACc\x90\xBD\xB9	fp\xA0\x81\x873\xFC\xE2\xB5l\xB8#\xB2\xACzP:\xCB\xEA!\xF9\xDC,\xFA\xB9	H\xB0\xA0\xC1\x83۶\x98\xB0aB\x82\xB7iۖ\xAD"6lj\x9BH\xF1ZCfӆm\xA2[M\xD4\xE40\xA0\xC6\x85Цađ(
+A#c$\xB7\xB6QSe'g$\x84\xF8\xB4
+c\xD9<\xE0\x9A\xDB6H\x84\xA6\xC4\xC8\x80\x9BG)\x80L\xD8\xE0
+\xB6!\xC0M[\xB6Wvpl#0\xA0A\x80m\xB1\xC0\x900\x9B\x8C\xE60p`ۉeh\xE3\xD6 \xB64 address@hidden 'address@hidden/R\xA4x\xEBP Ɇ%#>.="D\xE9\xA9gߞ= ;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/lp.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/lp.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/lp.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,3 @@
+GIF89a"\xE6\xE0\x98 \x80\x80\x80\x90p \xFF\xE0\x80P@@\xF0\xC0`\x99\x99\x99\xF0谐\x88p0( address@hidden address@hidden  address@hidden \xC0\x98 \xFF\xE8\xA0\xF0\xD0`\xB0\xA8\x90И address@hidden \xF6\xF1\xE4\xF0\xC8`\xF0\xD0p\xE0\xA80\x99\x99\x99\xE0\xA0 \xD5\xD2ɽ\x94\xF0\xE0\xA0Р \xFF\xFF\xFF!\xF9A,"ހA\x82\x83\x84\x85\x86\x87\x88\x85\x89\x8C\x8D\x8E\x8F\x8E\x8B\x90\x93\x8F7+\x97\x97\x8F\x9C\x9D\x9D\x85+3\x91\x87\x92\x83\xAD\xAE\xAD1\xB1\xB2=\x8A\x83\x83\xAB\x82\xBC\xA3)!&-\xB5\x82\xB7\x82\xB9A\xBE\xA45\xC1*2$,DŽ\x8B\xDC\xDCA\x8B\xE0A1\xCF\xC0!4*8$:>%\xC8A\xCA߅\xE335)\xE6\xE8\xEA#:\xEF\xF1\xCCb\xFC—\x87\x8Cx`(\xF0N\xDB<\xC0\xCE\x94\xC1\x80†>(\xF2\xE4\xA9P$\xAC\xA8#
+(
+J\xF0#\x87:address@hidden>(ᠨQ;\x93*]ʴ\xA9ӧP;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/lph.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/lph.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/lph.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,6 @@
+GIF89a$\xE6B\xB0\xA0\x90\xA0\x90\x80\xFF\xF0\xB0 address@hidden( \xFF\xD0p  \xFF\xF0\xA0\xE0\x98 \x90\x88p\xB0\x80 address@hidden address@hidden \xF0\xC0`\xE0\xA00\xEB\xE9\xE3\x80xpР `PP\xD7ӿ\xE0\xA0 \xBD\x94А\x90\x88\x80\x99\x99\x99\xF0\xE0\xA0\xF0\xE0\x90\xF6\xF1\xE4``Ppp`P@@\xE0\xB00\xE0\xB8P\x90p \xC0\x88\xD5\xD2\xC9И address@hidden,$\xE8\x80B\x82\x83\x84\x85\x86\x87\x88\x87
+\x89\x8C\x8D\x8E\x8F\x90\x82\x8B\x91\x94\x8F1\x98\x98%\x91?\x9D\x9E\x9E\x850)address@hidden@?\x85\xB2\xB3\xB2\xB6\xB79\x87\xAB\xACA\xAD\xB0\x84\xC2\xA4	
+;>#\xBA\xAD\xBD\xAC\xBF\x83.ĥ\xC7:͆\xABA\xE2\xE2\xAFB\x8B\x8B\xD5\xC6
++<μ\xBE\x85\xE9\xEC\xEE\xF0p\xA0\xA8Z9B\x8A\xF5sp\x80\x81\x87*(\xD0 \xE8J\x9A\xA0\x8C\xB5c\xC8 D(X\xD0\xF5\xE9S\xA1ll\xE8\xF1\xC1".T"\xC4\xE1\x855\x9C\xA2C
+"nd\x989\x88\x83\x892zX\xB8\x90a\x80ӧ\x88J\x9DJ\xB5\xAAիX\xB3>
+;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/tm.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/tm.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/tm.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,7 @@
+GIF89a$ address@hidden@\xB0\xA0\x80\xE0Ȁ\xB0\x80А\xD0\xC0\x90\xF0\xA80\xF0蠙\x99\x99\x90\x88\x80`H \x80h \xC0\xB0\x90\x90p ^YY\xFF؀\xF0؀\xFF\xC0P0  \xC0\x90 \xCAƷ\xE0\xA0 address@hidden@address@hidden \xE0Р\xC7ú00 \x80pP\x80x`\xE0\xA00\xF0\xF0\xA0p`P\xB9\xB8\xC3аp\xC0\xB8\x90\xC6\xC3\xCA\xFF\xD0P\x92\x84k\x9A\x90\x8A \x90\x80`\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF!\xF9,$ \xFE\x80\x82\x83\x84\x85\x86\x87\x88\x87\x89\x8C\x8D\x8E\x8F\x90\x82\x8B\x91\x94\x90\x97\x97\x94\x9B\x9C\x9C\x856\x91\xA9\xAA\x93\x83<\xA3	\xB6\x89\xA8\xAB\xA9\xAD\x82\xA4"\xC5ƹ\x88\xBB\xBC\xBE\xB2\x97\xB7Ȅ\xA8\xDCܬ\x8B\x8B\xE8\xE9		1D\x86ʫ\xCC\xE3.@	\xC6B
+;2\x8A\xBCz"0\xC2\\xC6L\x80\xD0\xC0\xC3\x80\xE7\xAD\x80!
+H0\xCCp\xA1C\xB2N\x9D
+q`q\xA3\xC2R\\xF0\xA2R!~<address@hidden:r\xF4\x80\xF1b\x8E'hqɔgӧP\x93E\x9D:\xB5'ի\x90\xACbݪ\x8B\xABW\xA7_\xC3*KV[ٳZ\xCFbM\xAB\x96*۶\xFEQ\xDF\xC2}*w.Ӻv+\xE1\xCD{\x8A\xEFֽ~\xC1\xAE:\xD8ma‡\xA1N\x8F1]\xC7MC\x964٥\xE4ɗ!gv\xBC\x99q\xE7ğ\x87.<zp\xE9\xC0\xA7\xFD\xA6\xE6\xBB:ok\xBB\xAF\xE7Ɔ;\xBBmm\xB5\xB7\xD1Vֻ[S﾿\xE5.;\x9Clq\xB1\xC7\xC3&\xFF\xBA\xDCks\xAE\xCF\xFF>]pu\xA9׻fǾ\xBDqw\xEF\xDF͆?\x9Ery\xF3磯=\x8F\xBE\xBC\xFA\xAB\xEF
+\xB3\x8F\x8F8={p\xF7\xE9\xC7\xCD\xCF~\xFB\xFE\xE0\x80\xEE\xFDW\xA0\x808\x9E~\x8A\xB8\xA0\x83\xE11\xF8\x82R!\x84\xDFI\x86\xDDix\x87\xDByh\x88ىț\x85\x92x\x9D\x89\xBE\xA1ء\x8Aձ\x9C\x82\xBA"address@hidden(doD6\xD2$#Ojw\xE4\x8BIƸ\xE4nQr\x97\xE0\x96FrY\xE3\x947V\x99㕕e9\x96\x98=\x92\x89\x99\x9A\x9A\xB1ə\x9B\x9E\xC1	\x9A\x9C\xA2\xD1I\x9A\x9D\xA6ቚ\x9E\xAA\xF1ɚ\x9F\xAE
+\x9B\xA0\xB2J\x9B\xA1\xB6!\x8A\x9B\xA2\xBA\xA19$\xA3\xC4Aj\x9C\xA4\xC8Q\xAA\x9C\xA5\xCCaꜦ\xD0q*\x9D\xA3Lz\xBA\xA8X\x8A
+\x9F\xA9\xF2\x81$\xA9e\xA2Z\x9F\x97H\xAA\xAA$\xABk\xD2ڦ\xADo\xE2\xA7\xAEs\xF2Z\xA7\xAFw\x9B\xA7\xB0{ۧ\xB1"\xA8\xB2\x832[\xA8\xB3\x87B\x9B\xA8\xB4\x8BRۨ\xACVZ\xA9\xB6\x93r[\xA9\xB7\x97\x82\x9B\xA9\xB8\x9B\x92۩\xB9\x9Fb;&\xBA\xA3\xAA\x9B&\xBB\xA7›*\xACT\xBA\xFB\xA8\xBC\xAFv\xA9\xEF\x97\xF4\x86io\xA8\xF8\xEEp\x83O\xF8o\xA9o\x98\xF0\x87\x8F\xD8\xF0\x89\xB7\xFAp\x8B;\xD7Z\xF1\xAD\xE7\x9A\xF1\xAE\xF7\xDA\xF1\xAF\xF2\xB0#[\xF2\xB1''\x9B\xF2\xB2+7\xDB\xF2\xB3/G\xF3\xB43W[\xF3\xB5\xFD\xAEz\xF3\xB6;w\xDB\xF3\xB7?\x87{r ;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/tmh.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/tmh.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/tmh.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,9 @@
+GIF89a$ address@hidden@\xE0Ȁ\xB0\xA0\x80\xF0\xE0\x90\xF0蠐\x88\x80\xF0\xA80\xD0\xC0\x90`H \xC0\xB0\x90\x99\x99\x99\x80h  \xC0\x90\xF0\xF0\xA0p`P\x9A\x90\x8A\xD0\xC0\x80^YY\xFF\xD0p00 \xF0؀\xFF\xE0\x90\xC0\xB8\x90\xE0\xA0 address@hidden@\x90p \xB9\xB8\xC3аp\xE0Р\xCAƷ \xFF\xC0P\xFF؀\xF0Ѐ\xE0\xA00\x80x`\x80pP\xFF\xD0P0  address@hidden \x90\x80`\x92\x84k\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF!\xF9,$ \xFE\x80\x82\x83\x84\x85\x86\x87\x88\x87\x89\x8C\x8D\x8E\x8F\x90\x82\x8B\x91\x94\x90\x97\x97\x94\x9B\x9C\x9C\x857
+\x91\xA9\xAA\x85
+$\xA3 \xB6
+\x89\xA8\xAB\xA9\xAD\x84
+\xA4;\xC5ƹ\x88\xBB\xBC\xBE\x83 \xB2\x97\xB7Ȅ\xA8	\xDCܬ\x8B\x8B\xE8\xE9C&\x86ʫ̂\xE3,-\xC6@=2\x87⩚\xF7\xC7\xC0
address@hidden<$\xEBԩІ\xE782|\xC0\xC1\x85(*R\xD0\xC0\x86\x83\x9B#^T\xA8P\x84\x86HG\x93)0\xE2\xE3G\x89D`\xA8\x92\xE3\x88\x99P\x81F\x9DJQЪX\xA7^\xCDʕ\xD2֮`}
+K\xD6\xD0زh\xC1\xA5]K\xE8,[\xB0\xFEn\xDFr\x8D++ݺT\xEF⍪w\xAF̾~\xBDx\xF0\xA3†\xC5&\x9E\xBB8+\xE2Ɗ W},\xB9me\xAD\x97\xF9f\x86J9s\xE7˟+\x87\x96<r\xE9Ƨ\xA7N\xBC\xDAp\xEB\xC1\xAF\xC7\xF6;{om\xBC\xB7\xEB斻\xFBmo\xB6\xBF\xD7O;m\xF1\xB2\xC7\xC9&'\xBC\xF9o\xF3J\xCB\xE1><R\xF4\xAE\xD7W?\xBC\x9D{w\xC5\xDFew^|\xF9D\xE3ힷ\xBA>r\xFBB\xE9'\xBF\x87?\xDFr}I\xF7\xF1珟7\xBF\xDA\xFD\xFE\xF1\x87\x80\xDE'\xA0f\xD6w g6\x98\xE0|:\xF7\xE0{B码*\x98!\x84R\xD8a{R\x87\xE1\x84 ~\xB8^\x88\x91\xA0h\x9D\x89\xE7\xA9\xE8݈0jH\xE2\x89,\x96\xE7\xA2T3\xB6Xcx7\x82#\x879ڸ\xE3w=\x9A$\x8FCv\xFEW$zIn\xB7${G\xD9duO\xBA\xF7\xA3\x87Q*9\xE5tU\x9A\xB5\xE5s]җ\xA5\x93_6\xA6}W\x968&\x95env\xE6 o\xEA\x97&\x8Dkr٦gw\x82\x96\xA7h{\x92֧i\xA2\xA8j\x83\xB2V\xA8k\x87–\xA8l\x8B\xD2֨m\x8F\xE2\xA9n\x93\xF2V\xA9o\x97\x97\xA9p\x9Bשq\x9F"\xAAr\xA32W'\x98\xA5Jw\xAA\x99\xA9bתvs긪\x9B\xAF\x927+\x9E\xB7\xEA\x99+\x9F\xBB\xFA\xD9+\xA0\xBF
+,\xA1\xC3Z,\xA2\xC7*\x9A,\xA3\xCB:\xDA,\xA4\xCFJ-\xA5\xD3ZZ-\xA6\xD7j\x9A-\xA7\xDBz\xDA-\xA8ߊ.\xA9\xE3\x9A\xAB\x90\xE5\xAAz.\x92\xE9\xBA\xDA.\xAC2\xAE+延ʫ%\xBD\xEA\xE1+\x9F\xBE\xFD\xF1;\xA0\xBDd\xFA\x8B \xC0l
+̠\xC1l'\xC22,b\xBC\xA90\xAAJ\xA7Xk\xBE\xB3Z\xF1\x8A\xBF1\x96\xD3\xDA1\x8E!\xE3Z\xB2\xAE'󚲯+۲\xB0/\xB3\xB13#[\xB3\xB273\x9B\xB3\xB3;C۳\xB4?S\xB4\xB5Cc[\xB4\xB6Gs\x9B\xB4\xB7K\x83[s ;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/tn.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/tn.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/tn.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,5 @@
+GIF89a \x80\x80\x80\x80\xFF\xFF\xFF!\xF9, \xFF\x8C\x8F\xA9\xEB\xBF\x9COҋ,ƭ\xBFEᦍPi:h\xAA\xACl\xF6\x9Eq5\xAB\xB5x'\xEE\xBC\xC7\xFD\xFBcSCSqt%I9]\xF6dD\xA5\xD3%g\xFA\xC1Z/[Jw\xF2\x95U\xB5\xE4\xB19\xA6\x9D\x9Fiۺ\xD9ơ\xCB\xF3w.ޢ\xB3\xF5p\xFE\xDD\x83\xE7dX#U\xA7\xB8Gxh\xC8\xF3\xE8	4)TIti\x94\x89\xB4\xA9\xD4\xC9\xD4*9JYjy\x8A\x99\xAA\xB9\xCA\xD9\xEA\xF9
+\xBA\xD8{U\xCB\xF5i;\xFBw땋\xDB\xF6\xEB,\xB6[X\xACv\xEC\x98\xEC\xB6,\xFALm:\x8DZ\xADz͚\xED\xBA
+\xDB-\xCB\xF8\xADNN+|N\x9C.\xDC,Wλn\xFC\x8E\xAF<\xCF\\xEF|\xBD/address@hidden
+\xB6D\xF8RaL\x86g>\xAC\x89\x93\xA3M\x8A;-\xF6\xC4R\xDFO\x8F9AY\x94dR\x93KQ6U\xF9\x94eT\x97SaV\x95y\x95\xA6ά7\xB7z5ʕgX\x9Fc\x81Z\x96\xE8W\xA5k\x99\xB6u\xFAj\\xA9s\xA9ֵzk^\xAD`\xF7v\xED\x98m`\xB7\x83\xE1\x96{\x98nb\xBB\x8B\xF16\xD6\xFB\x98\xAF\xE0Ʉ)\xB6\x8C\xB3b͌9;\xF6\xB4\xE4ʤ/\x97\xCE|zs\xEAΫ?\xB7\xFDz\xB4\xE9٨i\xAB\xB6\xCD\xB7kݰyˮ
+\xFCv\xF0\xDC\xC3w\xEF}\xFC\xB7\xF0\xE5ęw\x8E:\xDF;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/tp.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/tp.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/tp.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,6 @@
+GIF89a$ address@hidden \xFF\xF0\xC0\xFF\xE0\x80\xB0\xA8\x90\xFF\xE8\xA0\xFF\xE8\x90\xFF\xE0\x90\xFF\xD8p\xF0\xC8`\xF0\xC0P\xF0\xD0p\xE0\xA80\xF0\xA80\xF0\xE8\xB0\xF0\xB8P\xF0\xF0\xB0\xF0\xD0`\xB0\x80\xFF\xD0p\xF0\xB0@  \xFF\xF0\xA0\x80p`\x90\x88p\xE0\x98 \xB0\x80 address@hidden( \xA0x @80\xC0\x98 \xF0\xC0`p`PИ \x80xppp`\xE0\xB8P\xFF؀\xE0\xA00\xE0\xA0 \xD5\xD2\xC9\xC0\x88\x99\x99\x99\xBD\x94`PP\x90\x88\x80АP@@\xEB\xE9\xE3\xF0\xC8p\xF0\xE0\xA0Р \xE0\xB00``P\xF0\xE0\x90\xD7ӿ\x90p address@hidden@,$ address@hidden;<address@hidden	
+*6$\xB7\x82\xB9\x92\x85/\xC0\xA5\xC3&address@hidden@\xD0\xC2
address@hidden	\xE7\xE9\xEB.1\xF0\xF2v
+\x96\xEFA"address@hidden,\x88P\xF4\xE9S!L<address@hidden"\xC4\xE1ƃ9 \xE01ƒ!zdh\xB4\x8B\xE5	:|\x88\x91\xA1\x80ѣV*e\xD4s\xA9ӧ͠J\x85\xDAt\xAA\xD5GU\xAFjE\x94u\xABW]_\xC3rK\xF6aٳ]\xCFNM\xAB\x96j[\xB1l\xFF\xDF.\x8D+w%ݺ\x94\xEE\xE2U\xB5W\xAB޾<[\xFD+xla\xB7\x87\x9FNl\x96\xF1\Ǐ!ە<\x99r^˗1\xF3Ռ\x95sgρA3=\x9A\xB4a\xD3\xDBP;T\x955Xׯaϓ=[\xF6bȷ\xE7f\xBC;q\xEFÿ<p\xF1\xBE\xC7\xF7&ǻ\xBCns\xB9\xCF\xDFFo;]mu\xB4\xB4kþ^\x96;Y\xEFp\xB3\x83;\xFEky\xAF緦\xF7+\xBE=\xED\xF5W\xE1vo\x9B\xFEv\xFB\xAE\xE5\xAF\xC5\xCFZ\xBFT\xFF\x88\xD5\xF7\xAA\xA8\x81\xA8蔂\x91	\xE8\xE0}F\xF8`~\x9AƠRV6a\x92\x96a%f\xB6a\x81\x8A"'%\x82v\xE2f#&\x98\xA2g+~֢\x85/r\xA3#7\x866\xA3\x875j\x96ci;\x9A\xD8#f?&R\xE4i\xA9\xE2\xFF\x90\x96\xB9\x87&)%\x85QR\xA9$\x8CLR\xE6djUBy\xA5\x8DYJ\xB6%+a\xE2V\xA6ng\xF2\x96\xA6okצpo\xA7qs"W\xA7rw2\x97\xA7s{BקtR\xA8u\x83b\xD7%\x89\x87\xBA\x98(\x8D\x8B\xF2ب\x90\x8F.)\x96\x93\x82Y\xA9\x8F\x85v\x97\xE9w\x9B\x86w)\x91\x9D\x92\xAAy\xA3\xA2W\xAAz\xA7\xB2\xF7i\x93\xA9\xC6\xD7\xEA|\xABj\xF9\xEA~\xB1\x8A9\xEB\xB78\xA5\x97\xBB"\xFA%\xA6\xB5\x9A,\x9AêY,\x9BǺ\x99,\x9C\xCB\xCA\xD9,\x9D\xCF\xDA-\x9E\xD3\xEAY-\x9F\xD7\xFA\x99-\xA0\xDB
+\xDA-\xA1\xDF\xFA+\xA8\xE1jZ.\xA7\xE7z:.\xAB\xE9\x8A\xDA.\xA9\xEF\x9A/\xAA󪺮\xAC\xF5\xBA\x9A/\xAC\xF7ں/\xAD\xFD
+0\xB1[0\xB2+\x9B0\xB3;\xDB0\xB4K1\xB5[[18\xB6k\x9B1\xB7{\xDB1\xB8\x8B۫\xA2!\x9B[2\xBA'\xAB;2\xA3)\xBB\xDB2\xBC/\xCB3\xBD3ۻ\xB2\xA35\xEB\x9B3\xBF7C\xBA3\xC0=Kq ;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/tph.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/tph.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/tph.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,10 @@
+GIF89a$ \xE6B\x80\x80\x80\xD7\xDC\xE5Kc\xAC\xA0\x90\x80\xFF𰰠\x90\xFF\xF0\xC0 \xB0\xA8\x90\xFF\xE0\x80\xFF\xE8\xA0\xF0\xC8`\xFF\xE8\x90\xFF\xE0\x90\xFF\xD8p\xF0\xD0p\xF0\xC0P\xE0\xA80\xB0\x80\xF0\xA80\xF0\xF0\xB0\xF0\xB8P\xF0\xD0`\xF0谀p`\xE0\x98 address@hidden( \xFF\xD0p\xA0x @80\xB0\x80 \xF0\xB8@  \xFF\xF0\xA0\xC0\x98 \xF6\xF1\xE4\xC0\x88\x90p И \xF0\xE0\x90\xEB\xE9\xE3Р \xBD\x94\xE0\xA0 address@hidden@@\xE0\xB00\xE0\xB8P\xD5\xD2\xC9p`P\xF0\xE0\xA0`PP\xFF؀pp`\xE0\xA00\xFF\xFF\xFF!\xF9B,$ \xFF\x80B\x82\x83\x84\x85\x86\x87\x88\x87\x89\x8C\x8D\x8E\x8F\x90\x82\x8B\x91\x94\x8F&	\x98\x98+\x91\x9D\x9E\x9E\x85	*1\x90\xAC\xAD\x85\xB2\xB3\xB2\xB6\xB7;\x87\xAB\xAC\xAD\xB0\x84\xC2\xA4
+?56\xBA\xAD\xBD\xAC\xBF\x837ĥ$\xC7
+)͆\xAB\xE2\xE2\xAFB\x8B\x8B\xD5\xC6
+-<μ\xBE\x85\xE9$\xEC\xEE\xF0N\xF8\xA8Z9B\x8A\xF5\x83\xC0 
+.&\xE0 \xE8J\x9A\xA0\x8C\xB5cA\x85 &\x80\xD0\xF5\xE9S\xA1ll\xE8Q\x82:T"\xB4\xA19$\xB0(Q"D\x88	address@hidden(իXY\xCDʕ\xEA֮`#}
+K\xB6jٳ\x8EƢ]k\x8E\xAD[Bj\xFFߒ\x8D+,ݺ\\xEF\xE2Ūw\xAFW\xBFh\xFB\xAE$x\xB0X\xC3s\x87-\xAC\x98h㮌#\x8A,\xD9\xE5\xCAp1_\xBD\xAC\xB9m癜5\x87\xC6<\xBAriɧ\xA7n\xBCZqkį
+\xC7<pm\xBF\xB7\xF7\xE6ƻ\xBBno\xB9\xBF\xDFw;\x9Cm\xF1\xB5\xC7\xBD\x9CpsJ\xC9\xCFF/;=\xF1sH\xD5_Ǿ\xFDQv\xBB\xDDӆw<>\xD1w\xC8\xE5ͧ\x9F\xBC^Q{\xCB\xEF\xB5\xC6\xCF<_R}\xFB\xF7\xCF\xE7\xBDﹾ\xFE\xAC\xFF\xF1\xC5_\x80\x9B
+h`~\xFA\x97\xE0|\xFE\x85\xE0\x83
+B\xC8\xE0\x82\xF158\x95\x85\xCCIX!\x85\xEFa蜆r؞\x87Љ\xB8\x89\x87\x818\xA2\x89\xE9\xA1ȝ\x8A'address@hidden<v\xE7\xA3YC\xDEX\xE4\xFFvG\xAA\x97d\x8FK^\xD7${O\xE5sS\xBAW%\x93W6\x97%|[J\xD9\xE5r_\xCA&\x96c~V&}Anx\xA6\x97iv\xB6\xE6 s\xE2\xD7f\x88o\x92\xA7h{\x92֧i\xA2\xA8j\x83\xB2V\xA8k\x87–\xA8l\x8B\xD2֨m\x8F\xE2\xA9n\x93\xF2V\xA9o\x97\x97\xA9p\x9Bשq\x9F"\xAAry\xAA9\xAAt\xA7R\x97\xAAuw\xAEX\xAA\x9C\xABj\xF7*\x9F\xB3\xFAY+\xA0\xB7
+\x9A+\xA1\xBB\xDA+\xA2\xBF*,\xA3\xC3:Z,\xA4\xC7J\x9A,\xA5\xCBZ\xDA,\xA6\xCFj-\xA7\xD3zZ-\xA8׊\x9A-\xA9\xADƸ-\xAAߪ.\xABBv\xBB㸲\x9AK$\xBAో\x9E\xBB\xFB\xC1`\xAC\x{DAAB}\xA4\xBC\xE2[\xA0\xBE\xDA%\xBF\xD2\xFB\xAE\xBFV\x9C!\xC1\\xFC!\xC2b*\\xA2\xC3)2\x8C&\xC4/J'L\xC55b\x9C\xA3\xC5zj\xFCc\xB9 \xBBɱ\xA9#92\xAC%;y2\xAD+\xDB\xDA2\xAE/\xEB3\xAF3\xFBZ3\xB07\x9B3\xB1;\xDB3\xB2?+4\xB3C;[4\xB4GK\x9B4\xB5K[\xDB4\xB6OkV ;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/folders/vline.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/folders/vline.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/folders/vline.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,9 @@
+GIF89a \x80\x80\x80\x80\xFF\xFF\xFF!\xF9, \xFF\x8C\x8F\xA9\xEB\xBF\x9COҋ,\xC6z\xD3\xEEA`茤b\x9E\x99:\xA5lಱ:\x9F5y\x87\xB9\xB7o=\xF7\xAAK\xC3HuL\xFC.\xCBOr\xF5dD\xA5\xD3fk
+\xC3ZE\xDAn\xD5\xDD
+\xBF\xE40\xF8)&\x96\xD1\xE7dڸv\xB7\x8Fod\x9C>/֕\xF9\xE1jv\xA7\xD7\xF4w`H\xA8\xC8&\xE8G\xF8\x82\x98\xD5X\xF8(SIsi\x93\x89\xB3\xA9\xD3\xC9\xF3\xE9
+4	9\xCAt\xEATj\xB9\x8A٪\xF9\xCA\xEB9Z+zK\xBA(\x97\x8Bګ\xBA\x8B\xF7{5\xCCU<<x\xAC\x96\xEC\xB8\xD7L\xF9lm:\xCDw
+\xC8X\xCD\xCA\xED\xEA
+.+NKnk\x8E\x8B\xAE\xBB\xCDΫ\xEE\xFB\xDC.O\o|\x8F<\xAF\x9Cϼ\xEF\xDC\xDA?i\xA9
+\xB4V[Bm\xEEvs\xF8
+b8\x89\xE3(\x96\xB3xc:\x8D\xCE\xEBz\xA4\xC7^Hy\xF9\x8D\xB4w_J}%\xAE\xF4ג\xE0K\x811\xCE4X\xF3aΈ;'\xF6\xAC\xF8\xF3bЌC7\xED\xF2\xA8H\xA5$\x93:5\xC9eT\x95SY>uY\xE6U\x99Yin\xB5\xD9\xE7W\x9Dcy\x96\xF5yhZ\xA1k\x89\xB65\xFA)ԸK\xE96\x9D\x8B\xAB]\xA9{\xA9\x{1B5697}\xEB_\xAD\x81\xC1\xF6Z\x98lb\xB3\x8B\xD16V\xFB\x98md\xB7\x93\xE1V\x96\xAB\xF7r]\xCDw3{̙oh\xBF\xA36\\x9A\xF0iū\xB7v\xFCrlɳ)׶|3\xE8ܛyw\xDE
+\xB5o\xD1\xC3I7\x9Cur\xD7\xCBa7\x97\xFD\x9Cv;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/loading.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/loading.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/loading.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,6 @@
+GIF89a\xB3zzz\xF7\xF7\x{1CA28A}\xBC\xBC\xBC\x93\x93\x93\xCD\xCD͂\x82\x82\xE6\xE6\xE6\xDE\xDEެ\xAC\xAC\xC5\xC5\xC5\xEE\xEE\x9B\xB4\xB4\xB4\xA3\xA3\xA3\xFF\xFF\xFF!\xFFNETSCAPE2.0!\xF9	,L\xF0\xC9I\xAB\xBD5\xC0\x81lC`i\x9Dd‘	\xE5S\xB0\x828
address@hidden \xBB\xE2q\xC3nO"!\xF9	,address@hidden@address@hidden'address@hidden :)\xF54TI\xB2h\x9E\x93_p@(address@hidden(
+w!\xF9	,Z\xF0\xC99\xA3+P\xB2d\x870\xAE\xE3\x80\xAA\xD0!\xA3\xD1Q \x9CO\x9E\xF0ѭwx
+\xAE\xC9\xC8\xF3X*\x9E\x80S2(address@hidden,\x8C)\x94\x88cHe\xAD\xC5W\xE9L\xB0)[3\xC3X\xEC#!\xF9	,N\xF0\xC9I\xAB\xBD/\x841hB\xA8\xC2gJA*R\x91\x82\xEF3\xB0\xFCᓆ\x816\xF91\xC8b\xB9\xECaV	J\xC1Р\xA8\xA2\x93``\xCD46\x9E\xD5q[	\x95h\xE7'r&\xDF\xF0!\xF9	,Z\xF0\xC9I\xAB\xBD8\xC1\xB5]	ǕEX\x90B8\xA5\x8F,\xE4\xF0\xA4\x90,\xC0 0<8C\x90\xA4\x80\xEC(\x84%Ba20t\xA2UaY$\x88\xDDcp\xB2\xD5B\xB4\xACx̒\xC9tZ4\xC8\xC3\xEFS"!\xF9,Z\xF0\xC9I\xAB\xBDX\xC8V0(O\xC1q\xC2A``)address@hidden -\x8AG51(\xC4\xC0\x874c\x9A\x82C!
+\x94\xC3ɠXhh\xD0Gǐ\xF0h\xACe\xC0N\xB3\xB6\xF0	
+\xACffϷD;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/treeview-loading.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/treeview-loading.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/treeview-loading.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,27 @@
+GIF89a\xF7\xDC\xFA\xFA\xFA\xF9\xF9\xF9\xF7\xF7\xF7\xF6\xF6\xF6\xF8\xF8\xF8\xF5\xF5\xF5\xEF\xEF\xEF\xF2\xF2\xF2\xF3\xF3\xF3\xF0\xF0\xF0\xE8\xE8\xE8\xF1\xF1\xF1\xD1\xD1\xD1\xF4\xF4\xF4\xD3\xD3\xD3\xEC\xEC\xEC\xEB\xEB\xEB\xD9\xD9\xD9\xEE\xEE\xEE\xDF\xDF\xDF\xE2\xE2\xE2\xCE\xCE\xCE\xD7\xD7\xD7...---\xD2\xD2\xD2\xDE\xDE\xDE\xDA\xDA\xDA\xE1\xE1\xE1~~~\xA2\xA2\xA2___vvvoooVVVTTT^^^\x8C\x8C\x8C///<<<PPP333\xE0\xE0\xE0"""   \xD6\xD6\xD6\xE7\xE7\xE7\xCA\xCA\xCA\xDC\xDC\xDC\xE9\xE9\xE9\xD5\xD5՜\x9C\x9C\x97\x97\x97uuu\x8A\x8A\x8A\xCB\xCB˃\x83\x83\xAE\xAE\xAE\xA4\xA4\xA4\xC9\xC9\xC9+++\xAD\xAD\xADKKK,,,666aaammmSSS444;;;\xD8\xD8\xD8\x88\x88\x88777\xB6\xB6\xB6\xB4\xB4\xB4\x98\x98\x98\xC5\xC5\xC5xxxYYY\xB8\xB8\xB8sss\xAA\xAA\xAA(((\xDB\xDB۬\xAC\xAC\x9F\x9F\x9F\xA9\xA9\xA9###\x94\x94\x94\xB1\xB1\xB1yyy\xCD\xCD͓\x93\x93\x87\x87\x87!!!hhhRRR\xE5\xE5幹\xB9fffOOO]]]\x9B\x9B\x9B\xB3\xB3\xB3ttt\xA7\xA7\xA7888***)))CCCJJJ\xEA\xEA\xEA111iii\xAB\xAB\xAB555\xB7\xB7\xB7NNN\xC8\xC8\xC8999\xBC\xBC\xBC\\\ZZZ\xED\xED\xEDeee\xD4\xD4\xD4wwwqqq\x92\x92\x92GGG\xE6\xE6\xE6\xC7\xC7Ǖ\x95\x95\x8E\x8E\x8Eggg\x8F\x8F\x8F\xB5\xB5\xB5@@@222\x81\x81\x81\x8B\x8B\x8B\xA8\xA8\xA8\x84\x84\x84===\x86\x86\x86EEE\x80\x80\x80kkkWWWppp\xC6\xC6\xC6%%%UUUHHH\xCF\xCFϝ\x9D\x9D\xBF\xBF\xBF\xE4\xE4䑑\x91[[[\xE3\xE3㻻\xBB\x9E\x9E\x9E\xDD\xDDݲ\xB2\xB2|||\xA3\xA3\xA3:::$$$&&&\xC0\xC0\xC0\x82\x82\x82\xD0\xD0\xD0zzz\xBA\xBA\xBA\xB0\xB0\xB0\x96\x96\x96\xCC\xCC\xCCjjj'''\x8D\x8D\x8D}}}>>>lll\xAF\xAF\xAF\x9A\x9A\x9A\xC4\xC4\xC4LLL\xA0\xA0\xA0\xBE\xBE\xBEBBB\x90\x90\x90bbbQQQ{{{MMMrrr\xC3\xC3\xC3AAA\xC1\xC1\xC1\xA5\xA5\xA5\xC2\xC2\xC2000\xFB\xFB\xFB\xFC\xFC\xFC\xFD\xFD\xFD\xFE\xFE\xFE\xFF\xFF\xFF\xFF\xFF\xFF!\xFFNETSCAPE2.0!\xF9	\xDC,\xF9\xB9	address@hidden@address@hidden
+[ܰ8\x85'\x9F4,i\xEAl\xA0\xD9TJr\xC2\xC28\\xCC\xF9 \xC0\xB6>Lz\xB0M\xC05\x83\x8E\x95ڳ-\x80\xB6&address@hidden
+\xB4\x9D\x92\x88\xED\x9AE?:)X`\xA0\xC16\x89\xD7h\xBB\xB6\xEDS3V\x88\xE26\xD0!\x92\xA7&address@hidden@8\xB8\xC6
+$B2\xCBR`\xA8\xB2J\x84\xAF\xD9j?,address@hidden&\xAF\xD0&\xFC\xE3×:\xFF\xB8\xB09\xF5\xEA\xD6Eb\xAF!\xF9	\xDC,\xFB\xB9	\xB8\xAD\xE0\xC0\x83\xB7i+\xB8
address@hidden'
+Ć\xAD\x93\x88%a\xB4\xAC \xA2!fCl\xD9\xF4j\xA5쐩)>Jp\x88\xC9m\x80\x806j%ؖ\xED\xC0\x877J  \x80\xED
+ \xB0\xAD\x82\x8AFkr\xB8\xB6펊4, address@hidden	\x9B"\x84\xAC\x90H
address@hidden:(\xB3\xBDpt΅m
+,\xEADm\xD4ȅhB\xB6ml<2\xB0-\xC3m3\xB9\xE3dKM&@e\xC3\xC6\xED4zZ\xB4h\x83\xA9\x87O\xB0p\xE5\x87\xB3<](address@hidden	\xDC,\xF6\xB9	address@hidden<f\xBBf\xB0\xA4E\x85\xFA\x81\x85\xD4\x93\xB7	\xC8V\xC1ё.P\x98\xE9\xF3\x9A\x80m\xE8\\xF3\xE1\x8A"\xB65Ķ0\xD6
+
+.\xF8\xF0F\xD6\xA5g\xC5Q2a\xC5Hcp\x8D\xE2\xC6VPy\x80\xA06D\x92\xEC\xE2 @ۨP\xD6H\xC4Ȗ \x82<q\x98\x9D\x99\xF6[q
+\xC8T|-\x8B
+Un
+bK\xE3ƃ\xA6
+^$\x88\x80`[\x80\x92dF\xB1E\x89\xA8M9\xB6P\xB0dBZTY\xB5\xC6\xC15'M^\xCB\xE3\x8B˰h#h\xD2\xE05l\x87#4\xA9-$\xF2\x8B\xAD
+6\x87\xFD#\xF4\x92!\xF9	\xDC,\xF5\xB9	H\xB0\xA0\xC1\x83ܶ!$\xA8-\x9B6\x85\xB7a address@hidden<address@hidden )nh\xD8v\xF1#\x93.%v\xB8˄B9<b\xB0\x8E\x84)v\xB01\x87\x8E\x8C7C\xF1%\xC2\x97\x85R	\xE9\x81u\xDB,#'\xB0=0\xC0q5;\x94b+\x93\xA1ƶkL\xB8Vl\x97\x92HDa[B\xA18\x82D"\xC670\xE0\xE0p\xF7\x80	\xAB\xEA \xC0\x86[\x841Zƈ\xC0"\x80\x94	w(Ag"$\x80\xB1I\x8C\xD98܉\x80ol\xDAb\xD5\xC6y\x9B\xB2~\x8D\xC0\xB5\xE0\xB1.\x98q\xBA\xF5\x82!\xF9	\xDC,\xEE\xB9	address@hidden	\xB8\x96\xAD\xA1\xB6
+.(\xA0\x80\xB6m\\xA3\xC8\xC9;\xB20Q!b\xC36nCL\x82\x89@\x84\xE8#c6\x96D,!\x81C\x81\xB6:B\xDC\xC0\xC8\xD2\xC1KX<(\xF0T\x94,\xB71\xC1\xC0c\xDB5l3\xD5\xC9mٚd𴭀\x81'ٴm\x98\x8D\x81>\xB0X\xD0\xC1\x91\xAF:
+Qi\x816\x8F\xA2,Y\xC35\xAC\x95(I\xD0X80D\xA7p'R\xA4O\x93\xA9\x8C$\xA3\xDA!\xB6\x93X\xCC0ゅ
+\xD8:2\xCC\x80\x80k
+Ȧ
+'E\x84\xB5e#\xBEM\xA1\xF3\xE7\xD0!\xF9	\xDC,\xF6\xB9	\xC8m\xDB6\x82	\\x98P\xE1B\x83
+>|address@hidden
+\xA0\xC0\xA3\xA3"address@hidden>\xC1\x93BQ\x96"\x91\x90P\x88\xD1 6">\xB8\xCCؖi\xC36#`d\xBB\xA6m;$
+l# g\xDBX\xAD\xCA\+\x80\xF3\xC2L"(\x90\xAD\x86*\xD9<n\xBB\xD0&\x8A\x8Fls\xE2\x82\xD66\xC0(A\xC2ʶQ\xB6)\xF8\x93\xC1\x816\xDAXTk\x9B\x97\x9E|\xF1\xE5\xA3\xC5m\xD8`p\xABԥ!\xE8\xBC\xD8VU`\xDCm\xB1Rbˋ\x8C]\xE3\xACc\x90\xBD\xB9	fp\xA0\x81\x873\xFC\xE2\xB5l\xB8#\xB2\xACzP:\xCB\xEA!\xF9\xDC,\xFA\xB9	H\xB0\xA0\xC1\x83۶\x98\xB0aB\x82\xB7iۖ\xAD"6lj\x9BH\xF1ZCfӆm\xA2[M\xD4\xE40\xA0\xC6\x85Цađ(
+A#c$\xB7\xB6QSe'g$\x84\xF8\xB4
+c\xD9<\xE0\x9A\xDB6H\x84\xA6\xC4\xC8\x80\x9BG)\x80L\xD8\xE0
+\xB6!\xC0M[\xB6Wvpl#0\xA0A\x80m\xB1\xC0\x900\x9B\x8C\xE60p`ۉeh\xE3\xD6 \xB64 address@hidden 'address@hidden/R\xA4x\xEBP Ɇ%#>.="D\xE9\xA9gߞ= ;
\ No newline at end of file

Added: branches/wf4ever/public/images/yui/treeview-sprite.gif (0 => 3265)


--- branches/wf4ever/public/images/yui/treeview-sprite.gif	                        (rev 0)
+++ branches/wf4ever/public/images/yui/treeview-sprite.gif	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,40 @@
+GIF89a\xB8"\xF4mmmUU\xFFj\x85\xA9\x80\x80\x80\xC1\xC1\xC1\xD2\xE6\xFF\xD5\xE7\xFF\xD8\xE8\xFF\xDA\xE9\xFF\xDD\xEC\xFF\xDF\xED\xFF\xE2\xE2\xE4\xE6\xE6\xE7\xE9\xEA\xEA\xEB\xEC\xEC\xED\xEF\xEF\xE0\xED\xFF\xE3\xEE\xFF\xE4\xF0\xFF\xE6\xF1\xFF\xF0\xF1\xF1\xF2\xF2\xF4\xF5\xF5\xF5\xF6\xF6\xF7\xF7\xF9\xF9\xFA\xFA\xFA\xFB\xFB\xFB\xFF\xFF\xFF!\xF9,\xB8"\xFE`'\x8Edi\x92ĩ\xAEl\x97\xB6E,\xCFqYlx\xAE\xE3yg\xBB\x8DF\xC3\xF3e\x8EGL\x83jz\xA3sJ\xC5\.PQ\xE12\xBDz\xBD\xD9N\xC1b	\x98\xCF\xE6K\xA50p\x89+4n\x83\xD8\xEF\x8F\xCA\xE3.<\x81\x82\x83}\x86\x87\x88}
+\x8C\x8D\x8E\x8C}\x92\x93\x94\x92ab4\x99-\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF
+H\xB0\xA0\xC1\x83*\Ȱ\xA1Ç#J\x9CH\xB1\xA2ŋ3j\xDCȱ\xA3Ǐ C\x8AI\xB2\xA4ɓ(\xFES\xAA\ɲ\xA5˗0cʜI\xB3\xA6͉/`\xE5\1\xA0\xA7ϟ=Kaddress@hidden=\xCA4\xA9\x88\xA1\x88F\xA505\xAA\xD3\xA2j\xD5:address@hidden|\xB3\xB3\xE7ϠC\x8BM\xBA\xB4\xE9ӨS\xAB^ͺ\xB5\xEBװc˞M\xBB\xB6\xED۸s\xEB\xDEͻ\xB7\xEF\xDF\xC0\x83N\xBC\xB8\xF1\xE3ȓ+_μ\xB9\xF3\xE7УK\x9FN\xBD\xBA\xF5\xEBسk\xDFν\xBB\xF7\xEF\xE0m\xEEt5\xBEUyV\xE7W\xA5W\x95\xA2\xBD\xF7\xE1\xE3˟O\xBF\xBE\xFD\xFB\xF8\xF3\xEB\xDFϿ\xBF\xFF\xFF(\xE0\x80h\xE0\x81&\xFE\xA8\xE0\x826\xE8\xE0\x83F(\xE1\x84Vh\xE1\x85f\xA8\xE1\x86v\xE8\xE1\x87 \x86(\xE2\x88$B\xB8^*'j\x91	
+6D.
address@hidden
+DRə\x97\xAC\xC8b\x89l\xB6\xE9\xE6\x9Bp\xC6)\xE7\x9Ct\xD6i\xE7\x9Dx\xE6\xA9\xE7\x9E|\xF6\xE9矀*蠄j衈&\xAA袌6\xEA裐F*餔Vj\xE9\xA5Ϥx\x8A\xA6\x9Bq\xB6SM}
address@hidden|-\x98\x81\xF5U\xD8c\xC8&address@hidden@\xBC\xE8H,q;Va1\x90_\a>Xp\xC1\x91d\xF4As\x8D\\x80{\xD8Q%z\xE0q\xE5 0R\x98]\x86\x99\x8F\x98y&%i\\x83z\xB2\\x8AФ=\x8AѢ address@hidden'\x80m\x82\xD8%\x90\x8DB\xD0h\xC7b\xF6k\x8Bжj\xA7\xAD\x93ܯ\xBCm7\xDD\xE4\xE1m\x9E\xDE\xE8\xF1
+t܀\xCFx\xDD~\xB3W8\x8A\x87\xA3r\xF7\xE0y3\xBE\xB7\xE3}C\xFE\xB7\xE0\x94.\xB9\xE1\x97#\x9E\xB9\xE2\x89oڹ)\xFE\x8BW޸菓\xB9\xE9\x93[\x8E:\xE6\xABk\xDE:\xE7\x9B{;\xE8\x9F]{ѷ\x9D{һ/\xDD{ӿ?|\xD4\xC3O]|\xD5\xC7_\x9D|\xD6\xCBo\xDD|address@hidden
+\x90{\xF4^\xC17A\xF1U\x90|4_ѷA\xF5u\x90}t_\xE17B\xF9\x95\x90~'\xB4_
+\xF1\xB7B\xFD\xB5\x90/\xF4_\xA8\xBA\x8EΆ\xA5\xC3\xE1\xE9t\x98\xBA\xFA0\x87?\xDCa{\xC4"
+шD<\xA2\x93\xC8D\xD6\xF1ЉC\x84"\xA5\xB8D*6\xD1uO\xC4b\xB58E.VыW\x84\xFE\xDD8F\x96сYc\xB8F\x9E1tm\x84`%8G
+\xD6тw\xC4`5\xB8G\xF6уa E8H҄\x87Da"U\xB8H6҅\x8F\x84a$e8InQ\x8D\x97dc&\xDDXI2vҌ\x9FD\xE3&\xE58J:\x96Ҏ\xA7\xC4c*\xF5\xB8J>\xB6ҏ\xAFd,9KB\xD6Ґ\xB7Dd.\xB9KF\xF6ґ\xBF\x84d0%9LJӒ]\xC4d25\xB9LNӓ\xCFe4E\xD9LRV\x93|/\xD0TN\x84\xF9e\x93\x84ܤ&/\xAF\x89\xCA`\xEB'B\xA9D\xA5N
+\x94\xCAUE\xA9\x8A;\xE3\x95zz\xC5*\x8B\x89\x95>%\x80Y\xADW\xB5\xEA\x95]\xDA\xF0\x96$ -ȋAڗ\xC08t0\x84\x89ha$
address@hiddenJ\x99\xF9Ee\x96\x94\xA4#u&9U\xB9RV\xB6ԕ/\x85eLe9SZ\xD6Ԗ7\xC5eNu\xB9\xD3q\x9ET\xA5?\xB5fPM\xD9S_\x98GfR\x89\xB9Tc6\x99C-gTY:U\x97V\xA6W\x95iVi\xBAU\x9Bv\xA7_\xD5iXy:V\x9F\xA6T\xA8g%jY\x8D\xBAV\xA4\xB6U\xA9oej\\x9D:W\xA8\xA6U\xAAw\xA5j^\xAD\xBAW\xAC\xF6U\xAB\xE5j`\xBD:X\xB0V\xAC\x87%kb\xCDj\xC5Ƃѱ&],[%\xEBV\xCA\xC2ղr\xC5,address@hidden,4\x9F:ں\x96V\xB4\xD2$mjM\xBBZԊӵp\x9CflU\xFBZТնj\x85\xEDk\xFB\xD8\xDEF\xB7x\xAE^\x85\xCBW\xE2\xFAո\x80E\xAE`\x95KX\xE6ֹ\x88\x85\xAEb\xFE\xA5\xCBX\xDF~ֺ\xA1\xA5\xEEd\xB5[Y\xEE^ֻ\x99\xEFf\xC5\xDBY\xEC\xDEּ\xB9%\xEFi\xD5\xDBZ\xF6\xF2\xF6\xB7\xE8
+n|\x87;\xDF\xE2\xD6\xF7\xB8\xF7Mn~\x97\xBB\xDF\xE6\xF6\xF7\xB9\xFF\x8Dn\x80\xA7;\xE0\xEA\xC2\xF7\xC0\xD7Epv\xBC]w\xD7\xC1߅px%<^
+\x97W\xC1\xE7\xC5pz-\xBC^\xB7\xD7\xC3\xEFM\xB0\x88\xACa\xF9\x96\x98\xBE'\xB6o\x8A\xF1\xBBb\xFD\xB6\x98\xBF/\xF6o\x8C<cט\xC076\xF0\x883\xBC\xE3
+\xE7\xB8\xC1?~p\x90#<\xE4	\xB9\xC2G\xBEp\x8FM\xBCd7Y\xC5Ofq\x94]<eWY\xC6W\xA6q\x96m\xBCewY\xC7$\xFE2\x90\xC5,d2\xD9\xCCFF3\x92լ\xE40\xBB\x99\xC7o\xF61\x9B;\x9Cd:\xCF\xF9\xC3u\xC6\xF3\x9DCg&\xF7\xD9\xC9\x86r\xA0\xA5<h*\xDAʇ\xC6r\xA2\xB5\xFE\xBCh.7\xDAˏ3\x9C'-\xE7H\x8F\xD9\xD2e\xC6\xF4\x995\x9DfN\xAF\xD9\xD3m\xA6\xB4\xF9\xBEi\xC2p
+\x8C\xD4ڴ\xB3\x8D6[\xD6\xF2YԀ\xB6\x9D{f\x9D\xE7WWւ\xC65\xA1umh^#\xDA׊6\xA3\x85\xEDhbC\xDAؒ\xBE\xB5\xB2\xFD\x8C\xECK7;\xD3\xCF\xDEt\xB4;=\xEDOW;\xD4ˎu\xB6s\xBD\xED]w\xBB\xD7\xDF\xFEu\xB8\x83=\xEEa\x97\xBB\xD8\xE7>v\xBA\x93\xCD\xECu;\xDB\xDDІ\xB7\xB4\xE5Mmz[\xDB\xDE\xD8n\xB7\xBE\xB5\xBDon\xF7\xDB\xDB\xFFw\xC0\xC5=pr\xDC\xDCGw\xC2սpv\xF3\xFB\xE1\xFE\x868\xC0%.p\x8A\xDC\xE2\xC78\xC25\xAEp\x8E3\xDC\xE3\x8F\xB8\xC8'>\xF2\x8A\x97\xFC\xE2'\xCFx\xCA7\xBE\xF2\x8E\xB7\xFC\xE3/9\xC9gnr\x9A\xA3\xDC\xE6*\xC79\xCBu\xEEr\x9E\xC3\xDC\xE72\xAF\xB9\xD0o>\xFE\xF4\x9C}\xE7G\xEFy\xD2\xBE\xF4\xA0\xFD\xE9F\x87:ҥ\xAEt\xAA3\xDD\xEAN\x8F\xBA֧\xBE\xF5\xAAw\xFD\xEA_\xCF:\xD7\xC7\xEEu\xB2\x83\xDD\xECb/\xBB\xDAϾ\xF6\xB4\xB3\xFD\xEDn\x8F\xFB\xBB>w\x90\xD7=\xE6wzޛ\xBEw\xAC\xF7=\xECG{\xE0\xDB>x\xB8^\xEE\xF1\xA6{\xE2\xED\xBEx\xBC7^\xEF\x8F\xE7{\xE4\xFD>y\xC0W^\xF0\x97'|\xE6
+\xBFy\xC4\xCF[\xF1\x9Fg|\xE8?zȗ^\xF2\xA7\xA7|\xEA-\xBFz̷^\xF3\xAF\xE7|\xEC=_o\xD0\xD7^\xF4\xB7'}\xEEM\xBF{\xD4\xF7^\xF5\xBFg}\xF0]?|\xD8_\xF6ǧ\xFD\xBDm\xBF|\xDC7_\xF7\xCF\xE7}\xF4}?}\xE0W_\xF8\xD7'~\xF6\x8D\xBF}\xE4w_\xF9\xF9v~\xF8\xA1?~闟\xFA\xE7\xB7~\xFA\xB1\xBF~\x{DDDF}\xFB\xEF\xF7~\xFC\xC1\xAF\xEAk\xD7\xDF\xF7g\xFE\xFC\xCF\xFF\xFE\xCE\xF7\xF6\xFF\x97|\xF8}\xB8\xFEw\x80\x88\x80\xA8\x80Ȁ\x98\x80\xB8\x80؀\xF8\x80x\x81\x88\x81\x98\x81\xB8\x81\x98\xE2\x82\xE4'\x82\xE6G\x82\xE8g\x82ꇂ짂\xEEǂ\xF0\xE7\x82\xF2\x83\xF4\xA7g\xA0&\x83h\x83\x88\x83\xDACj(dj\xEEÃ*\xE4\x83)address@hidden
+\xF30\xE3"\x83f`#\xE3<U\x801^\xD0#S$3!%$\xB3pp2K\xA22R+\xF3/\x93%h\xB8%Q\x90_\x82\x8B\x803\x8C\xE0:\xB33\x96`H\xE8j\xAD\xA6[
+ԇ\xEE\x88 &\x88\xB5F\x88{f\x885H\x84\xF8\xA7\x88\xFA\xA7\x83\xE8\x88\x89Ȉ!H\x89#h\x89%\x88\x89'\xA8\x89Chk\x8Dȉ+\x8A-(\x8A/H\x8A1\xFEh\x8A3艕\xA8\x8A\x97Ȋ\x99芛\x8B\x9D\x88\x88\xF6\x87\x8A7h\x8B9\x88\x8B(\x89\xBC\xA8\x8B\x8F苑\x8C\x93(\x8B\xA1H\x8C\xA3h\x8C\xA5\x88\x8C\xA7\xA8\x8C\xA9H\x8B\x8BȌ\xB7\x8D\xB9(\x8D\xBB(\x8C\xBDH\x8D\xBF\x88\x8D\xC1\xA8\x8D\xC3茟ȍ\xD7荫(\x8E\xADH\x8E\xAFh\x8E\xB1\x88\x8E\xB3\xB8[\xEAX\x8C\xEDx\x8C\xF1\xB8\x8C\xF3،\xECx\x8F\x88\x8Fo\xA4\x8F\xA1T\x8F\xD1\xE8\x8F\xD3\x90\xD5\x8E\xD6X\x90y\x90\x99\x8D	\xB9\x8Dٍ\xFC([I[
+\x8Eɇ\xE9\x87\xFB\x98\x8F\x99\x91ُ\x889\x88!Y\x88#y\x88%\x99\x88y\x92\xB5\x98\x92\xE9\x91-	\x91/)\x91*\xF9\x8Ci\x905ɒ	\x939)\x931i\x91=\x89\x91.\xB9\x93>)address@hidden<I\x94 \xF9\x93Mɔ\xFE"\xE9\x94Q	\x95$)\x95UI\x95&i\x95Y\x89\x95(address@hidden"address@hidden,\x80\xD1P\x87q͒\x871тQӲu RC\x89\x94Kɛ\xBB\xA9\x94\xBFٛ\xC0Y\x94\xC2Y\x9C\xC1y\x9Cĉ\x9CO\xE9\x9B\xC9ٜ\xCB9\x9C\xCFi\x9C\xCE9\x95\xCC\x9D\xCAI\x9DЉ\x9D\xD2i\x9D\xD3y\x95թ\x9D\xD7\xE9\x9D\xD9)\x9E\xDB	\x9Eݹ\x95\xDFI\x9E቞\xE3ɞ婞\xE7I\x96oI\x93v)\x8F\xF5I\x8F\xF7i\xFE\x8F\xF3\x96\xF9\xF9\x8F\xFD\x90\xFF9\x90q\x99\x9E\xF2)\x97\xF4\xB9\x9Ff\xA9\xA0hɠj\xE9\xA0l	\xA1n\x89\xA0\xFC)\xA1ui\xA1w\x89\xA1\xF6\xA9\xA1\xF8ɡ\xFAI\xA1
+\xA2
+*\xA2J\xA2j\xA2J\xA0\xAA\xA2s\xE9\xA1\xFE\xE9\xA2
+\xA3\xA0
+I\xA3i\xA3)\xA35\xAA\xA37ʣ9\x8A\xA2
+\xA4*\xA4J\xA4j\xA4ʢ	\x8A\xA4/ʤ1\xEA\xA43\xEA\xA3)\xA56\x89\xA3S
+\xA5;\x8A\xA5=\xAA\xA5?\xAA\xA4ʥW\xEA\xA5!*\xA6#J\xA6%j\xA6'\x8A\xA6)ڞ+ʦ-
+\xA6UJ\xA58\xA9\xA6AJ\xA7Cj\xA7E\x8A\xA7GZI\xA8N\xF9\x83}Y\xDDt\xA7n\xBA\xA4z\x9A\xA4\x85\xFA\xA5\x87ڤ\x8B\xFA\xA4\x8D\xA5p:\xA7\x89:\xA6\x93Z\xA6\x95z\xA6\x97\x9A\xA6\x99\xBA\xA6\xEFY\xA0\xF1\x{198F6A5}\xA1\xFE\xBA\xA5\xA3ڥ\x9BZ\xA7\xA7J\xA8\x9Dڦ\xAB
+\xAA\xA9\x9A\xA7\xAF\xBA\xA7\xB1\x8A\xA8\xADj\xA8\xB3ʨ\xB7꨹
+\xA9\xA5\xA6\xBB*\xAA\xBFJ\xAA\xC1j\xAA\xB5\xAA\xA8\xC3\xEA\xAB\xC5J\xA9\xC9j\xA9ˊ\xA9ͪ\xA9\xCFʩ\xEA\xAAъ\xAAժ\xAA\xD3j\xAB\xD7
+\xAB\xDB*\xAB\xDDJ\xAB\xD9j\xACߊ\xAB㪫\xE5ʫ\xC7\xA7\x91:\xA0\xE9*\xA9\xE7
+\xAC\xEF*\xAC\xF1J\xAC᪬\xF5ʬ\xF7\xEA\xAC\xF9
+\xAD\xFB*\xAD\x9F\xAA\xAD\xFDj\xAD\x8B\xAD\xFF*\xAE˭\xEB\xAD	\xAEk\xAF
+\x8B\xAF\xAB\xAF˯\xEB\xAF\xDCy\xB1扱𩱞ʱ\xAC\xBA\xB0\xE4
+\xB2\xE6*\xB2\xE8:\xAF\xC8J\xB2\xF0\x8A\xB2\xF2\xAA\xB2\xF4Z\xB1\xEB\xB2\xEB\xB1\xD4ʲ'\xB3k\xB3
+\x8B\xB3+\xB3\xAB\xB3!\xEB\xB3#\xB4%K\xB3\xEAګEۮ
+٧\xA5\xF6\xA7\xFE\xF0\x93\x97.\xB4\x97\xC7H\x98&P\xB5V{\xB5U\xDBX\xBB\xB5\xA0\xB5\{\xB5^˵\xC0]\xEB_;\xB6Yk\xB6[\x8B\xB6i\xC0\xB6oK\xB6V\xEB\xB5h\xB7m\xABV;\xB6r\xB6j[\xB5{˷n\xFB\xB5s۷\x82\xDB3>address@hidden@ 2w\x80w\xBAPr\x86\xA4j\xE8\x81p\xAD\xAB%3\x87\x860%v(&t	>\x80\x87<\xA3\x87\x87K\xF0\xBB\xC0\xBC\xBF[\xA0\x848$P\xBCM\xC8;\xCA1J1\xCD+`1S\xF0\xBB0\xBD\xA31\xBF{\xDA\x9C\xFB\xBF\xBA\xE1\xBF{\xE9kp\xBE\xE8\xEF%\xBE\x82s\xBC1\xBE\x885\xE3\xE1{\xBB\x8C\xBE\xBA;	ڻ\xBD\xC2[\xC0%Q\xC0\x9C\xBCƻ\xE1\xAB
+\xB3
+\xCC\xC0`#\xBF{\xE1\xCB\xC0\x8B\xC1؛\xBD\xC9\xEB\xEC\xC1\xC9;\xBE\xC1\xFB\xB9d\xE0\xBEv\xBC\xA5k\xC2\xAC2\xF4;\xBF\xBF\xF6;\xBF\xC9k\xBF\x9B\xBF\xF0\xFD\xEB\xBF\xC00\xC0,\xBC\xDB;
\ No newline at end of file

Added: branches/wf4ever/public/_javascript_s/yui/connection.js (0 => 3265)


--- branches/wf4ever/public/_javascript_s/yui/connection.js	                        (rev 0)
+++ branches/wf4ever/public/_javascript_s/yui/connection.js	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,1567 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+/**
+ * The Connection Manager provides a simplified interface to the XMLHttpRequest
+ * object.  It handles cross-browser instantiantion of XMLHttpRequest, negotiates the
+ * interactive states and server response, returning the results to a pre-defined
+ * callback you create.
+ *
+ * @namespace YAHOO.util
+ * @module connection
+ * @requires yahoo
+ * @requires event
+ */
+
+/**
+ * The Connection Manager singleton provides methods for creating and managing
+ * asynchronous transactions.
+ *
+ * @class YAHOO.util.Connect
+ */
+
+YAHOO.util.Connect =
+{
+  /**
+   * @description Array of MSFT ActiveX ids for XMLHttpRequest.
+   * @property _msxml_progid
+   * @private
+   * @static
+   * @type array
+   */
+    _msxml_progid:[
+        'Microsoft.XMLHTTP',
+        'MSXML2.XMLHTTP.3.0',
+        'MSXML2.XMLHTTP'
+        ],
+
+  /**
+   * @description Object literal of HTTP header(s)
+   * @property _http_header
+   * @private
+   * @static
+   * @type object
+   */
+    _http_headers:{},
+
+  /**
+   * @description Determines if HTTP headers are set.
+   * @property _has_http_headers
+   * @private
+   * @static
+   * @type boolean
+   */
+    _has_http_headers:false,
+
+ /**
+  * @description Determines if a default header of
+  * Content-Type of 'application/x-www-form-urlencoded'
+  * will be added to any client HTTP headers sent for POST
+  * transactions.
+  * @property _use_default_post_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _use_default_post_header:true,
+
+ /**
+  * @description The default header used for POST transactions.
+  * @property _default_post_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',
+
+ /**
+  * @description The default header used for transactions involving the
+  * use of HTML forms.
+  * @property _default_form_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_form_header:'application/x-www-form-urlencoded',
+
+ /**
+  * @description Determines if a default header of
+  * 'X-Requested-With: XMLHttpRequest'
+  * will be added to each transaction.
+  * @property _use_default_xhr_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _use_default_xhr_header:true,
+
+ /**
+  * @description The default header value for the label
+  * "X-Requested-With".  This is sent with each
+  * transaction, by default, to identify the
+  * request as being made by YUI Connection Manager.
+  * @property _default_xhr_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_xhr_header:'XMLHttpRequest',
+
+ /**
+  * @description Determines if custom, default headers
+  * are set for each transaction.
+  * @property _has_default_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _has_default_headers:true,
+
+ /**
+   * @description Property modified by setForm() to determine if the data
+   * should be submitted as an HTML form.
+   * @property _isFormSubmit
+   * @private
+   * @static
+   * @type boolean
+   */
+	_isFormSubmit:false,
+
+ /**
+  * @description Determines if custom, default headers
+  * are set for each transaction.
+  * @property _has_default_header
+  * @private
+  * @static
+  * @type boolean
+  */
+    _default_headers:{},
+
+ /**
+  * @description Collection of polling references to the polling mechanism in handleReadyState.
+  * @property _poll
+  * @private
+  * @static
+  * @type object
+  */
+    _poll:{},
+
+ /**
+  * @description Queue of timeout values for each transaction callback with a defined timeout value.
+  * @property _timeOut
+  * @private
+  * @static
+  * @type object
+  */
+    _timeOut:{},
+
+  /**
+   * @description The polling frequency, in milliseconds, for HandleReadyState.
+   * when attempting to determine a transaction's XHR readyState.
+   * The default is 50 milliseconds.
+   * @property _polling_interval
+   * @private
+   * @static
+   * @type int
+   */
+     _polling_interval:50,
+
+  /**
+   * @description A transaction counter that increments the transaction id for each transaction.
+   * @property _transaction_id
+   * @private
+   * @static
+   * @type int
+   */
+     _transaction_id:0,
+
+  /**
+   * @description Custom event that fires at the start of a transaction
+   * @property startEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+    startEvent: new YAHOO.util.CustomEvent('start'),
+
+  /**
+   * @description Custom event that fires when a transaction response has completed.
+   * @property completeEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+    completeEvent: new YAHOO.util.CustomEvent('complete'),
+
+  /**
+   * @description Custom event that fires when handleTransactionResponse() determines a
+   * response in the HTTP 2xx range.
+   * @property successEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+    successEvent: new YAHOO.util.CustomEvent('success'),
+
+  /**
+   * @description Custom event that fires when handleTransactionResponse() determines a
+   * response in the HTTP 4xx/5xx range.
+   * @property failureEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+    failureEvent: new YAHOO.util.CustomEvent('failure'),
+
+  /**
+   * @description Custom event that fires when a transaction is successfully aborted.
+   * @property abortEvent
+   * @private
+   * @static
+   * @type CustomEvent
+   */
+    abortEvent: new YAHOO.util.CustomEvent('abort'),
+
+  /**
+   * @description A reference table that maps callback custom events members to its specific
+   * event name.
+   * @property _customEvents
+   * @private
+   * @static
+   * @type object
+   */
+    _customEvents:
+    {
+        onStart:['startEvent', 'start'],
+        onComplete:['completeEvent', 'complete'],
+        onSuccess:['successEvent', 'success'],
+        onFailure:['failureEvent', 'failure'],
+        onUpload:['uploadEvent', 'upload'],
+        onAbort:['abortEvent', 'abort']
+    },
+
+  /**
+   * @description Member to add an ActiveX id to the existing xml_progid array.
+   * In the event(unlikely) a new ActiveX id is introduced, it can be added
+   * without internal code modifications.
+   * @method setProgId
+   * @public
+   * @static
+   * @param {string} id The ActiveX id to be added to initialize the XHR object.
+   * @return void
+   */
+    setProgId:function(id)
+    {
+        this._msxml_progid.unshift(id);
+    },
+
+  /**
+   * @description Member to override the default POST header.
+   * @method setDefaultPostHeader
+   * @public
+   * @static
+   * @param {boolean} b Set and use default header - true or false .
+   * @return void
+   */
+    setDefaultPostHeader:function(b)
+    {
+        if(typeof b == 'string'){
+            this._default_post_header = b;
+			this._use_default_post_header = true;
+
+        }
+        else if(typeof b == 'boolean'){
+            this._use_default_post_header = b;
+        }
+    },
+
+  /**
+   * @description Member to override the default transaction header..
+   * @method setDefaultXhrHeader
+   * @public
+   * @static
+   * @param {boolean} b Set and use default header - true or false .
+   * @return void
+   */
+    setDefaultXhrHeader:function(b)
+    {
+        if(typeof b == 'string'){
+            this._default_xhr_header = b;
+        }
+        else{
+            this._use_default_xhr_header = b;
+        }
+    },
+
+  /**
+   * @description Member to modify the default polling interval.
+   * @method setPollingInterval
+   * @public
+   * @static
+   * @param {int} i The polling interval in milliseconds.
+   * @return void
+   */
+    setPollingInterval:function(i)
+    {
+        if(typeof i == 'number' && isFinite(i)){
+            this._polling_interval = i;
+        }
+    },
+
+  /**
+   * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
+   * the XMLHttpRequest instance and the transaction id.
+   * @method createXhrObject
+   * @private
+   * @static
+   * @param {int} transactionId Property containing the transaction id for this transaction.
+   * @return object
+   */
+    createXhrObject:function(transactionId)
+    {
+        var obj,http,i;
+        try
+        {
+            // Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
+            http = new XMLHttpRequest();
+            //  Object literal with http and tId properties
+            obj = { conn:http, tId:transactionId, xhr: true };
+        }
+        catch(e)
+        {
+            for(i=0; i<this._msxml_progid.length; ++i){
+                try
+                {
+                    // Instantiates XMLHttpRequest for IE and assign to http
+                    http = new ActiveXObject(this._msxml_progid[i]);
+                    //  Object literal with conn and tId properties
+                    obj = { conn:http, tId:transactionId, xhr: true };
+                    break;
+                }
+                catch(e1){}
+            }
+        }
+        finally
+        {
+            return obj;
+        }
+    },
+
+  /**
+   * @description This method is called by asyncRequest to create a
+   * valid connection object for the transaction.  It also passes a
+   * transaction id and increments the transaction id counter.
+   * @method getConnectionObject
+   * @private
+   * @static
+   * @return {object}
+   */
+    getConnectionObject:function(t)
+    {
+        var o, tId = this._transaction_id;
+
+        try
+        {
+            if(!t){
+                o = this.createXhrObject(tId);
+            }
+            else{
+                o = {tId:tId};
+                if(t==='xdr'){
+                    o.conn = this._transport;
+                    o.xdr = true;
+                }
+                else if(t==='upload'){
+                    o.upload = true;
+                }
+            }
+
+            if(o){
+                this._transaction_id++;
+            }
+        }
+        catch(e){}
+        return o;
+    },
+
+  /**
+   * @description Method for initiating an asynchronous request via the XHR object.
+   * @method asyncRequest
+   * @public
+   * @static
+   * @param {string} method HTTP transaction method
+   * @param {string} uri Fully qualified path of resource
+   * @param {callback} callback User-defined callback function or object
+   * @param {string} postData POST body
+   * @return {object} Returns the connection object
+   */
+    asyncRequest:function(method, uri, callback, postData)
+    {
+        var args = callback&&callback.argument?callback.argument:null,
+            YCM = this,
+            o, t;
+
+        if(this._isFileUpload){
+            t = 'upload';
+        }
+        else if(callback && callback.xdr){
+            t = 'xdr';
+        }
+
+        o = this.getConnectionObject(t);
+        if(!o){
+            return null;
+        }
+        else{
+
+            // Intialize any transaction-specific custom events, if provided.
+            if(callback && callback.customevents){
+                this.initCustomEvents(o, callback);
+            }
+
+            if(this._isFormSubmit){
+                if(this._isFileUpload){
+                    window.setTimeout(function(){YCM.uploadFile(o, callback, uri, postData);}, 10);
+                    return o;
+                }
+
+                // If the specified HTTP method is GET, setForm() will return an
+                // encoded string that is concatenated to the uri to
+                // create a querystring.
+                if(method.toUpperCase() == 'GET'){
+                    if(this._sFormData.length !== 0){
+                        // If the URI already contains a querystring, append an ampersand
+                        // and then concatenate _sFormData to the URI.
+                        uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
+                    }
+                }
+                else if(method.toUpperCase() == 'POST'){
+                    // If POST data exist in addition to the HTML form data,
+                    // it will be concatenated to the form data.
+                    postData = postData?this._sFormData + "&" + postData:this._sFormData;
+                }
+            }
+
+            if(method.toUpperCase() == 'GET' && (callback && callback.cache === false)){
+                // If callback.cache is defined and set to false, a
+                // timestamp value will be added to the querystring.
+                uri += ((uri.indexOf('?') == -1)?'?':'&') + "rnd=" + new Date().valueOf().toString();
+            }
+
+            // Each transaction will automatically include a custom header of
+            // "X-Requested-With: XMLHttpRequest" to identify the request as
+            // having originated from Connection Manager.
+            if(this._use_default_xhr_header){
+                if(!this._default_headers['X-Requested-With']){
+                    this.initHeader('X-Requested-With', this._default_xhr_header, true);
+                }
+            }
+
+            //If the transaction method is POST and the POST header value is set to true
+            //or a custom value, initalize the Content-Type header to this value.
+            if((method.toUpperCase() === 'POST' && this._use_default_post_header) && this._isFormSubmit === false){
+                this.initHeader('Content-Type', this._default_post_header);
+            }
+
+            if(o.xdr){
+                this.xdr(o, method, uri, callback, postData);
+                return o;
+            }
+
+            o.conn.open(method, uri, true);
+            //Initialize all default and custom HTTP headers,
+            if(this._has_default_headers || this._has_http_headers){
+                this.setHeader(o);
+            }
+
+            this.handleReadyState(o, callback);
+            o.conn.send(postData || '');
+
+            // Reset the HTML form data and state properties as
+            // soon as the data are submitted.
+            if(this._isFormSubmit === true){
+                this.resetFormState();
+            }
+
+            // Fire global custom event -- startEvent
+            this.startEvent.fire(o, args);
+
+            if(o.startEvent){
+                // Fire transaction custom event -- startEvent
+                o.startEvent.fire(o, args);
+            }
+
+            return o;
+        }
+    },
+
+  /**
+   * @description This method creates and subscribes custom events,
+   * specific to each transaction
+   * @method initCustomEvents
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {callback} callback The user-defined callback object
+   * @return {void}
+   */
+    initCustomEvents:function(o, callback)
+    {
+        var prop;
+        // Enumerate through callback.customevents members and bind/subscribe
+        // events that match in the _customEvents table.
+        for(prop in callback.customevents){
+            if(this._customEvents[prop][0]){
+                // Create the custom event
+                o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
+
+                // Subscribe the custom event
+                o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
+            }
+        }
+    },
+
+  /**
+   * @description This method serves as a timer that polls the XHR object's readyState
+   * property during a transaction, instead of binding a callback to the
+   * onreadystatechange event.  Upon readyState 4, handleTransactionResponse
+   * will process the response, and the timer will be cleared.
+   * @method handleReadyState
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {callback} callback The user-defined callback object
+   * @return {void}
+   */
+
+    handleReadyState:function(o, callback)
+
+    {
+        var oConn = this,
+            args = (callback && callback.argument)?callback.argument:null;
+
+        if(callback && callback.timeout){
+            this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
+        }
+
+        this._poll[o.tId] = window.setInterval(
+            function(){
+                if(o.conn && o.conn.readyState === 4){
+
+                    // Clear the polling interval for the transaction
+                    // and remove the reference from _poll.
+                    window.clearInterval(oConn._poll[o.tId]);
+                    delete oConn._poll[o.tId];
+
+                    if(callback && callback.timeout){
+                        window.clearTimeout(oConn._timeOut[o.tId]);
+                        delete oConn._timeOut[o.tId];
+                    }
+
+                    // Fire global custom event -- completeEvent
+                    oConn.completeEvent.fire(o, args);
+
+                    if(o.completeEvent){
+                        // Fire transaction custom event -- completeEvent
+                        o.completeEvent.fire(o, args);
+                    }
+
+                    oConn.handleTransactionResponse(o, callback);
+                }
+            }
+        ,this._polling_interval);
+    },
+
+  /**
+   * @description This method attempts to interpret the server response and
+   * determine whether the transaction was successful, or if an error or
+   * exception was encountered.
+   * @method handleTransactionResponse
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {object} callback The user-defined callback object
+   * @param {boolean} isAbort Determines if the transaction was terminated via abort().
+   * @return {void}
+   */
+    handleTransactionResponse:function(o, callback, isAbort)
+    {
+        var httpStatus, responseObject,
+            args = (callback && callback.argument)?callback.argument:null,
+            xdrS = (o.r && o.r.statusText === 'xdr:success')?true:false,
+            xdrF = (o.r && o.r.statusText === 'xdr:failure')?true:false,
+            xdrA = isAbort;
+
+        try
+        {
+            if((o.conn.status !== undefined && o.conn.status !== 0) || xdrS){
+                // XDR requests will not have HTTP status defined. The
+                // statusText property will define the response status
+                // set by the Flash transport.
+                httpStatus = o.conn.status;
+            }
+            else if(xdrF && !xdrA){
+                // Set XDR transaction failure to a status of 0, which
+                // resolves as an HTTP failure, instead of an exception.
+                httpStatus = 0;
+            }
+            else{
+                httpStatus = 13030;
+            }
+        }
+        catch(e){
+
+             // 13030 is a custom code to indicate the condition -- in Mozilla/FF --
+             // when the XHR object's status and statusText properties are
+             // unavailable, and a query attempt throws an exception.
+            httpStatus = 13030;
+        }
+
+        if((httpStatus >= 200 && httpStatus < 300) || httpStatus === 1223 || xdrS){
+            responseObject = o.xdr ? o.r : this.createResponseObject(o, args);
+            if(callback && callback.success){
+                if(!callback.scope){
+                    callback.success(responseObject);
+                }
+                else{
+                    // If a scope property is defined, the callback will be fired from
+                    // the context of the object.
+                    callback.success.apply(callback.scope, [responseObject]);
+                }
+            }
+
+            // Fire global custom event -- successEvent
+            this.successEvent.fire(responseObject);
+
+            if(o.successEvent){
+                // Fire transaction custom event -- successEvent
+                o.successEvent.fire(responseObject);
+            }
+        }
+        else{
+            switch(httpStatus){
+                // The following cases are wininet.dll error codes that may be encountered.
+                case 12002: // Server timeout
+                case 12029: // 12029 to 12031 correspond to dropped connections.
+                case 12030:
+                case 12031:
+                case 12152: // Connection closed by server.
+                case 13030: // See above comments for variable status.
+                    // XDR transactions will not resolve to this case, since the
+                    // response object is already built in the xdr response.
+                    responseObject = this.createExceptionObject(o.tId, args, (isAbort?isAbort:false));
+                    if(callback && callback.failure){
+                        if(!callback.scope){
+                            callback.failure(responseObject);
+                        }
+                        else{
+                            callback.failure.apply(callback.scope, [responseObject]);
+                        }
+                    }
+
+                    break;
+                default:
+                    responseObject = (o.xdr) ? o.response : this.createResponseObject(o, args);
+                    if(callback && callback.failure){
+                        if(!callback.scope){
+                            callback.failure(responseObject);
+                        }
+                        else{
+                            callback.failure.apply(callback.scope, [responseObject]);
+                        }
+                    }
+            }
+
+            // Fire global custom event -- failureEvent
+            this.failureEvent.fire(responseObject);
+
+            if(o.failureEvent){
+                // Fire transaction custom event -- failureEvent
+                o.failureEvent.fire(responseObject);
+            }
+
+        }
+
+        this.releaseObject(o);
+        responseObject = null;
+    },
+
+  /**
+   * @description This method evaluates the server response, creates and returns the results via
+   * its properties.  Success and failure cases will differ in the response
+   * object's property values.
+   * @method createResponseObject
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
+   * @return {object}
+   */
+    createResponseObject:function(o, callbackArg)
+    {
+        var obj = {}, headerObj = {},
+            i, headerStr, header, delimitPos;
+
+        try
+        {
+            headerStr = o.conn.getAllResponseHeaders();
+            header = headerStr.split('\n');
+            for(i=0; i<header.length; i++){
+                delimitPos = header[i].indexOf(':');
+                if(delimitPos != -1){
+                    headerObj[header[i].substring(0,delimitPos)] = YAHOO.lang.trim(header[i].substring(delimitPos+2));
+                }
+            }
+        }
+        catch(e){}
+
+        obj.tId = o.tId;
+        // Normalize IE's response to HTTP 204 when Win error 1223.
+        obj.status = (o.conn.status == 1223)?204:o.conn.status;
+        // Normalize IE's statusText to "No Content" instead of "Unknown".
+        obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
+        obj.getResponseHeader = headerObj;
+        obj.getAllResponseHeaders = headerStr;
+        obj.responseText = o.conn.responseText;
+        obj.responseXML = o.conn.responseXML;
+
+        if(callbackArg){
+            obj.argument = callbackArg;
+        }
+
+        return obj;
+    },
+
+  /**
+   * @description If a transaction cannot be completed due to dropped or closed connections,
+   * there may be not be enough information to build a full response object.
+   * The failure callback will be fired and this specific condition can be identified
+   * by a status property value of 0.
+   *
+   * If an abort was successful, the status property will report a value of -1.
+   *
+   * @method createExceptionObject
+   * @private
+   * @static
+   * @param {int} tId The Transaction Id
+   * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
+   * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
+   * @return {object}
+   */
+    createExceptionObject:function(tId, callbackArg, isAbort)
+    {
+        var COMM_CODE = 0,
+            COMM_ERROR = 'communication failure',
+            ABORT_CODE = -1,
+            ABORT_ERROR = 'transaction aborted',
+            obj = {};
+
+        obj.tId = tId;
+        if(isAbort){
+            obj.status = ABORT_CODE;
+            obj.statusText = ABORT_ERROR;
+        }
+        else{
+            obj.status = COMM_CODE;
+            obj.statusText = COMM_ERROR;
+        }
+
+        if(callbackArg){
+            obj.argument = callbackArg;
+        }
+
+        return obj;
+    },
+
+  /**
+   * @description Method that initializes the custom HTTP headers for the each transaction.
+   * @method initHeader
+   * @public
+   * @static
+   * @param {string} label The HTTP header label
+   * @param {string} value The HTTP header value
+   * @param {string} isDefault Determines if the specific header is a default header
+   * automatically sent with each transaction.
+   * @return {void}
+   */
+    initHeader:function(label, value, isDefault)
+    {
+        var headerObj = (isDefault)?this._default_headers:this._http_headers;
+
+        headerObj[label] = value;
+        if(isDefault){
+            this._has_default_headers = true;
+        }
+        else{
+            this._has_http_headers = true;
+        }
+    },
+
+
+  /**
+   * @description Accessor that sets the HTTP headers for each transaction.
+   * @method setHeader
+   * @private
+   * @static
+   * @param {object} o The connection object for the transaction.
+   * @return {void}
+   */
+    setHeader:function(o)
+    {
+        var prop;
+        if(this._has_default_headers){
+            for(prop in this._default_headers){
+                if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
+                    o.conn.setRequestHeader(prop, this._default_headers[prop]);
+                }
+            }
+        }
+
+        if(this._has_http_headers){
+            for(prop in this._http_headers){
+                if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
+                    o.conn.setRequestHeader(prop, this._http_headers[prop]);
+                }
+            }
+
+            this._http_headers = {};
+            this._has_http_headers = false;
+        }
+    },
+
+  /**
+   * @description Resets the default HTTP headers object
+   * @method resetDefaultHeaders
+   * @public
+   * @static
+   * @return {void}
+   */
+    resetDefaultHeaders:function(){
+        this._default_headers = {};
+        this._has_default_headers = false;
+    },
+
+  /**
+   * @description Method to terminate a transaction, if it has not reached readyState 4.
+   * @method abort
+   * @public
+   * @static
+   * @param {object} o The connection object returned by asyncRequest.
+   * @param {object} callback  User-defined callback object.
+   * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
+   * @return {boolean}
+   */
+    abort:function(o, callback, isTimeout)
+    {
+        var abortStatus,
+            args = (callback && callback.argument)?callback.argument:null;
+            o = o || {};
+
+        if(o.conn){
+            if(o.xhr){
+                if(this.isCallInProgress(o)){
+                    // Issue abort request
+                    o.conn.abort();
+
+                    window.clearInterval(this._poll[o.tId]);
+                    delete this._poll[o.tId];
+
+                    if(isTimeout){
+                        window.clearTimeout(this._timeOut[o.tId]);
+                        delete this._timeOut[o.tId];
+                    }
+
+                    abortStatus = true;
+                }
+            }
+            else if(o.xdr){
+                o.conn.abort(o.tId);
+                abortStatus = true;
+            }
+        }
+        else if(o.upload){
+            var frameId = 'yuiIO' + o.tId;
+            var io = document.getElementById(frameId);
+
+            if(io){
+                // Remove all listeners on the iframe prior to
+                // its destruction.
+                YAHOO.util.Event.removeListener(io, "load");
+                // Destroy the iframe facilitating the transaction.
+                document.body.removeChild(io);
+
+                if(isTimeout){
+                    window.clearTimeout(this._timeOut[o.tId]);
+                    delete this._timeOut[o.tId];
+                }
+
+                abortStatus = true;
+            }
+        }
+        else{
+            abortStatus = false;
+        }
+
+        if(abortStatus === true){
+            // Fire global custom event -- abortEvent
+            this.abortEvent.fire(o, args);
+
+            if(o.abortEvent){
+                // Fire transaction custom event -- abortEvent
+                o.abortEvent.fire(o, args);
+            }
+
+            this.handleTransactionResponse(o, callback, true);
+        }
+
+        return abortStatus;
+    },
+
+  /**
+   * @description Determines if the transaction is still being processed.
+   * @method isCallInProgress
+   * @public
+   * @static
+   * @param {object} o The connection object returned by asyncRequest
+   * @return {boolean}
+   */
+    isCallInProgress:function(o)
+    {
+        o = o || {};
+        // if the XHR object assigned to the transaction has not been dereferenced,
+        // then check its readyState status.  Otherwise, return false.
+        if(o.xhr && o.conn){
+            return o.conn.readyState !== 4 && o.conn.readyState !== 0;
+        }
+        else if(o.xdr && o.conn){
+            return o.conn.isCallInProgress(o.tId);
+        }
+        else if(o.upload === true){
+            return document.getElementById('yuiIO' + o.tId)?true:false;
+        }
+        else{
+            return false;
+        }
+    },
+
+  /**
+   * @description Dereference the XHR instance and the connection object after the transaction is completed.
+   * @method releaseObject
+   * @private
+   * @static
+   * @param {object} o The connection object
+   * @return {void}
+   */
+    releaseObject:function(o)
+    {
+        if(o && o.conn){
+            //dereference the XHR instance.
+            o.conn = null;
+
+
+            //dereference the connection object.
+            o = null;
+        }
+    }
+};
+
+/**
+  * @for YAHOO.util.Connect
+  */
+(function() {
+	var YCM = YAHOO.util.Connect, _fn = {};
+
+   /**
+    * @description This method creates and instantiates the Flash transport.
+    * @method _swf
+    * @private
+    * @static
+    * @param {string} URI to connection.swf.
+    * @return {void}
+    */
+	function _swf(uri) {
+		var o = '<object id="YUIConnectionSwf" type="application/x-shockwave-flash" data="" +
+				uri + '" width="0" height="0">' +
+				'<param name="movie" value="' + uri + '">' +
+				'<param name="allowScriptAccess" value="always">' +
+				'</object>',
+		    c = document.createElement('div');
+
+		document.body.appendChild(c);
+		c.innerHTML = o;
+	}
+
+   /**
+    * @description This method calls the public method on the
+    * Flash transport to start the XDR transaction.  It is analogous
+    * to Connection Manager's asyncRequest method.
+    * @method xdr
+    * @private
+    * @static
+    * @param {object} The transaction object.
+    * @param {string} HTTP request method.
+    * @param {string} URI for the transaction.
+    * @param {object} The transaction's callback object.
+    * @param {object} The JSON object used as HTTP POST data.
+    * @return {void}
+    */
+	function _xdr(o, m, u, c, d) {
+		_fn[parseInt(o.tId)] = { 'o':o, 'c':c };
+		if (d) {
+			c.method = m;
+			c.data = ""
+		}
+
+		o.conn.send(u, c, o.tId);
+	}
+
+   /**
+    * @description This method instantiates the Flash transport and
+    * establishes a static reference to it, used for all XDR requests.
+    * @method transport
+    * @public
+    * @static
+    * @param {string} URI to connection.swf.
+    * @return {void}
+    */
+	function _init(uri) {
+		_swf(uri);
+		YCM._transport = document.getElementById('YUIConnectionSwf');
+	}
+
+	function _xdrReady() {
+		YCM.xdrReadyEvent.fire();
+	}
+
+   /**
+    * @description This method fires the global and transaction start
+    * events.
+    * @method _xdrStart
+    * @private
+    * @static
+    * @param {object} The transaction object.
+    * @param {string} The transaction's callback object.
+    * @return {void}
+    */
+	function _xdrStart(o, cb) {
+		if (o) {
+			// Fire global custom event -- startEvent
+			YCM.startEvent.fire(o, cb.argument);
+
+			if(o.startEvent){
+				// Fire transaction custom event -- startEvent
+				o.startEvent.fire(o, cb.argument);
+			}
+		}
+	}
+
+   /**
+    * @description This method is the initial response handler
+    * for XDR transactions.  The Flash transport calls this
+    * function and sends the response payload.
+    * @method handleXdrResponse
+    * @private
+    * @static
+    * @param {object} The response object sent from the Flash transport.
+    * @return {void}
+    */
+	function _handleXdrResponse(r) {
+		var o = _fn[r.tId].o,
+			cb = _fn[r.tId].c;
+
+		if (r.statusText === 'xdr:start') {
+			_xdrStart(o, cb);
+			return;
+		}
+
+		r.responseText = decodeURI(r.responseText);
+		o.r = r;
+		if (cb.argument) {
+			o.r.argument = cb.argument;
+		}
+
+		this.handleTransactionResponse(o, cb, r.statusText === 'xdr:abort' ? true : false);
+		delete _fn[r.tId];
+	}
+
+	// Bind the functions to Connection Manager as static fields.
+	YCM.xdr = _xdr;
+	YCM.swf = _swf;
+	YCM.transport = _init;
+	YCM.xdrReadyEvent = new YAHOO.util.CustomEvent('xdrReady');
+	YCM.xdrReady = _xdrReady;
+	YCM.handleXdrResponse = _handleXdrResponse;
+})();
+
+/**
+  * @for YAHOO.util.Connect
+  */
+(function(){
+	var YCM = YAHOO.util.Connect,
+		YE = YAHOO.util.Event,
+		dM = document.documentMode ? document.documentMode : false;
+
+   /**
+	* @description Property modified by setForm() to determine if a file(s)
+	* upload is expected.
+	* @property _isFileUpload
+	* @private
+	* @static
+	* @type boolean
+	*/
+	YCM._isFileUpload = false;
+
+   /**
+	* @description Property modified by setForm() to set a reference to the HTML
+	* form node if the desired action is file upload.
+	* @property _formNode
+	* @private
+	* @static
+	* @type object
+	*/
+	YCM._formNode = null;
+
+   /**
+	* @description Property modified by setForm() to set the HTML form data
+	* for each transaction.
+	* @property _sFormData
+	* @private
+	* @static
+	* @type string
+	*/
+	YCM._sFormData = null;
+
+   /**
+	* @description Tracks the name-value pair of the "clicked" submit button if multiple submit
+	* buttons are present in an HTML form; and, if YAHOO.util.Event is available.
+	* @property _submitElementValue
+	* @private
+	* @static
+	* @type string
+	*/
+	YCM._submitElementValue = null;
+
+   /**
+    * @description Custom event that fires when handleTransactionResponse() determines a
+    * response in the HTTP 4xx/5xx range.
+    * @property failureEvent
+    * @private
+    * @static
+    * @type CustomEvent
+    */
+	YCM.uploadEvent = new YAHOO.util.CustomEvent('upload');
+
+   /**
+	* @description Determines whether YAHOO.util.Event is available and returns true or false.
+	* If true, an event listener is bound at the document level to trap click events that
+	* resolve to a target type of "Submit".  This listener will enable setForm() to determine
+	* the clicked "Submit" value in a multi-Submit button, HTML form.
+	* @property _hasSubmitListener
+	* @private
+	* @static
+	*/
+	YCM._hasSubmitListener = function() {
+		if(YE){
+			YE.addListener(
+				document,
+				'click',
+				function(e){
+					var obj = YE.getTarget(e),
+						name = obj.nodeName.toLowerCase();
+
+					if((name === 'input' || name === 'button') && (obj.type && obj.type.toLowerCase() == 'submit')){
+						YCM._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
+					}
+				});
+			return true;
+		}
+		return false;
+	}();
+
+  /**
+   * @description This method assembles the form label and value pairs and
+   * constructs an encoded string.
+   * asyncRequest() will automatically initialize the transaction with a
+   * a HTTP header Content-Type of application/x-www-form-urlencoded.
+   * @method setForm
+   * @public
+   * @static
+   * @param {string || object} form id or name attribute, or form object.
+   * @param {boolean} optional enable file upload.
+   * @param {boolean} optional enable file upload over SSL in IE only.
+   * @return {string} string of the HTML form field name and value pairs..
+   */
+	function _setForm(formId, isUpload, secureUri)
+	{
+		var oForm, oElement, oName, oValue, oDisabled,
+			hasSubmit = false,
+			data = "" item = 0,
+			i,len,j,jlen,opt;
+
+		this.resetFormState();
+
+		if(typeof formId == 'string'){
+			// Determine if the argument is a form id or a form name.
+			// Note form name usage is deprecated by supported
+			// here for legacy reasons.
+			oForm = (document.getElementById(formId) || document.forms[formId]);
+		}
+		else if(typeof formId == 'object'){
+			// Treat argument as an HTML form object.
+			oForm = formId;
+		}
+		else{
+			return;
+		}
+
+		// If the isUpload argument is true, setForm will call createFrame to initialize
+		// an iframe as the form target.
+		//
+		// The argument secureURI is also required by IE in SSL environments
+		// where the secureURI string is a fully qualified HTTP path, used to set the source
+		// of the iframe, to a stub resource in the same domain.
+		if(isUpload){
+
+			// Create iframe in preparation for file upload.
+			this.createFrame(secureUri?secureUri:null);
+
+			// Set form reference and file upload properties to true.
+			this._isFormSubmit = true;
+			this._isFileUpload = true;
+			this._formNode = oForm;
+
+			return;
+		}
+
+		// Iterate over the form elements collection to construct the
+		// label-value pairs.
+		for (i=0,len=oForm.elements.length; i<len; ++i){
+			oElement  = oForm.elements[i];
+			oDisabled = oElement.disabled;
+			
+
+			// Do not submit fields that are disabled or
+			// do not have a name attribute value.
+			if(!oDisabled && oName)
+			{
+				
+				oValue = encodeURIComponent(oElement.value);
+
+				switch(oElement.type)
+				{
+					// Safari, Opera, FF all default opt.value from .text if
+					// value attribute not specified in markup
+					case 'select-one':
+						if (oElement.selectedIndex > -1) {
+							opt = oElement.options[oElement.selectedIndex];
+							data[item++] = oName + encodeURIComponent(
+								(opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
+						}
+						break;
+					case 'select-multiple':
+						if (oElement.selectedIndex > -1) {
+							for(j=oElement.selectedIndex, jlen=oElement.options.length; j<jlen; ++j){
+								opt = oElement.options[j];
+								if (opt.selected) {
+									data[item++] = oName + encodeURIComponent(
+										(opt.attributes.value && opt.attributes.value.specified) ? opt.value : opt.text);
+								}
+							}
+						}
+						break;
+					case 'radio':
+					case 'checkbox':
+						if(oElement.checked){
+							data[item++] = oName + oValue;
+						}
+						break;
+					case 'file':
+						// stub case as XMLHttpRequest will only send the file path as a string.
+					case undefined:
+						// stub case for fieldset element which returns undefined.
+					case 'reset':
+						// stub case for input type reset button.
+					case 'button':
+						// stub case for input type button elements.
+						break;
+					case 'submit':
+						if(hasSubmit === false){
+							if(this._hasSubmitListener && this._submitElementValue){
+								data[item++] = this._submitElementValue;
+							}
+							hasSubmit = true;
+						}
+						break;
+					default:
+						data[item++] = oName + oValue;
+				}
+			}
+		}
+
+		this._isFormSubmit = true;
+		this._sFormData = data.join('&');
+
+
+		this.initHeader('Content-Type', this._default_form_header);
+
+		return this._sFormData;
+	}
+
+   /**
+    * @description Resets HTML form properties when an HTML form or HTML form
+    * with file upload transaction is sent.
+    * @method resetFormState
+    * @private
+    * @static
+    * @return {void}
+    */
+	function _resetFormState(){
+		this._isFormSubmit = false;
+		this._isFileUpload = false;
+		this._formNode = null;
+		this._sFormData = "";
+	}
+
+
+   /**
+    * @description Creates an iframe to be used for form file uploads.  It is remove from the
+    * document upon completion of the upload transaction.
+    * @method createFrame
+    * @private
+    * @static
+    * @param {string} optional qualified path of iframe resource for SSL in IE.
+    * @return {void}
+    */
+	function _createFrame(secureUri){
+
+		// IE does not allow the setting of id and name attributes as object
+		// properties via createElement().  A different iframe creation
+		// pattern is required for IE.
+		var frameId = 'yuiIO' + this._transaction_id,
+			ie9 = (dM === 9) ? true : false,
+			io;
+
+		if(YAHOO.env.ua.ie && !ie9){
+			io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
+
+			// IE will throw a security exception in an SSL environment if the
+			// iframe source is undefined.
+			if(typeof secureUri == 'boolean'){
+				io.src = '';
+			}
+		}
+		else{
+			io = document.createElement('iframe');
+			io.id = frameId;
+			io.name = frameId;
+		}
+
+		io.style.position = 'absolute';
+		io.style.top = '-1000px';
+		io.style.left = '-1000px';
+
+		document.body.appendChild(io);
+	}
+
+   /**
+    * @description Parses the POST data and creates hidden form elements
+    * for each key-value, and appends them to the HTML form object.
+    * @method appendPostData
+    * @private
+    * @static
+    * @param {string} postData The HTTP POST data
+    * @return {array} formElements Collection of hidden fields.
+    */
+	function _appendPostData(postData){
+		var formElements = [],
+			postMessage = postData.split('&'),
+			i, delimitPos;
+
+		for(i=0; i < postMessage.length; i++){
+			delimitPos = postMessage[i].indexOf('=');
+			if(delimitPos != -1){
+				formElements[i] = document.createElement('input');
+				formElements[i].type = 'hidden';
+				formElements[i].name = decodeURIComponent(postMessage[i].substring(0,delimitPos));
+				formElements[i].value = decodeURIComponent(postMessage[i].substring(delimitPos+1));
+				this._formNode.appendChild(formElements[i]);
+			}
+		}
+
+		return formElements;
+	}
+
+   /**
+    * @description Uploads HTML form, inclusive of files/attachments, using the
+    * iframe created in createFrame to facilitate the transaction.
+    * @method uploadFile
+    * @private
+    * @static
+    * @param {int} id The transaction id.
+    * @param {object} callback User-defined callback object.
+    * @param {string} uri Fully qualified path of resource.
+    * @param {string} postData POST data to be submitted in addition to HTML form.
+    * @return {void}
+    */
+	function _uploadFile(o, callback, uri, postData){
+		// Each iframe has an id prefix of "yuiIO" followed
+		// by the unique transaction id.
+		var frameId = 'yuiIO' + o.tId,
+		    uploadEncoding = 'multipart/form-data',
+		    io = document.getElementById(frameId),
+		    ie8 = (dM >= 8) ? true : false,
+		    oConn = this,
+			args = (callback && callback.argument)?callback.argument:null,
+            oElements,i,prop,obj, rawFormAttributes, uploadCallback;
+
+		// Track original HTML form attribute values.
+		rawFormAttributes = {
+			action:this._formNode.getAttribute('action'),
+			method:this._formNode.getAttribute('method'),
+			target:this._formNode.getAttribute('target')
+		};
+
+		// Initialize the HTML form properties in case they are
+		// not defined in the HTML form.
+		this._formNode.setAttribute('action', uri);
+		this._formNode.setAttribute('method', 'POST');
+		this._formNode.setAttribute('target', frameId);
+
+		if(YAHOO.env.ua.ie && !ie8){
+			// IE does not respect property enctype for HTML forms.
+			// Instead it uses the property - "encoding".
+			this._formNode.setAttribute('encoding', uploadEncoding);
+		}
+		else{
+			this._formNode.setAttribute('enctype', uploadEncoding);
+		}
+
+		if(postData){
+			oElements = this.appendPostData(postData);
+		}
+
+		// Start file upload.
+		this._formNode.submit();
+
+		// Fire global custom event -- startEvent
+		this.startEvent.fire(o, args);
+
+		if(o.startEvent){
+			// Fire transaction custom event -- startEvent
+			o.startEvent.fire(o, args);
+		}
+
+		// Start polling if a callback is present and the timeout
+		// property has been defined.
+		if(callback && callback.timeout){
+			this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
+		}
+
+		// Remove HTML elements created by appendPostData
+		if(oElements && oElements.length > 0){
+			for(i=0; i < oElements.length; i++){
+				this._formNode.removeChild(oElements[i]);
+			}
+		}
+
+		// Restore HTML form attributes to their original
+		// values prior to file upload.
+		for(prop in rawFormAttributes){
+			if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
+				if(rawFormAttributes[prop]){
+					this._formNode.setAttribute(prop, rawFormAttributes[prop]);
+				}
+				else{
+					this._formNode.removeAttribute(prop);
+				}
+			}
+		}
+
+		// Reset HTML form state properties.
+		this.resetFormState();
+
+		// Create the upload callback handler that fires when the iframe
+		// receives the load event.  Subsequently, the event handler is detached
+		// and the iframe removed from the document.
+		uploadCallback = function() {
+			var body, pre, text;
+
+			if(callback && callback.timeout){
+				window.clearTimeout(oConn._timeOut[o.tId]);
+				delete oConn._timeOut[o.tId];
+			}
+
+			// Fire global custom event -- completeEvent
+			oConn.completeEvent.fire(o, args);
+
+			if(o.completeEvent){
+				// Fire transaction custom event -- completeEvent
+				o.completeEvent.fire(o, args);
+			}
+
+			obj = {
+			    tId : o.tId,
+			    argument : args
+            };
+
+			try
+			{
+				body = io.contentWindow.document.getElementsByTagName('body')[0];
+				pre = io.contentWindow.document.getElementsByTagName('pre')[0];
+
+				if (body) {
+					if (pre) {
+						text = pre.textContent?pre.textContent:pre.innerText;
+					}
+					else {
+						text = body.textContent?body.textContent:body.innerText;
+					}
+				}
+				obj.responseText = text;
+				// responseText and responseXML will be populated with the same data from the iframe.
+				// Since the HTTP headers cannot be read from the iframe
+				obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
+			}
+			catch(e){}
+
+			if(callback && callback.upload){
+				if(!callback.scope){
+					callback.upload(obj);
+				}
+				else{
+					callback.upload.apply(callback.scope, [obj]);
+				}
+			}
+
+			// Fire global custom event -- uploadEvent
+			oConn.uploadEvent.fire(obj);
+
+			if(o.uploadEvent){
+				// Fire transaction custom event -- uploadEvent
+				o.uploadEvent.fire(obj);
+			}
+
+			YE.removeListener(io, "load", uploadCallback);
+
+			setTimeout(
+				function(){
+					document.body.removeChild(io);
+					oConn.releaseObject(o);
+				}, 100);
+		};
+
+		// Bind the onload handler to the iframe to detect the file upload response.
+		YE.addListener(io, "load", uploadCallback);
+	}
+
+	YCM.setForm = _setForm;
+	YCM.resetFormState = _resetFormState;
+	YCM.createFrame = _createFrame;
+	YCM.appendPostData = _appendPostData;
+	YCM.uploadFile = _uploadFile;
+})();
+
+YAHOO.register("connection", YAHOO.util.Connect, {version: "2.9.0", build: "2800"});

Added: branches/wf4ever/public/_javascript_s/yui/datasource.js (0 => 3265)


--- branches/wf4ever/public/_javascript_s/yui/datasource.js	                        (rev 0)
+++ branches/wf4ever/public/_javascript_s/yui/datasource.js	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,3133 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+(function () {
+
+var lang   = YAHOO.lang,
+    util   = YAHOO.util,
+    Ev     = util.Event;
+
+/**
+ * The DataSource utility provides a common configurable interface for widgets to
+ * access a variety of data, from _javascript_ arrays to online database servers.
+ *
+ * @module datasource
+ * @requires yahoo, event
+ * @optional json, get, connection 
+ * @title DataSource Utility
+ */
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * Base class for the YUI DataSource utility.
+ *
+ * @namespace YAHOO.util
+ * @class YAHOO.util.DataSourceBase
+ * @constructor
+ * @param oLiveData {HTMLElement}  Pointer to live data.
+ * @param oConfigs {object} (optional) Object literal of configuration values.
+ */
+util.DataSourceBase = function(oLiveData, oConfigs) {
+    if(oLiveData === null || oLiveData === undefined) {
+        return;
+    }
+    
+    this.liveData = oLiveData;
+    this._oQueue = {interval:null, conn:null, requests:[]};
+    this.responseSchema = {};   
+
+    // Set any config params passed in to override defaults
+    if(oConfigs && (oConfigs.constructor == Object)) {
+        for(var sConfig in oConfigs) {
+            if(sConfig) {
+                this[sConfig] = oConfigs[sConfig];
+            }
+        }
+    }
+    
+    // Validate and initialize public configs
+    var maxCacheEntries = this.maxCacheEntries;
+    if(!lang.isNumber(maxCacheEntries) || (maxCacheEntries < 0)) {
+        maxCacheEntries = 0;
+    }
+
+    // Initialize interval tracker
+    this._aIntervals = [];
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Custom Events
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Fired when a request is made to the local cache.
+     *
+     * @event cacheRequestEvent
+     * @param oArgs.request {Object} The request object.
+     * @param oArgs.callback {Object} The callback object.
+     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
+     */
+    this.createEvent("cacheRequestEvent");
+
+    /**
+     * Fired when data is retrieved from the local cache.
+     *
+     * @event cacheResponseEvent
+     * @param oArgs.request {Object} The request object.
+     * @param oArgs.response {Object} The response object.
+     * @param oArgs.callback {Object} The callback object.
+     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
+     */
+    this.createEvent("cacheResponseEvent");
+
+    /**
+     * Fired when a request is sent to the live data source.
+     *
+     * @event requestEvent
+     * @param oArgs.request {Object} The request object.
+     * @param oArgs.callback {Object} The callback object.
+     * @param oArgs.tId {Number} Transaction ID.     
+     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
+     */
+    this.createEvent("requestEvent");
+
+    /**
+     * Fired when live data source sends response.
+     *
+     * @event responseEvent
+     * @param oArgs.request {Object} The request object.
+     * @param oArgs.response {Object} The raw response object.
+     * @param oArgs.callback {Object} The callback object.
+     * @param oArgs.tId {Number} Transaction ID.     
+     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
+     */
+    this.createEvent("responseEvent");
+
+    /**
+     * Fired when response is parsed.
+     *
+     * @event responseParseEvent
+     * @param oArgs.request {Object} The request object.
+     * @param oArgs.response {Object} The parsed response object.
+     * @param oArgs.callback {Object} The callback object.
+     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
+     */
+    this.createEvent("responseParseEvent");
+
+    /**
+     * Fired when response is cached.
+     *
+     * @event responseCacheEvent
+     * @param oArgs.request {Object} The request object.
+     * @param oArgs.response {Object} The parsed response object.
+     * @param oArgs.callback {Object} The callback object.
+     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
+     */
+    this.createEvent("responseCacheEvent");
+    /**
+     * Fired when an error is encountered with the live data source.
+     *
+     * @event dataErrorEvent
+     * @param oArgs.request {Object} The request object.
+     * @param oArgs.response {String} The response object (if available).
+     * @param oArgs.callback {Object} The callback object.
+     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
+     * @param oArgs.message {String} The error message.
+     */
+    this.createEvent("dataErrorEvent");
+
+    /**
+     * Fired when the local cache is flushed.
+     *
+     * @event cacheFlushEvent
+     */
+    this.createEvent("cacheFlushEvent");
+
+    var DS = util.DataSourceBase;
+    this._sName = "DataSource instance" + DS._nIndex;
+    DS._nIndex++;
+};
+
+var DS = util.DataSourceBase;
+
+lang.augmentObject(DS, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DataSourceBase public constants
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Type is unknown.
+ *
+ * @property TYPE_UNKNOWN
+ * @type Number
+ * @final
+ * @default -1
+ */
+TYPE_UNKNOWN : -1,
+
+/**
+ * Type is a _javascript_ Array.
+ *
+ * @property TYPE_JSARRAY
+ * @type Number
+ * @final
+ * @default 0
+ */
+TYPE_JSARRAY : 0,
+
+/**
+ * Type is a _javascript_ Function.
+ *
+ * @property TYPE_JSFUNCTION
+ * @type Number
+ * @final
+ * @default 1
+ */
+TYPE_JSFUNCTION : 1,
+
+/**
+ * Type is hosted on a server via an XHR connection.
+ *
+ * @property TYPE_XHR
+ * @type Number
+ * @final
+ * @default 2
+ */
+TYPE_XHR : 2,
+
+/**
+ * Type is JSON.
+ *
+ * @property TYPE_JSON
+ * @type Number
+ * @final
+ * @default 3
+ */
+TYPE_JSON : 3,
+
+/**
+ * Type is XML.
+ *
+ * @property TYPE_XML
+ * @type Number
+ * @final
+ * @default 4
+ */
+TYPE_XML : 4,
+
+/**
+ * Type is plain text.
+ *
+ * @property TYPE_TEXT
+ * @type Number
+ * @final
+ * @default 5
+ */
+TYPE_TEXT : 5,
+
+/**
+ * Type is an HTML TABLE element. Data is parsed out of TR elements from all TBODY elements.
+ *
+ * @property TYPE_HTMLTABLE
+ * @type Number
+ * @final
+ * @default 6
+ */
+TYPE_HTMLTABLE : 6,
+
+/**
+ * Type is hosted on a server via a dynamic script node.
+ *
+ * @property TYPE_SCRIPTNODE
+ * @type Number
+ * @final
+ * @default 7
+ */
+TYPE_SCRIPTNODE : 7,
+
+/**
+ * Type is local.
+ *
+ * @property TYPE_LOCAL
+ * @type Number
+ * @final
+ * @default 8
+ */
+TYPE_LOCAL : 8,
+
+/**
+ * Error message for invalid dataresponses.
+ *
+ * @property ERROR_DATAINVALID
+ * @type String
+ * @final
+ * @default "Invalid data"
+ */
+ERROR_DATAINVALID : "Invalid data",
+
+/**
+ * Error message for null data responses.
+ *
+ * @property ERROR_DATANULL
+ * @type String
+ * @final
+ * @default "Null data"
+ */
+ERROR_DATANULL : "Null data",
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DataSourceBase private static properties
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Internal class variable to index multiple DataSource instances.
+ *
+ * @property DataSourceBase._nIndex
+ * @type Number
+ * @private
+ * @static
+ */
+_nIndex : 0,
+
+/**
+ * Internal class variable to assign unique transaction IDs.
+ *
+ * @property DataSourceBase._nTransactionId
+ * @type Number
+ * @private
+ * @static
+ */
+_nTransactionId : 0,
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DataSourceBase private static methods
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Clones object literal or array of object literals.
+ *
+ * @method DataSourceBase._cloneObject
+ * @param o {Object} Object.
+ * @private
+ * @static
+ */
+_cloneObject: function(o) {
+    if(!lang.isValue(o)) {
+        return o;
+    }
+
+    var copy = {};
+
+    if(Object.prototype.toString.apply(o) === "[object RegExp]") {
+        copy = o;
+    }
+    else if(lang.isFunction(o)) {
+        copy = o;
+    }
+    else if(lang.isArray(o)) {
+        var array = [];
+        for(var i=0,len=o.length;i<len;i++) {
+            array[i] = DS._cloneObject(o[i]);
+        }
+        copy = array;
+    }
+    else if(lang.isObject(o)) {
+        for (var x in o){
+            if(lang.hasOwnProperty(o, x)) {
+                if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) {
+                    copy[x] = DS._cloneObject(o[x]);
+                }
+                else {
+                    copy[x] = o[x];
+                }
+            }
+        }
+    }
+    else {
+        copy = o;
+    }
+
+    return copy;
+},
+    
+/**
+ * Get an XPath-specified value for a given field from an XML node or document.
+ *
+ * @method _getLocationValue
+ * @param field {String | Object} Field definition.
+ * @param context {Object} XML node or document to search within.
+ * @return {Object} Data value or null.
+ * @static
+ * @private
+ */
+_getLocationValue: function(field, context) {
+    var locator = field.locator || field.key || field,
+        xmldoc = context.ownerDocument || context,
+        result, res, value = null;
+
+    try {
+        // Standards mode
+        if(!lang.isUndefined(xmldoc.evaluate)) {
+            result = xmldoc.evaluate(locator, context, xmldoc.createNSResolver(!context.ownerDocument ? context.documentElement : context.ownerDocument.documentElement), 0, null);
+            while(res = result.iterateNext()) {
+                value = res.textContent;
+            }
+        }
+        // IE mode
+        else {
+            xmldoc.setProperty("SelectionLanguage", "XPath");
+            result = context.selectNodes(locator)[0];
+            value = result.value || result.text || null;
+        }
+        return value;
+
+    }
+    catch(e) {
+    }
+},
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DataSourceBase public static methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Executes a configured callback.  For object literal callbacks, the third
+ * param determines whether to execute the success handler or failure handler.
+ *  
+ * @method issueCallback
+ * @param callback {Function|Object} the callback to execute
+ * @param params {Array} params to be passed to the callback method
+ * @param error {Boolean} whether an error occurred
+ * @param scope {Object} the scope from which to execute the callback
+ * (deprecated - use an object literal callback)
+ * @static     
+ */
+issueCallback : function (callback,params,error,scope) {
+    if (lang.isFunction(callback)) {
+        callback.apply(scope, params);
+    } else if (lang.isObject(callback)) {
+        scope = callback.scope || scope || window;
+        var callbackFunc = callback.success;
+        if (error) {
+            callbackFunc = callback.failure;
+        }
+        if (callbackFunc) {
+            callbackFunc.apply(scope, params.concat([callback.argument]));
+        }
+    }
+},
+
+/**
+ * Converts data to type String.
+ *
+ * @method DataSourceBase.parseString
+ * @param oData {String | Number | Boolean | Date | Array | Object} Data to parse.
+ * The special values null and undefined will return null.
+ * @return {String} A string, or null.
+ * @static
+ */
+parseString : function(oData) {
+    // Special case null and undefined
+    if(!lang.isValue(oData)) {
+        return null;
+    }
+    
+    //Convert to string
+    var string = oData + "";
+
+    // Validate
+    if(lang.isString(string)) {
+        return string;
+    }
+    else {
+        return null;
+    }
+},
+
+/**
+ * Converts data to type Number.
+ *
+ * @method DataSourceBase.parseNumber
+ * @param oData {String | Number | Boolean} Data to convert. Note, the following
+ * values return as null: null, undefined, NaN, "". 
+ * @return {Number} A number, or null.
+ * @static
+ */
+parseNumber : function(oData) {
+    if(!lang.isValue(oData) || (oData === "")) {
+        return null;
+    }
+
+    //Convert to number
+    var number = oData * 1;
+    
+    // Validate
+    if(lang.isNumber(number)) {
+        return number;
+    }
+    else {
+        return null;
+    }
+},
+// Backward compatibility
+convertNumber : function(oData) {
+    return DS.parseNumber(oData);
+},
+
+/**
+ * Converts data to type Date.
+ *
+ * @method DataSourceBase.parseDate
+ * @param oData {Date | String | Number} Data to convert.
+ * @return {Date} A Date instance.
+ * @static
+ */
+parseDate : function(oData) {
+    var date = null;
+    
+    //Convert to date
+    if(lang.isValue(oData) && !(oData instanceof Date)) {
+        date = new Date(oData);
+    }
+    else {
+        return oData;
+    }
+    
+    // Validate
+    if(date instanceof Date) {
+        return date;
+    }
+    else {
+        return null;
+    }
+},
+// Backward compatibility
+convertDate : function(oData) {
+    return DS.parseDate(oData);
+}
+
+});
+
+// Done in separate step so referenced functions are defined.
+/**
+ * Data parsing functions.
+ * @property DataSource.Parser
+ * @type Object
+ * @static
+ */
+DS.Parser = {
+    string   : DS.parseString,
+    number   : DS.parseNumber,
+    date     : DS.parseDate
+};
+
+// Prototype properties and methods
+DS.prototype = {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DataSourceBase private properties
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Name of DataSource instance.
+ *
+ * @property _sName
+ * @type String
+ * @private
+ */
+_sName : null,
+
+/**
+ * Local cache of data result object literals indexed chronologically.
+ *
+ * @property _aCache
+ * @type Object[]
+ * @private
+ */
+_aCache : null,
+
+/**
+ * Local queue of request connections, enabled if queue needs to be managed.
+ *
+ * @property _oQueue
+ * @type Object
+ * @private
+ */
+_oQueue : null,
+
+/**
+ * Array of polling interval IDs that have been enabled, needed to clear all intervals.
+ *
+ * @property _aIntervals
+ * @type Array
+ * @private
+ */
+_aIntervals : null,
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DataSourceBase public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * 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.
+ *
+ * @property maxCacheEntries
+ * @type Number
+ * @default 0
+ */
+maxCacheEntries : 0,
+
+ /**
+ * Pointer to live database.
+ *
+ * @property liveData
+ * @type Object
+ */
+liveData : null,
+
+/**
+ * Where the live data is held:
+ * 
+ * <dl>  
+ *    <dt>TYPE_UNKNOWN</dt>
+ *    <dt>TYPE_LOCAL</dt>
+ *    <dt>TYPE_XHR</dt>
+ *    <dt>TYPE_SCRIPTNODE</dt>
+ *    <dt>TYPE_JSFUNCTION</dt>
+ * </dl> 
+ *  
+ * @property dataType
+ * @type Number
+ * @default YAHOO.util.DataSourceBase.TYPE_UNKNOWN
+ *
+ */
+dataType : DS.TYPE_UNKNOWN,
+
+/**
+ * Format of response:
+ *  
+ * <dl>  
+ *    <dt>TYPE_UNKNOWN</dt>
+ *    <dt>TYPE_JSARRAY</dt>
+ *    <dt>TYPE_JSON</dt>
+ *    <dt>TYPE_XML</dt>
+ *    <dt>TYPE_TEXT</dt>
+ *    <dt>TYPE_HTMLTABLE</dt> 
+ * </dl> 
+ *
+ * @property responseType
+ * @type Number
+ * @default YAHOO.util.DataSourceBase.TYPE_UNKNOWN
+ */
+responseType : DS.TYPE_UNKNOWN,
+
+/**
+ * Response schema object literal takes a combination of the following properties:
+ *
+ * <dl>
+ * <dt>resultsList</dt> <dd>Pointer to array of tabular data</dd>
+ * <dt>resultNode</dt> <dd>Pointer to node name of row data (XML data only)</dd>
+ * <dt>recordDelim</dt> <dd>Record delimiter (text data only)</dd>
+ * <dt>fieldDelim</dt> <dd>Field delimiter (text data only)</dd>
+ * <dt>fields</dt> <dd>Array of field names (aka keys), or array of object literals
+ * such as: {key:"fieldname",parser:YAHOO.util.DataSourceBase.parseDate}</dd>
+ * <dt>metaFields</dt> <dd>Object literal of keys to include in the oParsedResponse.meta collection</dd>
+ * <dt>metaNode</dt> <dd>Name of the node under which to search for meta information in XML response data</dd>
+ * </dl>
+ *
+ * @property responseSchema
+ * @type Object
+ */
+responseSchema : null,
+
+/**
+ * Additional arguments passed to the JSON parse routine.  The JSON string
+ * is the assumed first argument (where applicable).  This property is not
+ * set by default, but the parse methods will use it if present.
+ *
+ * @property parseJSONArgs
+ * @type {MIXED|Array} If an Array, contents are used as individual arguments.
+ *                     Otherwise, value is used as an additional argument.
+ */
+// property intentionally undefined
+ 
+/**
+ * When working with XML data, setting this property to true enables support for
+ * XPath-syntaxed locators in schema definitions.
+ *
+ * @property useXPath
+ * @type Boolean
+ * @default false
+ */
+useXPath : false,
+
+/**
+ * Clones entries before adding to cache.
+ *
+ * @property cloneBeforeCaching
+ * @type Boolean
+ * @default false
+ */
+cloneBeforeCaching : false,
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DataSourceBase public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Public accessor to the unique name of the DataSource instance.
+ *
+ * @method toString
+ * @return {String} Unique name of the DataSource instance.
+ */
+toString : function() {
+    return this._sName;
+},
+
+/**
+ * Overridable method passes request to cache and returns cached response if any,
+ * refreshing the hit in the cache as the newest item. Returns null if there is
+ * no cache hit.
+ *
+ * @method getCachedResponse
+ * @param oRequest {Object} Request object.
+ * @param oCallback {Object} Callback object.
+ * @param oCaller {Object} (deprecated) Use callback object.
+ * @return {Object} Cached response object or null.
+ */
+getCachedResponse : function(oRequest, oCallback, oCaller) {
+    var aCache = this._aCache;
+
+    // If cache is enabled...
+    if(this.maxCacheEntries > 0) {        
+        // Initialize local cache
+        if(!aCache) {
+            this._aCache = [];
+        }
+        // Look in local cache
+        else {
+            var nCacheLength = aCache.length;
+            if(nCacheLength > 0) {
+                var oResponse = null;
+                this.fireEvent("cacheRequestEvent", {request:oRequest,callback:oCallback,caller:oCaller});
+        
+                // Loop through each cached element
+                for(var i = nCacheLength-1; i >= 0; i--) {
+                    var oCacheElem = aCache[i];
+        
+                    // Defer cache hit logic to a public overridable method
+                    if(this.isCacheHit(oRequest,oCacheElem.request)) {
+                        // The cache returned a hit!
+                        // Grab the cached response
+                        oResponse = oCacheElem.response;
+                        this.fireEvent("cacheResponseEvent", {request:oRequest,response:oResponse,callback:oCallback,caller:oCaller});
+                        
+                        // Refresh the position of the cache hit
+                        if(i < nCacheLength-1) {
+                            // Remove element from its original location
+                            aCache.splice(i,1);
+                            // Add as newest
+                            this.addToCache(oRequest, oResponse);
+                        }
+                        
+                        // Add a cache flag
+                        oResponse.cached = true;
+                        break;
+                    }
+                }
+                return oResponse;
+            }
+        }
+    }
+    else if(aCache) {
+        this._aCache = null;
+    }
+    return null;
+},
+
+/**
+ * Default overridable method matches given request to given cached request.
+ * Returns true if is a hit, returns false otherwise.  Implementers should
+ * override this method to customize the cache-matching algorithm.
+ *
+ * @method isCacheHit
+ * @param oRequest {Object} Request object.
+ * @param oCachedRequest {Object} Cached request object.
+ * @return {Boolean} True if given request matches cached request, false otherwise.
+ */
+isCacheHit : function(oRequest, oCachedRequest) {
+    return (oRequest === oCachedRequest);
+},
+
+/**
+ * Adds a new item to the cache. If cache is full, evicts the stalest item
+ * before adding the new item.
+ *
+ * @method addToCache
+ * @param oRequest {Object} Request object.
+ * @param oResponse {Object} Response object to cache.
+ */
+addToCache : function(oRequest, oResponse) {
+    var aCache = this._aCache;
+    if(!aCache) {
+        return;
+    }
+
+    // If the cache is full, make room by removing stalest element (index=0)
+    while(aCache.length >= this.maxCacheEntries) {
+        aCache.shift();
+    }
+
+    // Add to cache in the newest position, at the end of the array
+    oResponse = (this.cloneBeforeCaching) ? DS._cloneObject(oResponse) : oResponse;
+    var oCacheElem = {request:oRequest,response:oResponse};
+    aCache[aCache.length] = oCacheElem;
+    this.fireEvent("responseCacheEvent", {request:oRequest,response:oResponse});
+},
+
+/**
+ * Flushes cache.
+ *
+ * @method flushCache
+ */
+flushCache : function() {
+    if(this._aCache) {
+        this._aCache = [];
+        this.fireEvent("cacheFlushEvent");
+    }
+},
+
+/**
+ * Sets up a polling mechanism to send requests at set intervals and forward
+ * responses to given callback.
+ *
+ * @method setInterval
+ * @param nMsec {Number} Length of interval in milliseconds.
+ * @param oRequest {Object} Request object.
+ * @param oCallback {Function} Handler function to receive the response.
+ * @param oCaller {Object} (deprecated) Use oCallback.scope.
+ * @return {Number} Interval ID.
+ */
+setInterval : function(nMsec, oRequest, oCallback, oCaller) {
+    if(lang.isNumber(nMsec) && (nMsec >= 0)) {
+        var oSelf = this;
+        var nId = setInterval(function() {
+            oSelf.makeConnection(oRequest, oCallback, oCaller);
+        }, nMsec);
+        this._aIntervals.push(nId);
+        return nId;
+    }
+    else {
+    }
+},
+
+/**
+ * Disables polling mechanism associated with the given interval ID. Does not
+ * affect transactions that are in progress.
+ *
+ * @method clearInterval
+ * @param nId {Number} Interval ID.
+ */
+clearInterval : function(nId) {
+    // Remove from tracker if there
+    var tracker = this._aIntervals || [];
+    for(var i=tracker.length-1; i>-1; i--) {
+        if(tracker[i] === nId) {
+            tracker.splice(i,1);
+            clearInterval(nId);
+        }
+    }
+},
+
+/**
+ * Disables all known polling intervals. Does not affect transactions that are
+ * in progress.
+ *
+ * @method clearAllIntervals
+ */
+clearAllIntervals : function() {
+    var tracker = this._aIntervals || [];
+    for(var i=tracker.length-1; i>-1; i--) {
+        clearInterval(tracker[i]);
+    }
+    tracker = [];
+},
+
+/**
+ * First looks for cached response, then sends request to live data. The
+ * following arguments are passed to the callback function:
+ *     <dl>
+ *     <dt><code>oRequest</code></dt>
+ *     <dd>The same value that was passed in as the first argument to sendRequest.</dd>
+ *     <dt><code>oParsedResponse</code></dt>
+ *     <dd>An object literal containing the following properties:
+ *         <dl>
+ *         <dt><code>tId</code></dt>
+ *         <dd>Unique transaction ID number.</dd>
+ *         <dt><code>results</code></dt>
+ *         <dd>Schema-parsed data results.</dd>
+ *         <dt><code>error</code></dt>
+ *         <dd>True in cases of data error.</dd>
+ *         <dt><code>cached</code></dt>
+ *         <dd>True when response is returned from DataSource cache.</dd> 
+ *         <dt><code>meta</code></dt>
+ *         <dd>Schema-parsed meta data.</dd>
+ *         </dl>
+ *     <dt><code>oPayload</code></dt>
+ *     <dd>The same value as was passed in as <code>argument</code> in the oCallback object literal.</dd>
+ *     </dl> 
+ *
+ * @method sendRequest
+ * @param oRequest {Object} Request object.
+ * @param oCallback {Object} An object literal with the following properties:
+ *     <dl>
+ *     <dt><code>success</code></dt>
+ *     <dd>The function to call when the data is ready.</dd>
+ *     <dt><code>failure</code></dt>
+ *     <dd>The function to call upon a response failure condition.</dd>
+ *     <dt><code>scope</code></dt>
+ *     <dd>The object to serve as the scope for the success and failure handlers.</dd>
+ *     <dt><code>argument</code></dt>
+ *     <dd>Arbitrary data that will be passed back to the success and failure handlers.</dd>
+ *     </dl> 
+ * @param oCaller {Object} (deprecated) Use oCallback.scope.
+ * @return {Number} Transaction ID, or null if response found in cache.
+ */
+sendRequest : function(oRequest, oCallback, oCaller) {
+    // First look in cache
+    var oCachedResponse = this.getCachedResponse(oRequest, oCallback, oCaller);
+    if(oCachedResponse) {
+        DS.issueCallback(oCallback,[oRequest,oCachedResponse],false,oCaller);
+        return null;
+    }
+
+
+    // Not in cache, so forward request to live data
+    return this.makeConnection(oRequest, oCallback, oCaller);
+},
+
+/**
+ * Overridable default method generates a unique transaction ID and passes 
+ * the live data reference directly to the  handleResponse function. This
+ * method should be implemented by subclasses to achieve more complex behavior
+ * or to access remote data.          
+ *
+ * @method makeConnection
+ * @param oRequest {Object} Request object.
+ * @param oCallback {Object} Callback object literal.
+ * @param oCaller {Object} (deprecated) Use oCallback.scope.
+ * @return {Number} Transaction ID.
+ */
+makeConnection : function(oRequest, oCallback, oCaller) {
+    var tId = DS._nTransactionId++;
+    this.fireEvent("requestEvent", {tId:tId, request:oRequest,callback:oCallback,caller:oCaller});
+
+    /* accounts for the following cases:
+    YAHOO.util.DataSourceBase.TYPE_UNKNOWN
+    YAHOO.util.DataSourceBase.TYPE_JSARRAY
+    YAHOO.util.DataSourceBase.TYPE_JSON
+    YAHOO.util.DataSourceBase.TYPE_HTMLTABLE
+    YAHOO.util.DataSourceBase.TYPE_XML
+    YAHOO.util.DataSourceBase.TYPE_TEXT
+    */
+    var oRawResponse = this.liveData;
+    
+    this.handleResponse(oRequest, oRawResponse, oCallback, oCaller, tId);
+    return tId;
+},
+
+/**
+ * Receives raw data response and type converts to XML, JSON, etc as necessary.
+ * Forwards oFullResponse to appropriate parsing function to get turned into
+ * oParsedResponse. Calls doBeforeCallback() and adds oParsedResponse to 
+ * the cache when appropriate before calling issueCallback().
+ * 
+ * The oParsedResponse object literal has the following properties:
+ * <dl>
+ *     <dd><dt>tId {Number}</dt> Unique transaction ID</dd>
+ *     <dd><dt>results {Array}</dt> Array of parsed data results</dd>
+ *     <dd><dt>meta {Object}</dt> Object literal of meta values</dd> 
+ *     <dd><dt>error {Boolean}</dt> (optional) True if there was an error</dd>
+ *     <dd><dt>cached {Boolean}</dt> (optional) True if response was cached</dd>
+ * </dl>
+ *
+ * @method handleResponse
+ * @param oRequest {Object} Request object
+ * @param oRawResponse {Object} The raw response from the live database.
+ * @param oCallback {Object} Callback object literal.
+ * @param oCaller {Object} (deprecated) Use oCallback.scope.
+ * @param tId {Number} Transaction ID.
+ */
+handleResponse : function(oRequest, oRawResponse, oCallback, oCaller, tId) {
+    this.fireEvent("responseEvent", {tId:tId, request:oRequest, response:oRawResponse,
+            callback:oCallback, caller:oCaller});
+    var xhr = (this.dataType == DS.TYPE_XHR) ? true : false;
+    var oParsedResponse = null;
+    var oFullResponse = oRawResponse;
+    
+    // Try to sniff data type if it has not been defined
+    if(this.responseType === DS.TYPE_UNKNOWN) {
+        var ctype = (oRawResponse && oRawResponse.getResponseHeader) ? oRawResponse.getResponseHeader["Content-Type"] : null;
+        if(ctype) {
+             // xml
+            if(ctype.indexOf("text/xml") > -1) {
+                this.responseType = DS.TYPE_XML;
+            }
+            else if(ctype.indexOf("application/json") > -1) { // json
+                this.responseType = DS.TYPE_JSON;
+            }
+            else if(ctype.indexOf("text/plain") > -1) { // text
+                this.responseType = DS.TYPE_TEXT;
+            }
+        }
+        else {
+            if(YAHOO.lang.isArray(oRawResponse)) { // array
+                this.responseType = DS.TYPE_JSARRAY;
+            }
+             // xml
+            else if(oRawResponse && oRawResponse.nodeType && (oRawResponse.nodeType === 9 || oRawResponse.nodeType === 1 || oRawResponse.nodeType === 11)) {
+                this.responseType = DS.TYPE_XML;
+            }
+            else if(oRawResponse && oRawResponse.nodeName && (oRawResponse.nodeName.toLowerCase() == "table")) { // table
+                this.responseType = DS.TYPE_HTMLTABLE;
+            }    
+            else if(YAHOO.lang.isObject(oRawResponse)) { // json
+                this.responseType = DS.TYPE_JSON;
+            }
+            else if(YAHOO.lang.isString(oRawResponse)) { // text
+                this.responseType = DS.TYPE_TEXT;
+            }
+        }
+    }
+
+    switch(this.responseType) {
+        case DS.TYPE_JSARRAY:
+            if(xhr && oRawResponse && oRawResponse.responseText) {
+                oFullResponse = oRawResponse.responseText; 
+            }
+            try {
+                // Convert to JS array if it's a string
+                if(lang.isString(oFullResponse)) {
+                    var parseArgs = [oFullResponse].concat(this.parseJSONArgs);
+                    // Check for YUI JSON Util
+                    if(lang.JSON) {
+                        oFullResponse = lang.JSON.parse.apply(lang.JSON,parseArgs);
+                    }
+                    // Look for JSON parsers using an API similar to json2.js
+                    else if(window.JSON && JSON.parse) {
+                        oFullResponse = JSON.parse.apply(JSON,parseArgs);
+                    }
+                    // Look for JSON parsers using an API similar to json.js
+                    else if(oFullResponse.parseJSON) {
+                        oFullResponse = oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1));
+                    }
+                    // No JSON lib found so parse the string
+                    else {
+                        // Trim leading spaces
+                        while (oFullResponse.length > 0 &&
+                                (oFullResponse.charAt(0) != "{") &&
+                                (oFullResponse.charAt(0) != "[")) {
+                            oFullResponse = oFullResponse.substring(1, oFullResponse.length);
+                        }
+
+                        if(oFullResponse.length > 0) {
+                            // Strip extraneous stuff at the end
+                            var arrayEnd =
+Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}"));
+                            oFullResponse = oFullResponse.substring(0,arrayEnd+1);
+
+                            // Turn the string into an object literal...
+                            // ...eval is necessary here
+                            oFullResponse = eval("(" + oFullResponse + ")");
+
+                        }
+                    }
+                }
+            }
+            catch(e1) {
+            }
+            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
+            oParsedResponse = this.parseArrayData(oRequest, oFullResponse);
+            break;
+        case DS.TYPE_JSON:
+            if(xhr && oRawResponse && oRawResponse.responseText) {
+                oFullResponse = oRawResponse.responseText;
+            }
+            try {
+                // Convert to JSON object if it's a string
+                if(lang.isString(oFullResponse)) {
+                    var parseArgs = [oFullResponse].concat(this.parseJSONArgs);
+                    // Check for YUI JSON Util
+                    if(lang.JSON) {
+                        oFullResponse = lang.JSON.parse.apply(lang.JSON,parseArgs);
+                    }
+                    // Look for JSON parsers using an API similar to json2.js
+                    else if(window.JSON && JSON.parse) {
+                        oFullResponse = JSON.parse.apply(JSON,parseArgs);
+                    }
+                    // Look for JSON parsers using an API similar to json.js
+                    else if(oFullResponse.parseJSON) {
+                        oFullResponse = oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1));
+                    }
+                    // No JSON lib found so parse the string
+                    else {
+                        // Trim leading spaces
+                        while (oFullResponse.length > 0 &&
+                                (oFullResponse.charAt(0) != "{") &&
+                                (oFullResponse.charAt(0) != "[")) {
+                            oFullResponse = oFullResponse.substring(1, oFullResponse.length);
+                        }
+    
+                        if(oFullResponse.length > 0) {
+                            // Strip extraneous stuff at the end
+                            var objEnd = Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}"));
+                            oFullResponse = oFullResponse.substring(0,objEnd+1);
+    
+                            // Turn the string into an object literal...
+                            // ...eval is necessary here
+                            oFullResponse = eval("(" + oFullResponse + ")");
+    
+                        }
+                    }
+                }
+            }
+            catch(e) {
+            }
+
+            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
+            oParsedResponse = this.parseJSONData(oRequest, oFullResponse);
+            break;
+        case DS.TYPE_HTMLTABLE:
+            if(xhr && oRawResponse.responseText) {
+                var el = document.createElement('div');
+                el.innerHTML = oRawResponse.responseText;
+                oFullResponse = el.getElementsByTagName('table')[0];
+            }
+            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
+            oParsedResponse = this.parseHTMLTableData(oRequest, oFullResponse);
+            break;
+        case DS.TYPE_XML:
+            if(xhr && oRawResponse.responseXML) {
+                oFullResponse = oRawResponse.responseXML;
+            }
+            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
+            oParsedResponse = this.parseXMLData(oRequest, oFullResponse);
+            break;
+        case DS.TYPE_TEXT:
+            if(xhr && lang.isString(oRawResponse.responseText)) {
+                oFullResponse = oRawResponse.responseText;
+            }
+            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
+            oParsedResponse = this.parseTextData(oRequest, oFullResponse);
+            break;
+        default:
+            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
+            oParsedResponse = this.parseData(oRequest, oFullResponse);
+            break;
+    }
+
+
+    // Clean up for consistent signature
+    oParsedResponse = oParsedResponse || {};
+    if(!oParsedResponse.results) {
+        oParsedResponse.results = [];
+    }
+    if(!oParsedResponse.meta) {
+        oParsedResponse.meta = {};
+    }
+
+    // Success
+    if(!oParsedResponse.error) {
+        // Last chance to touch the raw response or the parsed response
+        oParsedResponse = this.doBeforeCallback(oRequest, oFullResponse, oParsedResponse, oCallback);
+        this.fireEvent("responseParseEvent", {request:oRequest,
+                response:oParsedResponse, callback:oCallback, caller:oCaller});
+        // Cache the response
+        this.addToCache(oRequest, oParsedResponse);
+    }
+    // Error
+    else {
+        // Be sure the error flag is on
+        oParsedResponse.error = true;
+        this.fireEvent("dataErrorEvent", {request:oRequest, response: oRawResponse, callback:oCallback, 
+                caller:oCaller, message:DS.ERROR_DATANULL});
+    }
+
+    // Send the response back to the caller
+    oParsedResponse.tId = tId;
+    DS.issueCallback(oCallback,[oRequest,oParsedResponse],oParsedResponse.error,oCaller);
+},
+
+/**
+ * Overridable method gives implementers access to the original full response
+ * before the data gets parsed. Implementers should take care not to return an
+ * unparsable or otherwise invalid response.
+ *
+ * @method doBeforeParseData
+ * @param oRequest {Object} Request object.
+ * @param oFullResponse {Object} The full response from the live database.
+ * @param oCallback {Object} The callback object.  
+ * @return {Object} Full response for parsing.
+  
+ */
+doBeforeParseData : function(oRequest, oFullResponse, oCallback) {
+    return oFullResponse;
+},
+
+/**
+ * Overridable method gives implementers access to the original full response and
+ * the parsed response (parsed against the given schema) before the data
+ * is added to the cache (if applicable) and then sent back to callback function.
+ * This is your chance to access the raw response and/or populate the parsed
+ * response with any custom data.
+ *
+ * @method doBeforeCallback
+ * @param oRequest {Object} Request object.
+ * @param oFullResponse {Object} The full response from the live database.
+ * @param oParsedResponse {Object} The parsed response to return to calling object.
+ * @param oCallback {Object} The callback object. 
+ * @return {Object} Parsed response object.
+ */
+doBeforeCallback : function(oRequest, oFullResponse, oParsedResponse, oCallback) {
+    return oParsedResponse;
+},
+
+/**
+ * Overridable method parses data of generic RESPONSE_TYPE into a response object.
+ *
+ * @method parseData
+ * @param oRequest {Object} Request object.
+ * @param oFullResponse {Object} The full Array from the live database.
+ * @return {Object} Parsed response object with the following properties:<br>
+ *     - results {Array} Array of parsed data results<br>
+ *     - meta {Object} Object literal of meta values<br>
+ *     - error {Boolean} (optional) True if there was an error<br>
+ */
+parseData : function(oRequest, oFullResponse) {
+    if(lang.isValue(oFullResponse)) {
+        var oParsedResponse = {results:oFullResponse,meta:{}};
+        return oParsedResponse;
+
+    }
+    return null;
+},
+
+/**
+ * Overridable method parses Array data into a response object.
+ *
+ * @method parseArrayData
+ * @param oRequest {Object} Request object.
+ * @param oFullResponse {Object} The full Array from the live database.
+ * @return {Object} Parsed response object with the following properties:<br>
+ *     - results (Array) Array of parsed data results<br>
+ *     - error (Boolean) True if there was an error
+ */
+parseArrayData : function(oRequest, oFullResponse) {
+    if(lang.isArray(oFullResponse)) {
+        var results = [],
+            i, j,
+            rec, field, data;
+        
+        // Parse for fields
+        if(lang.isArray(this.responseSchema.fields)) {
+            var fields = this.responseSchema.fields;
+            for (i = fields.length - 1; i >= 0; --i) {
+                if (typeof fields[i] !== 'object') {
+                    fields[i] = { key : fields[i] };
+                }
+            }
+
+            var parsers = {}, p;
+            for (i = fields.length - 1; i >= 0; --i) {
+                p = (typeof fields[i].parser === 'function' ?
+                          fields[i].parser :
+                          DS.Parser[fields[i].parser+'']) || fields[i].converter;
+                if (p) {
+                    parsers[fields[i].key] = p;
+                }
+            }
+
+            var arrType = lang.isArray(oFullResponse[0]);
+            for(i=oFullResponse.length-1; i>-1; i--) {
+                var oResult = {};
+                rec = oFullResponse[i];
+                if (typeof rec === 'object') {
+                    for(j=fields.length-1; j>-1; j--) {
+                        field = fields[j];
+                        data = "" ? rec[j] : rec[field.key];
+
+                        if (parsers[field.key]) {
+                            data = ""
+                        }
+
+                        // Safety measure
+                        if(data ="" undefined) {
+                            data = ""
+                        }
+
+                        oResult[field.key] = data;
+                    }
+                }
+                else if (lang.isString(rec)) {
+                    for(j=fields.length-1; j>-1; j--) {
+                        field = fields[j];
+                        data = ""
+
+                        if (parsers[field.key]) {
+                            data = ""
+                        }
+
+                        // Safety measure
+                        if(data ="" undefined) {
+                            data = ""
+                        }
+
+                        oResult[field.key] = data;
+                    }                
+                }
+                results[i] = oResult;
+            }    
+        }
+        // Return entire data set
+        else {
+            results = oFullResponse;
+        }
+        var oParsedResponse = {results:results};
+        return oParsedResponse;
+
+    }
+    return null;
+},
+
+/**
+ * Overridable method parses plain text data into a response object.
+ *
+ * @method parseTextData
+ * @param oRequest {Object} Request object.
+ * @param oFullResponse {Object} The full text response from the live database.
+ * @return {Object} Parsed response object with the following properties:<br>
+ *     - results (Array) Array of parsed data results<br>
+ *     - error (Boolean) True if there was an error
+ */
+parseTextData : function(oRequest, oFullResponse) {
+    if(lang.isString(oFullResponse)) {
+        if(lang.isString(this.responseSchema.recordDelim) &&
+                lang.isString(this.responseSchema.fieldDelim)) {
+            var oParsedResponse = {results:[]};
+            var recDelim = this.responseSchema.recordDelim;
+            var fieldDelim = this.responseSchema.fieldDelim;
+            if(oFullResponse.length > 0) {
+                // Delete the last line delimiter at the end of the data if it exists
+                var newLength = oFullResponse.length-recDelim.length;
+                if(oFullResponse.substr(newLength) == recDelim) {
+                    oFullResponse = oFullResponse.substr(0, newLength);
+                }
+                if(oFullResponse.length > 0) {
+                    // Split along record delimiter to get an array of strings
+                    var recordsarray = oFullResponse.split(recDelim);
+                    // Cycle through each record
+                    for(var i = 0, len = recordsarray.length, recIdx = 0; i < len; ++i) {
+                        var bError = false,
+                            sRecord = recordsarray[i];
+                        if (lang.isString(sRecord) && (sRecord.length > 0)) {
+                            // Split each record along field delimiter to get data
+                            var fielddataarray = recordsarray[i].split(fieldDelim);
+                            var oResult = {};
+                            
+                            // Filter for fields data
+                            if(lang.isArray(this.responseSchema.fields)) {
+                                var fields = this.responseSchema.fields;
+                                for(var j=fields.length-1; j>-1; j--) {
+                                    try {
+                                        // Remove quotation marks from edges, if applicable
+                                        var data = ""
+                                        if (lang.isString(data)) {
+                                            if(data.charAt(0) == "\"") {
+                                                data = ""
+                                            }
+                                            if(data.charAt(data.length-1) == "\"") {
+                                                data = ""
+                                            }
+                                            var field = fields[j];
+                                            var key = (lang.isValue(field.key)) ? field.key : field;
+                                            // Backward compatibility
+                                            if(!field.parser && field.converter) {
+                                                field.parser = field.converter;
+                                            }
+                                            var parser = (typeof field.parser === 'function') ?
+                                                field.parser :
+                                                DS.Parser[field.parser+''];
+                                            if(parser) {
+                                                data = "" data);
+                                            }
+                                            // Safety measure
+                                            if(data ="" undefined) {
+                                                data = ""
+                                            }
+                                            oResult[key] = data;
+                                        }
+                                        else {
+                                            bError = true;
+                                        }
+                                    }
+                                    catch(e) {
+                                        bError = true;
+                                    }
+                                }
+                            }            
+                            // No fields defined so pass along all data as an array
+                            else {
+                                oResult = fielddataarray;
+                            }
+                            if(!bError) {
+                                oParsedResponse.results[recIdx++] = oResult;
+                            }
+                        }
+                    }
+                }
+            }
+            return oParsedResponse;
+        }
+    }
+    return null;
+            
+},
+
+/**
+ * Overridable method parses XML data for one result into an object literal.
+ *
+ * @method parseXMLResult
+ * @param result {XML} XML for one result.
+ * @return {Object} Object literal of data for one result.
+ */
+parseXMLResult : function(result) {
+    var oResult = {},
+        schema = this.responseSchema;
+        
+    try {
+        // Loop through each data field in each result using the schema
+        for(var m = schema.fields.length-1; m >= 0 ; m--) {
+            var field = schema.fields[m];
+            var key = (lang.isValue(field.key)) ? field.key : field;
+            var data = ""
+
+            if(this.useXPath) {
+                data = "" result);
+            }
+            else {
+                // Values may be held in an attribute...
+                var xmlAttr = result.attributes.getNamedItem(key);
+                if(xmlAttr) {
+                    data = ""
+                }
+                // ...or in a node
+                else {
+                    var xmlNode = result.getElementsByTagName(key);
+                    if(xmlNode && xmlNode.item(0)) {
+                        var item = xmlNode.item(0);
+                        // For IE, then DOM...
+                        data = "" ? ((item.text) ? item.text : (item.textContent) ? item.textContent : null) : null;
+                        // ...then fallback, but check for multiple child nodes
+                        if(!data) {
+                            var datapieces = [];
+                            for(var j=0, len=item.childNodes.length; j<len; j++) {
+                                if(item.childNodes[j].nodeValue) {
+                                    datapieces[datapieces.length] = item.childNodes[j].nodeValue;
+                                }
+                            }
+                            if(datapieces.length > 0) {
+                                data = ""
+                            }
+                        }
+                    }
+                }
+            }
+            
+            
+            // Safety net
+            if(data ="" null) {
+                   data = ""
+            }
+            // Backward compatibility
+            if(!field.parser && field.converter) {
+                field.parser = field.converter;
+            }
+            var parser = (typeof field.parser === 'function') ?
+                field.parser :
+                DS.Parser[field.parser+''];
+            if(parser) {
+                data = "" data);
+            }
+            // Safety measure
+            if(data ="" undefined) {
+                data = ""
+            }
+            oResult[key] = data;
+        }
+    }
+    catch(e) {
+    }
+
+    return oResult;
+},
+
+
+
+/**
+ * Overridable method parses XML data into a response object.
+ *
+ * @method parseXMLData
+ * @param oRequest {Object} Request object.
+ * @param oFullResponse {Object} The full XML response from the live database.
+ * @return {Object} Parsed response object with the following properties<br>
+ *     - results (Array) Array of parsed data results<br>
+ *     - error (Boolean) True if there was an error
+ */
+parseXMLData : function(oRequest, oFullResponse) {
+    var bError = false,
+        schema = this.responseSchema,
+        oParsedResponse = {meta:{}},
+        xmlList = null,
+        metaNode      = schema.metaNode,
+        metaLocators  = schema.metaFields || {},
+        i,k,loc,v;
+
+    // In case oFullResponse is something funky
+    try {
+        // Pull any meta identified
+        if(this.useXPath) {
+            for (k in metaLocators) {
+                oParsedResponse.meta[k] = YAHOO.util.DataSource._getLocationValue(metaLocators[k], oFullResponse);
+            }
+        }
+        else {
+            metaNode = metaNode ? oFullResponse.getElementsByTagName(metaNode)[0] :
+                       oFullResponse;
+
+            if (metaNode) {
+                for (k in metaLocators) {
+                    if (lang.hasOwnProperty(metaLocators, k)) {
+                        loc = metaLocators[k];
+                        // Look for a node
+                        v = metaNode.getElementsByTagName(loc)[0];
+
+                        if (v) {
+                            v = v.firstChild.nodeValue;
+                        } else {
+                            // Look for an attribute
+                            v = metaNode.attributes.getNamedItem(loc);
+                            if (v) {
+                                v = v.value;
+                            }
+                        }
+
+                        if (lang.isValue(v)) {
+                            oParsedResponse.meta[k] = v;
+                        }
+                    }
+                }
+            }
+        }
+        
+        // For result data
+        xmlList = (schema.resultNode) ?
+            oFullResponse.getElementsByTagName(schema.resultNode) :
+            null;
+    }
+    catch(e) {
+    }
+    if(!xmlList || !lang.isArray(schema.fields)) {
+        bError = true;
+    }
+    // Loop through each result
+    else {
+        oParsedResponse.results = [];
+        for(i = xmlList.length-1; i >= 0 ; --i) {
+            var oResult = this.parseXMLResult(xmlList.item(i));
+            // Capture each array of values into an array of results
+            oParsedResponse.results[i] = oResult;
+        }
+    }
+    if(bError) {
+        oParsedResponse.error = true;
+    }
+    else {
+    }
+    return oParsedResponse;
+},
+
+/**
+ * Overridable method parses JSON data into a response object.
+ *
+ * @method parseJSONData
+ * @param oRequest {Object} Request object.
+ * @param oFullResponse {Object} The full JSON from the live database.
+ * @return {Object} Parsed response object with the following properties<br>
+ *     - results (Array) Array of parsed data results<br>
+ *     - error (Boolean) True if there was an error
+ */
+parseJSONData : function(oRequest, oFullResponse) {
+    var oParsedResponse = {results:[],meta:{}};
+    
+    if(lang.isObject(oFullResponse) && this.responseSchema.resultsList) {
+        var schema = this.responseSchema,
+            fields          = schema.fields,
+            resultsList     = oFullResponse,
+            results         = [],
+            metaFields      = schema.metaFields || {},
+            fieldParsers    = [],
+            fieldPaths      = [],
+            simpleFields    = [],
+            bError          = false,
+            i,len,j,v,key,parser,path;
+
+        // Function to convert the schema's fields into walk paths
+        var buildPath = function (needle) {
+            var path = null, keys = [], i = 0;
+            if (needle) {
+                // Strip the ["string keys"] and [1] array indexes
+                needle = needle.
+                    replace(/\[(['"])(.*?)\1\]/g,
+                    function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
+                    replace(/\[(\d+)\]/g,
+                    function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
+                    replace(/^\./,''); // remove leading dot
+
+                // If the cleaned needle contains invalid characters, the
+                // path is invalid
+                if (!/address@hidden/.test(needle)) {
+                    path = needle.split('.');
+                    for (i=path.length-1; i >= 0; --i) {
+                        if (path[i].charAt(0) === '@') {
+                            path[i] = keys[parseInt(path[i].substr(1),10)];
+                        }
+                    }
+                }
+                else {
+                }
+            }
+            return path;
+        };
+
+
+        // Function to walk a path and return the pot of gold
+        var walkPath = function (path, origin) {
+            var v=origin,i=0,len=path.length;
+            for (;i<len && v;++i) {
+                v = v[path[i]];
+            }
+            return v;
+        };
+
+        // Parse the response
+        // Step 1. Pull the resultsList from oFullResponse (default assumes
+        // oFullResponse IS the resultsList)
+        path = buildPath(schema.resultsList);
+        if (path) {
+            resultsList = walkPath(path, oFullResponse);
+            if (resultsList === undefined) {
+                bError = true;
+            }
+        } else {
+            bError = true;
+        }
+        
+        if (!resultsList) {
+            resultsList = [];
+        }
+
+        if (!lang.isArray(resultsList)) {
+            resultsList = [resultsList];
+        }
+
+        if (!bError) {
+            // Step 2. Parse out field data if identified
+            if(schema.fields) {
+                var field;
+                // Build the field parser map and location paths
+                for (i=0, len=fields.length; i<len; i++) {
+                    field = fields[i];
+                    key    = field.key || field;
+                    parser = ((typeof field.parser === 'function') ?
+                        field.parser :
+                        DS.Parser[field.parser+'']) || field.converter;
+                    path   = buildPath(key);
+    
+                    if (parser) {
+                        fieldParsers[fieldParsers.length] = {key:key,parser:parser};
+                    }
+    
+                    if (path) {
+                        if (path.length > 1) {
+                            fieldPaths[fieldPaths.length] = {key:key,path:path};
+                        } else {
+                            simpleFields[simpleFields.length] = {key:key,path:path[0]};
+                        }
+                    } else {
+                    }
+                }
+
+                // Process the results, flattening the records and/or applying parsers if needed
+                for (i = resultsList.length - 1; i >= 0; --i) {
+                    var r = resultsList[i], rec = {};
+                    if(r) {
+                        for (j = simpleFields.length - 1; j >= 0; --j) {
+                            // Bug 1777850: data might be held in an array
+                            rec[simpleFields[j].key] =
+                                    (r[simpleFields[j].path] !== undefined) ?
+                                    r[simpleFields[j].path] : r[j];
+                        }
+
+                        for (j = fieldPaths.length - 1; j >= 0; --j) {
+                            rec[fieldPaths[j].key] = walkPath(fieldPaths[j].path,r);
+                        }
+
+                        for (j = fieldParsers.length - 1; j >= 0; --j) {
+                            var p = fieldParsers[j].key;
+                            rec[p] = fieldParsers[j].parser.call(this, rec[p]);
+                            if (rec[p] === undefined) {
+                                rec[p] = null;
+                            }
+                        }
+                    }
+                    results[i] = rec;
+                }
+            }
+            else {
+                results = resultsList;
+            }
+
+            for (key in metaFields) {
+                if (lang.hasOwnProperty(metaFields,key)) {
+                    path = buildPath(metaFields[key]);
+                    if (path) {
+                        v = walkPath(path, oFullResponse);
+                        oParsedResponse.meta[key] = v;
+                    }
+                }
+            }
+
+        } else {
+
+            oParsedResponse.error = true;
+        }
+
+        oParsedResponse.results = results;
+    }
+    else {
+        oParsedResponse.error = true;
+    }
+
+    return oParsedResponse;
+},
+
+/**
+ * Overridable method parses an HTML TABLE element reference into a response object.
+ * Data is parsed out of TR elements from all TBODY elements. 
+ *
+ * @method parseHTMLTableData
+ * @param oRequest {Object} Request object.
+ * @param oFullResponse {Object} The full HTML element reference from the live database.
+ * @return {Object} Parsed response object with the following properties<br>
+ *     - results (Array) Array of parsed data results<br>
+ *     - error (Boolean) True if there was an error
+ */
+parseHTMLTableData : function(oRequest, oFullResponse) {
+    var bError = false;
+    var elTable = oFullResponse;
+    var fields = this.responseSchema.fields;
+    var oParsedResponse = {results:[]};
+
+    if(lang.isArray(fields)) {
+        // Iterate through each TBODY
+        for(var i=0; i<elTable.tBodies.length; i++) {
+            var elTbody = elTable.tBodies[i];
+    
+            // Iterate through each TR
+            for(var j=elTbody.rows.length-1; j>-1; j--) {
+                var elRow = elTbody.rows[j];
+                var oResult = {};
+                
+                for(var k=fields.length-1; k>-1; k--) {
+                    var field = fields[k];
+                    var key = (lang.isValue(field.key)) ? field.key : field;
+                    var data = ""
+    
+                    // Backward compatibility
+                    if(!field.parser && field.converter) {
+                        field.parser = field.converter;
+                    }
+                    var parser = (typeof field.parser === 'function') ?
+                        field.parser :
+                        DS.Parser[field.parser+''];
+                    if(parser) {
+                        data = "" data);
+                    }
+                    // Safety measure
+                    if(data ="" undefined) {
+                        data = ""
+                    }
+                    oResult[key] = data;
+                }
+                oParsedResponse.results[j] = oResult;
+            }
+        }
+    }
+    else {
+        bError = true;
+    }
+
+    if(bError) {
+        oParsedResponse.error = true;
+    }
+    else {
+    }
+    return oParsedResponse;
+}
+
+};
+
+// DataSourceBase uses EventProvider
+lang.augmentProto(DS, util.EventProvider);
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * LocalDataSource class for in-memory data structs including _javascript_ arrays,
+ * _javascript_ object literals (JSON), XML documents, and HTML tables.
+ *
+ * @namespace YAHOO.util
+ * @class YAHOO.util.LocalDataSource
+ * @extends YAHOO.util.DataSourceBase 
+ * @constructor
+ * @param oLiveData {HTMLElement}  Pointer to live data.
+ * @param oConfigs {object} (optional) Object literal of configuration values.
+ */
+util.LocalDataSource = function(oLiveData, oConfigs) {
+    this.dataType = DS.TYPE_LOCAL;
+    
+    if(oLiveData) {
+        if(YAHOO.lang.isArray(oLiveData)) { // array
+            this.responseType = DS.TYPE_JSARRAY;
+        }
+         // xml
+        else if(oLiveData.nodeType && oLiveData.nodeType == 9) {
+            this.responseType = DS.TYPE_XML;
+        }
+        else if(oLiveData.nodeName && (oLiveData.nodeName.toLowerCase() == "table")) { // table
+            this.responseType = DS.TYPE_HTMLTABLE;
+            oLiveData = oLiveData.cloneNode(true);
+        }    
+        else if(YAHOO.lang.isString(oLiveData)) { // text
+            this.responseType = DS.TYPE_TEXT;
+        }
+        else if(YAHOO.lang.isObject(oLiveData)) { // json
+            this.responseType = DS.TYPE_JSON;
+        }
+    }
+    else {
+        oLiveData = [];
+        this.responseType = DS.TYPE_JSARRAY;
+    }
+    
+    util.LocalDataSource.superclass.constructor.call(this, oLiveData, oConfigs); 
+};
+
+// LocalDataSource extends DataSourceBase
+lang.extend(util.LocalDataSource, DS);
+
+// Copy static members to LocalDataSource class
+lang.augmentObject(util.LocalDataSource, DS);
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * FunctionDataSource class for _javascript_ functions.
+ *
+ * @namespace YAHOO.util
+ * @class YAHOO.util.FunctionDataSource
+ * @extends YAHOO.util.DataSourceBase  
+ * @constructor
+ * @param oLiveData {HTMLElement}  Pointer to live data.
+ * @param oConfigs {object} (optional) Object literal of configuration values.
+ */
+util.FunctionDataSource = function(oLiveData, oConfigs) {
+    this.dataType = DS.TYPE_JSFUNCTION;
+    oLiveData = oLiveData || function() {};
+    
+    util.FunctionDataSource.superclass.constructor.call(this, oLiveData, oConfigs); 
+};
+
+// FunctionDataSource extends DataSourceBase
+lang.extend(util.FunctionDataSource, DS, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// FunctionDataSource public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Context in which to execute the function. By default, is the DataSource
+ * instance itself. If set, the function will receive the DataSource instance
+ * as an additional argument. 
+ *
+ * @property scope
+ * @type Object
+ * @default null
+ */
+scope : null,
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// FunctionDataSource public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Overriding method passes query to a function. The returned response is then
+ * forwarded to the handleResponse function.
+ *
+ * @method makeConnection
+ * @param oRequest {Object} Request object.
+ * @param oCallback {Object} Callback object literal.
+ * @param oCaller {Object} (deprecated) Use oCallback.scope.
+ * @return {Number} Transaction ID.
+ */
+makeConnection : function(oRequest, oCallback, oCaller) {
+    var tId = DS._nTransactionId++;
+    this.fireEvent("requestEvent", {tId:tId,request:oRequest,callback:oCallback,caller:oCaller});
+
+    // Pass the request in as a parameter and
+    // forward the return value to the handler
+    
+    
+    var oRawResponse = (this.scope) ? this.liveData.call(this.scope, oRequest, this, oCallback) : this.liveData(oRequest, oCallback);
+    
+    // Try to sniff data type if it has not been defined
+    if(this.responseType === DS.TYPE_UNKNOWN) {
+        if(YAHOO.lang.isArray(oRawResponse)) { // array
+            this.responseType = DS.TYPE_JSARRAY;
+        }
+         // xml
+        else if(oRawResponse && oRawResponse.nodeType && oRawResponse.nodeType == 9) {
+            this.responseType = DS.TYPE_XML;
+        }
+        else if(oRawResponse && oRawResponse.nodeName && (oRawResponse.nodeName.toLowerCase() == "table")) { // table
+            this.responseType = DS.TYPE_HTMLTABLE;
+        }    
+        else if(YAHOO.lang.isObject(oRawResponse)) { // json
+            this.responseType = DS.TYPE_JSON;
+        }
+        else if(YAHOO.lang.isString(oRawResponse)) { // text
+            this.responseType = DS.TYPE_TEXT;
+        }
+    }
+
+    this.handleResponse(oRequest, oRawResponse, oCallback, oCaller, tId);
+    return tId;
+}
+
+});
+
+// Copy static members to FunctionDataSource class
+lang.augmentObject(util.FunctionDataSource, DS);
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * ScriptNodeDataSource class for accessing remote data via the YUI Get Utility. 
+ *
+ * @namespace YAHOO.util
+ * @class YAHOO.util.ScriptNodeDataSource
+ * @extends YAHOO.util.DataSourceBase  
+ * @constructor
+ * @param oLiveData {HTMLElement}  Pointer to live data.
+ * @param oConfigs {object} (optional) Object literal of configuration values.
+ */
+util.ScriptNodeDataSource = function(oLiveData, oConfigs) {
+    this.dataType = DS.TYPE_SCRIPTNODE;
+    oLiveData = oLiveData || "";
+    
+    util.ScriptNodeDataSource.superclass.constructor.call(this, oLiveData, oConfigs); 
+};
+
+// ScriptNodeDataSource extends DataSourceBase
+lang.extend(util.ScriptNodeDataSource, DS, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// ScriptNodeDataSource public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Alias to YUI Get Utility, to allow implementers to use a custom class.
+ *
+ * @property getUtility
+ * @type Object
+ * @default YAHOO.util.Get
+ */
+getUtility : util.Get,
+
+/**
+ * Defines request/response management in the following manner:
+ * <dl>
+ *     <!--<dt>queueRequests</dt>
+ *     <dd>If a request is already in progress, wait until response is returned before sending the next request.</dd>
+ *     <dt>cancelStaleRequests</dt>
+ *     <dd>If a request is already in progress, cancel it before sending the next request.</dd>-->
+ *     <dt>ignoreStaleResponses</dt>
+ *     <dd>Send all requests, but handle only the response for the most recently sent request.</dd>
+ *     <dt>allowAll</dt>
+ *     <dd>Send all requests and handle all responses.</dd>
+ * </dl>
+ *
+ * @property asyncMode
+ * @type String
+ * @default "allowAll"
+ */
+asyncMode : "allowAll",
+
+/**
+ * Callback string parameter name sent to the remote script. By default,
+ * requests are sent to
+ * &#60;URI&#62;?&#60;scriptCallbackParam&#62;=callback
+ *
+ * @property scriptCallbackParam
+ * @type String
+ * @default "callback"
+ */
+scriptCallbackParam : "callback",
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// ScriptNodeDataSource public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Creates a request callback that gets appended to the script URI. Implementers
+ * can customize this string to match their server's query syntax.
+ *
+ * @method generateRequestCallback
+ * @return {String} String fragment that gets appended to script URI that 
+ * specifies the callback function 
+ */
+generateRequestCallback : function(id) {
+    return "&" + this.scriptCallbackParam + "=YAHOO.util.ScriptNodeDataSource.callbacks["+id+"]" ;
+},
+
+/**
+ * Overridable method gives implementers access to modify the URI before the dynamic
+ * script node gets inserted. Implementers should take care not to return an
+ * invalid URI.
+ *
+ * @method doBeforeGetScriptNode
+ * @param {String} URI to the script 
+ * @return {String} URI to the script
+ */
+doBeforeGetScriptNode : function(sUri) {
+    return sUri;
+},
+
+/**
+ * Overriding method passes query to Get Utility. The returned
+ * response is then forwarded to the handleResponse function.
+ *
+ * @method makeConnection
+ * @param oRequest {Object} Request object.
+ * @param oCallback {Object} Callback object literal.
+ * @param oCaller {Object} (deprecated) Use oCallback.scope.
+ * @return {Number} Transaction ID.
+ */
+makeConnection : function(oRequest, oCallback, oCaller) {
+    var tId = DS._nTransactionId++;
+    this.fireEvent("requestEvent", {tId:tId,request:oRequest,callback:oCallback,caller:oCaller});
+    
+    // If there are no global pending requests, it is safe to purge global callback stack and global counter
+    if(util.ScriptNodeDataSource._nPending === 0) {
+        util.ScriptNodeDataSource.callbacks = [];
+        util.ScriptNodeDataSource._nId = 0;
+    }
+    
+    // ID for this request
+    var id = util.ScriptNodeDataSource._nId;
+    util.ScriptNodeDataSource._nId++;
+    
+    // Dynamically add handler function with a closure to the callback stack
+    var oSelf = this;
+    util.ScriptNodeDataSource.callbacks[id] = function(oRawResponse) {
+        if((oSelf.asyncMode !== "ignoreStaleResponses")||
+                (id === util.ScriptNodeDataSource.callbacks.length-1)) { // Must ignore stale responses
+                
+            // Try to sniff data type if it has not been defined
+            if(oSelf.responseType === DS.TYPE_UNKNOWN) {
+                if(YAHOO.lang.isArray(oRawResponse)) { // array
+                    oSelf.responseType = DS.TYPE_JSARRAY;
+                }
+                 // xml
+                else if(oRawResponse.nodeType && oRawResponse.nodeType == 9) {
+                    oSelf.responseType = DS.TYPE_XML;
+                }
+                else if(oRawResponse.nodeName && (oRawResponse.nodeName.toLowerCase() == "table")) { // table
+                    oSelf.responseType = DS.TYPE_HTMLTABLE;
+                }    
+                else if(YAHOO.lang.isObject(oRawResponse)) { // json
+                    oSelf.responseType = DS.TYPE_JSON;
+                }
+                else if(YAHOO.lang.isString(oRawResponse)) { // text
+                    oSelf.responseType = DS.TYPE_TEXT;
+                }
+            }
+
+            oSelf.handleResponse(oRequest, oRawResponse, oCallback, oCaller, tId);
+        }
+        else {
+        }
+    
+        delete util.ScriptNodeDataSource.callbacks[id];
+    };
+    
+    // We are now creating a request
+    util.ScriptNodeDataSource._nPending++;
+    var sUri = this.liveData + oRequest + this.generateRequestCallback(id);
+    sUri = this.doBeforeGetScriptNode(sUri);
+    this.getUtility.script(sUri,
+            {autopurge: true,
+            onsuccess: util.ScriptNodeDataSource._bumpPendingDown,
+            onfail: util.ScriptNodeDataSource._bumpPendingDown});
+
+    return tId;
+}
+
+});
+
+// Copy static members to ScriptNodeDataSource class
+lang.augmentObject(util.ScriptNodeDataSource, DS);
+
+// Copy static members to ScriptNodeDataSource class
+lang.augmentObject(util.ScriptNodeDataSource,  {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// ScriptNodeDataSource private static properties
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Unique ID to track requests.
+ *
+ * @property _nId
+ * @type Number
+ * @private
+ * @static
+ */
+_nId : 0,
+
+/**
+ * Counter for pending requests. When this is 0, it is safe to purge callbacks
+ * array.
+ *
+ * @property _nPending
+ * @type Number
+ * @private
+ * @static
+ */
+_nPending : 0,
+
+/**
+ * Global array of callback functions, one for each request sent.
+ *
+ * @property callbacks
+ * @type Function[]
+ * @static
+ */
+callbacks : []
+
+});
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * XHRDataSource class for accessing remote data via the YUI Connection Manager
+ * Utility
+ *
+ * @namespace YAHOO.util
+ * @class YAHOO.util.XHRDataSource
+ * @extends YAHOO.util.DataSourceBase  
+ * @constructor
+ * @param oLiveData {HTMLElement}  Pointer to live data.
+ * @param oConfigs {object} (optional) Object literal of configuration values.
+ */
+util.XHRDataSource = function(oLiveData, oConfigs) {
+    this.dataType = DS.TYPE_XHR;
+    this.connMgr = this.connMgr || util.Connect;
+    oLiveData = oLiveData || "";
+    
+    util.XHRDataSource.superclass.constructor.call(this, oLiveData, oConfigs); 
+};
+
+// XHRDataSource extends DataSourceBase
+lang.extend(util.XHRDataSource, DS, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// XHRDataSource public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Alias to YUI Connection Manager, to allow implementers to use a custom class.
+ *
+ * @property connMgr
+ * @type Object
+ * @default YAHOO.util.Connect
+ */
+connMgr: null,
+
+ /**
+ * Defines request/response management in the following manner:
+ * <dl>
+ *     <dt>queueRequests</dt>
+ *     <dd>If a request is already in progress, wait until response is returned
+ *     before sending the next request.</dd>
+ *
+ *     <dt>cancelStaleRequests</dt>
+ *     <dd>If a request is already in progress, cancel it before sending the next
+ *     request.</dd>
+ *
+ *     <dt>ignoreStaleResponses</dt>
+ *     <dd>Send all requests, but handle only the response for the most recently
+ *     sent request.</dd>
+ *
+ *     <dt>allowAll</dt>
+ *     <dd>Send all requests and handle all responses.</dd>
+ *
+ * </dl>
+ *
+ * @property connXhrMode
+ * @type String
+ * @default "allowAll"
+ */
+connXhrMode: "allowAll",
+
+ /**
+ * True if data is to be sent via POST. By default, data will be sent via GET.
+ *
+ * @property connMethodPost
+ * @type Boolean
+ * @default false
+ */
+connMethodPost: false,
+
+ /**
+ * The connection timeout defines how many  milliseconds the XHR connection will
+ * wait for a server response. Any non-zero value will enable the Connection Manager's
+ * Auto-Abort feature.
+ *
+ * @property connTimeout
+ * @type Number
+ * @default 0
+ */
+connTimeout: 0,
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// XHRDataSource public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Overriding method passes query to Connection Manager. The returned
+ * response is then forwarded to the handleResponse function.
+ *
+ * @method makeConnection
+ * @param oRequest {Object} Request object.
+ * @param oCallback {Object} Callback object literal.
+ * @param oCaller {Object} (deprecated) Use oCallback.scope.
+ * @return {Number} Transaction ID.
+ */
+makeConnection : function(oRequest, oCallback, oCaller) {
+
+    var oRawResponse = null;
+    var tId = DS._nTransactionId++;
+    this.fireEvent("requestEvent", {tId:tId,request:oRequest,callback:oCallback,caller:oCaller});
+
+    // Set up the callback object and
+    // pass the request in as a URL query and
+    // forward the response to the handler
+    var oSelf = this;
+    var oConnMgr = this.connMgr;
+    var oQueue = this._oQueue;
+
+    /**
+     * Define Connection Manager success handler
+     *
+     * @method _xhrSuccess
+     * @param oResponse {Object} HTTPXMLRequest object
+     * @private
+     */
+    var _xhrSuccess = function(oResponse) {
+        // If response ID does not match last made request ID,
+        // silently fail and wait for the next response
+        if(oResponse && (this.connXhrMode == "ignoreStaleResponses") &&
+                (oResponse.tId != oQueue.conn.tId)) {
+            return null;
+        }
+        // Error if no response
+        else if(!oResponse) {
+            this.fireEvent("dataErrorEvent", {request:oRequest, response:null,
+                    callback:oCallback, caller:oCaller,
+                    message:DS.ERROR_DATANULL});
+
+            // Send error response back to the caller with the error flag on
+            DS.issueCallback(oCallback,[oRequest, {error:true}], true, oCaller);
+
+            return null;
+        }
+        // Forward to handler
+        else {
+            // Try to sniff data type if it has not been defined
+            if(this.responseType === DS.TYPE_UNKNOWN) {
+                var ctype = (oResponse.getResponseHeader) ? oResponse.getResponseHeader["Content-Type"] : null;
+                if(ctype) {
+                    // xml
+                    if(ctype.indexOf("text/xml") > -1) {
+                        this.responseType = DS.TYPE_XML;
+                    }
+                    else if(ctype.indexOf("application/json") > -1) { // json
+                        this.responseType = DS.TYPE_JSON;
+                    }
+                    else if(ctype.indexOf("text/plain") > -1) { // text
+                        this.responseType = DS.TYPE_TEXT;
+                    }
+                }
+            }
+            this.handleResponse(oRequest, oResponse, oCallback, oCaller, tId);
+        }
+    };
+
+    /**
+     * Define Connection Manager failure handler
+     *
+     * @method _xhrFailure
+     * @param oResponse {Object} HTTPXMLRequest object
+     * @private
+     */
+    var _xhrFailure = function(oResponse) {
+        this.fireEvent("dataErrorEvent", {request:oRequest, response: oResponse,
+                callback:oCallback, caller:oCaller,
+                message:DS.ERROR_DATAINVALID});
+
+        // Backward compatibility
+        if(lang.isString(this.liveData) && lang.isString(oRequest) &&
+            (this.liveData.lastIndexOf("?") !== this.liveData.length-1) &&
+            (oRequest.indexOf("?") !== 0)){
+        }
+
+        // Send failure response back to the caller with the error flag on
+        oResponse = oResponse || {};
+        oResponse.error = true;
+        DS.issueCallback(oCallback,[oRequest,oResponse],true, oCaller);
+
+        return null;
+    };
+
+    /**
+     * Define Connection Manager callback object
+     *
+     * @property _xhrCallback
+     * @param oResponse {Object} HTTPXMLRequest object
+     * @private
+     */
+     var _xhrCallback = {
+        success:_xhrSuccess,
+        failure:_xhrFailure,
+        scope: this
+    };
+
+    // Apply Connection Manager timeout
+    if(lang.isNumber(this.connTimeout)) {
+        _xhrCallback.timeout = this.connTimeout;
+    }
+
+    // Cancel stale requests
+    if(this.connXhrMode == "cancelStaleRequests") {
+            // Look in queue for stale requests
+            if(oQueue.conn) {
+                if(oConnMgr.abort) {
+                    oConnMgr.abort(oQueue.conn);
+                    oQueue.conn = null;
+                }
+                else {
+                }
+            }
+    }
+
+    // Get ready to send the request URL
+    if(oConnMgr && oConnMgr.asyncRequest) {
+        var sLiveData = this.liveData;
+        var isPost = this.connMethodPost;
+        var sMethod = (isPost) ? "POST" : "GET";
+        // Validate request
+        var sUri = (isPost || !lang.isValue(oRequest)) ? sLiveData : sLiveData+oRequest;
+        var sRequest = (isPost) ? oRequest : null;
+
+        // Send the request right away
+        if(this.connXhrMode != "queueRequests") {
+            oQueue.conn = oConnMgr.asyncRequest(sMethod, sUri, _xhrCallback, sRequest);
+        }
+        // Queue up then send the request
+        else {
+            // Found a request already in progress
+            if(oQueue.conn) {
+                var allRequests = oQueue.requests;
+                // Add request to queue
+                allRequests.push({request:oRequest, callback:_xhrCallback});
+
+                // Interval needs to be started
+                if(!oQueue.interval) {
+                    oQueue.interval = setInterval(function() {
+                        // Connection is in progress
+                        if(oConnMgr.isCallInProgress(oQueue.conn)) {
+                            return;
+                        }
+                        else {
+                            // Send next request
+                            if(allRequests.length > 0) {
+                                // Validate request
+                                sUri = (isPost || !lang.isValue(allRequests[0].request)) ? sLiveData : sLiveData+allRequests[0].request;
+                                sRequest = (isPost) ? allRequests[0].request : null;
+                                oQueue.conn = oConnMgr.asyncRequest(sMethod, sUri, allRequests[0].callback, sRequest);
+
+                                // Remove request from queue
+                                allRequests.shift();
+                            }
+                            // No more requests
+                            else {
+                                clearInterval(oQueue.interval);
+                                oQueue.interval = null;
+                            }
+                        }
+                    }, 50);
+                }
+            }
+            // Nothing is in progress
+            else {
+                oQueue.conn = oConnMgr.asyncRequest(sMethod, sUri, _xhrCallback, sRequest);
+            }
+        }
+    }
+    else {
+        // Send null response back to the caller with the error flag on
+        DS.issueCallback(oCallback,[oRequest,{error:true}],true,oCaller);
+    }
+
+    return tId;
+}
+
+});
+
+// Copy static members to XHRDataSource class
+lang.augmentObject(util.XHRDataSource, DS);
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * Factory class for creating a BaseDataSource subclass instance. The sublcass is
+ * determined by oLiveData's type, unless the dataType config is explicitly passed in.  
+ *
+ * @namespace YAHOO.util
+ * @class YAHOO.util.DataSource
+ * @constructor
+ * @param oLiveData {HTMLElement}  Pointer to live data.
+ * @param oConfigs {object} (optional) Object literal of configuration values.
+ */
+util.DataSource = function(oLiveData, oConfigs) {
+    oConfigs = oConfigs || {};
+    
+    // Point to one of the subclasses, first by dataType if given, then by sniffing oLiveData type.
+    var dataType = oConfigs.dataType;
+    if(dataType) {
+        if(dataType == DS.TYPE_LOCAL) {
+            return new util.LocalDataSource(oLiveData, oConfigs);
+        }
+        else if(dataType == DS.TYPE_XHR) {
+            return new util.XHRDataSource(oLiveData, oConfigs);            
+        }
+        else if(dataType == DS.TYPE_SCRIPTNODE) {
+            return new util.ScriptNodeDataSource(oLiveData, oConfigs);            
+        }
+        else if(dataType == DS.TYPE_JSFUNCTION) {
+            return new util.FunctionDataSource(oLiveData, oConfigs);            
+        }
+    }
+    
+    if(YAHOO.lang.isString(oLiveData)) { // strings default to xhr
+        return new util.XHRDataSource(oLiveData, oConfigs);
+    }
+    else if(YAHOO.lang.isFunction(oLiveData)) {
+        return new util.FunctionDataSource(oLiveData, oConfigs);
+    }
+    else { // ultimate default is local
+        return new util.LocalDataSource(oLiveData, oConfigs);
+    }
+};
+
+// Copy static members to DataSource class
+lang.augmentObject(util.DataSource, DS);
+
+})();
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * The static Number class provides helper functions to deal with data of type
+ * Number.
+ *
+ * @namespace YAHOO.util
+ * @requires yahoo
+ * @class Number
+ * @static
+ */
+ YAHOO.util.Number = {
+ 
+     /**
+     * Takes a native _javascript_ Number and formats to a string for display.
+     *
+     * @method format
+     * @param nData {Number} Number.
+     * @param oConfig {Object} (Optional) Optional configuration values:
+     *  <dl>
+     *   <dt>format</dt>
+     *   <dd>String used as a template for formatting positive numbers.
+     *   {placeholders} in the string are applied from the values in this
+     *   config object. {number} is used to indicate where the numeric portion
+     *   of the output goes.  For example &quot;{prefix}{number} per item&quot;
+     *   might yield &quot;$5.25 per item&quot;.  The only required
+     *   {placeholder} is {number}.</dd>
+     *
+     *   <dt>negativeFormat</dt>
+     *   <dd>Like format, but applied to negative numbers.  If set to null,
+     *   defaults from the configured format, prefixed with -.  This is
+     *   separate from format to support formats like &quot;($12,345.67)&quot;.
+     *
+     *   <dt>prefix {String} (deprecated, use format/negativeFormat)</dt>
+     *   <dd>String prepended before each number, like a currency designator "$"</dd>
+     *   <dt>decimalPlaces {Number}</dt>
+     *   <dd>Number of decimal places to round.</dd>
+     *
+     *   <dt>decimalSeparator {String}</dt>
+     *   <dd>Decimal separator</dd>
+     *
+     *   <dt>thousandsSeparator {String}</dt>
+     *   <dd>Thousands separator</dd>
+     *
+     *   <dt>suffix {String} (deprecated, use format/negativeFormat)</dt>
+     *   <dd>String appended after each number, like " items" (note the space)</dd>
+     *  </dl>
+     * @return {String} Formatted number for display. Note, the following values
+     * return as "": null, undefined, NaN, "".
+     */
+    format : function(n, cfg) {
+        if (n === '' || n === null || !isFinite(n)) {
+            return '';
+        }
+
+        n   = +n;
+        cfg = YAHOO.lang.merge(YAHOO.util.Number.format.defaults, (cfg || {}));
+
+        var stringN = n+'',
+            absN   = Math.abs(n),
+            places = cfg.decimalPlaces || 0,
+            sep    = cfg.thousandsSeparator,
+            negFmt = cfg.negativeFormat || ('-' + cfg.format),
+            s, bits, i, precision;
+
+        if (negFmt.indexOf('#') > -1) {
+            // for backward compatibility of negativeFormat supporting '-#'
+            negFmt = negFmt.replace(/#/, cfg.format);
+        }
+
+        if (places < 0) {
+            // Get rid of the decimal info
+            s = absN - (absN % 1) + '';
+            i = s.length + places;
+
+            // avoid 123 vs decimalPlaces -4 (should return "0")
+            if (i > 0) {
+                // leverage toFixed by making 123 => 0.123 for the rounding
+                // operation, then add the appropriate number of zeros back on
+                s = Number('.' + s).toFixed(i).slice(2) +
+                    new Array(s.length - i + 1).join('0');
+            } else {
+                s = "0";
+            }
+        } else {
+            // Avoid toFixed on floats:
+            // Bug 2528976
+            // Bug 2528977
+            var unfloatedN = absN+'';
+            if(places > 0 || unfloatedN.indexOf('.') > 0) {
+                var power = Math.pow(10, places);
+                s = Math.round(absN * power) / power + '';
+                var dot = s.indexOf('.'),
+                    padding, zeroes;
+                
+                // Add padding
+                if(dot < 0) {
+                    padding = places;
+                    zeroes = (Math.pow(10, padding) + '').substring(1);
+                    if(places > 0) {
+                        s = s + '.' + zeroes;
+                    }
+                }
+                else {
+                    padding = places - (s.length - dot - 1);
+                    zeroes = (Math.pow(10, padding) + '').substring(1);
+                    s = s + zeroes;
+                }
+            }
+            else {
+                s = absN.toFixed(places)+'';
+            }
+        }
+
+        bits  = s.split(/\D/);
+
+        if (absN >= 1000) {
+            i  = bits[0].length % 3 || 3;
+
+            bits[0] = bits[0].slice(0,i) +
+                      bits[0].slice(i).replace(/(\d{3})/g, sep + '$1');
+
+        }
+
+        return YAHOO.util.Number.format._applyFormat(
+            (n < 0 ? negFmt : cfg.format),
+            bits.join(cfg.decimalSeparator),
+            cfg);
+    }
+};
+
+/**
+ * <p>Default values for Number.format behavior.  Override properties of this
+ * object if you want every call to Number.format in your system to use
+ * specific presets.</p>
+ *
+ * <p>Available keys include:</p>
+ * <ul>
+ *   <li>format</li>
+ *   <li>negativeFormat</li>
+ *   <li>decimalSeparator</li>
+ *   <li>decimalPlaces</li>
+ *   <li>thousandsSeparator</li>
+ *   <li>prefix/suffix or any other token you want to use in the format templates</li>
+ * </ul>
+ *
+ * @property Number.format.defaults
+ * @type {Object}
+ * @static
+ */
+YAHOO.util.Number.format.defaults = {
+    format : '{prefix}{number}{suffix}',
+    negativeFormat : null, // defaults to -(format)
+    decimalSeparator : '.',
+    decimalPlaces    : null,
+    thousandsSeparator : ''
+};
+
+/**
+ * Apply any special formatting to the "d,ddd.dd" string.  Takes either the
+ * cfg.format or cfg.negativeFormat template and replaces any {placeholders}
+ * with either the number or a value from a so-named property of the config
+ * object.
+ *
+ * @method Number.format._applyFormat
+ * @static
+ * @param tmpl {String} the cfg.format or cfg.numberFormat template string
+ * @param num {String} the number with separators and decimalPlaces applied
+ * @param data {Object} the config object, used here to populate {placeholder}s
+ * @return {String} the number with any decorators added
+ */
+YAHOO.util.Number.format._applyFormat = function (tmpl, num, data) {
+    return tmpl.replace(/\{(\w+)\}/g, function (_, token) {
+        return token === 'number' ? num :
+               token in data ? data[token] : '';
+    });
+};
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+(function () {
+
+var xPad=function (x, pad, r)
+{
+    if(typeof r === 'undefined')
+    {
+        r=10;
+    }
+    for( ; parseInt(x, 10)<r && r>1; r/=10) {
+        x = pad.toString() + x;
+    }
+    return x.toString();
+};
+
+
+/**
+ * The static Date class provides helper functions to deal with data of type Date.
+ *
+ * @namespace YAHOO.util
+ * @requires yahoo
+ * @class Date
+ * @static
+ */
+ var Dt = {
+    formats: {
+        a: function (d, l) { return l.a[d.getDay()]; },
+        A: function (d, l) { return l.A[d.getDay()]; },
+        b: function (d, l) { return l.b[d.getMonth()]; },
+        B: function (d, l) { return l.B[d.getMonth()]; },
+        C: function (d) { return xPad(parseInt(d.getFullYear()/100, 10), 0); },
+        d: ['getDate', '0'],
+        e: ['getDate', ' '],
+        g: function (d) { return xPad(parseInt(Dt.formats.G(d)%100, 10), 0); },
+        G: function (d) {
+                var y = d.getFullYear();
+                var V = parseInt(Dt.formats.V(d), 10);
+                var W = parseInt(Dt.formats.W(d), 10);
+    
+                if(W > V) {
+                    y++;
+                } else if(W===0 && V>=52) {
+                    y--;
+                }
+    
+                return y;
+            },
+        H: ['getHours', '0'],
+        I: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, 0); },
+        j: function (d) {
+                var gmd_1 = new Date('' + d.getFullYear() + '/1/1 GMT');
+                var gmdate = new Date('' + d.getFullYear() + '/' + (d.getMonth()+1) + '/' + d.getDate() + ' GMT');
+                var ms = gmdate - gmd_1;
+                var doy = parseInt(ms/60000/60/24, 10)+1;
+                return xPad(doy, 0, 100);
+            },
+        k: ['getHours', ' '],
+        l: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, ' '); },
+        m: function (d) { return xPad(d.getMonth()+1, 0); },
+        M: ['getMinutes', '0'],
+        p: function (d, l) { return l.p[d.getHours() >= 12 ? 1 : 0 ]; },
+        P: function (d, l) { return l.P[d.getHours() >= 12 ? 1 : 0 ]; },
+        s: function (d, l) { return parseInt(d.getTime()/1000, 10); },
+        S: ['getSeconds', '0'],
+        u: function (d) { var dow = d.getDay(); return dow===0?7:dow; },
+        U: function (d) {
+                var doy = parseInt(Dt.formats.j(d), 10);
+                var rdow = 6-d.getDay();
+                var woy = parseInt((doy+rdow)/7, 10);
+                return xPad(woy, 0);
+            },
+        V: function (d) {
+                var woy = parseInt(Dt.formats.W(d), 10);
+                var dow1_1 = (new Date('' + d.getFullYear() + '/1/1')).getDay();
+                // First week is 01 and not 00 as in the case of %U and %W,
+                // so we add 1 to the final result except if day 1 of the year
+                // is a Monday (then %W returns 01).
+                // We also need to subtract 1 if the day 1 of the year is 
+                // Friday-Sunday, so the resulting equation becomes:
+                var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
+                if(idow === 53 && (new Date('' + d.getFullYear() + '/12/31')).getDay() < 4)
+                {
+                    idow = 1;
+                }
+                else if(idow === 0)
+                {
+                    idow = Dt.formats.V(new Date('' + (d.getFullYear()-1) + '/12/31'));
+                }
+    
+                return xPad(idow, 0);
+            },
+        w: 'getDay',
+        W: function (d) {
+                var doy = parseInt(Dt.formats.j(d), 10);
+                var rdow = 7-Dt.formats.u(d);
+                var woy = parseInt((doy+rdow)/7, 10);
+                return xPad(woy, 0, 10);
+            },
+        y: function (d) { return xPad(d.getFullYear()%100, 0); },
+        Y: 'getFullYear',
+        z: function (d) {
+                var o = d.getTimezoneOffset();
+                var H = xPad(parseInt(Math.abs(o/60), 10), 0);
+                var M = xPad(Math.abs(o%60), 0);
+                return (o>0?'-':'+') + H + M;
+            },
+        Z: function (d) {
+		var tz = d.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/, '$2').replace(/[a-z ]/g, '');
+		if(tz.length > 4) {
+			tz = Dt.formats.z(d);
+		}
+		return tz;
+	},
+        '%': function (d) { return '%'; }
+    },
+
+    aggregates: {
+        c: 'locale',
+        D: '%m/%d/%y',
+        F: '%Y-%m-%d',
+        h: '%b',
+        n: '\n',
+        r: 'locale',
+        R: '%H:%M',
+        t: '\t',
+        T: '%H:%M:%S',
+        x: 'locale',
+        X: 'locale'
+        //'+': '%a %b %e %T %Z %Y'
+    },
+
+     /**
+     * Takes a native _javascript_ Date and formats to string for display to user.
+     *
+     * @method format
+     * @param oDate {Date} Date.
+     * @param oConfig {Object} (Optional) Object literal of configuration values:
+     *  <dl>
+     *   <dt>format &lt;String&gt;</dt>
+     *   <dd>
+     *   <p>
+     *   Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at 
+     *   <a href=""
+     *   </p>
+     *   <p>   
+     *   PHP added a few of its own, defined at <a href=""
+     *   </p>
+     *   <p>
+     *   This _javascript_ implementation supports all the PHP specifiers and a few more.  The full list is below:
+     *   </p>
+     *   <dl>
+     *    <dt>%a</dt> <dd>abbreviated weekday name according to the current locale</dd>
+     *    <dt>%A</dt> <dd>full weekday name according to the current locale</dd>
+     *    <dt>%b</dt> <dd>abbreviated month name according to the current locale</dd>
+     *    <dt>%B</dt> <dd>full month name according to the current locale</dd>
+     *    <dt>%c</dt> <dd>preferred date and time representation for the current locale</dd>
+     *    <dt>%C</dt> <dd>century number (the year divided by 100 and truncated to an integer, range 00 to 99)</dd>
+     *    <dt>%d</dt> <dd>day of the month as a decimal number (range 01 to 31)</dd>
+     *    <dt>%D</dt> <dd>same as %m/%d/%y</dd>
+     *    <dt>%e</dt> <dd>day of the month as a decimal number, a single digit is preceded by a space (range ' 1' to '31')</dd>
+     *    <dt>%F</dt> <dd>same as %Y-%m-%d (ISO 8601 date format)</dd>
+     *    <dt>%g</dt> <dd>like %G, but without the century</dd>
+     *    <dt>%G</dt> <dd>The 4-digit year corresponding to the ISO week number</dd>
+     *    <dt>%h</dt> <dd>same as %b</dd>
+     *    <dt>%H</dt> <dd>hour as a decimal number using a 24-hour clock (range 00 to 23)</dd>
+     *    <dt>%I</dt> <dd>hour as a decimal number using a 12-hour clock (range 01 to 12)</dd>
+     *    <dt>%j</dt> <dd>day of the year as a decimal number (range 001 to 366)</dd>
+     *    <dt>%k</dt> <dd>hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)</dd>
+     *    <dt>%l</dt> <dd>hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.) </dd>
+     *    <dt>%m</dt> <dd>month as a decimal number (range 01 to 12)</dd>
+     *    <dt>%M</dt> <dd>minute as a decimal number</dd>
+     *    <dt>%n</dt> <dd>newline character</dd>
+     *    <dt>%p</dt> <dd>either `AM' or `PM' according to the given time value, or the corresponding strings for the current locale</dd>
+     *    <dt>%P</dt> <dd>like %p, but lower case</dd>
+     *    <dt>%r</dt> <dd>time in a.m. and p.m. notation equal to %I:%M:%S %p</dd>
+     *    <dt>%R</dt> <dd>time in 24 hour notation equal to %H:%M</dd>
+     *    <dt>%s</dt> <dd>number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC</dd>
+     *    <dt>%S</dt> <dd>second as a decimal number</dd>
+     *    <dt>%t</dt> <dd>tab character</dd>
+     *    <dt>%T</dt> <dd>current time, equal to %H:%M:%S</dd>
+     *    <dt>%u</dt> <dd>weekday as a decimal number [1,7], with 1 representing Monday</dd>
+     *    <dt>%U</dt> <dd>week number of the current year as a decimal number, starting with the
+     *            first Sunday as the first day of the first week</dd>
+     *    <dt>%V</dt> <dd>The ISO 8601:1988 week number of the current year as a decimal number,
+     *            range 01 to 53, where week 1 is the first week that has at least 4 days
+     *            in the current year, and with Monday as the first day of the week.</dd>
+     *    <dt>%w</dt> <dd>day of the week as a decimal, Sunday being 0</dd>
+     *    <dt>%W</dt> <dd>week number of the current year as a decimal number, starting with the
+     *            first Monday as the first day of the first week</dd>
+     *    <dt>%x</dt> <dd>preferred date representation for the current locale without the time</dd>
+     *    <dt>%X</dt> <dd>preferred time representation for the current locale without the date</dd>
+     *    <dt>%y</dt> <dd>year as a decimal number without a century (range 00 to 99)</dd>
+     *    <dt>%Y</dt> <dd>year as a decimal number including the century</dd>
+     *    <dt>%z</dt> <dd>numerical time zone representation</dd>
+     *    <dt>%Z</dt> <dd>time zone name or abbreviation</dd>
+     *    <dt>%%</dt> <dd>a literal `%' character</dd>
+     *   </dl>
+     *  </dd>
+     * </dl>
+     * @param sLocale {String} (Optional) The locale to use when displaying days of week,
+     *  months of the year, and other locale specific strings.  The following locales are
+     *  built in:
+     *  <dl>
+     *   <dt>en</dt>
+     *   <dd>English</dd>
+     *   <dt>en-US</dt>
+     *   <dd>US English</dd>
+     *   <dt>en-GB</dt>
+     *   <dd>British English</dd>
+     *   <dt>en-AU</dt>
+     *   <dd>Australian English (identical to British English)</dd>
+     *  </dl>
+     *  More locales may be added by subclassing of YAHOO.util.DateLocale.
+     *  See YAHOO.util.DateLocale for more information.
+     * @return {HTML} Formatted date for display. Non-date values are passed
+     * through as-is.
+     * @sa YAHOO.util.DateLocale
+     */
+    format : function (oDate, oConfig, sLocale) {
+        oConfig = oConfig || {};
+        
+        if(!(oDate instanceof Date)) {
+            return YAHOO.lang.isValue(oDate) ? oDate : "";
+        }
+
+        var format = oConfig.format || "%m/%d/%Y";
+
+        // Be backwards compatible, support strings that are
+        // exactly equal to YYYY/MM/DD, DD/MM/YYYY and MM/DD/YYYY
+        if(format === 'YYYY/MM/DD') {
+            format = '%Y/%m/%d';
+        } else if(format === 'DD/MM/YYYY') {
+            format = '%d/%m/%Y';
+        } else if(format === 'MM/DD/YYYY') {
+            format = '%m/%d/%Y';
+        }
+        // end backwards compatibility block
+ 
+        sLocale = sLocale || "en";
+
+        // Make sure we have a definition for the requested locale, or default to en.
+        if(!(sLocale in YAHOO.util.DateLocale)) {
+            if(sLocale.replace(/-[a-zA-Z]+$/, '') in YAHOO.util.DateLocale) {
+                sLocale = sLocale.replace(/-[a-zA-Z]+$/, '');
+            } else {
+                sLocale = "en";
+            }
+        }
+
+        var aLocale = YAHOO.util.DateLocale[sLocale];
+
+        var replace_aggs = function (m0, m1) {
+            var f = Dt.aggregates[m1];
+            return (f === 'locale' ? aLocale[m1] : f);
+        };
+
+        var replace_formats = function (m0, m1) {
+            var f = Dt.formats[m1];
+            if(typeof f === 'string') {             // string => built in date function
+                return oDate[f]();
+            } else if(typeof f === 'function') {    // function => our own function
+                return f.call(oDate, oDate, aLocale);
+            } else if(typeof f === 'object' && typeof f[0] === 'string') {  // built in function with padding
+                return xPad(oDate[f[0]](), f[1]);
+            } else {
+                return m1;
+            }
+        };
+
+        // First replace aggregates (run in a loop because an agg may be made up of other aggs)
+        while(format.match(/%[cDFhnrRtTxX]/)) {
+            format = format.replace(/%([cDFhnrRtTxX])/g, replace_aggs);
+        }
+
+        // Now replace formats (do not run in a loop otherwise %%a will be replace with the value of %a)
+        var str = format.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g, replace_formats);
+
+        replace_aggs = replace_formats = undefined;
+
+        return str;
+    }
+ };
+ 
+ YAHOO.namespace("YAHOO.util");
+ YAHOO.util.Date = Dt;
+
+/**
+ * The DateLocale class is a container and base class for all
+ * localised date strings used by YAHOO.util.Date. It is used
+ * internally, but may be extended to provide new date localisations.
+ *
+ * To create your own DateLocale, follow these steps:
+ * <ol>
+ *  <li>Find an existing locale that matches closely with your needs</li>
+ *  <li>Use this as your base class.  Use YAHOO.util.DateLocale if nothing
+ *   matches.</li>
+ *  <li>Create your own class as an extension of the base class using
+ *   YAHOO.lang.merge, and add your own localisations where needed.</li>
+ * </ol>
+ * See the YAHOO.util.DateLocale['en-US'] and YAHOO.util.DateLocale['en-GB']
+ * classes which extend YAHOO.util.DateLocale['en'].
+ *
+ * For example, to implement locales for French french and Canadian french,
+ * we would do the following:
+ * <ol>
+ *  <li>For French french, we have no existing similar locale, so use
+ *   YAHOO.util.DateLocale as the base, and extend it:
+ *   <pre>
+ *      YAHOO.util.DateLocale['fr'] = YAHOO.lang.merge(YAHOO.util.DateLocale, {
+ *          a: ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam'],
+ *          A: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
+ *          b: ['jan', 'f&eacute;v', 'mar', 'avr', 'mai', 'jun', 'jui', 'ao&ucirc;', 'sep', 'oct', 'nov', 'd&eacute;c'],
+ *          B: ['janvier', 'f&eacute;vrier', 'mars', 'avril', 'mai', 'juin', 'juillet', 'ao&ucirc;t', 'septembre', 'octobre', 'novembre', 'd&eacute;cembre'],
+ *          c: '%a %d %b %Y %T %Z',
+ *          p: ['', ''],
+ *          P: ['', ''],
+ *          x: '%d.%m.%Y',
+ *          X: '%T'
+ *      });
+ *   </pre>
+ *  </li>
+ *  <li>For Canadian french, we start with French french and change the meaning of \%x:
+ *   <pre>
+ *      YAHOO.util.DateLocale['fr-CA'] = YAHOO.lang.merge(YAHOO.util.DateLocale['fr'], {
+ *          x: '%Y-%m-%d'
+ *      });
+ *   </pre>
+ *  </li>
+ * </ol>
+ *
+ * With that, you can use your new locales:
+ * <pre>
+ *    var d = new Date("2008/04/22");
+ *    YAHOO.util.Date.format(d, {format: "%A, %d %B == %x"}, "fr");
+ * </pre>
+ * will return:
+ * <pre>
+ *    mardi, 22 avril == 22.04.2008
+ * </pre>
+ * And
+ * <pre>
+ *    YAHOO.util.Date.format(d, {format: "%A, %d %B == %x"}, "fr-CA");
+ * </pre>
+ * Will return:
+ * <pre>
+ *   mardi, 22 avril == 2008-04-22
+ * </pre>
+ * @namespace YAHOO.util
+ * @requires yahoo
+ * @class DateLocale
+ */
+ YAHOO.util.DateLocale = {
+        a: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+        A: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+        b: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+        B: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+        c: '%a %d %b %Y %T %Z',
+        p: ['AM', 'PM'],
+        P: ['am', 'pm'],
+        r: '%I:%M:%S %p',
+        x: '%d/%m/%y',
+        X: '%T'
+ };
+
+ YAHOO.util.DateLocale['en'] = YAHOO.lang.merge(YAHOO.util.DateLocale, {});
+
+ YAHOO.util.DateLocale['en-US'] = YAHOO.lang.merge(YAHOO.util.DateLocale['en'], {
+        c: '%a %d %b %Y %I:%M:%S %p %Z',
+        x: '%m/%d/%Y',
+        X: '%I:%M:%S %p'
+ });
+
+ YAHOO.util.DateLocale['en-GB'] = YAHOO.lang.merge(YAHOO.util.DateLocale['en'], {
+        r: '%l:%M:%S %P %Z'
+ });
+ YAHOO.util.DateLocale['en-AU'] = YAHOO.lang.merge(YAHOO.util.DateLocale['en']);
+
+})();
+
+YAHOO.register("datasource", YAHOO.util.DataSource, {version: "2.9.0", build: "2800"});

Added: branches/wf4ever/public/_javascript_s/yui/datatable.js (0 => 3265)


--- branches/wf4ever/public/_javascript_s/yui/datatable.js	                        (rev 0)
+++ branches/wf4ever/public/_javascript_s/yui/datatable.js	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,19080 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+/**
+ * Mechanism to execute a series of callbacks in a non-blocking queue.  Each callback is executed via setTimout unless configured with a negative timeout, in which case it is run in blocking mode in the same execution thread as the previous callback.  Callbacks can be function references or object literals with the following keys:
+ * <ul>
+ *    <li><code>method</code> - {Function} REQUIRED the callback function.</li>
+ *    <li><code>scope</code> - {Object} the scope from which to execute the callback.  Default is the global window scope.</li>
+ *    <li><code>argument</code> - {Array} parameters to be passed to method as individual arguments.</li>
+ *    <li><code>timeout</code> - {number} millisecond delay to wait after previous callback completion before executing this callback.  Negative values cause immediate blocking execution.  Default 0.</li>
+ *    <li><code>until</code> - {Function} boolean function executed before each iteration.  Return true to indicate completion and proceed to the next callback.</li>
+ *    <li><code>iterations</code> - {Number} number of times to execute the callback before proceeding to the next callback in the chain. Incompatible with <code>until</code>.</li>
+ * </ul>
+ *
+ * @namespace YAHOO.util
+ * @class Chain
+ * @constructor
+ * @param callback* {Function|Object} Any number of callbacks to initialize the queue
+*/
+YAHOO.util.Chain = function () {
+    /**
+     * The callback queue
+     * @property q
+     * @type {Array}
+     * @private
+     */
+    this.q = [].slice.call(arguments);
+
+    /**
+     * Event fired when the callback queue is emptied via execution (not via
+     * a call to chain.stop().
+     * @event end
+     */
+    this.createEvent('end');
+};
+
+YAHOO.util.Chain.prototype = {
+    /**
+     * Timeout id used to pause or stop execution and indicate the execution state of the Chain.  0 indicates paused or stopped, -1 indicates blocking execution, and any positive number indicates non-blocking execution.
+     * @property id
+     * @type {number}
+     * @private
+     */
+    id   : 0,
+
+    /**
+     * Begin executing the chain, or resume execution from the last paused position.
+     * @method run
+     * @return {Chain} the Chain instance
+     */
+    run : function () {
+        // Grab the first callback in the queue
+        var c  = this.q[0],
+            fn;
+
+        // If there is no callback in the queue or the Chain is currently
+        // in an execution mode, return
+        if (!c) {
+            this.fireEvent('end');
+            return this;
+        } else if (this.id) {
+            return this;
+        }
+
+        fn = c.method || c;
+
+        if (typeof fn === 'function') {
+            var o    = c.scope || {},
+                args = c.argument || [],
+                ms   = c.timeout || 0,
+                me   = this;
+                
+            if (!(args instanceof Array)) {
+                args = [args];
+            }
+
+            // Execute immediately if the callback timeout is negative.
+            if (ms < 0) {
+                this.id = ms;
+                if (c.until) {
+                    for (;!c.until();) {
+                        // Execute the callback from scope, with argument
+                        fn.apply(o,args);
+                    }
+                } else if (c.iterations) {
+                    for (;c.iterations-- > 0;) {
+                        fn.apply(o,args);
+                    }
+                } else {
+                    fn.apply(o,args);
+                }
+                this.q.shift();
+                this.id = 0;
+                return this.run();
+            } else {
+                // If the until condition is set, check if we're done
+                if (c.until) {
+                    if (c.until()) {
+                        // Shift this callback from the queue and execute the next
+                        // callback
+                        this.q.shift();
+                        return this.run();
+                    }
+                // Otherwise if either iterations is not set or we're
+                // executing the last iteration, shift callback from the queue
+                } else if (!c.iterations || !--c.iterations) {
+                    this.q.shift();
+                }
+
+                // Otherwise set to execute after the configured timeout
+                this.id = setTimeout(function () {
+                    // Execute the callback from scope, with argument
+                    fn.apply(o,args);
+                    // Check if the Chain was not paused from inside the callback
+                    if (me.id) {
+                        // Indicate ready to run state
+                        me.id = 0;
+                        // Start the fun all over again
+                        me.run();
+                    }
+                },ms);
+            }
+        }
+
+        return this;
+    },
+    
+    /**
+     * Add a callback to the end of the queue
+     * @method add
+     * @param c {Function|Object} the callback function ref or object literal
+     * @return {Chain} the Chain instance
+     */
+    add  : function (c) {
+        this.q.push(c);
+        return this;
+    },
+
+    /**
+     * Pause the execution of the Chain after the current execution of the
+     * current callback completes.  If called interstitially, clears the
+     * timeout for the pending callback. Paused Chains can be restarted with
+     * chain.run()
+     * @method pause
+     * @return {Chain} the Chain instance
+     */
+    pause: function () {
+        // Conditional added for Caja compatibility
+        if (this.id > 0) {
+            clearTimeout(this.id);
+        }
+        this.id = 0;
+        return this;
+    },
+
+    /**
+     * Stop and clear the Chain's queue after the current execution of the
+     * current callback completes.
+     * @method stop
+     * @return {Chain} the Chain instance
+     */
+    stop : function () { 
+        this.pause();
+        this.q = [];
+        return this;
+    }
+};
+YAHOO.lang.augmentProto(YAHOO.util.Chain,YAHOO.util.EventProvider);
+
+/**
+ * Augments the Event Utility with a <code>delegate</code> method that 
+ * facilitates easy creation of delegated event listeners.  (Note: Using CSS 
+ * selectors as the filtering criteria for delegated event listeners requires 
+ * inclusion of the Selector Utility.)
+ *
+ * @module event-delegate
+ * @title Event Utility Event Delegation Module
+ * @namespace YAHOO.util
+ * @requires event
+ */
+
+(function () {
+
+    var Event = YAHOO.util.Event,
+        Lang = YAHOO.lang,
+        delegates = [],
+
+
+        getMatch = function(el, selector, container) {
+        
+            var returnVal;
+        
+            if (!el || el === container) {
+                returnVal = false;
+            }
+            else {
+                returnVal = YAHOO.util.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container);
+            }
+        
+            return returnVal;
+        
+        };
+
+
+    Lang.augmentObject(Event, {
+
+        /**
+         * Creates a delegate function used to call event listeners specified 
+         * via the <code>YAHOO.util.Event.delegate</code> method.
+         *
+         * @method _createDelegate
+         *
+         * @param {Function} fn        The method (event listener) to call.
+         * @param {Function|string} filter Function or CSS selector used to 
+         * determine for what element(s) the event listener should be called.        
+         * @param {Object}   obj    An arbitrary object that will be 
+         *                             passed as a parameter to the listener.
+         * @param {Boolean|object}  overrideContext  If true, the value of the 
+         *                             obj parameter becomes the execution context
+         *                          of the listener. If an object, this object
+         *                          becomes the execution context.
+         * @return {Function} Function that will call the event listener 
+         * specified by the <code>YAHOO.util.Event.delegate</code> method.
+         * @private
+         * @for Event
+         * @static
+         */
+        _createDelegate: function (fn, filter, obj, overrideContext) {
+
+            return function (event) {
+
+                var container = this,
+                    target = Event.getTarget(event),
+                    selector = filter,
+
+                    //    The user might have specified the document object 
+                    //    as the delegation container, in which case it is not 
+                    //    nessary to scope the provided CSS selector(s) to the 
+                    //    delegation container
+                    bDocument = (container.nodeType === 9),
+
+                    matchedEl,
+                    context,
+                    sID,
+                    sIDSelector;
+
+
+                if (Lang.isFunction(filter)) {
+                    matchedEl = filter(target);
+                }
+                else if (Lang.isString(filter)) {
+
+                    if (!bDocument) {
+
+                        sID = container.id;
+
+                        if (!sID) {
+                            sID = Event.generateId(container);
+                        }                        
+
+                        //    Scope all selectors to the container
+                        sIDSelector = ("#" + sID + " ");
+                        selector = (sIDSelector + filter).replace(/,/gi, ("," + sIDSelector));
+
+                    }
+
+
+                    if (YAHOO.util.Selector.test(target, selector)) {
+                        matchedEl = target;
+                    }
+                    else if (YAHOO.util.Selector.test(target, ((selector.replace(/,/gi, " *,")) + " *"))) {
+
+                        //    The target is a descendant of an element matching 
+                        //    the selector, so crawl up to find the ancestor that 
+                        //    matches the selector
+
+                        matchedEl = getMatch(target, selector, container);
+
+                    }
+
+                }
+
+
+                if (matchedEl) {
+
+                    //    The default context for delegated listeners is the 
+                    //    element that matched the filter.
+
+                    context = matchedEl;
+
+                    if (overrideContext) {
+                        if (overrideContext === true) {
+                            context = obj;
+                        } else {
+                            context = overrideContext;
+                        }
+                    }
+
+                    //    Call the listener passing in the container and the 
+                    //    element that matched the filter in case the user 
+                    //    needs those.
+
+                    return fn.call(context, event, matchedEl, container, obj);
+
+                }
+
+            };
+
+        },
+
+
+        /**
+         * Appends a delegated event listener.  Delegated event listeners 
+         * receive three arguments by default: the DOM event, the element  
+         * specified by the filtering function or CSS selector, and the 
+         * container element (the element to which the event listener is 
+         * bound).  (Note: Using the delegate method requires the event-delegate 
+         * module.  Using CSS selectors as the filtering criteria for delegated 
+         * event listeners requires inclusion of the Selector Utility.)
+         *
+         * @method delegate
+         *
+         * @param {String|HTMLElement|Array|NodeList} container An id, an element 
+         *  reference, or a collection of ids and/or elements to assign the 
+         *  listener to.
+         * @param {String}   type     The type of event listener to append
+         * @param {Function} fn        The method the event invokes
+         * @param {Function|string} filter Function or CSS selector used to 
+         * determine for what element(s) the event listener should be called. 
+         * When a function is specified, the function should return an 
+         * HTML element.  Using a CSS Selector requires the inclusion of the 
+         * CSS Selector Utility.
+         * @param {Object}   obj    An arbitrary object that will be 
+         *                             passed as a parameter to the listener
+         * @param {Boolean|object}  overrideContext  If true, the value of the obj parameter becomes
+         *                             the execution context of the listener. If an
+         *                             object, this object becomes the execution
+         *                             context.
+         * @return {Boolean} Returns true if the action was successful or defered,
+         *                   false if one or more of the elements 
+         *                   could not have the listener attached,
+         *                   or if the operation throws an exception.
+         * @static
+         * @for Event
+         */
+        delegate: function (container, type, fn, filter, obj, overrideContext) {
+
+            var sType = type,
+                fnMouseDelegate,
+                fnDelegate;
+
+
+            if (Lang.isString(filter) && !YAHOO.util.Selector) {
+                return false;
+            }
+
+
+            if (type == "mouseenter" || type == "mouseleave") {
+
+                if (!Event._createMouseDelegate) {
+                    return false;
+                }
+
+                //    Look up the real event--either mouseover or mouseout
+                sType = Event._getType(type);
+
+                fnMouseDelegate = Event._createMouseDelegate(fn, obj, overrideContext);
+
+                fnDelegate = Event._createDelegate(function (event, matchedEl, container) {
+
+                    return fnMouseDelegate.call(matchedEl, event, container);
+
+                }, filter, obj, overrideContext);
+
+            }
+            else {
+
+                fnDelegate = Event._createDelegate(fn, filter, obj, overrideContext);
+
+            }
+
+            delegates.push([container, sType, fn, fnDelegate]);
+            
+            return Event.on(container, sType, fnDelegate);
+
+        },
+
+
+        /**
+         * Removes a delegated event listener.
+         *
+         * @method removeDelegate
+         *
+         * @param {String|HTMLElement|Array|NodeList} container An id, an element 
+         *  reference, or a collection of ids and/or elements to remove
+         *  the listener from.
+         * @param {String} type The type of event to remove.
+         * @param {Function} fn The method the event invokes.  If fn is
+         *  undefined, then all event listeners for the type of event are 
+         *  removed.
+         * @return {boolean} Returns true if the unbind was successful, false 
+         *  otherwise.
+         * @static
+         * @for Event
+         */
+        removeDelegate: function (container, type, fn) {
+
+            var sType = type,
+                returnVal = false,
+                index,
+                cacheItem;
+
+            //    Look up the real event--either mouseover or mouseout
+            if (type == "mouseenter" || type == "mouseleave") {
+                sType = Event._getType(type);
+            }
+
+            index = Event._getCacheIndex(delegates, container, sType, fn);
+
+            if (index >= 0) {
+                cacheItem = delegates[index];
+            }
+
+
+            if (container && cacheItem) {
+
+                returnVal = Event.removeListener(cacheItem[0], cacheItem[1], cacheItem[3]);
+
+                if (returnVal) {
+                    delete delegates[index][2];
+                    delete delegates[index][3];
+                    delegates.splice(index, 1);
+                }        
+        
+            }
+
+            return returnVal;
+
+        }
+        
+    });
+
+}());
+
+
+/**
+ * Augments the Event Utility with support for the mouseenter and mouseleave
+ * events:  A mouseenter event fires the first time the mouse enters an
+ * element; a mouseleave event first the first time the mouse leaves an
+ * element.
+ *
+ * @module event-mouseenter
+ * @title Event Utility mouseenter and mouseout Module
+ * @namespace YAHOO.util
+ * @requires event
+ */
+
+(function () {
+
+    var Event = YAHOO.util.Event,
+        Lang = YAHOO.lang,
+
+        addListener = Event.addListener,
+        removeListener = Event.removeListener,
+        getListeners = Event.getListeners,
+
+        delegates = [],
+
+        specialTypes = {
+            mouseenter: "mouseover",
+            mouseleave: "mouseout"
+        },
+
+        remove = function(el, type, fn) {
+
+            var index = Event._getCacheIndex(delegates, el, type, fn),
+                cacheItem,
+                returnVal;
+
+            if (index >= 0) {
+                cacheItem = delegates[index];
+            }
+
+            if (el && cacheItem) {
+
+                //    removeListener will translate the value of type
+                returnVal = removeListener.call(Event, cacheItem[0], type, cacheItem[3]);
+
+                if (returnVal) {
+                    delete delegates[index][2];
+                    delete delegates[index][3];
+                    delegates.splice(index, 1);
+                }
+
+            }
+
+            return returnVal;
+
+        };
+
+
+    Lang.augmentObject(Event._specialTypes, specialTypes);
+
+    Lang.augmentObject(Event, {
+
+        /**
+         * Creates a delegate function used to call mouseover and mouseleave
+         * event listeners specified via the
+         * <code>YAHOO.util.Event.addListener</code>
+         * or <code>YAHOO.util.Event.on</code> method.
+         *
+         * @method _createMouseDelegate
+         *
+         * @param {Function} fn        The method (event listener) to call
+         * @param {Object}   obj    An arbitrary object that will be
+         *                             passed as a parameter to the listener
+         * @param {Boolean|object}  overrideContext  If true, the value of the
+         *                             obj parameter becomes the execution context
+         *                          of the listener. If an object, this object
+         *                          becomes the execution context.
+         * @return {Function} Function that will call the event listener
+         * specified by either the <code>YAHOO.util.Event.addListener</code>
+         * or <code>YAHOO.util.Event.on</code> method.
+         * @private
+         * @static
+         * @for Event
+         */
+        _createMouseDelegate: function (fn, obj, overrideContext) {
+
+            return function (event, container) {
+
+                var el = this,
+                    relatedTarget = Event.getRelatedTarget(event),
+                    context,
+                    args;
+
+                if (el != relatedTarget && !YAHOO.util.Dom.isAncestor(el, relatedTarget)) {
+
+                    context = el;
+
+                    if (overrideContext) {
+                        if (overrideContext === true) {
+                            context = obj;
+                        } else {
+                            context = overrideContext;
+                        }
+                    }
+
+                    // The default args passed back to a mouseenter or
+                    // mouseleave listener are: the event, and any object
+                    // the user passed when subscribing
+
+                    args = [event, obj];
+
+                    // Add the element and delegation container as arguments
+                    // when delegating mouseenter and mouseleave
+
+                    if (container) {
+                        args.splice(1, 0, el, container);
+                    }
+
+                    return fn.apply(context, args);
+
+                }
+
+            };
+
+        },
+
+        addListener: function (el, type, fn, obj, overrideContext) {
+
+            var fnDelegate,
+                returnVal;
+
+            if (specialTypes[type]) {
+
+                fnDelegate = Event._createMouseDelegate(fn, obj, overrideContext);
+
+                fnDelegate.mouseDelegate = true;
+
+                delegates.push([el, type, fn, fnDelegate]);
+
+                //    addListener will translate the value of type
+                returnVal = addListener.call(Event, el, type, fnDelegate);
+
+            }
+            else {
+                returnVal = addListener.apply(Event, arguments);
+            }
+
+            return returnVal;
+
+        },
+
+        removeListener: function (el, type, fn) {
+
+            var returnVal;
+
+            if (specialTypes[type]) {
+                returnVal = remove.apply(Event, arguments);
+            }
+            else {
+                returnVal = removeListener.apply(Event, arguments);
+            }
+
+            return returnVal;
+
+        },
+
+        getListeners: function (el, type) {
+
+            //    If the user specified the type as mouseover or mouseout,
+            //    need to filter out those used by mouseenter and mouseleave.
+            //    If the user specified the type as mouseenter or mouseleave,
+            //    need to filter out the true mouseover and mouseout listeners.
+
+            var listeners = [],
+                elListeners,
+                bMouseOverOrOut = (type === "mouseover" || type === "mouseout"),
+                bMouseDelegate,
+                i,
+                l;
+
+            if (type && (bMouseOverOrOut || specialTypes[type])) {
+
+                elListeners = getListeners.call(Event, el, this._getType(type));
+
+                if (elListeners) {
+
+                    for (i=elListeners.length-1; i>-1; i--) {
+
+                        l = elListeners[i];
+                        bMouseDelegate = l.fn.mouseDelegate;
+
+                        if ((specialTypes[type] && bMouseDelegate) || (bMouseOverOrOut && !bMouseDelegate)) {
+                            listeners.push(l);
+                        }
+
+                    }
+
+                }
+
+            }
+            else {
+                listeners = getListeners.apply(Event, arguments);
+            }
+
+            return (listeners && listeners.length) ? listeners : null;
+
+        }
+
+    }, true);
+
+    Event.on = Event.addListener;
+
+}());
+YAHOO.register("event-mouseenter", YAHOO.util.Event, {version: "2.9.0", build: "2800"});
+
+var Y = YAHOO,
+    Y_DOM = YAHOO.util.Dom,
+    EMPTY_ARRAY = [],
+    Y_UA = Y.env.ua,
+    Y_Lang = Y.lang,
+    Y_DOC = document,
+    Y_DOCUMENT_ELEMENT = Y_DOC.documentElement,
+
+    Y_DOM_inDoc = Y_DOM.inDocument,
+    Y_mix = Y_Lang.augmentObject,
+    Y_guid = Y_DOM.generateId,
+
+    Y_getDoc = function(element) {
+        var doc = Y_DOC;
+        if (element) {
+            doc = (element.nodeType === 9) ? element : // element === document
+                element.ownerDocument || // element === DOM node
+                element.document || // element === window
+                Y_DOC; // default
+        }
+
+        return doc;
+    },
+
+    Y_Array = function(o, startIdx) {
+        var l, a, start = startIdx || 0;
+
+        // IE errors when trying to slice HTMLElement collections
+        try {
+            return Array.prototype.slice.call(o, start);
+        } catch (e) {
+            a = [];
+            l = o.length;
+            for (; start < l; start++) {
+                a.push(o[start]);
+            }
+            return a;
+        }
+    },
+
+    Y_DOM_allById = function(id, root) {
+        root = root || Y_DOC;
+        var nodes = [],
+            ret = [],
+            i,
+            node;
+
+        if (root.querySelectorAll) {
+            ret = root.querySelectorAll('[id="' + id + '"]');
+        } else if (root.all) {
+            nodes = root.all(id);
+
+            if (nodes) {
+                // root.all may return HTMLElement or HTMLCollection.
+                // some elements are also HTMLCollection (FORM, SELECT).
+                if (nodes.nodeName) {
+                    if (nodes.id === id) { // avoid false positive on name
+                        ret.push(nodes);
+                        nodes = EMPTY_ARRAY; // done, no need to filter
+                    } else { //  prep for filtering
+                        nodes = [nodes];
+                    }
+                }
+
+                if (nodes.length) {
+                    // filter out matches on node.name
+                    // and element.id as reference to element with id === 'id'
+                    for (i = 0; node = nodes[i++];) {
+                        if (node.id === id  ||
+                                (node.attributes && node.attributes.id &&
+                                node.attributes.id.value === id)) {
+                            ret.push(node);
+                        }
+                    }
+                }
+            }
+        } else {
+            ret = [Y_getDoc(root).getElementById(id)];
+        }
+
+        return ret;
+    };
+
+/**
+ * The selector-native module provides support for native querySelector
+ * @module dom
+ * @submodule selector-native
+ * @for Selector
+ */
+
+/**
+ * Provides support for using CSS selectors to query the DOM
+ * @class Selector
+ * @static
+ * @for Selector
+ */
+
+var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+    OWNER_DOCUMENT = 'ownerDocument',
+
+Selector = {
+    _foundCache: [],
+
+    useNative: true,
+
+    _compare: ('sourceIndex' in Y_DOCUMENT_ELEMENT) ?
+        function(nodeA, nodeB) {
+            var a = nodeA.sourceIndex,
+                b = nodeB.sourceIndex;
+
+            if (a === b) {
+                return 0;
+            } else if (a > b) {
+                return 1;
+            }
+
+            return -1;
+
+        } : (Y_DOCUMENT_ELEMENT[COMPARE_DOCUMENT_POSITION] ?
+        function(nodeA, nodeB) {
+            if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
+                return -1;
+            } else {
+                return 1;
+            }
+        } :
+        function(nodeA, nodeB) {
+            var rangeA, rangeB, compare;
+            if (nodeA && nodeB) {
+                rangeA = nodeA[OWNER_DOCUMENT].createRange();
+                rangeA.setStart(nodeA, 0);
+                rangeB = nodeB[OWNER_DOCUMENT].createRange();
+                rangeB.setStart(nodeB, 0);
+                compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
+            }
+
+            return compare;
+
+    }),
+
+    _sort: function(nodes) {
+        if (nodes) {
+            nodes = Y_Array(nodes, 0, true);
+            if (nodes.sort) {
+                nodes.sort(Selector._compare);
+            }
+        }
+
+        return nodes;
+    },
+
+    _deDupe: function(nodes) {
+        var ret = [],
+            i, node;
+
+        for (i = 0; (node = nodes[i++]);) {
+            if (!node._found) {
+                ret[ret.length] = node;
+                node._found = true;
+            }
+        }
+
+        for (i = 0; (node = ret[i++]);) {
+            node._found = null;
+            node.removeAttribute('_found');
+        }
+
+        return ret;
+    },
+
+    /**
+     * Retrieves a set of nodes based on a given CSS selector.
+     * @method query
+     *
+     * @param {string} selector The CSS Selector to test the node against.
+     * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
+     * @param {Boolean} firstOnly optional Whether or not to return only the first match.
+     * @return {Array} An array of nodes that match the given selector.
+     * @static
+     */
+    query: function(selector, root, firstOnly, skipNative) {
+        if (typeof root == 'string') {
+            root = Y_DOM.get(root);
+            if (!root) {
+                return (firstOnly) ? null : [];
+            }
+        } else {
+            root = root || Y_DOC;
+        }
+
+        var ret = [],
+            useNative = (Selector.useNative && Y_DOC.querySelector && !skipNative),
+            queries = [[selector, root]],
+            query,
+            result,
+            i,
+            fn = (useNative) ? Selector._nativeQuery : Selector._bruteQuery;
+
+        if (selector && fn) {
+            // split group into seperate queries
+            if (!skipNative && // already done if skipping
+                    (!useNative || root.tagName)) { // split native when element scoping is needed
+                queries = Selector._splitQueries(selector, root);
+            }
+
+            for (i = 0; (query = queries[i++]);) {
+                result = fn(query[0], query[1], firstOnly);
+                if (!firstOnly) { // coerce DOM Collection to Array
+                    result = Y_Array(result, 0, true);
+                }
+                if (result) {
+                    ret = ret.concat(result);
+                }
+            }
+
+            if (queries.length > 1) { // remove dupes and sort by doc order
+                ret = Selector._sort(Selector._deDupe(ret));
+            }
+        }
+
+        Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
+        return (firstOnly) ? (ret[0] || null) : ret;
+
+    },
+
+    // allows element scoped queries to begin with combinator
+    // e.g. query('> p', document.body) === query('body > p')
+    _splitQueries: function(selector, node) {
+        var groups = selector.split(','),
+            queries = [],
+            prefix = '',
+            i, len;
+
+        if (node) {
+            // enforce for element scoping
+            if (node.tagName) {
+                node.id = node.id || Y_guid();
+                prefix = '[id="' + node.id + '"] ';
+            }
+
+            for (i = 0, len = groups.length; i < len; ++i) {
+                selector =  prefix + groups[i];
+                queries.push([selector, node]);
+            }
+        }
+
+        return queries;
+    },
+
+    _nativeQuery: function(selector, root, one) {
+        if (Y_UA.webkit && selector.indexOf(':checked') > -1 &&
+                (Selector.pseudos && Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected"
+            return Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
+        }
+        try {
+            //Y.log('trying native query with: ' + selector, 'info', 'selector-native');
+            return root['querySelector' + (one ? '' : 'All')](selector);
+        } catch(e) { // fallback to brute if available
+            //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
+            return Selector.query(selector, root, one, true); // redo with skipNative true
+        }
+    },
+
+    filter: function(nodes, selector) {
+        var ret = [],
+            i, node;
+
+        if (nodes && selector) {
+            for (i = 0; (node = nodes[i++]);) {
+                if (Selector.test(node, selector)) {
+                    ret[ret.length] = node;
+                }
+            }
+        } else {
+            Y.log('invalid filter input (nodes: ' + nodes +
+                    ', selector: ' + selector + ')', 'warn', 'Selector');
+        }
+
+        return ret;
+    },
+
+    test: function(node, selector, root) {
+        var ret = false,
+            groups = selector.split(','),
+            useFrag = false,
+            parent,
+            item,
+            items,
+            frag,
+            i, j, group;
+
+        if (node && node.tagName) { // only test HTMLElements
+
+            // we need a root if off-doc
+            if (!root && !Y_DOM_inDoc(node)) {
+                parent = node.parentNode;
+                if (parent) {
+                    root = parent;
+                } else { // only use frag when no parent to query
+                    frag = node[OWNER_DOCUMENT].createDocumentFragment();
+                    frag.appendChild(node);
+                    root = frag;
+                    useFrag = true;
+                }
+            }
+            root = root || node[OWNER_DOCUMENT];
+
+            if (!node.id) {
+                node.id = Y_guid();
+            }
+            for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
+                group += '[id="' + node.id + '"]';
+                items = Selector.query(group, root);
+
+                for (j = 0; item = items[j++];) {
+                    if (item === node) {
+                        ret = true;
+                        break;
+                    }
+                }
+                if (ret) {
+                    break;
+                }
+            }
+
+            if (useFrag) { // cleanup
+                frag.removeChild(node);
+            }
+        }
+
+        return ret;
+    }
+
+};
+
+YAHOO.util.Selector = Selector;
+/**
+ * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
+ * @module dom
+ * @submodule selector-css2
+ * @for Selector
+ */
+
+/**
+ * Provides helper methods for collecting and filtering DOM elements.
+ */
+
+var PARENT_NODE = 'parentNode',
+    TAG_NAME = 'tagName',
+    ATTRIBUTES = 'attributes',
+    COMBINATOR = 'combinator',
+    PSEUDOS = 'pseudos',
+
+    SelectorCSS2 = {
+        _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move?
+        SORT_RESULTS: true,
+        _children: function(node, tag) {
+            var ret = node.children,
+                i,
+                children = [],
+                childNodes,
+                child;
+
+            if (node.children && tag && node.children.tags) {
+                children = node.children.tags(tag);
+            } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
+                childNodes = ret || node.childNodes;
+                ret = [];
+                for (i = 0; (child = childNodes[i++]);) {
+                    if (child.tagName) {
+                        if (!tag || tag === child.tagName) {
+                            ret.push(child);
+                        }
+                    }
+                }
+            }
+
+            return ret || [];
+        },
+
+        _re: {
+            //attr: /(\[.*\])/g,
+            attr: /(\[[^\]]*\])/g,
+            //esc: /\\[:\[][\w\d\]]*/gi,
+            esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
+            //pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\))*)/i
+            pseudos: /(\([^\)]*\))/g
+        },
+
+        /**
+         * Mapping of shorthand tokens to corresponding attribute selector
+         * @property shorthand
+         * @type object
+         */
+        shorthand: {
+            //'\\#([^\\s\\\\(\\[:]*)': '[id=$1]',
+            '\\#(-?[_a-z]+[-\\w\\uE000]*)': '[id=$1]',
+            //'\\#([^\\s\\\.:\\[\\]]*)': '[id=$1]',
+            //'\\.([^\\s\\\\(\\[:]*)': '[className=$1]'
+            '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]'
+        },
+
+        /**
+         * List of operators and corresponding boolean functions.
+         * These functions are passed the attribute and the current node's value of the attribute.
+         * @property operators
+         * @type object
+         */
+        operators: {
+            '': function(node, attr) { return !!node.getAttribute(attr); }, // Just test for existence of attribute
+            //'': '.+',
+            //'=': '^{val}$', // equality
+            '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
+            '|=': '^{val}(?:-|$)' // optional hyphen-delimited
+        },
+
+        pseudos: {
+           'first-child': function(node) {
+                return Selector._children(node[PARENT_NODE])[0] === node;
+            }
+        },
+
+        _bruteQuery: function(selector, root, firstOnly) {
+            var ret = [],
+                nodes = [],
+                tokens = Selector._tokenize(selector),
+                token = tokens[tokens.length - 1],
+                rootDoc = Y_getDoc(root),
+                child,
+                id,
+                className,
+                tagName;
+
+
+            // if we have an initial ID, set to root when in document
+            /*
+            if (tokens[0] && rootDoc === root &&
+                    (id = tokens[0].id) &&
+                    rootDoc.getElementById(id)) {
+                root = rootDoc.getElementById(id);
+            }
+            */
+
+            if (token) {
+                // prefilter nodes
+                id = token.id;
+                className = token.className;
+                tagName = token.tagName || '*';
+
+                if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
+                    // try ID first, unless no root.all && root not in document
+                    // (root.all works off document, but not getElementById)
+                    // TODO: move to allById?
+                    if (id && (root.all || (root.nodeType === 9 || Y_DOM_inDoc(root)))) {
+                        nodes = Y_DOM_allById(id, root);
+                    // try className
+                    } else if (className) {
+                        nodes = root.getElementsByClassName(className);
+                    } else { // default to tagName
+                        nodes = root.getElementsByTagName(tagName);
+                    }
+
+                } else { // brute getElementsByTagName('*')
+                    child = root.firstChild;
+                    while (child) {
+                        if (child.tagName) { // only collect HTMLElements
+                            nodes.push(child);
+                        }
+                        child = child.nextSilbing || child.firstChild;
+                    }
+                }
+                if (nodes.length) {
+                    ret = Selector._filterNodes(nodes, tokens, firstOnly);
+                }
+            }
+
+            return ret;
+        },
+
+        _filterNodes: function(nodes, tokens, firstOnly) {
+            var i = 0,
+                j,
+                len = tokens.length,
+                n = len - 1,
+                result = [],
+                node = nodes[0],
+                tmpNode = node,
+                getters = Selector.getters,
+                operator,
+                combinator,
+                token,
+                path,
+                pass,
+                //FUNCTION = 'function',
+                value,
+                tests,
+                test;
+
+            //do {
+            for (i = 0; (tmpNode = node = nodes[i++]);) {
+                n = len - 1;
+                path = null;
+
+                testLoop:
+                while (tmpNode && tmpNode.tagName) {
+                    token = tokens[n];
+                    tests = token.tests;
+                    j = tests.length;
+                    if (j && !pass) {
+                        while ((test = tests[--j])) {
+                            operator = test[1];
+                            if (getters[test[0]]) {
+                                value = getters[test[0]](tmpNode, test[0]);
+                            } else {
+                                value = tmpNode[test[0]];
+                                // use getAttribute for non-standard attributes
+                                if (value === undefined && tmpNode.getAttribute) {
+                                    value = tmpNode.getAttribute(test[0]);
+                                }
+                            }
+
+                            if ((operator === '=' && value !== test[2]) ||  // fast path for equality
+                                (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
+                                operator.test && !operator.test(value)) ||  // regex test
+                                (!operator.test && // protect against RegExp as function (webkit)
+                                        typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test
+
+                                // skip non element nodes or non-matching tags
+                                if ((tmpNode = tmpNode[path])) {
+                                    while (tmpNode &&
+                                        (!tmpNode.tagName ||
+                                            (token.tagName && token.tagName !== tmpNode.tagName))
+                                    ) {
+                                        tmpNode = tmpNode[path];
+                                    }
+                                }
+                                continue testLoop;
+                            }
+                        }
+                    }
+
+                    n--; // move to next token
+                    // now that we've passed the test, move up the tree by combinator
+                    if (!pass && (combinator = token.combinator)) {
+                        path = combinator.axis;
+                        tmpNode = tmpNode[path];
+
+                        // skip non element nodes
+                        while (tmpNode && !tmpNode.tagName) {
+                            tmpNode = tmpNode[path];
+                        }
+
+                        if (combinator.direct) { // one pass only
+                            path = null;
+                        }
+
+                    } else { // success if we made it this far
+                        result.push(node);
+                        if (firstOnly) {
+                            return result;
+                        }
+                        break;
+                    }
+                }
+            }// while (tmpNode = node = nodes[++i]);
+            node = tmpNode = null;
+            return result;
+        },
+
+        combinators: {
+            ' ': {
+                axis: 'parentNode'
+            },
+
+            '>': {
+                axis: 'parentNode',
+                direct: true
+            },
+
+
+            '+': {
+                axis: 'previousSibling',
+                direct: true
+            }
+        },
+
+        _parsers: [
+            {
+                name: ATTRIBUTES,
+                //re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
+                re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,
+                fn: function(match, token) {
+                    var operator = match[2] || '',
+                        operators = Selector.operators,
+                        escVal = (match[3]) ? match[3].replace(/\\/g, '') : '',
+                        test;
+
+                    // add prefiltering for ID and CLASS
+                    if ((match[1] === 'id' && operator === '=') ||
+                            (match[1] === 'className' &&
+                            Y_DOCUMENT_ELEMENT.getElementsByClassName &&
+                            (operator === '~=' || operator === '='))) {
+                        token.prefilter = match[1];
+
+
+                        match[3] = escVal;
+
+                        // escape all but ID for prefilter, which may run through QSA (via Dom.allById)
+                        token[match[1]] = (match[1] === 'id') ? match[3] : escVal;
+
+                    }
+
+                    // add tests
+                    if (operator in operators) {
+                        test = operators[operator];
+                        if (typeof test === 'string') {
+                            match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1');
+                            test = new RegExp(test.replace('{val}', match[3]));
+                        }
+                        match[2] = test;
+                    }
+                    if (!token.last || token.prefilter !== match[1]) {
+                        return match.slice(1);
+                    }
+                }
+
+            },
+            {
+                name: TAG_NAME,
+                re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
+                fn: function(match, token) {
+                    var tag = match[1].toUpperCase();
+                    token.tagName = tag;
+
+                    if (tag !== '*' && (!token.last || token.prefilter)) {
+                        return [TAG_NAME, '=', tag];
+                    }
+                    if (!token.prefilter) {
+                        token.prefilter = 'tagName';
+                    }
+                }
+            },
+            {
+                name: COMBINATOR,
+                re: /^\s*([>+~]|\s)\s*/,
+                fn: function(match, token) {
+                }
+            },
+            {
+                name: PSEUDOS,
+                re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,
+                fn: function(match, token) {
+                    var test = Selector[PSEUDOS][match[1]];
+                    if (test) { // reorder match array and unescape special chars for tests
+                        if (match[2]) {
+                            match[2] = match[2].replace(/\\/g, '');
+                        }
+                        return [match[2], test];
+                    } else { // selector token not supported (possibly missing CSS3 module)
+                        return false;
+                    }
+                }
+            }
+            ],
+
+        _getToken: function(token) {
+            return {
+                tagName: null,
+                id: null,
+                className: null,
+                attributes: {},
+                combinator: null,
+                tests: []
+            };
+        },
+
+        /**
+            Break selector into token units per simple selector.
+            Combinator is attached to the previous token.
+         */
+        _tokenize: function(selector) {
+            selector = selector || '';
+            selector = Selector._replaceShorthand(Y_Lang.trim(selector));
+            var token = Selector._getToken(),     // one token per simple selector (left selector holds combinator)
+                query = selector, // original query for debug report
+                tokens = [],    // array of tokens
+                found = false,  // whether or not any matches were found this pass
+                match,         // the regex match
+                test,
+                i, parser;
+
+            /*
+                Search for selector patterns, store, and strip them from the selector string
+                until no patterns match (invalid selector) or we run out of chars.
+
+                Multiple attributes and pseudos are allowed, in any order.
+                for example:
+                    'form:first-child[type=button]:not(button)[lang|=en]'
+            */
+
+            outer:
+            do {
+                found = false; // reset after full pass
+
+                for (i = 0; (parser = Selector._parsers[i++]);) {
+                    if ( (match = parser.re.exec(selector)) ) { // note assignment
+                        if (parser.name !== COMBINATOR ) {
+                            token.selector = selector;
+                        }
+                        selector = selector.replace(match[0], ''); // strip current match from selector
+                        if (!selector.length) {
+                            token.last = true;
+                        }
+
+                        if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
+                            match[1] = Selector._attrFilters[match[1]];
+                        }
+
+                        test = parser.fn(match, token);
+                        if (test === false) { // selector not supported
+                            found = false;
+                            break outer;
+                        } else if (test) {
+                            token.tests.push(test);
+                        }
+
+                        if (!selector.length || parser.name === COMBINATOR) {
+                            tokens.push(token);
+                            token = Selector._getToken(token);
+                            if (parser.name === COMBINATOR) {
+                                token.combinator = Selector.combinators[match[1]];
+                            }
+                        }
+                        found = true;
+
+
+                    }
+                }
+            } while (found && selector.length);
+
+            if (!found || selector.length) { // not fully parsed
+                Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
+                tokens = [];
+            }
+            return tokens;
+        },
+
+        _replaceShorthand: function(selector) {
+            var shorthand = Selector.shorthand,
+                esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
+                attrs,
+                pseudos,
+                re, i, len;
+
+            if (esc) {
+                selector = selector.replace(Selector._re.esc, '\uE000');
+            }
+
+            attrs = selector.match(Selector._re.attr);
+            pseudos = selector.match(Selector._re.pseudos);
+
+            if (attrs) {
+                selector = selector.replace(Selector._re.attr, '\uE001');
+            }
+
+            if (pseudos) {
+                selector = selector.replace(Selector._re.pseudos, '\uE002');
+            }
+
+
+            for (re in shorthand) {
+                if (shorthand.hasOwnProperty(re)) {
+                    selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
+                }
+            }
+
+            if (attrs) {
+                for (i = 0, len = attrs.length; i < len; ++i) {
+                    selector = selector.replace(/\uE001/, attrs[i]);
+                }
+            }
+
+            if (pseudos) {
+                for (i = 0, len = pseudos.length; i < len; ++i) {
+                    selector = selector.replace(/\uE002/, pseudos[i]);
+                }
+            }
+
+            selector = selector.replace(/\[/g, '\uE003');
+            selector = selector.replace(/\]/g, '\uE004');
+
+            selector = selector.replace(/\(/g, '\uE005');
+            selector = selector.replace(/\)/g, '\uE006');
+
+            if (esc) {
+                for (i = 0, len = esc.length; i < len; ++i) {
+                    selector = selector.replace('\uE000', esc[i]);
+                }
+            }
+
+            return selector;
+        },
+
+        _attrFilters: {
+            'class': 'className',
+            'for': 'htmlFor'
+        },
+
+        getters: {
+            href: function(node, attr) {
+                return Y_DOM.getAttribute(node, attr);
+            }
+        }
+    };
+
+Y_mix(Selector, SelectorCSS2, true);
+Selector.getters.src = "" = Selector.getters.href;
+
+// IE wants class with native queries
+if (Selector.useNative && Y_DOC.querySelector) {
+    Selector.shorthand['\\.([^\\s\\\\(\\[:]*)'] = '[class~=$1]';
+}
+
+/**
+ * The selector css3 module provides support for css3 selectors.
+ * @module dom
+ * @submodule selector-css3
+ * @for Selector
+ */
+
+/*
+    an+b = get every _a_th node starting at the _b_th
+    0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element
+    1n+b =  get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
+    an+0 = get every _a_th element, "0" may be omitted
+*/
+
+Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
+
+Selector._getNth = function(node, expr, tag, reverse) {
+    Selector._reNth.test(expr);
+    var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
+        n = RegExp.$2, // "n"
+        oddeven = RegExp.$3, // "odd" or "even"
+        b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
+        result = [],
+        siblings = Selector._children(node.parentNode, tag),
+        op;
+
+    if (oddeven) {
+        a = 2; // always every other
+        op = '+';
+        n = 'n';
+        b = (oddeven === 'odd') ? 1 : 0;
+    } else if ( isNaN(a) ) {
+        a = (n) ? 1 : 0; // start from the first or no repeat
+    }
+
+    if (a === 0) { // just the first
+        if (reverse) {
+            b = siblings.length - b + 1;
+        }
+
+        if (siblings[b - 1] === node) {
+            return true;
+        } else {
+            return false;
+        }
+
+    } else if (a < 0) {
+        reverse = !!reverse;
+        a = Math.abs(a);
+    }
+
+    if (!reverse) {
+        for (var i = b - 1, len = siblings.length; i < len; i += a) {
+            if ( i >= 0 && siblings[i] === node ) {
+                return true;
+            }
+        }
+    } else {
+        for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) {
+            if ( i < len && siblings[i] === node ) {
+                return true;
+            }
+        }
+    }
+    return false;
+};
+
+Y_mix(Selector.pseudos, {
+    'root': function(node) {
+        return node === node.ownerDocument.documentElement;
+    },
+
+    'nth-child': function(node, expr) {
+        return Selector._getNth(node, expr);
+    },
+
+    'nth-last-child': function(node, expr) {
+        return Selector._getNth(node, expr, null, true);
+    },
+
+    'nth-of-type': function(node, expr) {
+        return Selector._getNth(node, expr, node.tagName);
+    },
+
+    'nth-last-of-type': function(node, expr) {
+        return Selector._getNth(node, expr, node.tagName, true);
+    },
+
+    'last-child': function(node) {
+        var children = Selector._children(node.parentNode);
+        return children[children.length - 1] === node;
+    },
+
+    'first-of-type': function(node) {
+        return Selector._children(node.parentNode, node.tagName)[0] === node;
+    },
+
+    'last-of-type': function(node) {
+        var children = Selector._children(node.parentNode, node.tagName);
+        return children[children.length - 1] === node;
+    },
+
+    'only-child': function(node) {
+        var children = Selector._children(node.parentNode);
+        return children.length === 1 && children[0] === node;
+    },
+
+    'only-of-type': function(node) {
+        var children = Selector._children(node.parentNode, node.tagName);
+        return children.length === 1 && children[0] === node;
+    },
+
+    'empty': function(node) {
+        return node.childNodes.length === 0;
+    },
+
+    'not': function(node, expr) {
+        return !Selector.test(node, expr);
+    },
+
+    'contains': function(node, expr) {
+        var text = node.innerText || node.textContent || '';
+        return text.indexOf(expr) > -1;
+    },
+
+    'checked': function(node) {
+        return (node.checked === true || node.selected === true);
+    },
+
+    enabled: function(node) {
+        return (node.disabled !== undefined && !node.disabled);
+    },
+
+    disabled: function(node) {
+        return (node.disabled);
+    }
+});
+
+Y_mix(Selector.operators, {
+    '^=': '^{val}', // Match starts with value
+    '!=': function(node, attr, val) { return node[attr] !== val; }, // Match starts with value
+    '$=': '{val}$', // Match ends with value
+    '*=': '{val}' // Match contains value as substring
+});
+
+Selector.combinators['~'] = {
+    axis: 'previousSibling'
+};
+YAHOO.register("selector", YAHOO.util.Selector, {version: "2.9.0", build: "2800"});
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+var Dom = YAHOO.util.Dom;
+
+/**
+ * The ColumnSet class defines and manages a DataTable's Columns,
+ * including nested hierarchies and access to individual Column instances.
+ *
+ * @namespace YAHOO.widget
+ * @class ColumnSet
+ * @uses YAHOO.util.EventProvider
+ * @constructor
+ * @param aDefinitions {Object[]} Array of object literals that define cells in
+ * the THEAD.
+ */
+YAHOO.widget.ColumnSet = function(aDefinitions) {
+    this._sId = Dom.generateId(null, "yui-cs"); // "yui-cs" + YAHOO.widget.ColumnSet._nCount;
+
+    // First clone the defs
+    aDefinitions = YAHOO.widget.DataTable._cloneObject(aDefinitions);
+    this._init(aDefinitions);
+
+    YAHOO.widget.ColumnSet._nCount++;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private member variables
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Internal class variable to index multiple ColumnSet instances.
+ *
+ * @property ColumnSet._nCount
+ * @type Number
+ * @private
+ * @static
+ */
+YAHOO.widget.ColumnSet._nCount = 0;
+
+YAHOO.widget.ColumnSet.prototype = {
+    /**
+     * Unique instance name.
+     *
+     * @property _sId
+     * @type String
+     * @private
+     */
+    _sId : null,
+
+    /**
+     * Array of object literal Column definitions passed to the constructor.
+     *
+     * @property _aDefinitions
+     * @type Object[]
+     * @private
+     */
+    _aDefinitions : null,
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Public member variables
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Top-down tree representation of Column hierarchy.
+     *
+     * @property tree
+     * @type YAHOO.widget.Column[]
+     */
+    tree : null,
+
+    /**
+     * Flattened representation of all Columns.
+     *
+     * @property flat
+     * @type YAHOO.widget.Column[]
+     * @default []
+     */
+    flat : null,
+
+    /**
+     * Array of Columns that map one-to-one to a table column.
+     *
+     * @property keys
+     * @type YAHOO.widget.Column[]
+     * @default []
+     */
+    keys : null,
+
+    /**
+     * ID index of nested parent hierarchies for HEADERS accessibility attribute.
+     *
+     * @property headers
+     * @type String[]
+     * @default []
+     */
+    headers : null,
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Private methods
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Initializes ColumnSet instance with data from Column definitions.
+     *
+     * @method _init
+     * @param aDefinitions {Object[]} Array of object literals that define cells in
+     * the THEAD .
+     * @private
+     */
+
+    _init : function(aDefinitions) {        
+        // DOM tree representation of all Columns
+        var tree = [];
+        // Flat representation of all Columns
+        var flat = [];
+        // Flat representation of only Columns that are meant to display data
+        var keys = [];
+        // Array of HEADERS attribute values for all keys in the "keys" array
+        var headers = [];
+
+        // Tracks current node list depth being tracked
+        var nodeDepth = -1;
+
+        // Internal recursive function to define Column instances
+        var parseColumns = function(nodeList, parent) {
+            // One level down
+            nodeDepth++;
+
+            // Create corresponding tree node if not already there for this depth
+            if(!tree[nodeDepth]) {
+                tree[nodeDepth] = [];
+            }
+
+
+            // Parse each node at this depth for attributes and any children
+            for(var j=0; j<nodeList.length; j++) {
+                var currentNode = nodeList[j];
+
+                // Instantiate a new Column for each node
+                var oColumn = new YAHOO.widget.Column(currentNode);
+                
+                // Cross-reference Column ID back to the original object literal definition
+                currentNode.yuiColumnId = oColumn._sId;
+                
+                // Add the new Column to the flat list
+                flat.push(oColumn);
+
+                // Assign its parent as an attribute, if applicable
+                if(parent) {
+                    oColumn._oParent = parent;
+                }
+
+                // The Column has descendants
+                if(YAHOO.lang.isArray(currentNode.children)) {
+                    oColumn.children = currentNode.children;
+
+                    // Determine COLSPAN value for this Column
+                    var terminalChildNodes = 0;
+                    var countTerminalChildNodes = function(ancestor) {
+                        var descendants = ancestor.children;
+                        // Drill down each branch and count terminal nodes
+                        for(var k=0; k<descendants.length; k++) {
+                            // Keep drilling down
+                            if(YAHOO.lang.isArray(descendants[k].children)) {
+                                countTerminalChildNodes(descendants[k]);
+                            }
+                            // Reached branch terminus
+                            else {
+                                terminalChildNodes++;
+                            }
+                        }
+                    };
+                    countTerminalChildNodes(currentNode);
+                    oColumn._nColspan = terminalChildNodes;
+
+                    // Cascade certain properties to children if not defined on their own
+                    var currentChildren = currentNode.children;
+                    for(var k=0; k<currentChildren.length; k++) {
+                        var child = currentChildren[k];
+                        if(oColumn.className && (child.className === undefined)) {
+                            child.className = oColumn.className;
+                        }
+                        if(oColumn.editor && (child.editor === undefined)) {
+                            child.editor = oColumn.editor;
+                        }
+                        //TODO: Deprecated
+                        if(oColumn.editorOptions && (child.editorOptions === undefined)) {
+                            child.editorOptions = oColumn.editorOptions;
+                        }
+                        if(oColumn.formatter && (child.formatter === undefined)) {
+                            child.formatter = oColumn.formatter;
+                        }
+                        if(oColumn.resizeable && (child.resizeable === undefined)) {
+                            child.resizeable = oColumn.resizeable;
+                        }
+                        if(oColumn.sortable && (child.sortable === undefined)) {
+                            child.sortable = oColumn.sortable;
+                        }
+                        if(oColumn.hidden) {
+                            child.hidden = true;
+                        }
+                        if(oColumn.width && (child.width === undefined)) {
+                            child.width = oColumn.width;
+                        }
+                        if(oColumn.minWidth && (child.minWidth === undefined)) {
+                            child.minWidth = oColumn.minWidth;
+                        }
+                        if(oColumn.maxAutoWidth && (child.maxAutoWidth === undefined)) {
+                            child.maxAutoWidth = oColumn.maxAutoWidth;
+                        }
+                        // Backward compatibility
+                        if(oColumn.type && (child.type === undefined)) {
+                            child.type = oColumn.type;
+                        }
+                        if(oColumn.type && !oColumn.formatter) {
+                            oColumn.formatter = oColumn.type;
+                        }
+                        if(oColumn.text && !YAHOO.lang.isValue(oColumn.label)) {
+                            oColumn.label = oColumn.text;
+                        }
+                        if(oColumn.parser) {
+                        }
+                        if(oColumn.sortOptions && ((oColumn.sortOptions.ascFunction) ||
+                                (oColumn.sortOptions.descFunction))) {
+                        }
+                    }
+
+                    // The children themselves must also be parsed for Column instances
+                    if(!tree[nodeDepth+1]) {
+                        tree[nodeDepth+1] = [];
+                    }
+                    parseColumns(currentChildren, oColumn);
+                }
+                // This Column does not have any children
+                else {
+                    oColumn._nKeyIndex = keys.length;
+                    oColumn._nColspan = 1;
+                    keys.push(oColumn);
+                }
+
+                // Add the Column to the top-down tree
+                tree[nodeDepth].push(oColumn);
+            }
+            nodeDepth--;
+        };
+
+        // Parse out Column instances from the array of object literals
+        if(YAHOO.lang.isArray(aDefinitions)) {
+            parseColumns(aDefinitions);
+
+            // Store the array
+            this._aDefinitions = aDefinitions;
+        }
+        else {
+            return null;
+        }
+
+        var i;
+
+        // Determine ROWSPAN value for each Column in the tree
+        var parseTreeForRowspan = function(tree) {
+            var maxRowDepth = 1;
+            var currentRow;
+            var currentColumn;
+
+            // Calculate the max depth of descendants for this row
+            var countMaxRowDepth = function(row, tmpRowDepth) {
+                tmpRowDepth = tmpRowDepth || 1;
+
+                for(var n=0; n<row.length; n++) {
+                    var col = row[n];
+                    // Column has children, so keep counting
+                    if(YAHOO.lang.isArray(col.children)) {
+                        tmpRowDepth++;
+                        countMaxRowDepth(col.children, tmpRowDepth);
+                        tmpRowDepth--;
+                    }
+                    // No children, is it the max depth?
+                    else {
+                        if(tmpRowDepth > maxRowDepth) {
+                            maxRowDepth = tmpRowDepth;
+                        }
+                    }
+
+                }
+            };
+
+            // Count max row depth for each row
+            for(var m=0; m<tree.length; m++) {
+                currentRow = tree[m];
+                countMaxRowDepth(currentRow);
+
+                // Assign the right ROWSPAN values to each Column in the row
+                for(var p=0; p<currentRow.length; p++) {
+                    currentColumn = currentRow[p];
+                    if(!YAHOO.lang.isArray(currentColumn.children)) {
+                        currentColumn._nRowspan = maxRowDepth;
+                    }
+                    else {
+                        currentColumn._nRowspan = 1;
+                    }
+                }
+
+                // Reset counter for next row
+                maxRowDepth = 1;
+            }
+        };
+        parseTreeForRowspan(tree);
+
+        // Store tree index values
+        for(i=0; i<tree[0].length; i++) {
+            tree[0][i]._nTreeIndex = i;
+        }
+
+        // Store header relationships in an array for HEADERS attribute
+        var recurseAncestorsForHeaders = function(i, oColumn) {
+            headers[i].push(oColumn.getSanitizedKey());
+            if(oColumn._oParent) {
+                recurseAncestorsForHeaders(i, oColumn._oParent);
+            }
+        };
+        for(i=0; i<keys.length; i++) {
+            headers[i] = [];
+            recurseAncestorsForHeaders(i, keys[i]);
+            headers[i] = headers[i].reverse();
+        }
+
+        // Save to the ColumnSet instance
+        this.tree = tree;
+        this.flat = flat;
+        this.keys = keys;
+        this.headers = headers;
+    },
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Public methods
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Returns unique name of the ColumnSet instance.
+     *
+     * @method getId
+     * @return {String} Unique name of the ColumnSet instance.
+     */
+
+    getId : function() {
+        return this._sId;
+    },
+
+    /**
+     * ColumnSet instance name, for logging.
+     *
+     * @method toString
+     * @return {String} Unique name of the ColumnSet instance.
+     */
+
+    toString : function() {
+        return "ColumnSet instance " + this._sId;
+    },
+
+    /**
+     * Public accessor to the definitions array.
+     *
+     * @method getDefinitions
+     * @return {Object[]} Array of object literal Column definitions.
+     */
+
+    getDefinitions : function() {
+        var aDefinitions = this._aDefinitions;
+        
+        // Internal recursive function to define Column instances
+        var parseColumns = function(nodeList, oSelf) {
+            // Parse each node at this depth for attributes and any children
+            for(var j=0; j<nodeList.length; j++) {
+                var currentNode = nodeList[j];
+                
+                // Get the Column for each node
+                var oColumn = oSelf.getColumnById(currentNode.yuiColumnId);
+                
+                if(oColumn) {    
+                    // Update the current values
+                    var oDefinition = oColumn.getDefinition();
+                    for(var name in oDefinition) {
+                        if(YAHOO.lang.hasOwnProperty(oDefinition, name)) {
+                            currentNode[name] = oDefinition[name];
+                        }
+                    }
+                }
+                            
+                // The Column has descendants
+                if(YAHOO.lang.isArray(currentNode.children)) {
+                    // The children themselves must also be parsed for Column instances
+                    parseColumns(currentNode.children, oSelf);
+                }
+            }
+        };
+
+        parseColumns(aDefinitions, this);
+        this._aDefinitions = aDefinitions;
+        return aDefinitions;
+    },
+
+    /**
+     * Returns Column instance with given ID.
+     *
+     * @method getColumnById
+     * @param column {String} Column ID.
+     * @return {YAHOO.widget.Column} Column instance.
+     */
+
+    getColumnById : function(column) {
+        if(YAHOO.lang.isString(column)) {
+            var allColumns = this.flat;
+            for(var i=allColumns.length-1; i>-1; i--) {
+                if(allColumns[i]._sId === column) {
+                    return allColumns[i];
+                }
+            }
+        }
+        return null;
+    },
+
+    /**
+     * Returns Column instance with given key or ColumnSet key index.
+     *
+     * @method getColumn
+     * @param column {String | Number} Column key or ColumnSet key index.
+     * @return {YAHOO.widget.Column} Column instance.
+     */
+
+    getColumn : function(column) {
+        if(YAHOO.lang.isNumber(column) && this.keys[column]) {
+            return this.keys[column];
+        }
+        else if(YAHOO.lang.isString(column)) {
+            var allColumns = this.flat;
+            var aColumns = [];
+            for(var i=0; i<allColumns.length; i++) {
+                if(allColumns[i].key === column) {
+                    aColumns.push(allColumns[i]);
+                }
+            }
+            if(aColumns.length === 1) {
+                return aColumns[0];
+            }
+            else if(aColumns.length > 1) {
+                return aColumns;
+            }
+        }
+        return null;
+    },
+
+    /**
+     * Public accessor returns array of given Column's desendants (if any), including itself.
+     *
+     * @method getDescendants
+     * @parem {YAHOO.widget.Column} Column instance.
+     * @return {Array} Array including the Column itself and all descendants (if any).
+     */
+    getDescendants : function(oColumn) {
+        var oSelf = this;
+        var allDescendants = [];
+        var i;
+
+        // Recursive function to loop thru all children
+        var parse = function(oParent) {
+            allDescendants.push(oParent);
+            // This Column has children
+            if(oParent.children) {
+                for(i=0; i<oParent.children.length; i++) {
+                    parse(oSelf.getColumn(oParent.children[i].key));
+                }
+            }
+        };
+        parse(oColumn);
+
+        return allDescendants;
+    }
+};
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * The Column class defines and manages attributes of DataTable Columns
+ *
+ * @namespace YAHOO.widget
+ * @class Column
+ * @constructor
+ * @param oConfigs {Object} Object literal of definitions.
+ */
+YAHOO.widget.Column = function(oConfigs) {
+    this._sId = Dom.generateId(null, "yui-col"); // "yui-col" + YAHOO.widget.Column._nCount;
+    
+    // Object literal defines Column attributes
+    if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
+        for(var sConfig in oConfigs) {
+            if(sConfig) {
+                this[sConfig] = oConfigs[sConfig];
+            }
+        }
+    }
+
+    // Assign a key if not found
+    if(!YAHOO.lang.isValue(this.key)) {
+        this.key = Dom.generateId(null, "yui-dt-col"); //"yui-dt-col" + YAHOO.widget.Column._nCount;
+    }
+    
+    // Assign a field if not found, defaults to key
+    if(!YAHOO.lang.isValue(this.field)) {
+        this.field = this.key;
+    }
+
+    // Increment counter
+    YAHOO.widget.Column._nCount++;
+
+    // Backward compatibility
+    if(this.width && !YAHOO.lang.isNumber(this.width)) {
+        this.width = null;
+    }
+    if(this.editor && YAHOO.lang.isString(this.editor)) {
+        this.editor = new YAHOO.widget.CellEditor(this.editor, this.editorOptions);
+    }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private member variables
+//
+/////////////////////////////////////////////////////////////////////////////
+
+YAHOO.lang.augmentObject(YAHOO.widget.Column, {
+    /**
+     * Internal class variable to index multiple Column instances.
+     *
+     * @property Column._nCount
+     * @type Number
+     * @private
+     * @static
+     */
+    _nCount : 0,
+
+    formatCheckbox : function(elCell, oRecord, oColumn, oData) {
+        YAHOO.widget.DataTable.formatCheckbox(elCell, oRecord, oColumn, oData);
+    },
+
+    formatCurrency : function(elCell, oRecord, oColumn, oData) {
+        YAHOO.widget.DataTable.formatCurrency(elCell, oRecord, oColumn, oData);
+    },
+
+    formatDate : function(elCell, oRecord, oColumn, oData) {
+        YAHOO.widget.DataTable.formatDate(elCell, oRecord, oColumn, oData);
+    },
+
+    formatEmail : function(elCell, oRecord, oColumn, oData) {
+        YAHOO.widget.DataTable.formatEmail(elCell, oRecord, oColumn, oData);
+    },
+
+    formatLink : function(elCell, oRecord, oColumn, oData) {
+        YAHOO.widget.DataTable.formatLink(elCell, oRecord, oColumn, oData);
+    },
+
+    formatNumber : function(elCell, oRecord, oColumn, oData) {
+        YAHOO.widget.DataTable.formatNumber(elCell, oRecord, oColumn, oData);
+    },
+
+    formatSelect : function(elCell, oRecord, oColumn, oData) {
+        YAHOO.widget.DataTable.formatDropdown(elCell, oRecord, oColumn, oData);
+    }
+});
+
+YAHOO.widget.Column.prototype = {
+    /**
+     * Unique String identifier assigned at instantiation.
+     *
+     * @property _sId
+     * @type String
+     * @private
+     */
+    _sId : null,
+
+    /**
+     * Reference to Column's current position index within its ColumnSet's keys
+     * array, if applicable. This property only applies to non-nested and bottom-
+     * level child Columns.
+     *
+     * @property _nKeyIndex
+     * @type Number
+     * @private
+     */
+    _nKeyIndex : null,
+
+    /**
+     * Reference to Column's current position index within its ColumnSet's tree
+     * array, if applicable. This property only applies to non-nested and top-
+     * level parent Columns.
+     *
+     * @property _nTreeIndex
+     * @type Number
+     * @private
+     */
+    _nTreeIndex : null,
+
+    /**
+     * Number of table cells the Column spans.
+     *
+     * @property _nColspan
+     * @type Number
+     * @private
+     */
+    _nColspan : 1,
+
+    /**
+     * Number of table rows the Column spans.
+     *
+     * @property _nRowspan
+     * @type Number
+     * @private
+     */
+    _nRowspan : 1,
+
+    /**
+     * Column's parent Column instance, or null.
+     *
+     * @property _oParent
+     * @type YAHOO.widget.Column
+     * @private
+     */
+    _oParent : null,
+
+    /**
+     * The DOM reference to the associated TH element.
+     *
+     * @property _elTh
+     * @type HTMLElement
+     * @private
+     */
+    _elTh : null,
+
+    /**
+     * The DOM reference to the associated TH element's liner DIV element.
+     *
+     * @property _elThLiner
+     * @type HTMLElement
+     * @private
+     */
+    _elThLiner : null,
+
+    /**
+     * The DOM reference to the associated TH element's label SPAN element.
+     *
+     * @property _elThLabel
+     * @type HTMLElement
+     * @private
+     */
+    _elThLabel : null,
+
+    /**
+     * The DOM reference to the associated resizerelement (if any).
+     *
+     * @property _elResizer
+     * @type HTMLElement
+     * @private
+     */
+    _elResizer : null,
+
+    /**
+     * Internal width tracker.
+     *
+     * @property _nWidth
+     * @type Number
+     * @private
+     */
+    _nWidth : null,
+
+    /**
+     * For unreg() purposes, a reference to the Column's DragDrop instance.
+     *
+     * @property _dd
+     * @type YAHOO.util.DragDrop
+     * @private
+     */
+    _dd : null,
+
+    /**
+     * For unreg() purposes, a reference to the Column resizer's DragDrop instance.
+     *
+     * @property _ddResizer
+     * @type YAHOO.util.DragDrop
+     * @private
+     */
+    _ddResizer : null,
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Public member variables
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Unique name, required. If "label" property is not provided, the "key"
+     * value will be treated as markup and inserted into the DOM as innerHTML.
+     *
+     * @property key
+     * @type String|HTML
+     */
+    key : null,
+
+    /**
+     * Associated database field, or null.
+     *
+     * @property field
+     * @type String
+     */
+    field : null,
+
+    /**
+     * Value displayed as Column header in the TH element. String value is
+     * treated as markup and inserted into the DOM as innerHTML.
+     *
+     * @property label
+     * @type HTML
+     */
+    label : null,
+
+    /**
+     * Column head cell ABBR for accessibility.
+     *
+     * @property abbr
+     * @type String
+     */
+    abbr : null,
+
+    /**
+     * Array of object literals that define children (nested headers) of a Column.
+     *
+     * @property children
+     * @type Object[]
+     */
+    children : null,
+
+    /**
+     * Column width (in pixels).
+     *
+     * @property width
+     * @type Number
+     */
+    width : null,
+
+    /**
+     * Minimum Column width (in pixels).
+     *
+     * @property minWidth
+     * @type Number
+     * @default null
+     */
+    minWidth : null,
+
+    /**
+     * When a width is not defined for a Column, maxAutoWidth defines an upper
+     * limit that the Column should be auto-sized to. If resizeable is enabled, 
+     * users may still resize to a greater width. Most useful for Columns intended
+     * to hold long unbroken, unwrapped Strings, such as URLs, to prevent very
+     * wide Columns from disrupting visual readability by inducing truncation.
+     *
+     * @property maxAutoWidth
+     * @type Number
+     * @default null
+     */
+    maxAutoWidth : null,
+
+    /**
+     * True if Column is in hidden state.
+     *
+     * @property hidden
+     * @type Boolean
+     * @default false     
+     */
+    hidden : false,
+
+    /**
+     * True if Column is in selected state.
+     *
+     * @property selected
+     * @type Boolean
+     * @default false     
+     */
+    selected : false,
+
+    /**
+     * Custom CSS class or array of classes to be applied to every cell in the Column.
+     *
+     * @property className
+     * @type String || String[]
+     */
+    className : null,
+
+    /**
+     * Cell formatter function, or a shortcut pointer to a function in the
+     * DataTable.Formatter object. The function, called from the DataTable's
+     * formatCell method, renders markup into the cell liner
+     * element and accepts the following arguments:
+     * <dl>
+     *    <dt>elLiner</dt>
+     *    <dd>The element to write innerHTML to.</dd>
+     *    <dt>oRecord</dt>
+     *    <dd>The associated Record for the row.</dd>
+     *    <dt>oColumn</dt>
+     *    <dd>The Column instance for the cell.</dd>
+     *    <dt>oData</dt>
+     *    <dd>The data value for the cell.</dd>
+     * </dl>
+     *
+     * @property formatter
+     * @type String || HTMLFunction
+     */
+    formatter : null,
+    
+    /**
+     * Config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
+     *
+     * @property currencyOptions
+     * @type Object
+     * @default null
+     */
+    currencyOptions : null,
+
+    /**
+     * Config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
+     *
+     * @property dateOptions
+     * @type Object
+     * @default null
+     */
+    dateOptions : null,
+
+    /**
+     * Array of dropdown values for formatter:"dropdown" cases. Can either be a
+     * simple array (e.g., ["Alabama","Alaska","Arizona","Arkansas"]) or a an
+     * array of objects (e.g., [{label:"Alabama", value:"AL"},
+     * {label:"Alaska", value:"AK"}, {label:"Arizona", value:"AZ"},
+     * {label:"Arkansas", value:"AR"}]). String values are treated as markup and
+     * inserted into the DOM as innerHTML.
+     *
+     * @property dropdownOptions
+     * @type HTML[] | Object[]
+     */
+    dropdownOptions : null,
+     
+    /**
+     * A CellEditor instance, otherwise Column is not editable.     
+     *
+     * @property editor
+     * @type YAHOO.widget.CellEditor
+     */
+    editor : null,
+
+    /**
+     * True if Column is resizeable, false otherwise. The Drag & Drop Utility is
+     * required to enable this feature. Only bottom-level and non-nested Columns are
+     * resizeble. 
+     *
+     * @property resizeable
+     * @type Boolean
+     * @default false
+     */
+    resizeable : false,
+
+    /**
+     * True if Column is sortable, false otherwise.
+     *
+     * @property sortable
+     * @type Boolean
+     * @default false
+     */
+    sortable : false,
+
+    /**
+     * @property sortOptions.defaultOrder
+     * @deprecated Use sortOptions.defaultDir.
+     */
+    /**
+     * Default sort direction for Column: YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC.
+     *
+     * @property sortOptions.defaultDir
+     * @type String
+     * @default null
+     */
+    /**
+     * Custom field to sort on.
+     *
+     * @property sortOptions.field
+     * @type String
+     * @default null
+     */
+    /**
+     * Custom sort handler. Signature: sortFunction(a, b, desc, field) where field is the sortOptions.field value
+     *
+     * @property sortOptions.sortFunction
+     * @type Function
+     * @default null
+     */
+    sortOptions : null,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Public methods
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Returns unique ID string.
+     *
+     * @method getId
+     * @return {String} Unique ID string.
+     */
+    getId : function() {
+        return this._sId;
+    },
+
+    /**
+     * Column instance name, for logging.
+     *
+     * @method toString
+     * @return {String} Column's unique name.
+     */
+    toString : function() {
+        return "Column instance " + this._sId;
+    },
+
+    /**
+     * Returns object literal definition.
+     *
+     * @method getDefinition
+     * @return {Object} Object literal definition.
+     */
+    getDefinition : function() {
+        var oDefinition = {};
+        
+        // Update the definition
+        oDefinition.abbr = this.abbr;
+        oDefinition.className = this.className;
+        oDefinition.editor = this.editor;
+        oDefinition.editorOptions = this.editorOptions; //TODO: deprecated
+        oDefinition.field = this.field;
+        oDefinition.formatter = this.formatter;
+        oDefinition.hidden = this.hidden;
+        oDefinition.key = this.key;
+        oDefinition.label = this.label;
+        oDefinition.minWidth = this.minWidth;
+        oDefinition.maxAutoWidth = this.maxAutoWidth;
+        oDefinition.resizeable = this.resizeable;
+        oDefinition.selected = this.selected;
+        oDefinition.sortable = this.sortable;
+        oDefinition.sortOptions = this.sortOptions;
+        oDefinition.width = this.width;
+        
+        // Bug 2529147
+        oDefinition._calculatedWidth = this._calculatedWidth;
+
+        return oDefinition;
+    },
+
+    /**
+     * Returns unique Column key.
+     *
+     * @method getKey
+     * @return {String} Column key.
+     */
+    getKey : function() {
+        return this.key;
+    },
+    
+    /**
+     * Returns field.
+     *
+     * @method getField
+     * @return {String} Column field.
+     */
+    getField : function() {
+        return this.field;
+    },
+    
+    /**
+     * Returns Column key which has been sanitized for DOM (class and ID) usage
+     * starts with letter, contains only letters, numbers, hyphen, or period.
+     *
+     * @method getSanitizedKey
+     * @return {String} Sanitized Column key.
+     */
+    getSanitizedKey : function() {
+        return this.getKey().replace(/[^\w\-]/g,"");
+    },
+
+    /**
+     * Public accessor returns Column's current position index within its
+     * ColumnSet's keys array, if applicable. Only non-nested and bottom-level
+     * child Columns will return a value.
+     *
+     * @method getKeyIndex
+     * @return {Number} Position index, or null.
+     */
+    getKeyIndex : function() {
+        return this._nKeyIndex;
+    },
+
+    /**
+     * Public accessor returns Column's current position index within its
+     * ColumnSet's tree array, if applicable. Only non-nested and top-level parent
+     * Columns will return a value;
+     *
+     * @method getTreeIndex
+     * @return {Number} Position index, or null.
+     */
+    getTreeIndex : function() {
+        return this._nTreeIndex;
+    },
+
+    /**
+     * Public accessor returns Column's parent instance if any, or null otherwise.
+     *
+     * @method getParent
+     * @return {YAHOO.widget.Column} Column's parent instance.
+     */
+    getParent : function() {
+        return this._oParent;
+    },
+
+    /**
+     * Public accessor returns Column's calculated COLSPAN value.
+     *
+     * @method getColspan
+     * @return {Number} Column's COLSPAN value.
+     */
+    getColspan : function() {
+        return this._nColspan;
+    },
+    // Backward compatibility
+    getColSpan : function() {
+        return this.getColspan();
+    },
+
+    /**
+     * Public accessor returns Column's calculated ROWSPAN value.
+     *
+     * @method getRowspan
+     * @return {Number} Column's ROWSPAN value.
+     */
+    getRowspan : function() {
+        return this._nRowspan;
+    },
+
+    /**
+     * Returns DOM reference to the key TH element.
+     *
+     * @method getThEl
+     * @return {HTMLElement} TH element.
+     */
+    getThEl : function() {
+        return this._elTh;
+    },
+
+    /**
+     * Returns DOM reference to the TH's liner DIV element. Introduced since
+     * resizeable Columns may have an extra resizer liner, making the DIV liner
+     * not reliably the TH element's first child.               
+     *
+     * @method getThLInerEl
+     * @return {HTMLElement} TH element.
+     */
+    getThLinerEl : function() {
+        return this._elThLiner;
+    },
+    
+    /**
+     * Returns DOM reference to the resizer element, or null.
+     *
+     * @method getResizerEl
+     * @return {HTMLElement} DIV element.
+     */
+    getResizerEl : function() {
+        return this._elResizer;
+    },
+
+    // Backward compatibility
+    /**
+     * @method getColEl
+     * @deprecated Use getThEl
+     */
+    getColEl : function() {
+        return this.getThEl();
+    },
+    getIndex : function() {
+        return this.getKeyIndex();
+    },
+    format : function() {
+    }
+};
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * Sort static utility to support Column sorting.
+ *
+ * @namespace YAHOO.util
+ * @class Sort
+ * @static
+ */
+YAHOO.util.Sort = {
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Public methods
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Comparator function for simple case-insensitive string sorting.
+     *
+     * @method compare
+     * @param a {Object} First sort argument.
+     * @param b {Object} Second sort argument.
+     * @param desc {Boolean} True if sort direction is descending, false if
+     * sort direction is ascending.
+     * @return {Boolean} Return -1 when a < b. Return 0 when a = b.
+     * Return 1 when a > b.
+     */
+    compare: function(a, b, desc) {
+        if((a === null) || (typeof a == "undefined")) {
+            if((b === null) || (typeof b == "undefined")) {
+                return 0;
+            }
+            else {
+                return 1;
+            }
+        }
+        else if((b === null) || (typeof b == "undefined")) {
+            return -1;
+        }
+
+        if(a.constructor == String) {
+            a = a.toLowerCase();
+        }
+        if(b.constructor == String) {
+            b = b.toLowerCase();
+        }
+        if(a < b) {
+            return (desc) ? 1 : -1;
+        }
+        else if (a > b) {
+            return (desc) ? -1 : 1;
+        }
+        else {
+            return 0;
+        }
+    }
+};
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * ColumnDD subclasses DragDrop to support rearrangeable Columns.
+ *
+ * @namespace YAHOO.util
+ * @class ColumnDD
+ * @extends YAHOO.util.DDProxy
+ * @constructor
+ * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param elTh {HTMLElement} TH element reference.
+ * @param elTarget {HTMLElement} Drag target element.
+ */
+YAHOO.widget.ColumnDD = function(oDataTable, oColumn, elTh, elTarget) {
+    if(oDataTable && oColumn && elTh && elTarget) {
+        this.datatable = oDataTable;
+        this.table = oDataTable.getTableEl();
+        this.column = oColumn;
+        this.headCell = elTh;
+        this.pointer = elTarget;
+        this.newIndex = null;
+        this.init(elTh);
+        this.initFrame(); // Needed for DDProxy
+        this.invalidHandleTypes = {};
+
+        // Set top/bottom padding to account for children of nested columns
+        this.setPadding(10, 0, (this.datatable.getTheadEl().offsetHeight + 10) , 0);
+
+        YAHOO.util.Event.on(window, 'resize', function() {
+            this.initConstraints();
+        }, this, true);
+    }
+    else {
+    }
+};
+
+if(YAHOO.util.DDProxy) {
+    YAHOO.extend(YAHOO.widget.ColumnDD, YAHOO.util.DDProxy, {
+        initConstraints: function() {
+            //Get the top, right, bottom and left positions
+            var region = YAHOO.util.Dom.getRegion(this.table),
+                //Get the element we are working on
+                el = this.getEl(),
+                //Get the xy position of it
+                xy = YAHOO.util.Dom.getXY(el),
+                //Get the width and height
+                width = parseInt(YAHOO.util.Dom.getStyle(el, 'width'), 10),
+                height = parseInt(YAHOO.util.Dom.getStyle(el, 'height'), 10),
+                //Set left to x minus left
+                left = ((xy[0] - region.left) + 15), //Buffer of 15px
+                //Set right to right minus x minus width
+                right = ((region.right - xy[0] - width) + 15);
+    
+            //Set the constraints based on the above calculations
+            this.setXConstraint(left, right);
+            this.setYConstraint(10, 10);            
+        },
+        _resizeProxy: function() {
+            YAHOO.widget.ColumnDD.superclass._resizeProxy.apply(this, arguments);
+            var dragEl = this.getDragEl(),
+                el = this.getEl();
+
+            YAHOO.util.Dom.setStyle(this.pointer, 'height', (this.table.parentNode.offsetHeight + 10) + 'px');
+            YAHOO.util.Dom.setStyle(this.pointer, 'display', 'block');
+            var xy = YAHOO.util.Dom.getXY(el);
+            YAHOO.util.Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]);
+            
+            YAHOO.util.Dom.setStyle(dragEl, 'height', this.datatable.getContainerEl().offsetHeight + "px");
+            YAHOO.util.Dom.setStyle(dragEl, 'width', (parseInt(YAHOO.util.Dom.getStyle(dragEl, 'width'),10) + 4) + 'px');
+            YAHOO.util.Dom.setXY(this.dragEl, xy);
+        },
+        onMouseDown: function() {
+                this.initConstraints();
+                this.resetConstraints();
+        },
+        clickValidator: function(e) {
+            if(!this.column.hidden) {
+                var target = YAHOO.util.Event.getTarget(e);
+                return ( this.isValidHandleChild(target) &&
+                            (this.id == this.handleElId ||
+                                this.DDM.handleWasClicked(target, this.id)) );
+            }
+        },
+        onDragOver: function(ev, id) {
+            // Validate target as a Column
+            var target = this.datatable.getColumn(id);
+            if(target) {                
+                // Validate target as a top-level parent
+                var targetIndex = target.getTreeIndex();
+                while((targetIndex === null) && target.getParent()) {
+                    target = target.getParent();
+                    targetIndex = target.getTreeIndex();
+                }
+                if(targetIndex !== null) {
+                    // Are we placing to left or right of target?
+                    var elTarget = target.getThEl();
+                    var newIndex = targetIndex;
+                    var mouseX = YAHOO.util.Event.getPageX(ev),
+                        targetX = YAHOO.util.Dom.getX(elTarget),
+                        midX = targetX + ((YAHOO.util.Dom.get(elTarget).offsetWidth)/2),
+                        currentIndex =  this.column.getTreeIndex();
+                    
+                    if (mouseX < midX) {
+                       YAHOO.util.Dom.setX(this.pointer, targetX);
+                    } else {
+                        var targetWidth = parseInt(elTarget.offsetWidth, 10);
+                        YAHOO.util.Dom.setX(this.pointer, (targetX + targetWidth));
+                        newIndex++;
+                    }
+                    if (targetIndex > currentIndex) {
+                        newIndex--;
+                    }
+                    if(newIndex < 0) {
+                        newIndex = 0;
+                    }
+                    else if(newIndex > this.datatable.getColumnSet().tree[0].length) {
+                        newIndex = this.datatable.getColumnSet().tree[0].length;
+                    }
+                    this.newIndex = newIndex;
+                }
+            }
+        },
+        onDragDrop: function() {
+            this.datatable.reorderColumn(this.column, this.newIndex);
+        },
+        endDrag: function() {
+            this.newIndex = null;
+            YAHOO.util.Dom.setStyle(this.pointer, 'display', 'none');
+        }
+    });
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * ColumnResizer subclasses DragDrop to support resizeable Columns.
+ *
+ * @namespace YAHOO.util
+ * @class ColumnResizer
+ * @extends YAHOO.util.DDProxy
+ * @constructor
+ * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param elTh {HTMLElement} TH element reference.
+ * @param sHandleElId {String} DOM ID of the handle element that causes the resize.
+ * @param elProxy {HTMLElement} Resizer proxy element.
+ */
+YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elTh, sHandleId, elProxy) {
+    if(oDataTable && oColumn && elTh && sHandleId) {
+        this.datatable = oDataTable;
+        this.column = oColumn;
+        this.headCell = elTh;
+        this.headCellLiner = oColumn.getThLinerEl();
+        this.resizerLiner = elTh.firstChild;
+        this.init(sHandleId, sHandleId, {dragOnly:true, dragElId: elProxy.id});
+        this.initFrame(); // Needed for proxy
+        this.resetResizerEl(); // Needed when rowspan > 0
+
+        // Set right padding for bug 1858462
+        this.setPadding(0, 1, 0, 0);
+    }
+    else {
+    }
+};
+
+if(YAHOO.util.DD) {
+    YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DDProxy, {
+        /////////////////////////////////////////////////////////////////////////////
+        //
+        // Public methods
+        //
+        /////////////////////////////////////////////////////////////////////////////
+        /**
+         * Resets resizer element.
+         *
+         * @method resetResizerEl
+         */
+        resetResizerEl : function() {
+            var resizerStyle = YAHOO.util.Dom.get(this.handleElId).style;
+            resizerStyle.left = "auto";
+            resizerStyle.right = 0;
+            resizerStyle.top = "auto";
+            resizerStyle.bottom = 0;
+            resizerStyle.height = this.headCell.offsetHeight+"px";
+        },
+    
+        /////////////////////////////////////////////////////////////////////////////
+        //
+        // Public DOM event handlers
+        //
+        /////////////////////////////////////////////////////////////////////////////
+    
+        /**
+         * Handles mouseup events on the Column resizer.
+         *
+         * @method onMouseUp
+         * @param e {string} The mouseup event
+         */
+        onMouseUp : function(e) {
+            // Reset height of all resizer els in case TH's have changed height
+            var allKeys = this.datatable.getColumnSet().keys,
+                col;
+            for(var i=0, len=allKeys.length; i<len; i++) {
+                col = allKeys[i];
+                if(col._ddResizer) {
+                    col._ddResizer.resetResizerEl();
+                }
+            }
+            this.resetResizerEl();
+            
+            var el = this.headCellLiner;
+            var newWidth = el.offsetWidth -
+                (parseInt(YAHOO.util.Dom.getStyle(el,"paddingLeft"),10)|0) -
+                (parseInt(YAHOO.util.Dom.getStyle(el,"paddingRight"),10)|0);
+
+            this.datatable.fireEvent("columnResizeEvent", {column:this.column,target:this.headCell,width:newWidth});
+        },
+    
+        /**
+         * Handles mousedown events on the Column resizer.
+         *
+         * @method onMouseDown
+         * @param e {string} The mousedown event
+         */
+        onMouseDown : function(e) {
+            this.startWidth = this.headCellLiner.offsetWidth;
+            this.startX = YAHOO.util.Event.getXY(e)[0];
+            this.nLinerPadding = (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0) +
+                    (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0);
+        },
+    
+        /**
+         * Custom clickValidator to ensure Column is not in hidden state.
+         *
+         * @method clickValidator
+         * @param {Event} e
+         * @private
+         */
+        clickValidator : function(e) {
+            if(!this.column.hidden) {
+                var target = YAHOO.util.Event.getTarget(e);
+                return ( this.isValidHandleChild(target) &&
+                            (this.id == this.handleElId ||
+                                this.DDM.handleWasClicked(target, this.id)) );
+            }
+        },
+    
+        /**
+         * Handles start drag on the Column resizer.
+         *
+         * @method startDrag
+         * @param e {string} The drag event
+         */
+        startDrag : function() {
+            // Shrinks height of all resizer els to not hold open TH els
+            var allKeys = this.datatable.getColumnSet().keys,
+                thisKey = this.column.getKeyIndex(),
+                col;
+            for(var i=0, len=allKeys.length; i<len; i++) {
+                col = allKeys[i];
+                if(col._ddResizer) {
+                    YAHOO.util.Dom.get(col._ddResizer.handleElId).style.height = "1em";
+                }
+            }
+        },
+
+        /**
+         * Handles drag events on the Column resizer.
+         *
+         * @method onDrag
+         * @param e {string} The drag event
+         */
+        onDrag : function(e) {
+            var newX = YAHOO.util.Event.getXY(e)[0];
+            if(newX > YAHOO.util.Dom.getX(this.headCellLiner)) {
+                var offsetX = newX - this.startX;
+                var newWidth = this.startWidth + offsetX - this.nLinerPadding;
+                if(newWidth > 0) {
+                    this.datatable.setColumnWidth(this.column, newWidth);
+                }
+            }
+        }
+    });
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Deprecated
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @property editorOptions
+ * @deprecated Pass configs directly to CellEditor constructor. 
+ */
+
+
+(function () {
+
+var lang   = YAHOO.lang,
+    util   = YAHOO.util,
+    widget = YAHOO.widget,
+    
+    Dom    = util.Dom,
+    Ev     = util.Event,
+    DT     = widget.DataTable;
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * A RecordSet defines and manages a set of Records.
+ *
+ * @namespace YAHOO.widget
+ * @class RecordSet
+ * @param data {Object || Object[]} An object literal or an array of data.
+ * @constructor
+ */
+YAHOO.widget.RecordSet = function(data) {
+    this._init(data);
+};
+
+var RS = widget.RecordSet;
+
+/**
+ * Internal class variable to name multiple Recordset instances.
+ *
+ * @property RecordSet._nCount
+ * @type Number
+ * @private
+ * @static
+ */
+RS._nCount = 0;
+
+RS.prototype = {
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Private member variables
+    //
+    /////////////////////////////////////////////////////////////////////////////
+    /**
+     * Unique String identifier assigned at instantiation.
+     *
+     * @property _sId
+     * @type String
+     * @private
+     */
+    _sId : null,
+
+    /**
+     * Internal counter of how many Records are in the RecordSet.
+     *
+     * @property _length
+     * @type Number
+     * @private
+     * @deprecated No longer used
+     */
+    //_length : null,
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Private methods
+    //
+    /////////////////////////////////////////////////////////////////////////////
+    
+    /**
+     * Initializer.
+     *
+     * @method _init
+     * @param data {Object || Object[]} An object literal or an array of data.
+     * @private
+     */
+    _init : function(data) {
+        // Internal variables
+        this._sId = Dom.generateId(null, "yui-rs");// "yui-rs" + widget.RecordSet._nCount;
+        widget.RecordSet._nCount++;
+        this._records = [];
+        //this._length = 0;
+
+        this._initEvents();
+
+        if(data) {
+            if(lang.isArray(data)) {
+                this.addRecords(data);
+            }
+            else if(lang.isObject(data)) {
+                this.addRecord(data);
+            }
+        }
+
+    },
+    
+    /**
+     * Initializes custom events.
+     *
+     * @method _initEvents
+     * @private
+     */
+    _initEvents : function() {
+        this.createEvent("recordAddEvent");
+        this.createEvent("recordsAddEvent");
+        this.createEvent("recordSetEvent");
+        this.createEvent("recordsSetEvent");
+        this.createEvent("recordUpdateEvent");
+        this.createEvent("recordDeleteEvent");
+        this.createEvent("recordsDeleteEvent");
+        this.createEvent("resetEvent");
+        this.createEvent("recordValueUpdateEvent");
+    },
+
+    /**
+     * Adds one Record to the RecordSet at the given index. If index is null,
+     * then adds the Record to the end of the RecordSet.
+     *
+     * @method _addRecord
+     * @param oData {Object} An object literal of data.
+     * @param index {Number} (optional) Position index.
+     * @return {YAHOO.widget.Record} A Record instance.
+     * @private
+     */
+    _addRecord : function(oData, index) {
+        var oRecord = new YAHOO.widget.Record(oData);
+        
+        if(YAHOO.lang.isNumber(index) && (index > -1)) {
+            this._records.splice(index,0,oRecord);
+        }
+        else {
+            //index = this.getLength();
+            //this._records[index] = oRecord;
+            this._records[this._records.length] = oRecord;
+        }
+        //this._length++;
+        return oRecord;
+    },
+
+    /**
+     * Sets/replaces one Record to the RecordSet at the given index.  Existing
+     * Records with higher indexes are not shifted.  If no index specified, the
+     * Record is added to the end of the RecordSet.
+     *
+     * @method _setRecord
+     * @param oData {Object} An object literal of data.
+     * @param index {Number} (optional) Position index.
+     * @return {YAHOO.widget.Record} A Record instance.
+     * @private
+     */
+    _setRecord : function(oData, index) {
+        if (!lang.isNumber(index) || index < 0) {
+            index = this._records.length;
+        }
+        return (this._records[index] = new widget.Record(oData));
+        /*
+        if(lang.isNumber(index) && (index > -1)) {
+            this._records[index] = oRecord;
+            if((index+1) > this.getLength()) {
+                this._length = index+1;
+            }
+        }
+        else {
+            this._records[this.getLength()] = oRecord;
+            this._length++;
+        }
+        return oRecord;
+        */
+    },
+
+    /**
+     * Deletes Records from the RecordSet at the given index. If range is null,
+     * then only one Record is deleted.
+     *
+     * @method _deleteRecord
+     * @param index {Number} Position index.
+     * @param range {Number} (optional) How many Records to delete
+     * @private
+     */
+    _deleteRecord : function(index, range) {
+        if(!lang.isNumber(range) || (range < 0)) {
+            range = 1;
+        }
+        this._records.splice(index, range);
+        //this._length = this._length - range;
+    },
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Public methods
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Returns unique name of the RecordSet instance.
+     *
+     * @method getId
+     * @return {String} Unique name of the RecordSet instance.
+     */
+    getId : function() {
+        return this._sId;
+    },
+
+    /**
+     * Public accessor to the unique name of the RecordSet instance.
+     *
+     * @method toString
+     * @return {String} Unique name of the RecordSet instance.
+     */
+    toString : function() {
+        return "RecordSet instance " + this._sId;
+    },
+
+    /**
+     * Returns the number of Records held in the RecordSet.
+     *
+     * @method getLength
+     * @return {Number} Number of records in the RecordSet.
+     */
+    getLength : function() {
+            //return this._length;
+            return this._records.length;
+    },
+
+    /**
+     * Returns Record by ID or RecordSet position index.
+     *
+     * @method getRecord
+     * @param record {YAHOO.widget.Record | Number | String} Record instance,
+     * RecordSet position index, or Record ID.
+     * @return {YAHOO.widget.Record} Record object.
+     */
+    getRecord : function(record) {
+        var i;
+        if(record instanceof widget.Record) {
+            for(i=0; i<this._records.length; i++) {
+                if(this._records[i] && (this._records[i]._sId === record._sId)) {
+                    return record;
+                }
+            }
+        }
+        else if(lang.isNumber(record)) {
+            if((record > -1) && (record < this.getLength())) {
+                return this._records[record];
+            }
+        }
+        else if(lang.isString(record)) {
+            for(i=0; i<this._records.length; i++) {
+                if(this._records[i] && (this._records[i]._sId === record)) {
+                    return this._records[i];
+                }
+            }
+        }
+        // Not a valid Record for this RecordSet
+        return null;
+
+    },
+
+    /**
+     * Returns an array of Records from the RecordSet.
+     *
+     * @method getRecords
+     * @param index {Number} (optional) Recordset position index of which Record to
+     * start at.
+     * @param range {Number} (optional) Number of Records to get.
+     * @return {YAHOO.widget.Record[]} Array of Records starting at given index and
+     * length equal to given range. If index is not given, all Records are returned.
+     */
+    getRecords : function(index, range) {
+        if(!lang.isNumber(index)) {
+            return this._records;
+        }
+        if(!lang.isNumber(range)) {
+            return this._records.slice(index);
+        }
+        return this._records.slice(index, index+range);
+    },
+
+    /**
+     * Returns a boolean indicating whether Records exist in the RecordSet at the
+     * specified index range.  Returns true if and only if a Record exists at each
+     * index in the range.
+     * @method hasRecords
+     * @param index
+     * @param range
+     * @return {Boolean} true if all indices are populated in the RecordSet
+     */
+    hasRecords : function (index, range) {
+        var recs = this.getRecords(index,range);
+        for (var i = 0; i < range; ++i) {
+            if (typeof recs[i] === 'undefined') {
+                return false;
+            }
+        }
+        return true;
+    },
+
+    /**
+     * Returns current position index for the given Record.
+     *
+     * @method getRecordIndex
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @return {Number} Record's RecordSet position index.
+     */
+
+    getRecordIndex : function(oRecord) {
+        if(oRecord) {
+            for(var i=this._records.length-1; i>-1; i--) {
+                if(this._records[i] && oRecord.getId() === this._records[i].getId()) {
+                    return i;
+                }
+            }
+        }
+        return null;
+
+    },
+
+    /**
+     * Adds one Record to the RecordSet at the given index. If index is null,
+     * then adds the Record to the end of the RecordSet.
+     *
+     * @method addRecord
+     * @param oData {Object} An object literal of data.
+     * @param index {Number} (optional) Position index.
+     * @return {YAHOO.widget.Record} A Record instance.
+     */
+    addRecord : function(oData, index) {
+        if(lang.isObject(oData)) {
+            var oRecord = this._addRecord(oData, index);
+            this.fireEvent("recordAddEvent",{record:oRecord,data:oData});
+            return oRecord;
+        }
+        else {
+            return null;
+        }
+    },
+
+    /**
+     * Adds multiple Records at once to the RecordSet at the given index with the
+     * given object literal data. If index is null, then the new Records are
+     * added to the end of the RecordSet.
+     *
+     * @method addRecords
+     * @param aData {Object[]} An object literal data or an array of data object literals.
+     * @param index {Number} (optional) Position index.
+     * @return {YAHOO.widget.Record[]} An array of Record instances.
+     */
+    addRecords : function(aData, index) {
+        if(lang.isArray(aData)) {
+            var newRecords = [],
+                idx,i,len;
+
+            index = lang.isNumber(index) ? index : this._records.length;
+            idx = index;
+
+            // Can't go backwards bc we need to preserve order
+            for(i=0,len=aData.length; i<len; ++i) {
+                if(lang.isObject(aData[i])) {
+                    var record = this._addRecord(aData[i], idx++);
+                    newRecords.push(record);
+                }
+           }
+            this.fireEvent("recordsAddEvent",{records:newRecords,data:aData});
+           return newRecords;
+        }
+        else if(lang.isObject(aData)) {
+            var oRecord = this._addRecord(aData);
+            this.fireEvent("recordsAddEvent",{records:[oRecord],data:aData});
+            return oRecord;
+        }
+        else {
+            return null;
+        }
+    },
+
+    /**
+     * Sets or replaces one Record to the RecordSet at the given index. Unlike
+     * addRecord, an existing Record at that index is not shifted to preserve it.
+     * If no index is specified, it adds the Record to the end of the RecordSet.
+     *
+     * @method setRecord
+     * @param oData {Object} An object literal of data.
+     * @param index {Number} (optional) Position index.
+     * @return {YAHOO.widget.Record} A Record instance.
+     */
+    setRecord : function(oData, index) {
+        if(lang.isObject(oData)) {
+            var oRecord = this._setRecord(oData, index);
+            this.fireEvent("recordSetEvent",{record:oRecord,data:oData});
+            return oRecord;
+        }
+        else {
+            return null;
+        }
+    },
+
+    /**
+     * Sets or replaces multiple Records at once to the RecordSet with the given
+     * data, starting at the given index. If index is not specified, then the new
+     * Records are added to the end of the RecordSet.
+     *
+     * @method setRecords
+     * @param aData {Object[]} An array of object literal data.
+     * @param index {Number} (optional) Position index.
+     * @return {YAHOO.widget.Record[]} An array of Record instances.
+     */
+    setRecords : function(aData, index) {
+        var Rec   = widget.Record,
+            a     = lang.isArray(aData) ? aData : [aData],
+            added = [],
+            i = 0, l = a.length, j = 0;
+
+        index = parseInt(index,10)|0;
+
+        for(; i < l; ++i) {
+            if (typeof a[i] === 'object' && a[i]) {
+                added[j++] = this._records[index + i] = new Rec(a[i]);
+            }
+        }
+
+        this.fireEvent("recordsSetEvent",{records:added,data:aData});
+        // Backward compatibility for bug 1918245
+        this.fireEvent("recordsSet",{records:added,data:aData});
+
+        if (a.length && !added.length) {
+        }
+
+        return added;
+    },
+
+    /**
+     * Updates given Record with given data.
+     *
+     * @method updateRecord
+     * @param record {YAHOO.widget.Record | Number | String} A Record instance,
+     * a RecordSet position index, or a Record ID.
+     * @param oData {Object} Object literal of new data.
+     * @return {YAHOO.widget.Record} Updated Record, or null.
+     */
+    updateRecord : function(record, oData) {
+        var oRecord = this.getRecord(record);
+        if(oRecord && lang.isObject(oData)) {
+            // Copy data from the Record for the event that gets fired later
+            var oldData = {};
+            for(var key in oRecord._oData) {
+                if(lang.hasOwnProperty(oRecord._oData, key)) {
+                    oldData[key] = oRecord._oData[key];
+                }
+            }
+            oRecord._oData = oData;
+            this.fireEvent("recordUpdateEvent",{record:oRecord,newData:oData,oldData:oldData});
+            return oRecord;
+        }
+        else {
+            return null;
+        }
+    },
+
+    /**
+     * @method updateKey
+     * @deprecated Use updateRecordValue
+     */
+    updateKey : function(record, sKey, oData) {
+        this.updateRecordValue(record, sKey, oData);
+    },
+    /**
+     * Sets given Record at given key to given data.
+     *
+     * @method updateRecordValue
+     * @param record {YAHOO.widget.Record | Number | String} A Record instance,
+     * a RecordSet position index, or a Record ID.
+     * @param sKey {String} Key name.
+     * @param oData {Object} New data.
+     */
+    updateRecordValue : function(record, sKey, oData) {
+        var oRecord = this.getRecord(record);
+        if(oRecord) {
+            var oldData = null;
+            var keyValue = oRecord._oData[sKey];
+            // Copy data from the Record for the event that gets fired later
+            if(keyValue && lang.isObject(keyValue)) {
+                oldData = {};
+                for(var key in keyValue)  {
+                    if(lang.hasOwnProperty(keyValue, key)) {
+                        oldData[key] = keyValue[key];
+                    }
+                }
+            }
+            // Copy by value
+            else {
+                oldData = keyValue;
+            }
+
+            oRecord._oData[sKey] = oData;
+            this.fireEvent("keyUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
+            this.fireEvent("recordValueUpdateEvent",{record:oRecord,key:sKey,newData:oData,oldData:oldData});
+        }
+        else {
+        }
+    },
+
+    /**
+     * Replaces all Records in RecordSet with new object literal data.
+     *
+     * @method replaceRecords
+     * @param data {Object || Object[]} An object literal of data or an array of
+     * data object literals.
+     * @return {YAHOO.widget.Record || YAHOO.widget.Record[]} A Record instance or
+     * an array of Records.
+     */
+    replaceRecords : function(data) {
+        this.reset();
+        return this.addRecords(data);
+    },
+
+    /**
+     * Sorts all Records by given function. Records keep their unique IDs but will
+     * have new RecordSet position indexes.
+     *
+     * @method sortRecords
+     * @param fnSort {Function} Reference to a sort function.
+     * @param desc {Boolean} True if sort direction is descending, false if sort
+     * direction is ascending.
+     * @param field {String} The field to sort by, from sortOptions.field
+     * @return {YAHOO.widget.Record[]} Sorted array of Records.
+     */
+    sortRecords : function(fnSort, desc, field) {
+        return this._records.sort(function(a, b) {return fnSort(a, b, desc, field);});
+    },
+
+    /**
+     * Reverses all Records, so ["one", "two", "three"] becomes ["three", "two", "one"].
+     *
+     * @method reverseRecords
+     * @return {YAHOO.widget.Record[]} Reverse-sorted array of Records.
+     */
+    reverseRecords : function() {
+        return this._records.reverse();
+    },
+
+    /**
+     * Removes the Record at the given position index from the RecordSet. If a range
+     * is also provided, removes that many Records, starting from the index. Length
+     * of RecordSet is correspondingly shortened.
+     *
+     * @method deleteRecord
+     * @param index {Number} Record's RecordSet position index.
+     * @return {Object} A copy of the data held by the deleted Record.
+     */
+    deleteRecord : function(index) {
+        if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
+            var oData = this.getRecord(index).getData();
+            
+            this._deleteRecord(index);
+            this.fireEvent("recordDeleteEvent",{data:oData,index:index});
+            return oData;
+        }
+        else {
+            return null;
+        }
+    },
+
+    /**
+     * Removes the Record at the given position index from the RecordSet. If a range
+     * is also provided, removes that many Records, starting from the index. Length
+     * of RecordSet is correspondingly shortened.
+     *
+     * @method deleteRecords
+     * @param index {Number} Record's RecordSet position index.
+     * @param range {Number} (optional) How many Records to delete.
+     * @return {Object[]} An array of copies of the data held by the deleted Records.     
+     */
+    deleteRecords : function(index, range) {
+        if(!lang.isNumber(range)) {
+            range = 1;
+        }
+        if(lang.isNumber(index) && (index > -1) && (index < this.getLength())) {
+            var recordsToDelete = this.getRecords(index, range);
+            var deletedData = [], // this mistakenly held Records, not data
+                deletedObjects = []; // this passes data only
+            
+            for(var i=0; i<recordsToDelete.length; i++) {
+                deletedData[deletedData.length] = recordsToDelete[i]; // backward compatibility
+                deletedObjects[deletedObjects.length] = recordsToDelete[i].getData();
+            }
+            this._deleteRecord(index, range);
+
+            this.fireEvent("recordsDeleteEvent",{data:deletedData,deletedData:deletedObjects,index:index});
+
+            return deletedData;
+        }
+        else {
+            return null;
+        }
+    },
+
+    /**
+     * Deletes all Records from the RecordSet.
+     *
+     * @method reset
+     */
+    reset : function() {
+        this._records = [];
+        //this._length = 0;
+        this.fireEvent("resetEvent");
+    }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Custom Events
+//
+/////////////////////////////////////////////////////////////////////////////
+
+// RecordSet uses EventProvider
+lang.augmentProto(RS, util.EventProvider);
+
+/**
+ * Fired when a new Record is added to the RecordSet.
+ *
+ * @event recordAddEvent
+ * @param oArgs.record {YAHOO.widget.Record} The Record instance.
+ * @param oArgs.data {Object} Data added.
+ */
+
+/**
+ * Fired when multiple Records are added to the RecordSet at once.
+ *
+ * @event recordsAddEvent
+ * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
+ * @param oArgs.data {Object[]} Data added.
+ */
+
+/**
+ * Fired when a Record is set in the RecordSet.
+ *
+ * @event recordSetEvent
+ * @param oArgs.record {YAHOO.widget.Record} The Record instance.
+ * @param oArgs.data {Object} Data added.
+ */
+
+/**
+ * Fired when multiple Records are set in the RecordSet at once.
+ *
+ * @event recordsSetEvent
+ * @param oArgs.records {YAHOO.widget.Record[]} An array of Record instances.
+ * @param oArgs.data {Object[]} Data added.
+ */
+
+/**
+ * Fired when a Record is updated with new data.
+ *
+ * @event recordUpdateEvent
+ * @param oArgs.record {YAHOO.widget.Record} The Record instance.
+ * @param oArgs.newData {Object} New data.
+ * @param oArgs.oldData {Object} Old data.
+ */
+
+/**
+ * Fired when a Record is deleted from the RecordSet.
+ *
+ * @event recordDeleteEvent
+ * @param oArgs.data {Object} The data held by the deleted Record,
+ * or an array of data object literals if multiple Records were deleted at once.
+ * @param oArgs.index {Object} Index of the deleted Record.
+ */
+
+/**
+ * Fired when multiple Records are deleted from the RecordSet at once.
+ *
+ * @event recordsDeleteEvent
+ * @param oArgs.data {Object[]} An array of deleted Records.
+ * @param oArgs.deletedData {Object[]} An array of deleted data.
+ * @param oArgs.index {Object} Index of the first deleted Record.
+ */
+
+/**
+ * Fired when all Records are deleted from the RecordSet at once.
+ *
+ * @event resetEvent
+ */
+
+/**
+ * @event keyUpdateEvent    
+ * @deprecated Use recordValueUpdateEvent     
+ */
+
+/**
+ * Fired when a Record value is updated with new data.
+ *
+ * @event recordValueUpdateEvent
+ * @param oArgs.record {YAHOO.widget.Record} The Record instance.
+ * @param oArgs.key {String} The updated key.
+ * @param oArgs.newData {Object} New data.
+ * @param oArgs.oldData {Object} Old data.
+ *
+ */
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * The Record class defines a DataTable record.
+ *
+ * @namespace YAHOO.widget
+ * @class Record
+ * @constructor
+ * @param oConfigs {Object} (optional) Object literal of key/value pairs.
+ */
+YAHOO.widget.Record = function(oLiteral) {
+    this._nCount = widget.Record._nCount;
+    this._sId = Dom.generateId(null, "yui-rec");//"yui-rec" + this._nCount;
+    widget.Record._nCount++;
+    this._oData = {};
+    if(lang.isObject(oLiteral)) {
+        for(var sKey in oLiteral) {
+            if(lang.hasOwnProperty(oLiteral, sKey)) {
+                this._oData[sKey] = oLiteral[sKey];
+            }
+        }
+    }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private member variables
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Internal class variable to give unique IDs to Record instances.
+ *
+ * @property Record._nCount
+ * @type Number
+ * @private
+ */
+YAHOO.widget.Record._nCount = 0;
+
+YAHOO.widget.Record.prototype = {
+    /**
+     * Immutable unique count assigned at instantiation. Remains constant while a
+     * Record's position index can change from sorting.
+     *
+     * @property _nCount
+     * @type Number
+     * @private
+     */
+    _nCount : null,
+
+    /**
+     * Immutable unique ID assigned at instantiation. Remains constant while a
+     * Record's position index can change from sorting.
+     *
+     * @property _sId
+     * @type String
+     * @private
+     */
+    _sId : null,
+
+    /**
+     * Holds data for the Record in an object literal.
+     *
+     * @property _oData
+     * @type Object
+     * @private
+     */
+    _oData : null,
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Public member variables
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Public methods
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Returns unique count assigned at instantiation.
+     *
+     * @method getCount
+     * @return Number
+     */
+    getCount : function() {
+        return this._nCount;
+    },
+
+    /**
+     * Returns unique ID assigned at instantiation.
+     *
+     * @method getId
+     * @return String
+     */
+    getId : function() {
+        return this._sId;
+    },
+
+    /**
+     * Returns data for the Record for a field if given, or the entire object
+     * literal otherwise.
+     *
+     * @method getData
+     * @param sField {String} (Optional) The field from which to retrieve data value.
+     * @return Object
+     */
+    getData : function(sField) {
+        if(lang.isString(sField)) {
+            return this._oData[sField];
+        }
+        else {
+            return this._oData;
+        }
+    },
+
+    /**
+     * Sets given data at the given key. Use the RecordSet method updateRecordValue to trigger
+     * events. 
+     *
+     * @method setData
+     * @param sKey {String} The key of the new value.
+     * @param oData {MIXED} The new value.
+     */
+    setData : function(sKey, oData) {
+        this._oData[sKey] = oData;
+    }
+};
+
+})();
+
+(function () {
+
+var lang   = YAHOO.lang,
+    util   = YAHOO.util,
+    widget = YAHOO.widget,
+    ua     = YAHOO.env.ua,
+    
+    Dom    = util.Dom,
+    Ev     = util.Event,
+    DS     = util.DataSourceBase;
+
+/**
+ * The DataTable widget provides a progressively enhanced DHTML control for
+ * displaying tabular data across A-grade browsers.
+ *
+ * @module datatable
+ * @requires yahoo, dom, event, element, datasource
+ * @optional dragdrop, dragdrop
+ * @title DataTable Widget
+ */
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+/**
+ * DataTable class for the YUI DataTable widget.
+ *
+ * @namespace YAHOO.widget
+ * @class DataTable
+ * @extends YAHOO.util.Element
+ * @constructor
+ * @param elContainer {HTMLElement} Container element for the TABLE.
+ * @param aColumnDefs {Object[]} Array of object literal Column definitions.
+ * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param oConfigs {object} (optional) Object literal of configuration values.
+ */
+YAHOO.widget.DataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
+    var DT = widget.DataTable;
+    
+    ////////////////////////////////////////////////////////////////////////////
+    // Backward compatibility for SDT, but prevent infinite loops
+    
+    if(oConfigs && oConfigs.scrollable) {
+        return new YAHOO.widget.ScrollingDataTable(elContainer,aColumnDefs,oDataSource,oConfigs);
+    }
+    
+    ////////////////////////////////////////////////////////////////////////////
+    // Initialization
+
+    // Internal vars
+    this._nIndex = DT._nCount;
+    this._sId = Dom.generateId(null, "yui-dt");// "yui-dt"+this._nIndex;
+    this._oChainRender = new YAHOO.util.Chain();
+    this._oChainRender.subscribe("end",this._onRenderChainEnd, this, true);
+
+    // Initialize configs
+    this._initConfigs(oConfigs);
+
+    // Initialize DataSource
+    this._initDataSource(oDataSource);
+    if(!this._oDataSource) {
+        return;
+    }
+
+    // Initialize ColumnSet
+    this._initColumnSet(aColumnDefs);
+    if(!this._oColumnSet) {
+        return;
+    }
+
+    // Initialize RecordSet
+    this._initRecordSet();
+    if(!this._oRecordSet) {
+    }
+
+    // Initialize Attributes
+    DT.superclass.constructor.call(this, elContainer, this.configs);
+
+    // Initialize DOM elements
+    var okDom = this._initDomElements(elContainer);
+    if(!okDom) {
+        return;
+    }
+            
+    // Show message as soon as config is available
+    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
+    
+    ////////////////////////////////////////////////////////////////////////////
+    // Once per instance
+    this._initEvents();
+
+    DT._nCount++;
+    DT._nCurrentCount++;
+    
+    ////////////////////////////////////////////////////////////////////////////
+    // Data integration
+
+    // Send a simple initial request
+    var oCallback = {
+        success : this.onDataReturnSetRows,
+        failure : this.onDataReturnSetRows,
+        scope   : this,
+        argument: this.getState()
+    };
+    
+    var initialLoad = this.get("initialLoad");
+    if(initialLoad === true) {
+        this._oDataSource.sendRequest(this.get("initialRequest"), oCallback);
+    }
+    // Do not send an initial request at all
+    else if(initialLoad === false) {
+        this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);
+    }
+    // Send an initial request with a custom payload
+    else {
+        var oCustom = initialLoad || {};
+        oCallback.argument = oCustom.argument || {};
+        this._oDataSource.sendRequest(oCustom.request, oCallback);
+    }
+};
+
+var DT = widget.DataTable;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public constants
+//
+/////////////////////////////////////////////////////////////////////////////
+
+lang.augmentObject(DT, {
+
+    /**
+     * Class name assigned to outer DataTable container.
+     *
+     * @property DataTable.CLASS_DATATABLE
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt"
+     */
+    CLASS_DATATABLE : "yui-dt",
+
+    /**
+     * Class name assigned to liner DIV elements.
+     *
+     * @property DataTable.CLASS_LINER
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-liner"
+     */
+    CLASS_LINER : "yui-dt-liner",
+
+    /**
+     * Class name assigned to display label elements.
+     *
+     * @property DataTable.CLASS_LABEL
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-label"
+     */
+    CLASS_LABEL : "yui-dt-label",
+
+    /**
+     * Class name assigned to messaging elements.
+     *
+     * @property DataTable.CLASS_MESSAGE
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-message"
+     */
+    CLASS_MESSAGE : "yui-dt-message",
+
+    /**
+     * Class name assigned to mask element when DataTable is disabled.
+     *
+     * @property DataTable.CLASS_MASK
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-mask"
+     */
+    CLASS_MASK : "yui-dt-mask",
+
+    /**
+     * Class name assigned to data elements.
+     *
+     * @property DataTable.CLASS_DATA
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-data"
+     */
+    CLASS_DATA : "yui-dt-data",
+
+    /**
+     * Class name assigned to Column drag target.
+     *
+     * @property DataTable.CLASS_COLTARGET
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-coltarget"
+     */
+    CLASS_COLTARGET : "yui-dt-coltarget",
+
+    /**
+     * Class name assigned to resizer handle elements.
+     *
+     * @property DataTable.CLASS_RESIZER
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-resizer"
+     */
+    CLASS_RESIZER : "yui-dt-resizer",
+
+    /**
+     * Class name assigned to resizer liner elements.
+     *
+     * @property DataTable.CLASS_RESIZERLINER
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-resizerliner"
+     */
+    CLASS_RESIZERLINER : "yui-dt-resizerliner",
+
+    /**
+     * Class name assigned to resizer proxy elements.
+     *
+     * @property DataTable.CLASS_RESIZERPROXY
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-resizerproxy"
+     */
+    CLASS_RESIZERPROXY : "yui-dt-resizerproxy",
+
+    /**
+     * Class name assigned to CellEditor container elements.
+     *
+     * @property DataTable.CLASS_EDITOR
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-editor"
+     */
+    CLASS_EDITOR : "yui-dt-editor",
+
+    /**
+     * Class name assigned to CellEditor container shim.
+     *
+     * @property DataTable.CLASS_EDITOR_SHIM
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-editor-shim"
+     */
+    CLASS_EDITOR_SHIM : "yui-dt-editor-shim",
+
+    /**
+     * Class name assigned to paginator container elements.
+     *
+     * @property DataTable.CLASS_PAGINATOR
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-paginator"
+     */
+    CLASS_PAGINATOR : "yui-dt-paginator",
+
+    /**
+     * Class name assigned to page number indicators.
+     *
+     * @property DataTable.CLASS_PAGE
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-page"
+     */
+    CLASS_PAGE : "yui-dt-page",
+
+    /**
+     * Class name assigned to default indicators.
+     *
+     * @property DataTable.CLASS_DEFAULT
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-default"
+     */
+    CLASS_DEFAULT : "yui-dt-default",
+
+    /**
+     * Class name assigned to previous indicators.
+     *
+     * @property DataTable.CLASS_PREVIOUS
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-previous"
+     */
+    CLASS_PREVIOUS : "yui-dt-previous",
+
+    /**
+     * Class name assigned next indicators.
+     *
+     * @property DataTable.CLASS_NEXT
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-next"
+     */
+    CLASS_NEXT : "yui-dt-next",
+
+    /**
+     * Class name assigned to first elements.
+     *
+     * @property DataTable.CLASS_FIRST
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-first"
+     */
+    CLASS_FIRST : "yui-dt-first",
+
+    /**
+     * Class name assigned to last elements.
+     *
+     * @property DataTable.CLASS_LAST
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-last"
+     */
+    CLASS_LAST : "yui-dt-last",
+
+    /**
+     * Class name assigned to Record elements.
+     *
+     * @property DataTable.CLASS_REC
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-rec"
+     */
+    CLASS_REC : "yui-dt-rec",
+
+    /**
+     * Class name assigned to even elements.
+     *
+     * @property DataTable.CLASS_EVEN
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-even"
+     */
+    CLASS_EVEN : "yui-dt-even",
+
+    /**
+     * Class name assigned to odd elements.
+     *
+     * @property DataTable.CLASS_ODD
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-odd"
+     */
+    CLASS_ODD : "yui-dt-odd",
+
+    /**
+     * Class name assigned to selected elements.
+     *
+     * @property DataTable.CLASS_SELECTED
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-selected"
+     */
+    CLASS_SELECTED : "yui-dt-selected",
+
+    /**
+     * Class name assigned to highlighted elements.
+     *
+     * @property DataTable.CLASS_HIGHLIGHTED
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-highlighted"
+     */
+    CLASS_HIGHLIGHTED : "yui-dt-highlighted",
+
+    /**
+     * Class name assigned to hidden elements.
+     *
+     * @property DataTable.CLASS_HIDDEN
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-hidden"
+     */
+    CLASS_HIDDEN : "yui-dt-hidden",
+
+    /**
+     * Class name assigned to disabled elements.
+     *
+     * @property DataTable.CLASS_DISABLED
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-disabled"
+     */
+    CLASS_DISABLED : "yui-dt-disabled",
+
+    /**
+     * Class name assigned to empty indicators.
+     *
+     * @property DataTable.CLASS_EMPTY
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-empty"
+     */
+    CLASS_EMPTY : "yui-dt-empty",
+
+    /**
+     * Class name assigned to loading indicatorx.
+     *
+     * @property DataTable.CLASS_LOADING
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-loading"
+     */
+    CLASS_LOADING : "yui-dt-loading",
+
+    /**
+     * Class name assigned to error indicators.
+     *
+     * @property DataTable.CLASS_ERROR
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-error"
+     */
+    CLASS_ERROR : "yui-dt-error",
+
+    /**
+     * Class name assigned to editable elements.
+     *
+     * @property DataTable.CLASS_EDITABLE
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-editable"
+     */
+    CLASS_EDITABLE : "yui-dt-editable",
+
+    /**
+     * Class name assigned to draggable elements.
+     *
+     * @property DataTable.CLASS_DRAGGABLE
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-draggable"
+     */
+    CLASS_DRAGGABLE : "yui-dt-draggable",
+
+    /**
+     * Class name assigned to resizeable elements.
+     *
+     * @property DataTable.CLASS_RESIZEABLE
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-resizeable"
+     */
+    CLASS_RESIZEABLE : "yui-dt-resizeable",
+
+    /**
+     * Class name assigned to scrollable elements.
+     *
+     * @property DataTable.CLASS_SCROLLABLE
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-scrollable"
+     */
+    CLASS_SCROLLABLE : "yui-dt-scrollable",
+
+    /**
+     * Class name assigned to sortable elements.
+     *
+     * @property DataTable.CLASS_SORTABLE
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-sortable"
+     */
+    CLASS_SORTABLE : "yui-dt-sortable",
+
+    /**
+     * Class name assigned to ascending elements.
+     *
+     * @property DataTable.CLASS_ASC
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-asc"
+     */
+    CLASS_ASC : "yui-dt-asc",
+
+    /**
+     * Class name assigned to descending elements.
+     *
+     * @property DataTable.CLASS_DESC
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-desc"
+     */
+    CLASS_DESC : "yui-dt-desc",
+
+    /**
+     * Class name assigned to BUTTON elements and/or container elements.
+     *
+     * @property DataTable.CLASS_BUTTON
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-button"
+     */
+    CLASS_BUTTON : "yui-dt-button",
+
+    /**
+     * Class name assigned to INPUT TYPE=CHECKBOX elements and/or container elements.
+     *
+     * @property DataTable.CLASS_CHECKBOX
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-checkbox"
+     */
+    CLASS_CHECKBOX : "yui-dt-checkbox",
+
+    /**
+     * Class name assigned to SELECT elements and/or container elements.
+     *
+     * @property DataTable.CLASS_DROPDOWN
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-dropdown"
+     */
+    CLASS_DROPDOWN : "yui-dt-dropdown",
+
+    /**
+     * Class name assigned to INPUT TYPE=RADIO elements and/or container elements.
+     *
+     * @property DataTable.CLASS_RADIO
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-radio"
+     */
+    CLASS_RADIO : "yui-dt-radio",
+
+    /////////////////////////////////////////////////////////////////////////
+    //
+    // Private static properties
+    //
+    /////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Internal class variable for indexing multiple DataTable instances.
+     *
+     * @property DataTable._nCount
+     * @type Number
+     * @private
+     * @static
+     */
+    _nCount : 0,
+
+    /**
+     * Internal class variable tracking current number of DataTable instances,
+     * so that certain class values can be reset when all instances are destroyed.          
+     *
+     * @property DataTable._nCurrentCount
+     * @type Number
+     * @private
+     * @static
+     */
+    _nCurrentCount : 0,
+
+    /**
+     * Reference to the STYLE node that is dynamically created and updated
+     * in order to manage Column widths.
+     *
+     * @property DataTable._elDynStyleNode
+     * @type HTMLElement
+     * @private
+     * @static     
+     */
+    _elDynStyleNode : null,
+
+    /**
+     * Set to true if _elDynStyleNode cannot be populated due to browser incompatibility.
+     *
+     * @property DataTable._bDynStylesFallback
+     * @type boolean
+     * @private
+     * @static     
+     */
+    _bDynStylesFallback : (ua.ie) ? true : false,
+
+    /**
+     * Object literal hash of Columns and their dynamically create style rules.
+     *
+     * @property DataTable._oDynStyles
+     * @type Object
+     * @private
+     * @static     
+     */
+    _oDynStyles : {},
+
+    /////////////////////////////////////////////////////////////////////////
+    //
+    // Private static methods
+    //
+    /////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Clones object literal or array of object literals.
+     *
+     * @method DataTable._cloneObject
+     * @param o {Object} Object.
+     * @private
+     * @static     
+     */
+    _cloneObject: function(o) {
+        if(!lang.isValue(o)) {
+            return o;
+        }
+
+        var copy = {};
+
+        if(o instanceof YAHOO.widget.BaseCellEditor) {
+            copy = o;
+        }
+        else if(Object.prototype.toString.apply(o) === "[object RegExp]") {
+            copy = o;
+        }
+        else if(lang.isFunction(o)) {
+            copy = o;
+        }
+        else if(lang.isArray(o)) {
+            var array = [];
+            for(var i=0,len=o.length;i<len;i++) {
+                array[i] = DT._cloneObject(o[i]);
+            }
+            copy = array;
+        }
+        else if(lang.isObject(o)) {
+            for (var x in o){
+                if(lang.hasOwnProperty(o, x)) {
+                    if(lang.isValue(o[x]) && lang.isObject(o[x]) || lang.isArray(o[x])) {
+                        copy[x] = DT._cloneObject(o[x]);
+                    }
+                    else {
+                        copy[x] = o[x];
+                    }
+                }
+            }
+        }
+        else {
+            copy = o;
+        }
+
+        return copy;
+    },
+
+    /**
+     * Formats a BUTTON element.
+     *
+     * @method DataTable.formatButton
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {HTML} Data value for the cell. By default, the value
+     * is what gets written to the BUTTON. String values are treated as markup
+     * and inserted into the DOM with innerHTML.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatButton : function(el, oRecord, oColumn, oData, oDataTable) {
+        var sValue = lang.isValue(oData) ? oData : "Click";
+        //TODO: support YAHOO.widget.Button
+        //if(YAHOO.widget.Button) {
+
+        //}
+        //else {
+            el.innerHTML = "<button type=\"button\" class=\""+
+                    DT.CLASS_BUTTON + "\">" + sValue + "</button>";
+        //}
+    },
+
+    /**
+     * Formats a CHECKBOX element.
+     *
+     * @method DataTable.formatCheckbox
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {Object | Boolean | HTML} Data value for the cell. Can be a simple
+     * Boolean to indicate whether checkbox is checked or not. Can be object literal
+     * {checked:bBoolean, label:sLabel}. String values are treated as markup
+     * and inserted into the DOM with innerHTML.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatCheckbox : function(el, oRecord, oColumn, oData, oDataTable) {
+        var bChecked = oData;
+        bChecked = (bChecked) ? " checked=\"checked\"" : "";
+        el.innerHTML = "<input type=\"checkbox\"" + bChecked +
+                " class=\"" + DT.CLASS_CHECKBOX + "\" />";
+    },
+
+    /**
+     * Formats currency. Default unit is USD.
+     *
+     * @method DataTable.formatCurrency
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {Number} Data value for the cell.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatCurrency : function(el, oRecord, oColumn, oData, oDataTable) {
+        var oDT = oDataTable || this;
+        el.innerHTML = util.Number.format(oData, oColumn.currencyOptions || oDT.get("currencyOptions"));
+    },
+
+    /**
+     * Formats _javascript_ Dates.
+     *
+     * @method DataTable.formatDate
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {Object} Data value for the cell, or null. String values are
+     * treated as markup and inserted into the DOM with innerHTML.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatDate : function(el, oRecord, oColumn, oData, oDataTable) {
+        var oDT = oDataTable || this,
+            oConfig = oColumn.dateOptions || oDT.get("dateOptions");
+        el.innerHTML = util.Date.format(oData, oConfig, oConfig.locale);
+    },
+
+    /**
+     * Formats SELECT elements.
+     *
+     * @method DataTable.formatDropdown
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {Object} Data value for the cell, or null. String values may
+     * be treated as markup and inserted into the DOM with innerHTML as element
+     * label.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatDropdown : function(el, oRecord, oColumn, oData, oDataTable) {
+        var oDT = oDataTable || this,
+            selectedValue = (lang.isValue(oData)) ? oData : oRecord.getData(oColumn.field),
+            options = (lang.isArray(oColumn.dropdownOptions)) ?
+                oColumn.dropdownOptions : null,
+
+            selectEl,
+            collection = el.getElementsByTagName("select");
+
+        // Create the form element only once, so we can attach the onChange listener
+        if(collection.length === 0) {
+            // Create SELECT element
+            selectEl = document.createElement("select");
+            selectEl.className = DT.CLASS_DROPDOWN;
+            selectEl = el.appendChild(selectEl);
+
+            // Add event listener
+            Ev.addListener(selectEl,"change",oDT._onDropdownChange,oDT);
+        }
+
+        selectEl = collection[0];
+
+        // Update the form element
+        if(selectEl) {
+            // Clear out previous options
+            selectEl.innerHTML = "";
+
+            // We have options to populate
+            if(options) {
+                // Create OPTION elements
+                for(var i=0; i<options.length; i++) {
+                    var option = options[i];
+                    var optionEl = document.createElement("option");
+                    optionEl.value = (lang.isValue(option.value)) ?
+                            option.value : option;
+                    // Bug 2334323: Support legacy text, support label for consistency with DropdownCellEditor
+                    optionEl.innerHTML = (lang.isValue(option.text)) ?
+                            option.text : (lang.isValue(option.label)) ? option.label : option;
+                    optionEl = selectEl.appendChild(optionEl);
+                    if (optionEl.value == selectedValue) {
+                        optionEl.selected = true;
+                    }
+                }
+            }
+            // Selected value is our only option
+            else {
+                selectEl.innerHTML = "<option selected value=\"" + selectedValue + "\">" + selectedValue + "</option>";
+            }
+        }
+        else {
+            el.innerHTML = lang.isValue(oData) ? oData : "";
+        }
+    },
+
+    /**
+     * Formats emails.
+     *
+     * @method DataTable.formatEmail
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {String} Data value for the cell, or null. Values are
+     * HTML-escaped.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatEmail : function(el, oRecord, oColumn, oData, oDataTable) {
+        if(lang.isString(oData)) {
+            oData = lang.escapeHTML(oData);
+            el.innerHTML = "<a href="" + oData + "\">" + oData + "</a>";
+        }
+        else {
+            el.innerHTML = lang.isValue(oData) ? lang.escapeHTML(oData.toString()) : "";
+        }
+    },
+
+    /**
+     * Formats links.
+     *
+     * @method DataTable.formatLink
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {String} Data value for the cell, or null. Values are
+     * HTML-escaped
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatLink : function(el, oRecord, oColumn, oData, oDataTable) {
+        if(lang.isString(oData)) {
+            oData = lang.escapeHTML(oData);
+            el.innerHTML = "<a href="" + oData + "\">" + oData + "</a>";
+        }
+        else {
+            el.innerHTML = lang.isValue(oData) ? lang.escapeHTML(oData.toString()) : "";
+        }
+    },
+
+    /**
+     * Formats numbers.
+     *
+     * @method DataTable.formatNumber
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {Object} Data value for the cell, or null.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatNumber : function(el, oRecord, oColumn, oData, oDataTable) {
+        var oDT = oDataTable || this;
+        el.innerHTML = util.Number.format(oData, oColumn.numberOptions || oDT.get("numberOptions"));
+    },
+
+    /**
+     * Formats INPUT TYPE=RADIO elements.
+     *
+     * @method DataTable.formatRadio
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {Object} (Optional) Data value for the cell.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatRadio : function(el, oRecord, oColumn, oData, oDataTable) {
+        var oDT = oDataTable || this,
+            bChecked = oData;
+        bChecked = (bChecked) ? " checked=\"checked\"" : "";
+        el.innerHTML = "<input type=\"radio\"" + bChecked +
+                " name=\""+oDT.getId()+"-col-" + oColumn.getSanitizedKey() + "\"" +
+                " class=\"" + DT.CLASS_RADIO+ "\" />";
+    },
+
+    /**
+     * Formats text strings.
+     *
+     * @method DataTable.formatText
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {String} (Optional) Data value for the cell. Values are
+     * HTML-escaped.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatText : function(el, oRecord, oColumn, oData, oDataTable) {
+        var value = (lang.isValue(oData)) ? oData : "";
+        el.innerHTML = lang.escapeHTML(value.toString());
+    },
+
+    /**
+     * Formats TEXTAREA elements.
+     *
+     * @method DataTable.formatTextarea
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {Object} (Optional) Data value for the cell. Values are
+     * HTML-escaped.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatTextarea : function(el, oRecord, oColumn, oData, oDataTable) {
+        var value = (lang.isValue(oData)) ? lang.escapeHTML(oData.toString()) : "",
+            markup = "<textarea>" + value + "</textarea>";
+        el.innerHTML = markup;
+    },
+
+    /**
+     * Formats INPUT TYPE=TEXT elements.
+     *
+     * @method DataTable.formatTextbox
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {Object} (Optional) Data value for the cell. Values are
+     * HTML-escaped.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatTextbox : function(el, oRecord, oColumn, oData, oDataTable) {
+        var value = (lang.isValue(oData)) ? lang.escapeHTML(oData.toString()) : "",
+            markup = "<input type=\"text\" value=\"" + value + "\" />";
+        el.innerHTML = markup;
+    },
+
+    /**
+     * Default cell formatter
+     *
+     * @method DataTable.formatDefault
+     * @param el {HTMLElement} The element to format with markup.
+     * @param oRecord {YAHOO.widget.Record} Record instance.
+     * @param oColumn {YAHOO.widget.Column} Column instance.
+     * @param oData {HTML} (Optional) Data value for the cell. String values are
+     * treated as markup and inserted into the DOM with innerHTML.
+     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
+     * @static
+     */
+    formatDefault : function(el, oRecord, oColumn, oData, oDataTable) {
+        el.innerHTML = (lang.isValue(oData) && oData !== "") ? oData.toString() : "&#160;";
+    },
+
+    /**
+     * Validates data value to type Number, doing type conversion as
+     * necessary. A valid Number value is return, else null is returned
+     * if input value does not validate.
+     *
+     *
+     * @method DataTable.validateNumber
+     * @param oData {Object} Data to validate.
+     * @static
+    */
+    validateNumber : function(oData) {
+        //Convert to number
+        var number = oData * 1;
+
+        // Validate
+        if(lang.isNumber(number)) {
+            return number;
+        }
+        else {
+            return undefined;
+        }
+    }
+});
+
+// Done in separate step so referenced functions are defined.
+/**
+ * Registry of cell formatting functions, enables shortcut pointers in Column
+ * definition formatter value (i.e., {key:"myColumn", formatter:"date"}).
+ * @property DataTable.Formatter
+ * @type Object
+ * @static
+ */
+DT.Formatter = {
+    button   : DT.formatButton,
+    checkbox : DT.formatCheckbox,
+    currency : DT.formatCurrency,
+    "date"   : DT.formatDate,
+    dropdown : DT.formatDropdown,
+    email    : DT.formatEmail,
+    link     : DT.formatLink,
+    "number" : DT.formatNumber,
+    radio    : DT.formatRadio,
+    text     : DT.formatText,
+    textarea : DT.formatTextarea,
+    textbox  : DT.formatTextbox,
+
+    defaultFormatter : DT.formatDefault
+};
+
+lang.extend(DT, util.Element, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Superclass methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Implementation of Element's abstract method. Sets up config values.
+ *
+ * @method initAttributes
+ * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
+ * @private
+ */
+
+initAttributes : function(oConfigs) {
+    oConfigs = oConfigs || {};
+    DT.superclass.initAttributes.call(this, oConfigs);
+
+    /**
+    * @attribute summary
+    * @description String value for the SUMMARY attribute.
+    * @type String
+    * @default ""    
+    */
+    this.setAttributeConfig("summary", {
+        value: "",
+        validator: lang.isString,
+        method: function(sSummary) {
+            if(this._elTable) {
+                this._elTable.summary = sSummary;
+            }
+        }
+    });
+
+    /**
+    * @attribute selectionMode
+    * @description Specifies row or cell selection mode. Accepts the following strings:
+    *    <dl>
+    *      <dt>"standard"</dt>
+    *      <dd>Standard row selection with support for modifier keys to enable
+    *      multiple selections.</dd>
+    *
+    *      <dt>"single"</dt>
+    *      <dd>Row selection with modifier keys disabled to not allow
+    *      multiple selections.</dd>
+    *
+    *      <dt>"singlecell"</dt>
+    *      <dd>Cell selection with modifier keys disabled to not allow
+    *      multiple selections.</dd>
+    *
+    *      <dt>"cellblock"</dt>
+    *      <dd>Cell selection with support for modifier keys to enable multiple
+    *      selections in a block-fashion, like a spreadsheet.</dd>
+    *
+    *      <dt>"cellrange"</dt>
+    *      <dd>Cell selection with support for modifier keys to enable multiple
+    *      selections in a range-fashion, like a calendar.</dd>
+    *    </dl>
+    *
+    * @default "standard"
+    * @type String
+    */
+    this.setAttributeConfig("selectionMode", {
+        value: "standard",
+        validator: lang.isString
+    });
+
+    /**
+    * @attribute sortedBy
+    * @description Object literal provides metadata for initial sort values if
+    * data will arrive pre-sorted:
+    * <dl>
+    *     <dt>sortedBy.key</dt>
+    *     <dd>{String} Key of sorted Column</dd>
+    *     <dt>sortedBy.dir</dt>
+    *     <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
+    * </dl>
+    * @type Object | null
+    */
+    this.setAttributeConfig("sortedBy", {
+        value: null,
+        // TODO: accepted array for nested sorts
+        validator: function(oNewSortedBy) {
+            if(oNewSortedBy) {
+                return (lang.isObject(oNewSortedBy) && oNewSortedBy.key);
+            }
+            else {
+                return ( null);
+            }
+        },
+        method: function(oNewSortedBy) {
+            // Stash the previous value
+            var oOldSortedBy = this.get("sortedBy");
+            
+            // Workaround for bug 1827195
+            this._configs.sortedBy.value = oNewSortedBy;
+
+            // Remove ASC/DESC from TH
+            var oOldColumn,
+                nOldColumnKeyIndex,
+                oNewColumn,
+                nNewColumnKeyIndex;
+                
+            if(this._elThead) {
+                if(oOldSortedBy && oOldSortedBy.key && oOldSortedBy.dir) {
+                    oOldColumn = this._oColumnSet.getColumn(oOldSortedBy.key);
+                    nOldColumnKeyIndex = oOldColumn.getKeyIndex();
+                    
+                    // Remove previous UI from THEAD
+                    var elOldTh = oOldColumn.getThEl();
+                    Dom.removeClass(elOldTh, oOldSortedBy.dir);
+                    this.formatTheadCell(oOldColumn.getThLinerEl().firstChild, oOldColumn, oNewSortedBy);
+                }
+                if(oNewSortedBy) {
+                     ? oNewSortedBy.column : this._oColumnSet.getColumn(oNewSortedBy.key);
+                    nNewColumnKeyIndex = oNewColumn.getKeyIndex();
+    
+                    // Update THEAD with new UI
+                    var elNewTh = oNewColumn.getThEl();
+                    // Backward compatibility
+                    if(oNewSortedBy.dir && ((oNewSortedBy.dir == "asc") ||  (oNewSortedBy.dir == "desc"))) {
+                        var newClass = (oNewSortedBy.dir == "desc") ?
+                                DT.CLASS_DESC :
+                                DT.CLASS_ASC;
+                        Dom.addClass(elNewTh, newClass);
+                    }
+                    else {
+                         var sortClass = oNewSortedBy.dir || DT.CLASS_ASC;
+                         Dom.addClass(elNewTh, sortClass);
+                    }
+                    this.formatTheadCell(oNewColumn.getThLinerEl().firstChild, oNewColumn, oNewSortedBy);
+                }
+            }
+          
+            if(this._elTbody) {
+                // Update TBODY UI
+                this._elTbody.style.display = "none";
+                var allRows = this._elTbody.rows,
+                    allCells;
+                for(var i=allRows.length-1; i>-1; i--) {
+                    allCells = allRows[i].childNodes;
+                    if(allCells[nOldColumnKeyIndex]) {
+                        Dom.removeClass(allCells[nOldColumnKeyIndex], oOldSortedBy.dir);
+                    }
+                    if(allCells[nNewColumnKeyIndex]) {
+                        Dom.addClass(allCells[nNewColumnKeyIndex], oNewSortedBy.dir);
+                    }
+                }
+                this._elTbody.style.display = "";
+            }
+                
+            this._clearTrTemplateEl();
+        }
+    });
+    
+    /**
+    * @attribute paginator
+    * @description An instance of YAHOO.widget.Paginator.
+    * @default null
+    * @type {Object|YAHOO.widget.Paginator}
+    */
+    this.setAttributeConfig("paginator", {
+        value : null,
+        validator : function (val) {
+            return val === null || val instanceof widget.Paginator;
+        },
+        method : function () { this._updatePaginator.apply(this,arguments); }
+    });
+
+    /**
+    * @attribute caption
+    * @description Value for the CAPTION element. String values are treated as
+    * markup and inserted into the DOM with innerHTML. NB: Not supported in
+    * ScrollingDataTable.    
+    * @type HTML
+    */
+    this.setAttributeConfig("caption", {
+        value: null,
+        validator: lang.isString,
+        method: function(sCaption) {
+            this._initCaptionEl(sCaption);
+        }
+    });
+
+    /**
+    * @attribute draggableColumns
+    * @description True if Columns are draggable to reorder, false otherwise.
+    * The Drag & Drop Utility is required to enable this feature. Only top-level
+    * and non-nested Columns are draggable. Write once.
+    * @default false
+    * @type Boolean
+    */
+    this.setAttributeConfig("draggableColumns", {
+        value: false,
+        validator: lang.isBoolean,
+        method: function(oParam) {
+            if(this._elThead) {
+                if(oParam) {
+                    this._initDraggableColumns();
+                }
+                else {
+                    this._destroyDraggableColumns();
+                }
+            }
+        }
+    });
+
+    /**
+    * @attribute renderLoopSize      
+    * @description A value greater than 0 enables DOM rendering of rows to be
+    * executed from a non-blocking timeout queue and sets how many rows to be
+    * rendered per timeout. Recommended for very large data sets.     
+    * @type Number      
+    * @default 0      
+    */      
+     this.setAttributeConfig("renderLoopSize", {
+         value: 0,
+         validator: lang.isNumber
+     });
+
+    /**
+    * @attribute sortFunction
+    * @description Default Column sort function, receives the following args:
+    *    <dl>
+    *      <dt>a {Object}</dt>
+    *      <dd>First sort argument.</dd>
+    *      <dt>b {Object}</dt>
+    *      <dd>Second sort argument.</dd>
+
+    *      <dt>desc {Boolean}</dt>
+    *      <dd>True if sort direction is descending, false if
+    * sort direction is ascending.</dd>
+    *      <dt>field {String}</dt>
+    *      <dd>The field to sort by, from sortOptions.field</dd>
+    *   </dl>
+    * @type function
+    */
+    this.setAttributeConfig("sortFunction", {
+        value: function(a, b, desc, field) {
+            var compare = YAHOO.util.Sort.compare,
+                sorted = compare(a.getData(field),b.getData(field), desc);
+            if(sorted === 0) {
+                return compare(a.getCount(),b.getCount(), desc); // Bug 1932978
+            }
+            else {
+                return sorted;
+            }
+        }
+    });
+
+    /**
+    * @attribute formatRow
+    * @description A function that accepts a TR element and its associated Record
+    * for custom formatting. The function must return TRUE in order to automatically
+    * continue formatting of child TD elements, else TD elements will not be
+    * automatically formatted.
+    * @type function
+    * @default null
+    */
+    this.setAttributeConfig("formatRow", {
+        value: null,
+        validator: lang.isFunction
+    });
+
+    /**
+    * @attribute generateRequest
+    * @description A function that converts an object literal of desired DataTable
+    * states into a request value which is then passed to the DataSource's
+    * sendRequest method in order to retrieve data for those states. This
+    * function is passed an object literal of state data and a reference to the
+    * DataTable instance:
+    *     
+    * <dl>
+    *   <dt>pagination<dt>
+    *   <dd>        
+    *         <dt>offsetRecord</dt>
+    *         <dd>{Number} Index of the first Record of the desired page</dd>
+    *         <dt>rowsPerPage</dt>
+    *         <dd>{Number} Number of rows per page</dd>
+    *   </dd>
+    *   <dt>sortedBy</dt>
+    *   <dd>                
+    *         <dt>key</dt>
+    *         <dd>{String} Key of sorted Column</dd>
+    *         <dt>dir</dt>
+    *         <dd>{String} Sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
+    *   </dd>
+    *   <dt>self</dt>
+    *   <dd>The DataTable instance</dd>
+    * </dl>
+    * 
+    * and by default returns a String of syntax:
+    * "sort={sortColumn}&dir={sortDir}&startIndex={pageStartIndex}&results={rowsPerPage}"
+    * @type function
+    * @default HTMLFunction
+    */
+    this.setAttributeConfig("generateRequest", {
+        value: function(oState, oSelf) {
+            // Set defaults
+            oState = oState || {pagination:null, sortedBy:null};
+            var sort = encodeURIComponent((oState.sortedBy) ? oState.sortedBy.key : oSelf.getColumnSet().keys[0].getKey());
+            var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "desc" : "asc";
+            var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0;
+            var results = (oState.pagination) ? oState.pagination.rowsPerPage : null;
+            
+            // Build the request
+            return  "sort=" + sort +
+                    "&dir=" + dir +
+                    "&startIndex=" + startIndex +
+                    ((results !== null) ? "&results=" + results : "");
+        },
+        validator: lang.isFunction
+    });
+
+    /**
+    * @attribute initialRequest
+    * @description Defines the initial request that gets sent to the DataSource
+    * during initialization. Value is ignored if initialLoad is set to any value
+    * other than true.    
+    * @type MIXED
+    * @default null
+    */
+    this.setAttributeConfig("initialRequest", {
+        value: null
+    });
+
+    /**
+    * @attribute initialLoad
+    * @description Determines whether or not to load data at instantiation. By
+    * default, will trigger a sendRequest() to the DataSource and pass in the
+    * request defined by initialRequest. If set to false, data will not load
+    * at instantiation. Alternatively, implementers who wish to work with a 
+    * custom payload may pass in an object literal with the following values:
+    *     
+    *    <dl>
+    *      <dt>request (MIXED)</dt>
+    *      <dd>Request value.</dd>
+    *
+    *      <dt>argument (MIXED)</dt>
+    *      <dd>Custom data that will be passed through to the callback function.</dd>
+    *    </dl>
+    *
+    *                    
+    * @type Boolean | Object
+    * @default true
+    */
+    this.setAttributeConfig("initialLoad", {
+        value: true
+    });
+    
+    /**
+    * @attribute dynamicData
+    * @description If true, sorting and pagination are relegated to the DataSource
+    * for handling, using the request returned by the "generateRequest" function.
+    * Each new DataSource response blows away all previous Records. False by default, so 
+    * sorting and pagination will be handled directly on the client side, without
+    * causing any new requests for data from the DataSource.
+    * @type Boolean
+    * @default false
+    */
+    this.setAttributeConfig("dynamicData", {
+        value: false,
+        validator: lang.isBoolean
+    });
+
+    /**
+     * @attribute MSG_EMPTY
+     * @description Message to display if DataTable has no data. String
+     * values are treated as markup and inserted into the DOM with innerHTML.
+     * @type HTML
+     * @default "No records found."
+     */
+     this.setAttributeConfig("MSG_EMPTY", {
+         value: "No records found.",
+         validator: lang.isString
+     });      
+
+    /**
+     * @attribute MSG_LOADING
+     * @description Message to display while DataTable is loading data. String
+     * values are treated as markup and inserted into the DOM with innerHTML.
+     * @type HTML
+     * @default "Loading..."
+     */      
+     this.setAttributeConfig("MSG_LOADING", {
+         value: "Loading...",
+         validator: lang.isString
+     });      
+
+    /**
+     * @attribute MSG_ERROR
+     * @description Message to display while DataTable has data error. String
+     * values are treated as markup and inserted into the DOM with innerHTML.
+     * @type HTML
+     * @default "Data error."
+     */      
+     this.setAttributeConfig("MSG_ERROR", {
+         value: "Data error.",
+         validator: lang.isString
+     });
+
+    /**
+     * @attribute MSG_SORTASC
+     * @description Message to display in tooltip to sort Column in ascending
+     * order. String values are treated as markup and inserted into the DOM as
+     * innerHTML.
+     * @type HTML
+     * @default "Click to sort ascending"
+     */      
+     this.setAttributeConfig("MSG_SORTASC", {      
+         value: "Click to sort ascending",      
+         validator: lang.isString,
+         method: function(sParam) {
+            if(this._elThead) {
+                for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
+                    if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_ASC) {
+                        allKeys[i]._elThLabel.firstChild.title = sParam;
+                    }
+                }
+            }      
+         }
+     });
+
+    /**
+     * @attribute MSG_SORTDESC
+     * @description Message to display in tooltip to sort Column in descending
+     * order. String values are treated as markup and inserted into the DOM as
+     * innerHTML.
+     * @type HTML
+     * @default "Click to sort descending"
+     */      
+     this.setAttributeConfig("MSG_SORTDESC", {      
+         value: "Click to sort descending",      
+         validator: lang.isString,
+         method: function(sParam) {
+            if(this._elThead) {
+                for(var i=0, allKeys=this.getColumnSet().keys, len=allKeys.length; i<len; i++) {
+                    if(allKeys[i].sortable && this.getColumnSortDir(allKeys[i]) === DT.CLASS_DESC) {
+                        allKeys[i]._elThLabel.firstChild.title = sParam;
+                    }
+                }
+            }               
+         }
+     });
+     
+    /**
+     * @attribute currencySymbol
+     * @deprecated Use currencyOptions.
+     */
+    this.setAttributeConfig("currencySymbol", {
+        value: "$",
+        validator: lang.isString
+    });
+    
+    /**
+     * Default config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
+     * @attribute currencyOptions
+     * @type Object
+     * @default {prefix: $, decimalPlaces:2, decimalSeparator:".", thousandsSeparator:","}
+     */
+    this.setAttributeConfig("currencyOptions", {
+        value: {
+            prefix: this.get("currencySymbol"), // TODO: deprecate currencySymbol
+            decimalPlaces:2,
+            decimalSeparator:".",
+            thousandsSeparator:","
+        }
+    });
+    
+    /**
+     * Default config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
+     * @attribute dateOptions
+     * @type Object
+     * @default {format:"%m/%d/%Y", locale:"en"}
+     */
+    this.setAttributeConfig("dateOptions", {
+        value: {format:"%m/%d/%Y", locale:"en"}
+    });
+    
+    /**
+     * Default config passed to YAHOO.util.Number.format() by the 'number' Column formatter.
+     * @attribute numberOptions
+     * @type Object
+     * @default {decimalPlaces:0, thousandsSeparator:","}
+     */
+    this.setAttributeConfig("numberOptions", {
+        value: {
+            decimalPlaces:0,
+            thousandsSeparator:","
+        }
+    });
+
+},
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private member variables
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * True if instance is initialized, so as to fire the initEvent after render.
+ *
+ * @property _bInit
+ * @type Boolean
+ * @default true
+ * @private
+ */
+_bInit : true,
+
+/**
+ * Index assigned to instance.
+ *
+ * @property _nIndex
+ * @type Number
+ * @private
+ */
+_nIndex : null,
+
+/**
+ * Counter for IDs assigned to TR elements.
+ *
+ * @property _nTrCount
+ * @type Number
+ * @private
+ */
+_nTrCount : 0,
+
+/**
+ * Counter for IDs assigned to TD elements.
+ *
+ * @property _nTdCount
+ * @type Number
+ * @private
+ */
+_nTdCount : 0,
+
+/**
+ * Unique id assigned to instance "yui-dtN", useful prefix for generating unique
+ * DOM ID strings and log messages.
+ *
+ * @property _sId
+ * @type String
+ * @private
+ */
+_sId : null,
+
+/**
+ * Render chain.
+ *
+ * @property _oChainRender
+ * @type YAHOO.util.Chain
+ * @private
+ */
+_oChainRender : null,
+
+/**
+ * DOM reference to the container element for the DataTable instance into which
+ * all other elements get created.
+ *
+ * @property _elContainer
+ * @type HTMLElement
+ * @private
+ */
+_elContainer : null,
+
+/**
+ * DOM reference to the mask element for the DataTable instance which disables it.
+ *
+ * @property _elMask
+ * @type HTMLElement
+ * @private
+ */
+_elMask : null,
+
+/**
+ * DOM reference to the TABLE element for the DataTable instance.
+ *
+ * @property _elTable
+ * @type HTMLElement
+ * @private
+ */
+_elTable : null,
+
+/**
+ * DOM reference to the CAPTION element for the DataTable instance.
+ *
+ * @property _elCaption
+ * @type HTMLElement
+ * @private
+ */
+_elCaption : null,
+
+/**
+ * DOM reference to the COLGROUP element for the DataTable instance.
+ *
+ * @property _elColgroup
+ * @type HTMLElement
+ * @private
+ */
+_elColgroup : null,
+
+/**
+ * DOM reference to the THEAD element for the DataTable instance.
+ *
+ * @property _elThead
+ * @type HTMLElement
+ * @private
+ */
+_elThead : null,
+
+/**
+ * DOM reference to the primary TBODY element for the DataTable instance.
+ *
+ * @property _elTbody
+ * @type HTMLElement
+ * @private
+ */
+_elTbody : null,
+
+/**
+ * DOM reference to the secondary TBODY element used to display DataTable messages.
+ *
+ * @property _elMsgTbody
+ * @type HTMLElement
+ * @private
+ */
+_elMsgTbody : null,
+
+/**
+ * DOM reference to the secondary TBODY element's single TR element used to display DataTable messages.
+ *
+ * @property _elMsgTr
+ * @type HTMLElement
+ * @private
+ */
+_elMsgTr : null,
+
+/**
+ * DOM reference to the secondary TBODY element's single TD element used to display DataTable messages.
+ *
+ * @property _elMsgTd
+ * @type HTMLElement
+ * @private
+ */
+_elMsgTd : null,
+
+/**
+ * Element reference to shared Column drag target.
+ *
+ * @property _elColumnDragTarget
+ * @type HTMLElement
+ * @private
+ */
+_elColumnDragTarget : null,
+
+/**
+ * Element reference to shared Column resizer proxy.
+ *
+ * @property _elColumnResizerProxy
+ * @type HTMLElement
+ * @private
+ */
+_elColumnResizerProxy : null,
+
+/**
+ * DataSource instance for the DataTable instance.
+ *
+ * @property _oDataSource
+ * @type YAHOO.util.DataSource
+ * @private
+ */
+_oDataSource : null,
+
+/**
+ * ColumnSet instance for the DataTable instance.
+ *
+ * @property _oColumnSet
+ * @type YAHOO.widget.ColumnSet
+ * @private
+ */
+_oColumnSet : null,
+
+/**
+ * RecordSet instance for the DataTable instance.
+ *
+ * @property _oRecordSet
+ * @type YAHOO.widget.RecordSet
+ * @private
+ */
+_oRecordSet : null,
+
+/**
+ * The active CellEditor instance for the DataTable instance.
+ *
+ * @property _oCellEditor
+ * @type YAHOO.widget.CellEditor
+ * @private
+ */
+_oCellEditor : null,
+
+/**
+ * ID string of first TR element of the current DataTable page.
+ *
+ * @property _sFirstTrId
+ * @type String
+ * @private
+ */
+_sFirstTrId : null,
+
+/**
+ * ID string of the last TR element of the current DataTable page.
+ *
+ * @property _sLastTrId
+ * @type String
+ * @private
+ */
+_sLastTrId : null,
+
+/**
+ * Template row to create all new rows from.
+ * @property _elTrTemplate
+ * @type {HTMLElement}
+ * @private 
+ */
+_elTrTemplate : null,
+
+/**
+ * Sparse array of custom functions to set column widths for browsers that don't
+ * support dynamic CSS rules.  Functions are added at the index representing
+ * the number of rows they update.
+ *
+ * @property _aDynFunctions
+ * @type Array
+ * @private
+ */
+_aDynFunctions : [],
+
+/**
+ * Disabled state.
+ *
+ * @property _disabled
+ * @type Boolean
+ * @private
+ */
+_disabled : false,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Clears browser text selection. Useful to call on rowSelectEvent or
+ * cellSelectEvent to prevent clicks or dblclicks from selecting text in the
+ * browser.
+ *
+ * @method clearTextSelection
+ */
+clearTextSelection : function() {
+    var sel;
+    if(window.getSelection) {
+        sel = window.getSelection();
+    }
+    else if(document.getSelection) {
+        sel = document.getSelection();
+    }
+    else if(document.selection) {
+        sel = document.selection;
+    }
+    if(sel) {
+        if(sel.empty) {
+            sel.empty();
+        }
+        else if (sel.removeAllRanges) {
+            sel.removeAllRanges();
+        }
+        else if(sel.collapse) {
+            sel.collapse();
+        }
+    }
+},
+
+/**
+ * Sets focus on the given element.
+ *
+ * @method _focusEl
+ * @param el {HTMLElement} Element.
+ * @private
+ */
+_focusEl : function(el) {
+    el = el || this._elTbody;
+    // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
+    // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
+    // strange unexpected things as the user clicks on buttons and other controls.
+    setTimeout(function() {
+        try {
+            el.focus();
+        }
+        catch(e) {
+        }
+    },0);
+},
+
+/**
+ * Forces Gecko repaint.
+ *
+ * @method _repaintGecko
+ * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
+ * @private
+ */
+_repaintGecko : (ua.gecko) ? 
+    function(el) {
+        el = el || this._elContainer;
+        var parent = el.parentNode;
+        var nextSibling = el.nextSibling;
+        parent.insertBefore(parent.removeChild(el), nextSibling);
+    } : function() {},
+
+/**
+ * Forces Opera repaint.
+ *
+ * @method _repaintOpera
+ * @private 
+ */
+_repaintOpera : (ua.opera) ? 
+    function() {
+        if(ua.opera) {
+            document.documentElement.className += " ";
+            document.documentElement.className = YAHOO.lang.trim(document.documentElement.className);
+        }
+    } : function() {} ,
+
+/**
+ * Forces Webkit repaint.
+ *
+ * @method _repaintWebkit
+ * @el {HTMLElement} (Optional) Element to repaint, otherwise entire document body.
+ * @private
+ */
+_repaintWebkit : (ua.webkit) ? 
+    function(el) {
+        el = el || this._elContainer;
+        var parent = el.parentNode;
+        var nextSibling = el.nextSibling;
+        parent.insertBefore(parent.removeChild(el), nextSibling);
+    } : function() {},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// INIT FUNCTIONS
+
+/**
+ * Initializes object literal of config values.
+ *
+ * @method _initConfigs
+ * @param oConfig {Object} Object literal of config values.
+ * @private
+ */
+_initConfigs : function(oConfigs) {
+    if(!oConfigs || !lang.isObject(oConfigs)) {
+        oConfigs = {};
+    }
+    this.configs = oConfigs;
+},
+
+/**
+ * Initializes ColumnSet.
+ *
+ * @method _initColumnSet
+ * @param aColumnDefs {Object[]} Array of object literal Column definitions.
+ * @private
+ */
+_initColumnSet : function(aColumnDefs) {
+    var oColumn, i, len;
+    
+    if(this._oColumnSet) {
+        // First clear _oDynStyles for existing ColumnSet and
+        // uregister CellEditor Custom Events
+        for(i=0, len=this._oColumnSet.keys.length; i<len; i++) {
+            oColumn = this._oColumnSet.keys[i];
+            DT._oDynStyles["."+this.getId()+"-col-"+oColumn.getSanitizedKey()+" ."+DT.CLASS_LINER] = undefined;
+            if(oColumn.editor && oColumn.editor.unsubscribeAll) { // Backward compatibility
+                oColumn.editor.unsubscribeAll();
+            }
+        }
+        
+        this._oColumnSet = null;
+        this._clearTrTemplateEl();
+    }
+    
+    if(lang.isArray(aColumnDefs)) {
+        this._oColumnSet =  new YAHOO.widget.ColumnSet(aColumnDefs);
+    }
+    // Backward compatibility
+    else if(aColumnDefs instanceof YAHOO.widget.ColumnSet) {
+        this._oColumnSet =  aColumnDefs;
+    }
+
+    // Register CellEditor Custom Events
+    var allKeys = this._oColumnSet.keys;
+    for(i=0, len=allKeys.length; i<len; i++) {
+        oColumn = allKeys[i];
+        if(oColumn.editor && oColumn.editor.subscribe) { // Backward incompatibility
+            oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true);
+            oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true);
+            oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true);
+            oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true);
+            oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true);
+            oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true);
+            oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true);
+            oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true);
+        }
+    }
+},
+
+/**
+ * Initializes DataSource.
+ *
+ * @method _initDataSource
+ * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
+ * @private
+ */
+_initDataSource : function(oDataSource) {
+    this._oDataSource = null;
+    if(oDataSource && (lang.isFunction(oDataSource.sendRequest))) {
+        this._oDataSource = oDataSource;
+    }
+    // Backward compatibility
+    else {
+        var tmpTable = null;
+        var tmpContainer = this._elContainer;
+        var i=0;
+        //TODO: this will break if re-initing DS at runtime for SDT
+        // Peek in container child nodes to see if TABLE already exists
+        if(tmpContainer.hasChildNodes()) {
+            var tmpChildren = tmpContainer.childNodes;
+            for(i=0; i<tmpChildren.length; i++) {
+                if(tmpChildren[i].nodeName && tmpChildren[i].nodeName.toLowerCase() == "table") {
+                    tmpTable = tmpChildren[i];
+                    break;
+                }
+            }
+            if(tmpTable) {
+                var tmpFieldsArray = [];
+                for(; i<this._oColumnSet.keys.length; i++) {
+                    tmpFieldsArray.push({key:this._oColumnSet.keys[i].key});
+                }
+
+                this._oDataSource = new DS(tmpTable);
+                this._oDataSource.responseType = DS.TYPE_HTMLTABLE;
+                this._oDataSource.responseSchema = {fields: tmpFieldsArray};
+            }
+        }
+    }
+},
+
+/**
+ * Initializes RecordSet.
+ *
+ * @method _initRecordSet
+ * @private
+ */
+_initRecordSet : function() {
+    if(this._oRecordSet) {
+        this._oRecordSet.reset();
+    }
+    else {
+        this._oRecordSet = new YAHOO.widget.RecordSet();
+    }
+},
+
+/**
+ * Initializes DOM elements.
+ *
+ * @method _initDomElements
+ * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID. 
+ * return {Boolean} False in case of error, otherwise true 
+ * @private
+ */
+_initDomElements : function(elContainer) {
+    // Outer container
+    this._initContainerEl(elContainer);
+    // TABLE
+    this._initTableEl(this._elContainer);
+    // COLGROUP
+    this._initColgroupEl(this._elTable);
+    // THEAD
+    this._initTheadEl(this._elTable);
+    
+    // Message TBODY
+    this._initMsgTbodyEl(this._elTable);  
+
+    // Primary TBODY
+    this._initTbodyEl(this._elTable);
+
+    if(!this._elContainer || !this._elTable || !this._elColgroup ||  !this._elThead || !this._elTbody || !this._elMsgTbody) {
+        return false;
+    }
+    else {
+        return true;
+    }
+},
+
+/**
+ * Destroy's the DataTable outer container element, if available.
+ *
+ * @method _destroyContainerEl
+ * @param elContainer {HTMLElement} Reference to the container element. 
+ * @private
+ */
+_destroyContainerEl : function(elContainer) {
+        var columns = this._oColumnSet.keys,
+        elements, i;
+
+        Dom.removeClass(elContainer, DT.CLASS_DATATABLE);
+
+    // Bug 2528783
+    Ev.purgeElement( elContainer );
+    Ev.purgeElement( this._elThead, true ); // recursive to get resize handles
+    Ev.purgeElement( this._elTbody );
+    Ev.purgeElement( this._elMsgTbody );
+
+    // because change doesn't bubble, each select (via formatDropdown) gets
+    // its own subscription
+    elements = elContainer.getElementsByTagName( 'select' );
+
+    if ( elements.length ) {
+        Ev.detachListener( elements, 'change' );
+    }
+
+    for ( i = columns.length - 1; i >= 0; --i ) {
+        if ( columns[i].editor ) {
+            Ev.purgeElement( columns[i].editor._elContainer );
+        }
+    }
+
+    elContainer.innerHTML = "";
+    
+    this._elContainer = null;
+    this._elColgroup = null;
+    this._elThead = null;
+    this._elTbody = null;
+},
+
+/**
+ * Initializes the DataTable outer container element, including a mask.
+ *
+ * @method _initContainerEl
+ * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
+ * @private
+ */
+_initContainerEl : function(elContainer) {
+    // Validate container
+    elContainer = Dom.get(elContainer);
+    
+    if(elContainer && elContainer.nodeName && (elContainer.nodeName.toLowerCase() == "div")) {
+        // Destroy previous
+        this._destroyContainerEl(elContainer);
+
+        Dom.addClass(elContainer, DT.CLASS_DATATABLE);
+        Ev.addListener(elContainer, "focus", this._onTableFocus, this);
+        Ev.addListener(elContainer, "dblclick", this._onTableDblclick, this);
+        this._elContainer = elContainer;
+        
+        var elMask = document.createElement("div");
+        elMask.className = DT.CLASS_MASK;
+        elMask.style.display = "none";
+        this._elMask = elContainer.appendChild(elMask);
+    }
+},
+
+/**
+ * Destroy's the DataTable TABLE element, if available.
+ *
+ * @method _destroyTableEl
+ * @private
+ */
+_destroyTableEl : function() {
+    var elTable = this._elTable;
+    if(elTable) {
+        Ev.purgeElement(elTable, true);
+        elTable.parentNode.removeChild(elTable);
+        this._elCaption = null;
+        this._elColgroup = null;
+        this._elThead = null;
+        this._elTbody = null;
+    }
+},
+
+/**
+ * Creates HTML markup CAPTION element.
+ *
+ * @method _initCaptionEl
+ * @param sCaption {HTML} Caption value. String values are treated as markup and
+ * inserted into the DOM with innerHTML.
+ * @private
+ */
+_initCaptionEl : function(sCaption) {
+    if(this._elTable && sCaption) {
+        // Create CAPTION element
+        if(!this._elCaption) { 
+            this._elCaption = this._elTable.createCaption();
+        }
+        // Set CAPTION value
+        this._elCaption.innerHTML = sCaption;
+    }
+    else if(this._elCaption) {
+        this._elCaption.parentNode.removeChild(this._elCaption);
+    }
+},
+
+/**
+ * Creates HTML markup for TABLE, COLGROUP, THEAD and TBODY elements in outer
+ * container element.
+ *
+ * @method _initTableEl
+ * @param elContainer {HTMLElement} Container element into which to create TABLE.
+ * @private
+ */
+_initTableEl : function(elContainer) {
+    if(elContainer) {
+        // Destroy previous
+        this._destroyTableEl();
+    
+        // Create TABLE
+        this._elTable = elContainer.appendChild(document.createElement("table"));  
+         
+        // Set SUMMARY attribute
+        this._elTable.summary = this.get("summary");
+        
+        // Create CAPTION element
+        if(this.get("caption")) {
+            this._initCaptionEl(this.get("caption"));
+        }
+
+        // Set up mouseover/mouseout events via mouseenter/mouseleave delegation
+        Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "thead ."+DT.CLASS_LABEL, this);
+        Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "thead ."+DT.CLASS_LABEL, this);
+        Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "tbody.yui-dt-data>tr>td", this);
+        Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "tbody.yui-dt-data>tr>td", this);
+        Ev.delegate(this._elTable, "mouseenter", this._onTableMouseover, "tbody.yui-dt-message>tr>td", this);
+        Ev.delegate(this._elTable, "mouseleave", this._onTableMouseout, "tbody.yui-dt-message>tr>td", this);
+    }
+},
+
+/**
+ * Destroy's the DataTable COLGROUP element, if available.
+ *
+ * @method _destroyColgroupEl
+ * @private
+ */
+_destroyColgroupEl : function() {
+    var elColgroup = this._elColgroup;
+    if(elColgroup) {
+        var elTable = elColgroup.parentNode;
+        Ev.purgeElement(elColgroup, true);
+        elTable.removeChild(elColgroup);
+        this._elColgroup = null;
+    }
+},
+
+/**
+ * Initializes COLGROUP and COL elements for managing minWidth.
+ *
+ * @method _initColgroupEl
+ * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
+ * @private
+ */
+_initColgroupEl : function(elTable) {
+    if(elTable) {
+        // Destroy previous
+        this._destroyColgroupEl();
+
+        // Add COLs to DOCUMENT FRAGMENT
+        var allCols = this._aColIds || [],
+            allKeys = this._oColumnSet.keys,
+            i = 0, len = allCols.length,
+            elCol, oColumn,
+            elFragment = document.createDocumentFragment(),
+            elColTemplate = document.createElement("col");
+    
+        for(i=0,len=allKeys.length; i<len; i++) {
+            oColumn = allKeys[i];
+            elCol = elFragment.appendChild(elColTemplate.cloneNode(false));
+        }
+    
+        // Create COLGROUP
+        var elColgroup = elTable.insertBefore(document.createElement("colgroup"), elTable.firstChild);
+        elColgroup.appendChild(elFragment);
+        this._elColgroup = elColgroup;
+    }
+},
+
+/**
+ * Adds a COL element to COLGROUP at given index.
+ *
+ * @method _insertColgroupColEl
+ * @param index {Number} Index of new COL element.
+ * @private
+ */
+_insertColgroupColEl : function(index) {
+    if(lang.isNumber(index)&& this._elColgroup) {
+        var nextSibling = this._elColgroup.childNodes[index] || null;
+        this._elColgroup.insertBefore(document.createElement("col"), nextSibling);
+    }
+},
+
+/**
+ * Removes a COL element to COLGROUP at given index.
+ *
+ * @method _removeColgroupColEl
+ * @param index {Number} Index of removed COL element.
+ * @private
+ */
+_removeColgroupColEl : function(index) {
+    if(lang.isNumber(index) && this._elColgroup && this._elColgroup.childNodes[index]) {
+        this._elColgroup.removeChild(this._elColgroup.childNodes[index]);
+    }
+},
+
+/**
+ * Reorders a COL element from old index(es) to new index.
+ *
+ * @method _reorderColgroupColEl
+ * @param aKeyIndexes {Number[]} Array of indexes of removed COL element.
+ * @param newIndex {Number} New index. 
+ * @private
+ */
+_reorderColgroupColEl : function(aKeyIndexes, newIndex) {
+    if(lang.isArray(aKeyIndexes) && lang.isNumber(newIndex) && this._elColgroup && (this._elColgroup.childNodes.length > aKeyIndexes[aKeyIndexes.length-1])) {
+        var i,
+            tmpCols = [];
+        // Remove COL
+        for(i=aKeyIndexes.length-1; i>-1; i--) {
+            tmpCols.push(this._elColgroup.removeChild(this._elColgroup.childNodes[aKeyIndexes[i]]));
+        }
+        // Insert COL
+        var nextSibling = this._elColgroup.childNodes[newIndex] || null;
+        for(i=tmpCols.length-1; i>-1; i--) {
+            this._elColgroup.insertBefore(tmpCols[i], nextSibling);
+        }
+    }
+},
+
+/**
+ * Destroy's the DataTable THEAD element, if available.
+ *
+ * @method _destroyTheadEl
+ * @private
+ */
+_destroyTheadEl : function() {
+    var elThead = this._elThead;
+    if(elThead) {
+        var elTable = elThead.parentNode;
+        Ev.purgeElement(elThead, true);
+        this._destroyColumnHelpers();
+        elTable.removeChild(elThead);
+        this._elThead = null;
+    }
+},
+
+/**
+ * Initializes THEAD element.
+ *
+ * @method _initTheadEl
+ * @param elTable {HTMLElement} TABLE element into which to create COLGROUP.
+ * @param {HTMLElement} Initialized THEAD element. 
+ * @private
+ */
+_initTheadEl : function(elTable) {
+    elTable = elTable || this._elTable;
+    
+    if(elTable) {
+        // Destroy previous
+        this._destroyTheadEl();
+    
+        //TODO: append to DOM later for performance
+        var elThead = (this._elColgroup) ?
+            elTable.insertBefore(document.createElement("thead"), this._elColgroup.nextSibling) :
+            elTable.appendChild(document.createElement("thead"));
+    
+        // Set up DOM events for THEAD
+        Ev.addListener(elThead, "focus", this._onTheadFocus, this);
+        Ev.addListener(elThead, "keydown", this._onTheadKeydown, this);
+        Ev.addListener(elThead, "mousedown", this._onTableMousedown, this);
+        Ev.addListener(elThead, "mouseup", this._onTableMouseup, this);
+        Ev.addListener(elThead, "click", this._onTheadClick, this);
+        
+        // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave
+        // delegation at the TABLE level
+
+        // Since we can't listen for click and dblclick on the same element...
+        // Attach separately to THEAD and TBODY
+        ///Ev.addListener(elThead, "dblclick", this._onTableDblclick, this);
+        
+       var oColumnSet = this._oColumnSet,
+            oColumn, i,j, l;
+        
+        // Add TRs to the THEAD
+        var colTree = oColumnSet.tree;
+        var elTh;
+        for(i=0; i<colTree.length; i++) {
+            var elTheadTr = elThead.appendChild(document.createElement("tr"));
+    
+            // ...and create TH cells
+            for(j=0; j<colTree[i].length; j++) {
+                oColumn = colTree[i][j];
+                elTh = elTheadTr.appendChild(document.createElement("th"));
+                this._initThEl(elTh,oColumn);
+            }
+    
+                // Set FIRST/LAST on THEAD rows
+                if(i === 0) {
+                    Dom.addClass(elTheadTr, DT.CLASS_FIRST);
+                }
+                if(i === (colTree.length-1)) {
+                    Dom.addClass(elTheadTr, DT.CLASS_LAST);
+                }
+
+        }
+
+        // Set FIRST/LAST on edge TH elements using the values in ColumnSet headers array
+        var aFirstHeaders = oColumnSet.headers[0] || [];
+        for(i=0; i<aFirstHeaders.length; i++) {
+            Dom.addClass(Dom.get(this.getId() +"-th-"+aFirstHeaders[i]), DT.CLASS_FIRST);
+        }
+        var aLastHeaders = oColumnSet.headers[oColumnSet.headers.length-1] || [];
+        for(i=0; i<aLastHeaders.length; i++) {
+            Dom.addClass(Dom.get(this.getId() +"-th-"+aLastHeaders[i]), DT.CLASS_LAST);
+        }
+        
+
+        ///TODO: try _repaintGecko(this._elContainer) instead
+        // Bug 1806891
+        if(ua.webkit && ua.webkit < 420) {
+            var oSelf = this;
+            setTimeout(function() {
+                elThead.style.display = "";
+            },0);
+            elThead.style.display = 'none';
+        }
+        
+        this._elThead = elThead;
+        
+        // Column helpers needs _elThead to exist
+        this._initColumnHelpers();  
+    }
+},
+
+/**
+ * Populates TH element as defined by Column.
+ *
+ * @method _initThEl
+ * @param elTh {HTMLElement} TH element reference.
+ * @param oColumn {YAHOO.widget.Column} Column object.
+ * @private
+ */
+_initThEl : function(elTh, oColumn) {
+    elTh.id = this.getId() + "-th-" + oColumn.getSanitizedKey(); // Needed for accessibility, getColumn by TH, and ColumnDD
+    elTh.innerHTML = "";
+    elTh.rowSpan = oColumn.getRowspan();
+    elTh.colSpan = oColumn.getColspan();
+    oColumn._elTh = elTh;
+    
+    var elThLiner = elTh.appendChild(document.createElement("div"));
+    elThLiner.id = elTh.id + "-liner"; // Needed for resizer
+    elThLiner.className = DT.CLASS_LINER;
+    oColumn._elThLiner = elThLiner;
+    
+    var elThLabel = elThLiner.appendChild(document.createElement("span"));
+    elThLabel.className = DT.CLASS_LABEL;    
+
+    // Assign abbr attribute
+    if(oColumn.abbr) {
+        elTh.abbr = oColumn.abbr;
+    }
+    // Clear minWidth on hidden Columns
+    if(oColumn.hidden) {
+        this._clearMinWidth(oColumn);
+    }
+        
+    elTh.className = this._getColumnClassNames(oColumn);
+            
+    // Set Column width...
+    if(oColumn.width) {
+        // Validate minWidth
+        var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
+                oColumn.minWidth : oColumn.width;
+        // ...for fallback cases
+        if(DT._bDynStylesFallback) {
+            elTh.firstChild.style.overflow = 'hidden';
+            elTh.firstChild.style.width = nWidth + 'px';        
+        }
+        // ...for non fallback cases
+        else {
+            this._setColumnWidthDynStyles(oColumn, nWidth + 'px', 'hidden');
+        }
+    }
+
+    this.formatTheadCell(elThLabel, oColumn, this.get("sortedBy"));
+    oColumn._elThLabel = elThLabel;
+},
+
+/**
+ * Outputs markup into the given TH based on given Column.
+ *
+ * @method formatTheadCell
+ * @param elCellLabel {HTMLElement} The label SPAN element within the TH liner,
+ * not the liner DIV element.     
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param oSortedBy {Object} Sort state object literal.
+*/
+formatTheadCell : function(elCellLabel, oColumn, oSortedBy) {
+    var sKey = oColumn.getKey();
+    var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
+
+    // Add accessibility link for sortable Columns
+    if(oColumn.sortable) {
+        // Calculate the direction
+        var sSortClass = this.getColumnSortDir(oColumn, oSortedBy);
+        var bDesc = (sSortClass === DT.CLASS_DESC);
+
+        // This is the sorted Column
+        if(oSortedBy && (oColumn.key === oSortedBy.key)) {
+            bDesc = !(oSortedBy.dir === DT.CLASS_DESC);
+        }
+
+        // Generate a unique HREF for visited status
+        var sHref = this.getId() + "-href-" + oColumn.getSanitizedKey();
+        
+        // Generate a dynamic TITLE for sort status
+        var sTitle = (bDesc) ? this.get("MSG_SORTDESC") : this.get("MSG_SORTASC");
+        
+        // Format the element
+        elCellLabel.innerHTML = "<a href="" + sHref + "\" title=\"" + sTitle + "\" class=\"" + DT.CLASS_SORTABLE + "\">" + sLabel + "</a>";
+    }
+    // Just display the label for non-sortable Columns
+    else {
+        elCellLabel.innerHTML = sLabel;
+    }
+},
+
+/**
+ * Disables DD from top-level Column TH elements.
+ *
+ * @method _destroyDraggableColumns
+ * @private
+ */
+_destroyDraggableColumns : function() {
+    var oColumn, elTh;
+    for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
+        oColumn = this._oColumnSet.tree[0][i];
+        if(oColumn._dd) {
+            oColumn._dd = oColumn._dd.unreg();
+            Dom.removeClass(oColumn.getThEl(), DT.CLASS_DRAGGABLE);       
+        }
+    }
+    
+    // Destroy column drag proxy
+    this._destroyColumnDragTargetEl();
+},
+
+/**
+ * Initializes top-level Column TH elements into DD instances.
+ *
+ * @method _initDraggableColumns
+ * @private
+ */
+_initDraggableColumns : function() {
+    this._destroyDraggableColumns();
+    if(util.DD) {
+        var oColumn, elTh, elDragTarget;
+        for(var i=0, len=this._oColumnSet.tree[0].length; i<len; i++) {
+            oColumn = this._oColumnSet.tree[0][i];
+            elTh = oColumn.getThEl();
+            Dom.addClass(elTh, DT.CLASS_DRAGGABLE);
+            elDragTarget = this._initColumnDragTargetEl();
+            oColumn._dd = new YAHOO.widget.ColumnDD(this, oColumn, elTh, elDragTarget);
+        }
+    }
+    else {
+    }
+},
+
+/**
+ * Destroys shared Column drag target.
+ *
+ * @method _destroyColumnDragTargetEl
+ * @private
+ */
+_destroyColumnDragTargetEl : function() {
+    if(this._elColumnDragTarget) {
+        var el = this._elColumnDragTarget;
+        YAHOO.util.Event.purgeElement(el);
+        el.parentNode.removeChild(el);
+        this._elColumnDragTarget = null;
+    }
+},
+
+/**
+ * Creates HTML markup for shared Column drag target.
+ *
+ * @method _initColumnDragTargetEl
+ * @return {HTMLElement} Reference to Column drag target.
+ * @private
+ */
+_initColumnDragTargetEl : function() {
+    if(!this._elColumnDragTarget) {
+        // Attach Column drag target element as first child of body
+        var elColumnDragTarget = document.createElement('div');
+        elColumnDragTarget.id = this.getId() + "-coltarget";
+        elColumnDragTarget.className = DT.CLASS_COLTARGET;
+        elColumnDragTarget.style.display = "none";
+        document.body.insertBefore(elColumnDragTarget, document.body.firstChild);
+
+        // Internal tracker of Column drag target
+        this._elColumnDragTarget = elColumnDragTarget;
+
+    }
+    return this._elColumnDragTarget;
+},
+
+/**
+ * Disables resizeability on key Column TH elements.
+ *
+ * @method _destroyResizeableColumns
+ * @private
+ */
+_destroyResizeableColumns : function() {
+    var aKeys = this._oColumnSet.keys;
+    for(var i=0, len=aKeys.length; i<len; i++) {
+        if(aKeys[i]._ddResizer) {
+            aKeys[i]._ddResizer = aKeys[i]._ddResizer.unreg();
+            Dom.removeClass(aKeys[i].getThEl(), DT.CLASS_RESIZEABLE);
+        }
+    }
+
+    // Destroy resizer proxy
+    this._destroyColumnResizerProxyEl();
+},
+
+/**
+ * Initializes resizeability on key Column TH elements.
+ *
+ * @method _initResizeableColumns
+ * @private
+ */
+_initResizeableColumns : function() {
+    this._destroyResizeableColumns();
+    if(util.DD) {
+        var oColumn, elTh, elThLiner, elThResizerLiner, elThResizer, elResizerProxy, cancelClick;
+        for(var i=0, len=this._oColumnSet.keys.length; i<len; i++) {
+            oColumn = this._oColumnSet.keys[i];
+            if(oColumn.resizeable) {
+                elTh = oColumn.getThEl();
+                Dom.addClass(elTh, DT.CLASS_RESIZEABLE);
+                elThLiner = oColumn.getThLinerEl();
+                
+                // Bug 1915349: So resizer is as tall as TH when rowspan > 1
+                // Create a separate resizer liner with position:relative
+                elThResizerLiner = elTh.appendChild(document.createElement("div"));
+                elThResizerLiner.className = DT.CLASS_RESIZERLINER;
+                
+                // Move TH contents into the new resizer liner
+                elThResizerLiner.appendChild(elThLiner);
+                
+                // Create the resizer
+                elThResizer = elThResizerLiner.appendChild(document.createElement("div"));
+                elThResizer.id = elTh.id + "-resizer"; // Needed for ColumnResizer
+                elThResizer.className = DT.CLASS_RESIZER;
+                oColumn._elResizer = elThResizer;
+
+                // Create the resizer proxy, once per instance
+                elResizerProxy = this._initColumnResizerProxyEl();
+                oColumn._ddResizer = new YAHOO.util.ColumnResizer(
+                        this, oColumn, elTh, elThResizer, elResizerProxy);
+                cancelClick = function(e) {
+                    Ev.stopPropagation(e);
+                };
+                Ev.addListener(elThResizer,"click",cancelClick);
+            }
+        }
+    }
+    else {
+    }
+},
+
+/**
+ * Destroys shared Column resizer proxy.
+ *
+ * @method _destroyColumnResizerProxyEl
+ * @return {HTMLElement} Reference to Column resizer proxy.
+ * @private
+ */
+_destroyColumnResizerProxyEl : function() {
+    if(this._elColumnResizerProxy) {
+        var el = this._elColumnResizerProxy;
+        YAHOO.util.Event.purgeElement(el);
+        el.parentNode.removeChild(el);
+        this._elColumnResizerProxy = null;
+    }
+},
+
+/**
+ * Creates HTML markup for shared Column resizer proxy.
+ *
+ * @method _initColumnResizerProxyEl
+ * @return {HTMLElement} Reference to Column resizer proxy.
+ * @private
+ */
+_initColumnResizerProxyEl : function() {
+    if(!this._elColumnResizerProxy) {
+        // Attach Column resizer element as first child of body
+        var elColumnResizerProxy = document.createElement("div");
+        elColumnResizerProxy.id = this.getId() + "-colresizerproxy"; // Needed for ColumnResizer
+        elColumnResizerProxy.className = DT.CLASS_RESIZERPROXY;
+        document.body.insertBefore(elColumnResizerProxy, document.body.firstChild);
+
+        // Internal tracker of Column resizer proxy
+        this._elColumnResizerProxy = elColumnResizerProxy;
+    }
+    return this._elColumnResizerProxy;
+},
+
+/**
+ * Destroys elements associated with Column functionality: ColumnDD and ColumnResizers.
+ *
+ * @method _destroyColumnHelpers
+ * @private
+ */
+_destroyColumnHelpers : function() {
+    this._destroyDraggableColumns();
+    this._destroyResizeableColumns();
+},
+
+/**
+ * Initializes elements associated with Column functionality: ColumnDD and ColumnResizers.
+ *
+ * @method _initColumnHelpers
+ * @private
+ */
+_initColumnHelpers : function() {
+    if(this.get("draggableColumns")) {
+        this._initDraggableColumns();
+    }
+    this._initResizeableColumns();
+},
+
+/**
+ * Destroy's the DataTable TBODY element, if available.
+ *
+ * @method _destroyTbodyEl
+ * @private
+ */
+_destroyTbodyEl : function() {
+    var elTbody = this._elTbody;
+    if(elTbody) {
+        var elTable = elTbody.parentNode;
+        Ev.purgeElement(elTbody, true);
+        elTable.removeChild(elTbody);
+        this._elTbody = null;
+    }
+},
+
+/**
+ * Initializes TBODY element for data.
+ *
+ * @method _initTbodyEl
+ * @param elTable {HTMLElement} TABLE element into which to create TBODY .
+ * @private
+ */
+_initTbodyEl : function(elTable) {
+    if(elTable) {
+        // Destroy previous
+        this._destroyTbodyEl();
+        
+        // Create TBODY
+        var elTbody = elTable.appendChild(document.createElement("tbody"));
+        elTbody.tabIndex = 0;
+        elTbody.className = DT.CLASS_DATA;
+    
+        // Set up DOM events for TBODY
+        Ev.addListener(elTbody, "focus", this._onTbodyFocus, this);
+        Ev.addListener(elTbody, "mousedown", this._onTableMousedown, this);
+        Ev.addListener(elTbody, "mouseup", this._onTableMouseup, this);
+        Ev.addListener(elTbody, "keydown", this._onTbodyKeydown, this);
+        Ev.addListener(elTbody, "click", this._onTbodyClick, this);
+
+        // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave
+        // delegation at the TABLE level
+
+        // Since we can't listen for click and dblclick on the same element...
+        // Attach separately to THEAD and TBODY
+        ///Ev.addListener(elTbody, "dblclick", this._onTableDblclick, this);
+        
+    
+        // IE puts focus outline in the wrong place
+        if(ua.ie) {
+            elTbody.hideFocus=true;
+        }
+
+        this._elTbody = elTbody;
+    }
+},
+
+/**
+ * Destroy's the DataTable message TBODY element, if available.
+ *
+ * @method _destroyMsgTbodyEl
+ * @private
+ */
+_destroyMsgTbodyEl : function() {
+    var elMsgTbody = this._elMsgTbody;
+    if(elMsgTbody) {
+        var elTable = elMsgTbody.parentNode;
+        Ev.purgeElement(elMsgTbody, true);
+        elTable.removeChild(elMsgTbody);
+        this._elTbody = null;
+    }
+},
+
+/**
+ * Initializes TBODY element for messaging.
+ *
+ * @method _initMsgTbodyEl
+ * @param elTable {HTMLElement} TABLE element into which to create TBODY 
+ * @private
+ */
+_initMsgTbodyEl : function(elTable) {
+    if(elTable) {
+        var elMsgTbody = document.createElement("tbody");
+        elMsgTbody.className = DT.CLASS_MESSAGE;
+        var elMsgTr = elMsgTbody.appendChild(document.createElement("tr"));
+        elMsgTr.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
+        this._elMsgTr = elMsgTr;
+        var elMsgTd = elMsgTr.appendChild(document.createElement("td"));
+        elMsgTd.colSpan = this._oColumnSet.keys.length || 1;
+        elMsgTd.className = DT.CLASS_FIRST + " " + DT.CLASS_LAST;
+        this._elMsgTd = elMsgTd;
+        elMsgTbody = elTable.insertBefore(elMsgTbody, this._elTbody);
+        var elMsgLiner = elMsgTd.appendChild(document.createElement("div"));
+        elMsgLiner.className = DT.CLASS_LINER;
+        this._elMsgTbody = elMsgTbody;
+
+        // Set up DOM events for TBODY
+        Ev.addListener(elMsgTbody, "focus", this._onTbodyFocus, this);
+        Ev.addListener(elMsgTbody, "mousedown", this._onTableMousedown, this);
+        Ev.addListener(elMsgTbody, "mouseup", this._onTableMouseup, this);
+        Ev.addListener(elMsgTbody, "keydown", this._onTbodyKeydown, this);
+        Ev.addListener(elMsgTbody, "click", this._onTbodyClick, this);
+
+        // Bug 2528073: mouseover/mouseout handled via mouseenter/mouseleave
+        // delegation at the TABLE level
+    }
+},
+
+/**
+ * Initialize internal event listeners
+ *
+ * @method _initEvents
+ * @private
+ */
+_initEvents : function () {
+    // Initialize Column sort
+    this._initColumnSort();
+        
+    // Add the document level click listener
+    YAHOO.util.Event.addListener(document, "click", this._onDocumentClick, this);
+
+    // Paginator integration
+    this.subscribe("paginatorChange",function () {
+        this._handlePaginatorChange.apply(this,arguments);
+    });
+
+    this.subscribe("initEvent",function () {
+        this.renderPaginator();
+    });
+
+    // Initialize CellEditor integration
+    this._initCellEditing();
+},
+
+/**      
+  * Initializes Column sorting.      
+  *      
+  * @method _initColumnSort      
+  * @private      
+  */      
+_initColumnSort : function() {
+    this.subscribe("theadCellClickEvent", this.onEventSortColumn);      
+
+    // Backward compatibility
+    var oSortedBy = this.get("sortedBy");
+    if(oSortedBy) {
+        if(oSortedBy.dir == "desc") {
+            this._configs.sortedBy.value.dir = DT.CLASS_DESC;
+        }
+        else if(oSortedBy.dir == "asc") {
+            this._configs.sortedBy.value.dir = DT.CLASS_ASC;
+        }
+    }
+},
+
+/**      
+  * Initializes CellEditor integration.      
+  *      
+  * @method _initCellEditing      
+  * @private      
+  */      
+_initCellEditing : function() {
+    this.subscribe("editorBlurEvent",function () {
+        this.onEditorBlurEvent.apply(this,arguments);
+    });
+    this.subscribe("editorBlockEvent",function () {
+        this.onEditorBlockEvent.apply(this,arguments);
+    });
+    this.subscribe("editorUnblockEvent",function () {
+        this.onEditorUnblockEvent.apply(this,arguments);
+    });
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// DOM MUTATION FUNCTIONS
+
+/**
+ * Retruns classnames to represent current Column states.
+ * @method _getColumnClassnames 
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param aAddClasses {String[]} An array of additional classnames to add to the
+ * return value.  
+ * @return {String} A String of classnames to be assigned to TH or TD elements
+ * for given Column.  
+ * @private 
+ */
+_getColumnClassNames : function (oColumn, aAddClasses) {
+    var allClasses;
+    
+    // Add CSS classes
+    if(lang.isString(oColumn.className)) {
+        // Single custom class
+        allClasses = [oColumn.className];
+    }
+    else if(lang.isArray(oColumn.className)) {
+        // Array of custom classes
+        allClasses = oColumn.className;
+    }
+    else {
+        // no custom classes
+        allClasses = [];
+    }
+    
+    // Hook for setting width with via dynamic style uses key since ID is too disposable
+    allClasses[allClasses.length] = this.getId() + "-col-" +oColumn.getSanitizedKey();
+
+    // Column key - minus any chars other than "A-Z", "a-z", "0-9", "_", "-", ".", or ":"
+    allClasses[allClasses.length] = "yui-dt-col-" +oColumn.getSanitizedKey();
+
+    var isSortedBy = this.get("sortedBy") || {};
+    // Sorted
+    if(oColumn.key === isSortedBy.key) {
+        allClasses[allClasses.length] = isSortedBy.dir || '';
+    }
+    // Hidden
+    if(oColumn.hidden) {
+        allClasses[allClasses.length] = DT.CLASS_HIDDEN;
+    }
+    // Selected
+    if(oColumn.selected) {
+        allClasses[allClasses.length] = DT.CLASS_SELECTED;
+    }
+    // Sortable
+    if(oColumn.sortable) {
+        allClasses[allClasses.length] = DT.CLASS_SORTABLE;
+    }
+    // Resizeable
+    if(oColumn.resizeable) {
+        allClasses[allClasses.length] = DT.CLASS_RESIZEABLE;
+    }
+    // Editable
+    if(oColumn.editor) {
+        allClasses[allClasses.length] = DT.CLASS_EDITABLE;
+    }
+    
+    // Addtnl classes, including First/Last
+    if(aAddClasses) {
+        allClasses = allClasses.concat(aAddClasses);
+    }
+    
+    return allClasses.join(' ');  
+},
+
+/**
+ * Clears TR element template in response to any Column state change.
+ * @method _clearTrTemplateEl
+ * @private 
+ */
+_clearTrTemplateEl : function () {
+    this._elTrTemplate = null;
+},
+
+/**
+ * Returns a new TR element template with TD elements classed with current
+ * Column states.
+ * @method _getTrTemplateEl 
+ * @return {HTMLElement} A TR element to be cloned and added to the DOM.
+ * @private 
+ */
+_getTrTemplateEl : function (oRecord, index) {
+    // Template is already available
+    if(this._elTrTemplate) {
+        return this._elTrTemplate;
+    }
+    // Template needs to be created
+    else {
+        var d   = document,
+            tr  = d.createElement('tr'),
+            td  = d.createElement('td'),
+            div = d.createElement('div');
+    
+        // Append the liner element
+        td.appendChild(div);
+
+        // Create TD elements into DOCUMENT FRAGMENT
+        var df = document.createDocumentFragment(),
+            allKeys = this._oColumnSet.keys,
+            elTd;
+
+        // Set state for each TD;
+        var aAddClasses;
+        for(var i=0, keysLen=allKeys.length; i<keysLen; i++) {
+            // Clone the TD template
+            elTd = td.cloneNode(true);
+
+            // Format the base TD
+            elTd = this._formatTdEl(allKeys[i], elTd, i, (i===keysLen-1));
+                        
+            df.appendChild(elTd);
+        }
+        tr.appendChild(df);
+        tr.className = DT.CLASS_REC;
+        this._elTrTemplate = tr;
+        return tr;
+    }   
+},
+
+/**
+ * Formats a basic TD element.
+ * @method _formatTdEl 
+ * @param oColumn {YAHOO.widget.Column} Associated Column instance. 
+ * @param elTd {HTMLElement} An unformatted TD element.
+ * @param index {Number} Column key index. 
+ * @param isLast {Boolean} True if Column is last key of the ColumnSet.
+ * @return {HTMLElement} A formatted TD element.
+ * @private 
+ */
+_formatTdEl : function (oColumn, elTd, index, isLast) {
+    var oColumnSet = this._oColumnSet;
+    
+    // Set the TD's accessibility headers
+    var allHeaders = oColumnSet.headers,
+        allColHeaders = allHeaders[index],
+        sTdHeaders = "",
+        sHeader;
+    for(var j=0, headersLen=allColHeaders.length; j < headersLen; j++) {
+        sHeader = this._sId + "-th-" + allColHeaders[j] + ' ';
+        sTdHeaders += sHeader;
+    }
+    elTd.headers = sTdHeaders;
+    
+    // Class the TD element
+    var aAddClasses = [];
+    if(index === 0) {
+        aAddClasses[aAddClasses.length] = DT.CLASS_FIRST;
+    }
+    if(isLast) {
+        aAddClasses[aAddClasses.length] = DT.CLASS_LAST;
+    }
+    elTd.className = this._getColumnClassNames(oColumn, aAddClasses);
+
+    // Class the liner element
+    elTd.firstChild.className = DT.CLASS_LINER;
+
+    // Set Column width for fallback cases
+    if(oColumn.width && DT._bDynStylesFallback) {
+        // Validate minWidth
+        var nWidth = (oColumn.minWidth && (oColumn.width < oColumn.minWidth)) ?
+                oColumn.minWidth : oColumn.width;
+        elTd.firstChild.style.overflow = 'hidden';
+        elTd.firstChild.style.width = nWidth + 'px';
+    }
+    
+    return elTd;
+},
+
+
+/**
+ * Create a new TR element for a given Record and appends it with the correct
+ * number of Column-state-classed TD elements. Striping is the responsibility of
+ * the calling function, which may decide to stripe the single row, a subset of
+ * rows, or all the rows.
+ * @method _createTrEl
+ * @param oRecord {YAHOO.widget.Record} Record instance
+ * @return {HTMLElement} The new TR element.  This must be added to the DOM.
+ * @private 
+ */
+_addTrEl : function (oRecord) {
+    var elTrTemplate = this._getTrTemplateEl();
+    
+    // Clone the TR template.
+    var elTr = elTrTemplate.cloneNode(true);
+    
+    // Populate content
+    return this._updateTrEl(elTr,oRecord);
+},
+
+/**
+ * Formats the contents of the given TR's TD elements with data from the given
+ * Record. Only innerHTML should change, nothing structural.
+ *
+ * @method _updateTrEl
+ * @param elTr {HTMLElement} The TR element to update.
+ * @param oRecord {YAHOO.widget.Record} The associated Record instance.
+ * @return {HTMLElement} DOM reference to the new TR element.
+ * @private
+ */
+_updateTrEl : function(elTr, oRecord) {
+    var ok = this.get("formatRow") ? this.get("formatRow").call(this, elTr, oRecord) : true;
+    if(ok) {
+        // Hide the row to prevent constant reflows
+        elTr.style.display = 'none';
+        
+        // Update TD elements with new data
+        var allTds = elTr.childNodes,
+            elTd;
+        for(var i=0,len=allTds.length; i<len; ++i) {
+            elTd = allTds[i];
+            
+            // Set the cell content
+            this.formatCell(allTds[i].firstChild, oRecord, this._oColumnSet.keys[i]);
+        }
+        
+        // Redisplay the row for reflow
+        elTr.style.display = '';
+    }
+    
+     // Record-to-TR association and tracking of FIRST/LAST
+    var oldId = elTr.id,
+        newId = oRecord.getId();
+    if(this._sFirstTrId === oldId) {
+        this._sFirstTrId = newId;
+    }
+    if(this._sLastTrId === oldId) {
+        this._sLastTrId = newId;
+    }
+    elTr.id = newId;
+    return elTr;
+},
+
+
+/**
+ * Deletes TR element by DOM reference or by DataTable page row index.
+ *
+ * @method _deleteTrEl
+ * @param row {HTMLElement | Number} TR element reference or Datatable page row index.
+ * @return {Boolean} Returns true if successful, else returns false.
+ * @private
+ */
+_deleteTrEl : function(row) {
+    var rowIndex;
+
+    // Get page row index for the element
+    if(!lang.isNumber(row)) {
+        rowIndex = Dom.get(row).sectionRowIndex;
+    }
+    else {
+        rowIndex = row;
+    }
+    if(lang.isNumber(rowIndex) && (rowIndex > -2) && (rowIndex < this._elTbody.rows.length)) {
+        // Cannot use tbody.deleteRow due to IE6 instability
+        //return this._elTbody.deleteRow(rowIndex);
+        return this._elTbody.removeChild(this._elTbody.rows[row]);
+    }
+    else {
+        return null;
+    }
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// CSS/STATE FUNCTIONS
+
+
+
+
+/**
+ * Removes the class YAHOO.widget.DataTable.CLASS_FIRST from the first TR element
+ * of the DataTable page and updates internal tracker.
+ *
+ * @method _unsetFirstRow
+ * @private
+ */
+_unsetFirstRow : function() {
+    // Remove FIRST
+    if(this._sFirstTrId) {
+        Dom.removeClass(this._sFirstTrId, DT.CLASS_FIRST);
+        this._sFirstTrId = null;
+    }
+},
+
+/**
+ * Assigns the class YAHOO.widget.DataTable.CLASS_FIRST to the first TR element
+ * of the DataTable page and updates internal tracker.
+ *
+ * @method _setFirstRow
+ * @private
+ */
+_setFirstRow : function() {
+    this._unsetFirstRow();
+    var elTr = this.getFirstTrEl();
+    if(elTr) {
+        // Set FIRST
+        Dom.addClass(elTr, DT.CLASS_FIRST);
+        this._sFirstTrId = elTr.id;
+    }
+},
+
+/**
+ * Removes the class YAHOO.widget.DataTable.CLASS_LAST from the last TR element
+ * of the DataTable page and updates internal tracker.
+ *
+ * @method _unsetLastRow
+ * @private
+ */
+_unsetLastRow : function() {
+    // Unassign previous class
+    if(this._sLastTrId) {
+        Dom.removeClass(this._sLastTrId, DT.CLASS_LAST);
+        this._sLastTrId = null;
+    }   
+},
+
+/**
+ * Assigns the class YAHOO.widget.DataTable.CLASS_LAST to the last TR element
+ * of the DataTable page and updates internal tracker.
+ *
+ * @method _setLastRow
+ * @private
+ */
+_setLastRow : function() {
+    this._unsetLastRow();
+    var elTr = this.getLastTrEl();
+    if(elTr) {
+        // Assign class
+        Dom.addClass(elTr, DT.CLASS_LAST);
+        this._sLastTrId = elTr.id;
+    }
+},
+
+/**
+ * Assigns the classes DT.CLASS_EVEN and DT.CLASS_ODD to one, many, or all TR elements.
+ *
+ * @method _setRowStripes
+ * @param row {HTMLElement | String | Number} (optional) HTML TR element reference
+ * or string ID, or page row index of where to start striping.
+ * @param range {Number} (optional) If given, how many rows to stripe, otherwise
+ * stripe all the rows until the end.
+ * @private
+ */
+_setRowStripes : function(row, range) {
+    // Default values stripe all rows
+    var allRows = this._elTbody.rows,
+        nStartIndex = 0,
+        nEndIndex = allRows.length,
+        aOdds = [], nOddIdx = 0,
+        aEvens = [], nEvenIdx = 0;
+
+    // Stripe a subset
+    if((row !== null) && (row !== undefined)) {
+        // Validate given start row
+        var elStartRow = this.getTrEl(row);
+        if(elStartRow) {
+            nStartIndex = elStartRow.sectionRowIndex;
+
+            // Validate given range
+            if(lang.isNumber(range) && (range > 1)) {
+                nEndIndex = nStartIndex + range;
+            }
+        }
+    }
+
+    for(var i=nStartIndex; i<nEndIndex; i++) {
+        if(i%2) {
+            aOdds[nOddIdx++] = allRows[i];
+        } else {
+            aEvens[nEvenIdx++] = allRows[i];
+        }
+    }
+
+    if (aOdds.length) {
+        Dom.replaceClass(aOdds, DT.CLASS_EVEN, DT.CLASS_ODD);
+    }
+
+    if (aEvens.length) {
+        Dom.replaceClass(aEvens, DT.CLASS_ODD, DT.CLASS_EVEN);
+    }
+},
+
+/**
+ * Assigns the class DT.CLASS_SELECTED to TR and TD elements.
+ *
+ * @method _setSelections
+ * @private
+ */
+_setSelections : function() {
+    // Keep track of selected rows
+    var allSelectedRows = this.getSelectedRows();
+    // Keep track of selected cells
+    var allSelectedCells = this.getSelectedCells();
+    // Anything to select?
+    if((allSelectedRows.length>0) || (allSelectedCells.length > 0)) {
+        var oColumnSet = this._oColumnSet,
+            el;
+        // Loop over each row
+        for(var i=0; i<allSelectedRows.length; i++) {
+            el = Dom.get(allSelectedRows[i]);
+            if(el) {
+                Dom.addClass(el, DT.CLASS_SELECTED);
+            }
+        }
+        // Loop over each cell
+        for(i=0; i<allSelectedCells.length; i++) {
+            el = Dom.get(allSelectedCells[i].recordId);
+            if(el) {
+                Dom.addClass(el.childNodes[oColumnSet.getColumn(allSelectedCells[i].columnKey).getKeyIndex()], DT.CLASS_SELECTED);
+            }
+        }
+    }       
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private DOM Event Handlers
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Validates minWidths whenever the render chain ends.
+ *
+ * @method _onRenderChainEnd
+ * @private
+ */
+_onRenderChainEnd : function() {
+    // Hide loading message
+    this.hideTableMessage();
+    
+    // Show empty message
+    if(this._elTbody.rows.length === 0) {
+        this.showTableMessage(this.get("MSG_EMPTY"), DT.CLASS_EMPTY);        
+    }
+
+    // Execute in timeout thread to give implementers a chance
+    // to subscribe after the constructor
+    var oSelf = this;
+    setTimeout(function() {
+        if((oSelf instanceof DT) && oSelf._sId) {        
+            // Init event
+            if(oSelf._bInit) {
+                oSelf._bInit = false;
+                oSelf.fireEvent("initEvent");
+            }
+    
+            // Render event
+            oSelf.fireEvent("renderEvent");
+            // Backward compatibility
+            oSelf.fireEvent("refreshEvent");
+    
+            // Post-render routine
+            oSelf.validateColumnWidths();
+    
+            // Post-render event
+            oSelf.fireEvent("postRenderEvent");
+            
+            /*if(YAHOO.example.Performance.trialStart) {
+                YAHOO.example.Performance.trialStart = null;
+            }*/
+            
+        }
+    }, 0);
+},
+
+/**
+ * Handles click events on the DOCUMENT.
+ *
+ * @method _onDocumentClick
+ * @param e {HTMLEvent} The click event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onDocumentClick : function(e, oSelf) {
+    var elTarget = Ev.getTarget(e);
+    var elTag = elTarget.nodeName.toLowerCase();
+
+    if(!Dom.isAncestor(oSelf._elContainer, elTarget)) {
+        oSelf.fireEvent("tableBlurEvent");
+
+        // Fires editorBlurEvent when click is not within the TABLE.
+        // For cases when click is within the TABLE, due to timing issues,
+        // the editorBlurEvent needs to get fired by the lower-level DOM click
+        // handlers below rather than by the TABLE click handler directly.
+        if(oSelf._oCellEditor) {
+            if(oSelf._oCellEditor.getContainerEl) {
+                var elContainer = oSelf._oCellEditor.getContainerEl();
+                // Only if the click was not within the CellEditor container
+                if(!Dom.isAncestor(elContainer, elTarget) &&
+                        (elContainer.id !== elTarget.id)) {
+                    oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
+                }
+            }
+            // Backward Compatibility
+            else if(oSelf._oCellEditor.isActive) {
+                // Only if the click was not within the Cell Editor container
+                if(!Dom.isAncestor(oSelf._oCellEditor.container, elTarget) &&
+                        (oSelf._oCellEditor.container.id !== elTarget.id)) {
+                    oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
+                }
+            }
+        }
+    }
+},
+
+/**
+ * Handles focus events on the DataTable instance.
+ *
+ * @method _onTableFocus
+ * @param e {HTMLEvent} The focus event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTableFocus : function(e, oSelf) {
+    oSelf.fireEvent("tableFocusEvent");
+},
+
+/**
+ * Handles focus events on the THEAD element.
+ *
+ * @method _onTheadFocus
+ * @param e {HTMLEvent} The focus event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTheadFocus : function(e, oSelf) {
+    oSelf.fireEvent("theadFocusEvent");
+    oSelf.fireEvent("tableFocusEvent");
+},
+
+/**
+ * Handles focus events on the TBODY element.
+ *
+ * @method _onTbodyFocus
+ * @param e {HTMLEvent} The focus event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTbodyFocus : function(e, oSelf) {
+    oSelf.fireEvent("tbodyFocusEvent");
+    oSelf.fireEvent("tableFocusEvent");
+},
+
+/**
+ * Handles mouseover events on the DataTable instance.
+ *
+ * @method _onTableMouseover
+ * @param e {HTMLEvent} The mouseover event.
+ * @param origTarget {HTMLElement} The mouseenter delegated element.
+ * @param container {HTMLElement} The mouseenter delegation container.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTableMouseover : function(e, origTarget, container, oSelf) {
+    var elTarget = origTarget;
+    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
+    var bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                 return;
+            case "a":
+                break;
+            case "td":
+                bKeepBubbling = oSelf.fireEvent("cellMouseoverEvent",{target:elTarget,event:e});
+                break;
+            case "span":
+                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
+                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseoverEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseoverEvent",{target:elTarget,event:e});
+                }
+                break;
+            case "th":
+                bKeepBubbling = oSelf.fireEvent("theadCellMouseoverEvent",{target:elTarget,event:e});
+                // Backward compatibility
+                bKeepBubbling = oSelf.fireEvent("headerCellMouseoverEvent",{target:elTarget,event:e});
+                break;
+            case "tr":
+                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
+                    bKeepBubbling = oSelf.fireEvent("theadRowMouseoverEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerRowMouseoverEvent",{target:elTarget,event:e});
+                }
+                else {
+                    bKeepBubbling = oSelf.fireEvent("rowMouseoverEvent",{target:elTarget,event:e});
+                }
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableMouseoverEvent",{target:(elTarget || oSelf._elContainer),event:e});
+},
+
+/**
+ * Handles mouseout events on the DataTable instance.
+ *
+ * @method _onTableMouseout
+ * @param e {HTMLEvent} The mouseout event.
+ * @param origTarget {HTMLElement} The mouseleave delegated element.
+ * @param container {HTMLElement} The mouseleave delegation container.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTableMouseout : function(e, origTarget, container, oSelf) {
+    var elTarget = origTarget;
+    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
+    var bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                return;
+            case "a":
+                break;
+            case "td":
+                bKeepBubbling = oSelf.fireEvent("cellMouseoutEvent",{target:elTarget,event:e});
+                break;
+            case "span":
+                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
+                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseoutEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseoutEvent",{target:elTarget,event:e});
+                }
+                break;
+            case "th":
+                bKeepBubbling = oSelf.fireEvent("theadCellMouseoutEvent",{target:elTarget,event:e});
+                // Backward compatibility
+                bKeepBubbling = oSelf.fireEvent("headerCellMouseoutEvent",{target:elTarget,event:e});
+                break;
+            case "tr":
+                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
+                    bKeepBubbling = oSelf.fireEvent("theadRowMouseoutEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerRowMouseoutEvent",{target:elTarget,event:e});
+                }
+                else {
+                    bKeepBubbling = oSelf.fireEvent("rowMouseoutEvent",{target:elTarget,event:e});
+                }
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableMouseoutEvent",{target:(elTarget || oSelf._elContainer),event:e});
+},
+
+/**
+ * Handles mousedown events on the DataTable instance.
+ *
+ * @method _onTableMousedown
+ * @param e {HTMLEvent} The mousedown event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTableMousedown : function(e, oSelf) {
+    var elTarget = Ev.getTarget(e);
+    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
+    var bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                return;
+            case "a":
+                break;
+            case "td":
+                bKeepBubbling = oSelf.fireEvent("cellMousedownEvent",{target:elTarget,event:e});
+                break;
+            case "span":
+                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
+                    bKeepBubbling = oSelf.fireEvent("theadLabelMousedownEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerLabelMousedownEvent",{target:elTarget,event:e});
+                }
+                break;
+            case "th":
+                bKeepBubbling = oSelf.fireEvent("theadCellMousedownEvent",{target:elTarget,event:e});
+                // Backward compatibility
+                bKeepBubbling = oSelf.fireEvent("headerCellMousedownEvent",{target:elTarget,event:e});
+                break;
+            case "tr":
+                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
+                    bKeepBubbling = oSelf.fireEvent("theadRowMousedownEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerRowMousedownEvent",{target:elTarget,event:e});
+                }
+                else {
+                    bKeepBubbling = oSelf.fireEvent("rowMousedownEvent",{target:elTarget,event:e});
+                }
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableMousedownEvent",{target:(elTarget || oSelf._elContainer),event:e});
+},
+
+/**
+ * Handles mouseup events on the DataTable instance.
+ *
+ * @method _onTableMouseup
+ * @param e {HTMLEvent} The mouseup event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTableMouseup : function(e, oSelf) {
+    var elTarget = Ev.getTarget(e);
+    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
+    var bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                return;
+            case "a":
+                break;
+            case "td":
+                bKeepBubbling = oSelf.fireEvent("cellMouseupEvent",{target:elTarget,event:e});
+                break;
+            case "span":
+                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
+                    bKeepBubbling = oSelf.fireEvent("theadLabelMouseupEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerLabelMouseupEvent",{target:elTarget,event:e});
+                }
+                break;
+            case "th":
+                bKeepBubbling = oSelf.fireEvent("theadCellMouseupEvent",{target:elTarget,event:e});
+                // Backward compatibility
+                bKeepBubbling = oSelf.fireEvent("headerCellMouseupEvent",{target:elTarget,event:e});
+                break;
+            case "tr":
+                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
+                    bKeepBubbling = oSelf.fireEvent("theadRowMouseupEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerRowMouseupEvent",{target:elTarget,event:e});
+                }
+                else {
+                    bKeepBubbling = oSelf.fireEvent("rowMouseupEvent",{target:elTarget,event:e});
+                }
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableMouseupEvent",{target:(elTarget || oSelf._elContainer),event:e});
+},
+
+/**
+ * Handles dblclick events on the DataTable instance.
+ *
+ * @method _onTableDblclick
+ * @param e {HTMLEvent} The dblclick event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTableDblclick : function(e, oSelf) {
+    var elTarget = Ev.getTarget(e);
+    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
+    var bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                return;
+            case "td":
+                bKeepBubbling = oSelf.fireEvent("cellDblclickEvent",{target:elTarget,event:e});
+                break;
+            case "span":
+                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
+                    bKeepBubbling = oSelf.fireEvent("theadLabelDblclickEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerLabelDblclickEvent",{target:elTarget,event:e});
+                }
+                break;
+            case "th":
+                bKeepBubbling = oSelf.fireEvent("theadCellDblclickEvent",{target:elTarget,event:e});
+                // Backward compatibility
+                bKeepBubbling = oSelf.fireEvent("headerCellDblclickEvent",{target:elTarget,event:e});
+                break;
+            case "tr":
+                if(elTarget.parentNode.nodeName.toLowerCase() == "thead") {
+                    bKeepBubbling = oSelf.fireEvent("theadRowDblclickEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerRowDblclickEvent",{target:elTarget,event:e});
+                }
+                else {
+                    bKeepBubbling = oSelf.fireEvent("rowDblclickEvent",{target:elTarget,event:e});
+                }
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableDblclickEvent",{target:(elTarget || oSelf._elContainer),event:e});
+},
+/**
+ * Handles keydown events on the THEAD element.
+ *
+ * @method _onTheadKeydown
+ * @param e {HTMLEvent} The key event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTheadKeydown : function(e, oSelf) {
+    var elTarget = Ev.getTarget(e);
+    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
+    var bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                return;
+            case "input":
+            case "textarea":
+                // TODO: implement textareaKeyEvent
+                break;
+            case "thead":
+                bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
+},
+
+/**
+ * Handles keydown events on the TBODY element. Handles selection behavior,
+ * provides hooks for ENTER to edit functionality.
+ *
+ * @method _onTbodyKeydown
+ * @param e {HTMLEvent} The key event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTbodyKeydown : function(e, oSelf) {
+    var sMode = oSelf.get("selectionMode");
+
+    if(sMode == "standard") {
+        oSelf._handleStandardSelectionByKey(e);
+    }
+    else if(sMode == "single") {
+        oSelf._handleSingleSelectionByKey(e);
+    }
+    else if(sMode == "cellblock") {
+        oSelf._handleCellBlockSelectionByKey(e);
+    }
+    else if(sMode == "cellrange") {
+        oSelf._handleCellRangeSelectionByKey(e);
+    }
+    else if(sMode == "singlecell") {
+        oSelf._handleSingleCellSelectionByKey(e);
+    }
+    
+    if(oSelf._oCellEditor) {
+        if(oSelf._oCellEditor.fireEvent) {
+            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
+        }
+        else if(oSelf._oCellEditor.isActive) {
+            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
+        }
+    }
+
+    var elTarget = Ev.getTarget(e);
+    var elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase();
+    var bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                return;
+            case "tbody":
+                bKeepBubbling = oSelf.fireEvent("tbodyKeyEvent",{target:elTarget,event:e});
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
+},
+
+/**
+ * Handles click events on the THEAD element.
+ *
+ * @method _onTheadClick
+ * @param e {HTMLEvent} The click event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTheadClick : function(e, oSelf) {
+    // This blurs the CellEditor
+    if(oSelf._oCellEditor) {
+        if(oSelf._oCellEditor.fireEvent) {
+            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
+        }
+        // Backward compatibility
+        else if(oSelf._oCellEditor.isActive) {
+            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
+        }
+    }
+
+    var elTarget = Ev.getTarget(e),
+        elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(),
+        bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                return;
+            case "input":
+                var sType = elTarget.type.toLowerCase();
+                if(sType == "checkbox") {
+                    bKeepBubbling = oSelf.fireEvent("theadCheckboxClickEvent",{target:elTarget,event:e});
+                }
+                else if(sType == "radio") {
+                    bKeepBubbling = oSelf.fireEvent("theadRadioClickEvent",{target:elTarget,event:e});
+                }
+                else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
+                    if(!elTarget.disabled) {
+                        bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
+                    }
+                    else {
+                        bKeepBubbling = false;
+                    }
+                }
+                else if (elTarget.disabled){
+                    bKeepBubbling = false;
+                }
+                break;
+            case "a":
+                bKeepBubbling = oSelf.fireEvent("theadLinkClickEvent",{target:elTarget,event:e});
+                break;
+            case "button":
+                if(!elTarget.disabled) {
+                    bKeepBubbling = oSelf.fireEvent("theadButtonClickEvent",{target:elTarget,event:e});
+                }
+                else {
+                    bKeepBubbling = false;
+                }
+                break;
+            case "span":
+                if(Dom.hasClass(elTarget, DT.CLASS_LABEL)) {
+                    bKeepBubbling = oSelf.fireEvent("theadLabelClickEvent",{target:elTarget,event:e});
+                    // Backward compatibility
+                    bKeepBubbling = oSelf.fireEvent("headerLabelClickEvent",{target:elTarget,event:e});
+                }
+                break;
+            case "th":
+                bKeepBubbling = oSelf.fireEvent("theadCellClickEvent",{target:elTarget,event:e});
+                // Backward compatibility
+                bKeepBubbling = oSelf.fireEvent("headerCellClickEvent",{target:elTarget,event:e});
+                break;
+            case "tr":
+                bKeepBubbling = oSelf.fireEvent("theadRowClickEvent",{target:elTarget,event:e});
+                // Backward compatibility
+                bKeepBubbling = oSelf.fireEvent("headerRowClickEvent",{target:elTarget,event:e});
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
+},
+
+/**
+ * Handles click events on the primary TBODY element.
+ *
+ * @method _onTbodyClick
+ * @param e {HTMLEvent} The click event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onTbodyClick : function(e, oSelf) {
+    // This blurs the CellEditor
+    if(oSelf._oCellEditor) {
+        if(oSelf._oCellEditor.fireEvent) {
+            oSelf._oCellEditor.fireEvent("blurEvent", {editor: oSelf._oCellEditor});
+        }
+        else if(oSelf._oCellEditor.isActive) {
+            oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
+        }
+    }
+
+    // Fire Custom Events
+    var elTarget = Ev.getTarget(e),
+        elTag = elTarget.nodeName && elTarget.nodeName.toLowerCase(),
+        bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                return;
+            case "input":
+                var sType = elTarget.type.toLowerCase();
+                if(sType == "checkbox") {
+                    bKeepBubbling = oSelf.fireEvent("checkboxClickEvent",{target:elTarget,event:e});
+                }
+                else if(sType == "radio") {
+                    bKeepBubbling = oSelf.fireEvent("radioClickEvent",{target:elTarget,event:e});
+                }
+                else if((sType == "button") || (sType == "image") || (sType == "submit") || (sType == "reset")) {
+                    if(!elTarget.disabled) {
+                        bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
+                    }
+                    else {
+                        bKeepBubbling = false;
+                    }
+                }
+                else if (elTarget.disabled){
+                    bKeepBubbling = false;
+                }
+                break;
+            case "a":
+                bKeepBubbling = oSelf.fireEvent("linkClickEvent",{target:elTarget,event:e});
+                break;
+            case "button":
+                if(!elTarget.disabled) {
+                    bKeepBubbling = oSelf.fireEvent("buttonClickEvent",{target:elTarget,event:e});
+                }
+                else {
+                    bKeepBubbling = false;
+                }
+                break;
+            case "td":
+                bKeepBubbling = oSelf.fireEvent("cellClickEvent",{target:elTarget,event:e});
+                break;
+            case "tr":
+                bKeepBubbling = oSelf.fireEvent("rowClickEvent",{target:elTarget,event:e});
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableClickEvent",{target:(elTarget || oSelf._elContainer),event:e});
+},
+
+/**
+ * Handles change events on SELECT elements within DataTable.
+ *
+ * @method _onDropdownChange
+ * @param e {HTMLEvent} The change event.
+ * @param oSelf {YAHOO.wiget.DataTable} DataTable instance.
+ * @private
+ */
+_onDropdownChange : function(e, oSelf) {
+    var elTarget = Ev.getTarget(e);
+    oSelf.fireEvent("dropdownChangeEvent", {event:e, target:elTarget});
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public member variables
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns object literal of initial configs.
+ *
+ * @property configs
+ * @type Object
+ * @default {} 
+ */
+configs: null,
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Returns unique id assigned to instance, which is a useful prefix for
+ * generating unique DOM ID strings.
+ *
+ * @method getId
+ * @return {String} Unique ID of the DataSource instance.
+ */
+getId : function() {
+    return this._sId;
+},
+
+/**
+ * DataSource instance name, for logging.
+ *
+ * @method toString
+ * @return {String} Unique name of the DataSource instance.
+ */
+
+toString : function() {
+    return "DataTable instance " + this._sId;
+},
+
+/**
+ * Returns the DataTable instance's DataSource instance.
+ *
+ * @method getDataSource
+ * @return {YAHOO.util.DataSource} DataSource instance.
+ */
+getDataSource : function() {
+    return this._oDataSource;
+},
+
+/**
+ * Returns the DataTable instance's ColumnSet instance.
+ *
+ * @method getColumnSet
+ * @return {YAHOO.widget.ColumnSet} ColumnSet instance.
+ */
+getColumnSet : function() {
+    return this._oColumnSet;
+},
+
+/**
+ * Returns the DataTable instance's RecordSet instance.
+ *
+ * @method getRecordSet
+ * @return {YAHOO.widget.RecordSet} RecordSet instance.
+ */
+getRecordSet : function() {
+    return this._oRecordSet;
+},
+
+/**
+ * Returns on object literal representing the DataTable instance's current
+ * state with the following properties:
+ * <dl>
+ * <dt>pagination</dt>
+ * <dd>Instance of YAHOO.widget.Paginator</dd>
+ *
+ * <dt>sortedBy</dt>
+ * <dd>
+ *     <dl>
+ *         <dt>sortedBy.key</dt>
+ *         <dd>{String} Key of sorted Column</dd>
+ *         <dt>sortedBy.dir</dt>
+ *         <dd>{String} Initial sort direction, either YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC</dd>
+ *     </dl>
+ * </dd>
+ *
+ * <dt>selectedRows</dt>
+ * <dd>Array of selected rows by Record ID.</dd>
+ *
+ * <dt>selectedCells</dt>
+ * <dd>Selected cells as an array of object literals:
+ *     {recordId:sRecordId, columnKey:sColumnKey}</dd>
+ * </dl>
+ *  
+ * @method getState
+ * @return {Object} DataTable instance state object literal values.
+ */
+getState : function() {
+    return {
+        totalRecords: this.get('paginator') ? this.get('paginator').get("totalRecords") : this._oRecordSet.getLength(),
+        pagination: this.get("paginator") ? this.get("paginator").getState() : null,
+        sortedBy: this.get("sortedBy"),
+        selectedRows: this.getSelectedRows(),
+        selectedCells: this.getSelectedCells()
+    };
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// DOM ACCESSORS
+
+/**
+ * Returns DOM reference to the DataTable's container element.
+ *
+ * @method getContainerEl
+ * @return {HTMLElement} Reference to DIV element.
+ */
+getContainerEl : function() {
+    return this._elContainer;
+},
+
+/**
+ * Returns DOM reference to the DataTable's TABLE element.
+ *
+ * @method getTableEl
+ * @return {HTMLElement} Reference to TABLE element.
+ */
+getTableEl : function() {
+    return this._elTable;
+},
+
+/**
+ * Returns DOM reference to the DataTable's THEAD element.
+ *
+ * @method getTheadEl
+ * @return {HTMLElement} Reference to THEAD element.
+ */
+getTheadEl : function() {
+    return this._elThead;
+},
+
+/**
+ * Returns DOM reference to the DataTable's primary TBODY element.
+ *
+ * @method getTbodyEl
+ * @return {HTMLElement} Reference to TBODY element.
+ */
+getTbodyEl : function() {
+    return this._elTbody;
+},
+
+/**
+ * Returns DOM reference to the DataTable's secondary TBODY element that is
+ * used to display messages.
+ *
+ * @method getMsgTbodyEl
+ * @return {HTMLElement} Reference to TBODY element.
+ */
+getMsgTbodyEl : function() {
+    return this._elMsgTbody;
+},
+
+/**
+ * Returns DOM reference to the TD element within the secondary TBODY that is
+ * used to display messages.
+ *
+ * @method getMsgTdEl
+ * @return {HTMLElement} Reference to TD element.
+ */
+getMsgTdEl : function() {
+    return this._elMsgTd;
+},
+
+/**
+ * Returns the corresponding TR reference for a given DOM element, ID string or
+ * page row index. If the given identifier is a child of a TR element,
+ * then DOM tree is traversed until a parent TR element is returned, otherwise
+ * null. Returns null if the row is not considered a primary row (i.e., row
+ * extensions).
+ *
+ * @method getTrEl
+ * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Which row to
+ * get: by element reference, ID string, page row index, or Record.
+ * @return {HTMLElement} Reference to TR element, or null.
+ */
+getTrEl : function(row) {
+    // By Record
+    if(row instanceof YAHOO.widget.Record) {
+        return document.getElementById(row.getId());
+    }
+    // By page row index
+    else if(lang.isNumber(row)) {
+        var dataRows = Dom.getElementsByClassName(DT.CLASS_REC, "tr", this._elTbody);
+        return dataRows && dataRows[row] ? dataRows[row] : null;
+    }
+    // By ID string or element reference
+    else if(row) {
+        var elRow = (lang.isString(row)) ? document.getElementById(row) : row;
+
+        // Validate HTML element
+        if(elRow && elRow.ownerDocument == document) {
+            // Validate TR element
+            if(elRow.nodeName.toLowerCase() != "tr") {
+                // Traverse up the DOM to find the corresponding TR element
+                elRow = Dom.getAncestorByTagName(elRow,"tr");
+            }
+
+            return elRow;
+        }
+    }
+
+    return null;
+},
+
+/**
+ * Returns DOM reference to the first primary TR element in the DataTable page, or null.
+ *
+ * @method getFirstTrEl
+ * @return {HTMLElement} Reference to TR element.
+ */
+getFirstTrEl : function() {
+    var allRows = this._elTbody.rows,
+        i=0;
+    while(allRows[i]) {
+        if(this.getRecord(allRows[i])) {
+            return allRows[i];
+        }
+        i++;
+    }
+    return null;
+
+},
+
+/**
+ * Returns DOM reference to the last primary TR element in the DataTable page, or null.
+ *
+ * @method getLastTrEl
+ * @return {HTMLElement} Reference to last TR element.
+ */
+getLastTrEl : function() {
+    var allRows = this._elTbody.rows,
+        i=allRows.length-1;
+    while(i>-1) {
+        if(this.getRecord(allRows[i])) {
+            return allRows[i];
+        }
+        i--;
+    }
+    return null;
+},
+
+/**
+ * Returns DOM reference to the next TR element from the given primary TR element, or null.
+ *
+ * @method getNextTrEl
+ * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
+ * reference, ID string, page row index, or Record from which to get next TR element.
+ * @param forcePrimary {Boolean} (optional) If true, will only return TR elements
+ * that correspond to Records. Non-primary rows (such as row expansions)
+ * will be skipped.
+ * @return {HTMLElement} Reference to next TR element.
+ */
+getNextTrEl : function(row, forcePrimary) {
+    var nThisTrIndex = this.getTrIndex(row);
+    if(nThisTrIndex !== null) {
+        var allRows = this._elTbody.rows;
+        if(forcePrimary) {
+            while(nThisTrIndex < allRows.length-1) {
+                row = allRows[nThisTrIndex+1];
+                if(this.getRecord(row)) {
+                    return row;
+                }
+                nThisTrIndex++;
+            }
+        }
+        else {
+            if(nThisTrIndex < allRows.length-1) {
+                return allRows[nThisTrIndex+1];
+            }
+        }
+    }
+
+    return null;
+},
+
+/**
+ * Returns DOM reference to the previous TR element from the given primary TR element, or null.
+ *
+ * @method getPreviousTrEl
+ * @param row {HTMLElement | String | Number | YAHOO.widget.Record} Element
+ * reference, ID string, page row index, or Record from which to get previous TR element.
+ * @param forcePrimary {Boolean} (optional) If true, will only return TR elements
+ * from rothat correspond to Records. Non-primary rows (such as row expansions)
+ * will be skipped.
+ * @return {HTMLElement} Reference to previous TR element.
+ */
+getPreviousTrEl : function(row, forcePrimary) {
+    var nThisTrIndex = this.getTrIndex(row);
+    if(nThisTrIndex !== null) {
+        var allRows = this._elTbody.rows;
+
+        if(forcePrimary) {
+            while(nThisTrIndex > 0) {
+                row = allRows[nThisTrIndex-1];
+                if(this.getRecord(row)) {
+                    return row;
+                }
+                nThisTrIndex--;
+            }
+        }
+        else {
+            if(nThisTrIndex > 0) {
+                return allRows[nThisTrIndex-1];
+            }
+        }
+    }
+
+    return null;
+},
+
+
+/**
+ * Workaround for IE bug where hidden or not-in-dom elements cause cellIndex
+ * value to be incorrect.
+ *
+ * @method getCellIndex
+ * @param cell {HTMLElement | Object} TD element or child of a TD element, or
+ * object literal of syntax {record:oRecord, column:oColumn}.
+ * @return {Number} TD.cellIndex value.
+ */
+getCellIndex : function(cell) {
+    cell = this.getTdEl(cell);
+    if(cell) {
+        if(ua.ie > 0) {
+            var i=0,
+                tr = cell.parentNode,
+                allCells = tr.childNodes,
+                len = allCells.length;
+            for(; i<len; i++) {
+                if(allCells[i] == cell) {
+                    return i;
+                }
+            }
+        }
+        else {
+            return cell.cellIndex;
+        }
+    }
+},
+
+/**
+ * Returns DOM reference to a TD liner element.
+ *
+ * @method getTdLinerEl
+ * @param cell {HTMLElement | Object} TD element or child of a TD element, or
+ * object literal of syntax {record:oRecord, column:oColumn}.
+ * @return {HTMLElement} Reference to TD liner element.
+ */
+getTdLinerEl : function(cell) {
+    var elCell = this.getTdEl(cell);
+    return elCell.firstChild || null;
+},
+
+/**
+ * Returns DOM reference to a TD element. Returns null if the row is not
+ * considered a primary row (i.e., row extensions).
+ *
+ * @method getTdEl
+ * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
+ * object literal of syntax {record:oRecord, column:oColumn}.
+ * @return {HTMLElement} Reference to TD element.
+ */
+getTdEl : function(cell) {
+    var elCell;
+    var el = Dom.get(cell);
+
+    // Validate HTML element
+    if(el && (el.ownerDocument == document)) {
+        // Validate TD element
+        if(el.nodeName.toLowerCase() != "td") {
+            // Traverse up the DOM to find the corresponding TR element
+            elCell = Dom.getAncestorByTagName(el, "td");
+        }
+        else {
+            elCell = el;
+        }
+        
+        // Make sure the TD is in this TBODY or is not in DOM
+        // Bug 2527707 and bug 2263558
+        if(elCell && ((elCell.parentNode.parentNode == this._elTbody) ||
+            (elCell.parentNode.parentNode === null) ||
+            (elCell.parentNode.parentNode.nodeType === 11))) {
+            // Now we can return the TD element
+            return elCell;
+        }
+    }
+    else if(cell) {
+        var oRecord, nColKeyIndex;
+
+        if(lang.isString(cell.columnKey) && lang.isString(cell.recordId)) {
+            oRecord = this.getRecord(cell.recordId);
+            var oColumn = this.getColumn(cell.columnKey);
+            if(oColumn) {
+                nColKeyIndex = oColumn.getKeyIndex();
+            }
+
+        }
+        if(cell.record && cell.column && cell.column.getKeyIndex) {
+            oRecord = cell.record;
+            nColKeyIndex = cell.column.getKeyIndex();
+        }
+        var elRow = this.getTrEl(oRecord);
+        if((nColKeyIndex !== null) && elRow && elRow.cells && elRow.cells.length > 0) {
+            return elRow.cells[nColKeyIndex] || null;
+        }
+    }
+
+    return null;
+},
+
+/**
+ * Returns DOM reference to the first primary TD element in the DataTable page (by default),
+ * the first TD element of the optionally given row, or null.
+ *
+ * @method getFirstTdEl
+ * @param row {HTMLElement} (optional) row from which to get first TD
+ * @return {HTMLElement} Reference to TD element.
+ */
+getFirstTdEl : function(row) {
+    var elRow = lang.isValue(row) ? this.getTrEl(row) : this.getFirstTrEl();
+    if(elRow) {
+        if(elRow.cells && elRow.cells.length > 0) {
+            return elRow.cells[0];
+        }
+        else if(elRow.childNodes && elRow.childNodes.length > 0) {
+            return elRow.childNodes[0];
+        }
+    }
+    return null;
+},
+
+/**
+ * Returns DOM reference to the last primary TD element in the DataTable page (by default),
+ * the first TD element of the optionally given row, or null.
+ *
+ * @method getLastTdEl
+ * @param row {HTMLElement} (optional) row from which to get first TD
+ * @return {HTMLElement} Reference to last TD element.
+ */
+getLastTdEl : function(row) {
+    var elRow = lang.isValue(row) ? this.getTrEl(row) : this.getLastTrEl();
+    if(elRow) {
+        if(elRow.cells && elRow.cells.length > 0) {
+            return elRow.cells[elRow.cells.length-1];
+        }
+        else if(elRow.childNodes && elRow.childNodes.length > 0) {
+            return elRow.childNodes[elRow.childNodes.length-1];
+        }
+    }
+    return null;
+},
+
+/**
+ * Returns DOM reference to the next TD element from the given cell, or null.
+ *
+ * @method getNextTdEl
+ * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
+ * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
+ * @return {HTMLElement} Reference to next TD element, or null.
+ */
+getNextTdEl : function(cell) {
+    var elCell = this.getTdEl(cell);
+    if(elCell) {
+        var nThisTdIndex = this.getCellIndex(elCell);
+        var elRow = this.getTrEl(elCell);
+        if(elRow.cells && (elRow.cells.length) > 0 && (nThisTdIndex < elRow.cells.length-1)) {
+            return elRow.cells[nThisTdIndex+1];
+        }
+        else if(elRow.childNodes && (elRow.childNodes.length) > 0 && (nThisTdIndex < elRow.childNodes.length-1)) {
+            return elRow.childNodes[nThisTdIndex+1];
+        }
+        else {
+            var elNextRow = this.getNextTrEl(elRow);
+            if(elNextRow) {
+                return elNextRow.cells[0];
+            }
+        }
+    }
+    return null;
+},
+
+/**
+ * Returns DOM reference to the previous TD element from the given cell, or null.
+ *
+ * @method getPreviousTdEl
+ * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
+ * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
+ * @return {HTMLElement} Reference to previous TD element, or null.
+ */
+getPreviousTdEl : function(cell) {
+    var elCell = this.getTdEl(cell);
+    if(elCell) {
+        var nThisTdIndex = this.getCellIndex(elCell);
+        var elRow = this.getTrEl(elCell);
+        if(nThisTdIndex > 0) {
+            if(elRow.cells && elRow.cells.length > 0) {
+                return elRow.cells[nThisTdIndex-1];
+            }
+            else if(elRow.childNodes && elRow.childNodes.length > 0) {
+                return elRow.childNodes[nThisTdIndex-1];
+            }
+        }
+        else {
+            var elPreviousRow = this.getPreviousTrEl(elRow);
+            if(elPreviousRow) {
+                return this.getLastTdEl(elPreviousRow);
+            }
+        }
+    }
+    return null;
+},
+
+/**
+ * Returns DOM reference to the above TD element from the given cell, or null.
+ *
+ * @method getAboveTdEl
+ * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
+ * object literal of syntax {record:oRecord, column:oColumn} from which to get next TD element.
+ * @param forcePrimary {Boolean} (optional) If true, will only return TD elements
+ * from rows that correspond to Records. Non-primary rows (such as row expansions)
+ * will be skipped.
+ * @return {HTMLElement} Reference to above TD element, or null.
+ */
+getAboveTdEl : function(cell, forcePrimary) {
+    var elCell = this.getTdEl(cell);
+    if(elCell) {
+        var elPreviousRow = this.getPreviousTrEl(elCell, forcePrimary);
+        if(elPreviousRow ) {
+            var cellIndex = this.getCellIndex(elCell);
+            if(elPreviousRow.cells && elPreviousRow.cells.length > 0) {
+                return elPreviousRow.cells[cellIndex] ? elPreviousRow.cells[cellIndex] : null;
+            }
+            else if(elPreviousRow.childNodes && elPreviousRow.childNodes.length > 0) {
+                return elPreviousRow.childNodes[cellIndex] ? elPreviousRow.childNodes[cellIndex] : null;
+            }
+        }
+    }
+    return null;
+},
+
+/**
+ * Returns DOM reference to the below TD element from the given cell, or null.
+ *
+ * @method getBelowTdEl
+ * @param cell {HTMLElement | String | Object} DOM element reference or string ID, or
+ * object literal of syntax {record:oRecord, column:oColumn} from which to get previous TD element.
+ * @param forcePrimary {Boolean} (optional) If true, will only return TD elements
+ * from rows that correspond to Records. Non-primary rows (such as row expansions)
+ * will be skipped.
+ * @return {HTMLElement} Reference to below TD element, or null.
+ */
+getBelowTdEl : function(cell, forcePrimary) {
+    var elCell = this.getTdEl(cell);
+    if(elCell) {
+        var elNextRow = this.getNextTrEl(elCell, forcePrimary);
+        if(elNextRow) {
+            var cellIndex = this.getCellIndex(elCell);
+            if(elNextRow.cells && elNextRow.cells.length > 0) {
+                return elNextRow.cells[cellIndex] ? elNextRow.cells[cellIndex] : null;
+            }
+            else if(elNextRow.childNodes && elNextRow.childNodes.length > 0) {
+                return elNextRow.childNodes[cellIndex] ? elNextRow.childNodes[cellIndex] : null;
+            }
+        }
+    }
+    return null;
+},
+
+/**
+ * Returns DOM reference to a TH liner element. Needed to normalize for resizeable 
+ * Columns, which have an additional resizer liner DIV element between the TH
+ * element and the liner DIV element. 
+ *
+ * @method getThLinerEl
+ * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
+ * DOM element reference, or string ID.
+ * @return {HTMLElement} Reference to TH liner element.
+ */
+getThLinerEl : function(theadCell) {
+    var oColumn = this.getColumn(theadCell);
+    return (oColumn) ? oColumn.getThLinerEl() : null;
+},
+
+/**
+ * Returns DOM reference to a TH element.
+ *
+ * @method getThEl
+ * @param theadCell {YAHOO.widget.Column | HTMLElement | String} Column instance,
+ * DOM element reference, or string ID.
+ * @return {HTMLElement} Reference to TH element.
+ */
+getThEl : function(theadCell) {
+    var elTh;
+
+    // Validate Column instance
+    if(theadCell instanceof YAHOO.widget.Column) {
+        var oColumn = theadCell;
+        elTh = oColumn.getThEl();
+        if(elTh) {
+            return elTh;
+        }
+    }
+    // Validate HTML element
+    else {
+        var el = Dom.get(theadCell);
+
+        if(el && (el.ownerDocument == document)) {
+            // Validate TH element
+            if(el.nodeName.toLowerCase() != "th") {
+                // Traverse up the DOM to find the corresponding TR element
+                elTh = Dom.getAncestorByTagName(el,"th");
+            }
+            else {
+                elTh = el;
+            }
+
+            return elTh;
+        }
+    }
+
+    return null;
+},
+
+/**
+ * Returns the page row index of given primary row. Returns null if the row is not on the
+ * current DataTable page, or if row is not considered a primary row (i.e., row
+ * extensions).
+ *
+ * @method getTrIndex
+ * @param row {HTMLElement | String | YAHOO.widget.Record | Number} DOM or ID
+ * string reference to an element within the DataTable page, a Record instance,
+ * or a Record's RecordSet index.
+ * @return {Number} Page row index, or null if data row does not exist or is not on current page.
+ */
+getTrIndex : function(row) {
+    var record = this.getRecord(row),
+        index = this.getRecordIndex(record),
+        tr;
+    if(record) {
+        tr = this.getTrEl(record);
+        if(tr) {
+            return tr.sectionRowIndex;
+        }
+        else {
+            var oPaginator = this.get("paginator");
+            if(oPaginator) {
+                return oPaginator.get('recordOffset') + index;
+            }
+            else {
+                return index;
+            }
+        }
+    }
+    return null;
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// TABLE FUNCTIONS
+
+/**
+ * Loads new data. Convenience method that calls DataSource's sendRequest()
+ * method under the hood.
+ *
+ * @method load
+ * @param oConfig {object} Optional configuration parameters:
+ *
+ * <dl>
+ * <dt>request</dt><dd>Pass in a new request, or initialRequest is used.</dd>
+ * <dt>callback</dt><dd>Pass in DataSource sendRequest() callback object, or the following is used:
+ *    <dl>
+ *      <dt>success</dt><dd>datatable.onDataReturnInitializeTable</dd>
+ *      <dt>failure</dt><dd>datatable.onDataReturnInitializeTable</dd>
+ *      <dt>scope</dt><dd>datatable</dd>
+ *      <dt>argument</dt><dd>datatable.getState()</dd>
+ *    </dl>
+ * </dd>
+ * <dt>datasource</dt><dd>Pass in a new DataSource instance to override the current DataSource for this transaction.</dd>
+ * </dl>
+ */
+load : function(oConfig) {
+    oConfig = oConfig || {};
+
+    (oConfig.datasource || this._oDataSource).sendRequest(oConfig.request || this.get("initialRequest"), oConfig.callback || {
+        success: this.onDataReturnInitializeTable,
+        failure: this.onDataReturnInitializeTable,
+        scope: this,
+        argument: this.getState()
+    });
+},
+
+/**
+ * Resets a RecordSet with the given data and populates the page view
+ * with the new data. Any previous data, and selection and sort states are
+ * cleared. New data should be added as a separate step. 
+ *
+ * @method initializeTable
+ */
+initializeTable : function() {
+    // Reset init flag
+    this._bInit = true;
+    
+    // Clear the RecordSet
+    this._oRecordSet.reset();
+
+    // Clear the Paginator's totalRecords if paginating
+    var pag = this.get('paginator');
+    if (pag) {
+        pag.set('totalRecords',0);
+    }
+
+    // Clear selections
+    this._unselectAllTrEls();
+    this._unselectAllTdEls();
+    this._aSelections = null;
+    this._oAnchorRecord = null;
+    this._oAnchorCell = null;
+    
+    // Clear sort
+    this.set("sortedBy", null);
+},
+
+/**
+ * Internal wrapper calls run() on render Chain instance.
+ *
+ * @method _runRenderChain
+ * @private 
+ */
+_runRenderChain : function() {
+    this._oChainRender.run();
+},
+
+/**
+ * Returns array of Records for current view. For example, if paginated, it
+ * returns the subset of Records for current page.
+ *
+ * @method _getViewRecords
+ * @protected
+ * @return {Array} Array of Records to display in current view.
+ */
+_getViewRecords : function() {
+    // Paginator is enabled, show a subset of Records
+    var oPaginator = this.get('paginator');
+    if(oPaginator) {
+        return this._oRecordSet.getRecords(
+                        oPaginator.getStartIndex(),
+                        oPaginator.getRowsPerPage());
+    }
+    // Not paginated, show all records
+    else {
+        return this._oRecordSet.getRecords();
+    }
+
+},
+
+/**
+ * Renders the view with existing Records from the RecordSet while
+ * maintaining sort, pagination, and selection states. For performance, reuses
+ * existing DOM elements when possible while deleting extraneous elements.
+ *
+ * @method render
+ */
+render : function() {
+//YAHOO.example.Performance.trialStart = new Date();
+
+    this._oChainRender.stop();
+
+    this.fireEvent("beforeRenderEvent");
+
+    var i, j, k, len,
+        allRecords = this._getViewRecords();
+
+
+    // From the top, update in-place existing rows, so as to reuse DOM elements
+    var elTbody = this._elTbody,
+        loopN = this.get("renderLoopSize"),
+        nRecordsLength = allRecords.length;
+    
+    // Table has rows
+    if(nRecordsLength > 0) {                
+        elTbody.style.display = "none";
+        while(elTbody.lastChild) {
+            elTbody.removeChild(elTbody.lastChild);
+        }
+        elTbody.style.display = "";
+
+        // Set up the loop Chain to render rows
+        this._oChainRender.add({
+            method: function(oArg) {
+                if((this instanceof DT) && this._sId) {
+                    var i = oArg.nCurrentRecord,
+                        endRecordIndex = ((oArg.nCurrentRecord+oArg.nLoopLength) > nRecordsLength) ?
+                                nRecordsLength : (oArg.nCurrentRecord+oArg.nLoopLength),
+                        elRow, nextSibling;
+
+                    elTbody.style.display = "none";
+                    
+                    for(; i<endRecordIndex; i++) {
+                        elRow = Dom.get(allRecords[i].getId());
+                        elRow = elRow || this._addTrEl(allRecords[i]);
+                        nextSibling = elTbody.childNodes[i] || null;
+                        elTbody.insertBefore(elRow, nextSibling);
+                    }
+                    elTbody.style.display = "";
+                    
+                    // Set up for the next loop
+                    oArg.nCurrentRecord = i;
+                }
+            },
+            scope: this,
+            iterations: (loopN > 0) ? Math.ceil(nRecordsLength/loopN) : 1,
+            argument: {
+                nCurrentRecord: 0,//nRecordsLength-1,  // Start at first Record
+                nLoopLength: (loopN > 0) ? loopN : nRecordsLength
+            },
+            timeout: (loopN > 0) ? 0 : -1
+        });
+        
+        // Post-render tasks
+        this._oChainRender.add({
+            method: function(oArg) {
+                if((this instanceof DT) && this._sId) {
+                    while(elTbody.rows.length > nRecordsLength) {
+                        elTbody.removeChild(elTbody.lastChild);
+                    }
+                    this._setFirstRow();
+                    this._setLastRow();
+                    this._setRowStripes();
+                    this._setSelections();
+                }
+            },
+            scope: this,
+            timeout: (loopN > 0) ? 0 : -1
+        });
+     
+    }
+    // Table has no rows
+    else {
+        // Set up the loop Chain to delete rows
+        var nTotal = elTbody.rows.length;
+        if(nTotal > 0) {
+            this._oChainRender.add({
+                method: function(oArg) {
+                    if((this instanceof DT) && this._sId) {
+                        var i = oArg.nCurrent,
+                            loopN = oArg.nLoopLength,
+                            nIterEnd = (i - loopN < 0) ? 0 : i - loopN;
+    
+                        elTbody.style.display = "none";
+                        
+                        for(; i>nIterEnd; i--) {
+                            elTbody.deleteRow(-1);
+                        }
+                        elTbody.style.display = "";
+                        
+                        // Set up for the next loop
+                        oArg.nCurrent = i;
+                    }
+                },
+                scope: this,
+                iterations: (loopN > 0) ? Math.ceil(nTotal/loopN) : 1,
+                argument: {
+                    nCurrent: nTotal, 
+                    nLoopLength: (loopN > 0) ? loopN : nTotal
+                },
+                timeout: (loopN > 0) ? 0 : -1
+            });
+        }
+    }
+    this._runRenderChain();
+},
+
+/**
+ * Disables DataTable UI.
+ *
+ * @method disable
+ */
+disable : function() {
+    this._disabled = true;
+    var elTable = this._elTable;
+    var elMask = this._elMask;
+    elMask.style.width = elTable.offsetWidth + "px";
+    elMask.style.height = elTable.offsetHeight + "px";
+    elMask.style.left = elTable.offsetLeft + "px";
+    elMask.style.display = "";
+    this.fireEvent("disableEvent");
+},
+
+/**
+ * Undisables DataTable UI.
+ *
+ * @method undisable
+ */
+undisable : function() {
+    this._disabled = false;
+    this._elMask.style.display = "none";
+    this.fireEvent("undisableEvent");
+},
+
+ /**
+ * Returns disabled state.
+ *
+ * @method isDisabled
+ * @return {Boolean} True if UI is disabled, otherwise false
+ */
+isDisabled : function() {
+    return this._disabled;
+},
+
+/**
+ * Nulls out the entire DataTable instance and related objects, removes attached
+ * event listeners, and clears out DOM elements inside the container. After
+ * calling this method, the instance reference should be expliclitly nulled by
+ * implementer, as in myDataTable = null. Use with caution!
+ *
+ * @method destroy
+ */
+destroy : function() {
+    // Store for later
+    var instanceName = this.toString();
+
+    this._oChainRender.stop();
+    
+    // Destroy ColumnDD and ColumnResizers
+    this._destroyColumnHelpers();
+    
+    // Destroy all CellEditors
+    var oCellEditor;
+    for(var i=0, len=this._oColumnSet.flat.length; i<len; i++) {
+        oCellEditor = this._oColumnSet.flat[i].editor;
+        if(oCellEditor && oCellEditor.destroy) {
+            oCellEditor.destroy();
+            this._oColumnSet.flat[i].editor = null;
+        }
+    }
+
+    // Destroy Paginator
+    this._destroyPaginator();
+
+    // Unhook custom events
+    this._oRecordSet.unsubscribeAll();
+    this.unsubscribeAll();
+
+    // Unhook DOM events
+    Ev.removeListener(document, "click", this._onDocumentClick);
+    
+    // Clear out the container
+    this._destroyContainerEl(this._elContainer);
+
+    // Null out objects
+    for(var param in this) {
+        if(lang.hasOwnProperty(this, param)) {
+            this[param] = null;
+        }
+    }
+    
+    // Clean up static values
+    DT._nCurrentCount--;
+    
+    if(DT._nCurrentCount < 1) {
+        if(DT._elDynStyleNode) {
+            document.getElementsByTagName('head')[0].removeChild(DT._elDynStyleNode);
+            DT._elDynStyleNode = null;
+        }
+    }
+
+},
+
+/**
+ * Displays message within secondary TBODY.
+ *
+ * @method showTableMessage
+ * @param sHTML {HTML} (optional) Value for innerHTML.
+ * @param sClassName {String} (optional) Classname.
+ */
+showTableMessage : function(sHTML, sClassName) {
+    var elCell = this._elMsgTd;
+    if(lang.isString(sHTML)) {
+        elCell.firstChild.innerHTML = sHTML;
+    }
+    if(lang.isString(sClassName)) {
+        elCell.className = sClassName;
+    }
+
+    this._elMsgTbody.style.display = "";
+
+    this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
+},
+
+/**
+ * Hides secondary TBODY.
+ *
+ * @method hideTableMessage
+ */
+hideTableMessage : function() {
+    if(this._elMsgTbody.style.display != "none") {
+        this._elMsgTbody.style.display = "none";
+        this._elMsgTbody.parentNode.style.width = "";
+        this.fireEvent("tableMsgHideEvent");
+    }
+},
+
+/**
+ * Brings focus to the TBODY element. Alias to focusTbodyEl.
+ *
+ * @method focus
+ */
+focus : function() {
+    this.focusTbodyEl();
+},
+
+/**
+ * Brings focus to the THEAD element.
+ *
+ * @method focusTheadEl
+ */
+focusTheadEl : function() {
+    this._focusEl(this._elThead);
+},
+
+/**
+ * Brings focus to the TBODY element.
+ *
+ * @method focusTbodyEl
+ */
+focusTbodyEl : function() {
+    this._focusEl(this._elTbody);
+},
+
+/**
+ * Setting display:none on DataTable or any parent may impact width validations.
+ * After setting display back to "", implementers should call this method to 
+ * manually perform those validations.
+ *
+ * @method onShow
+ */
+onShow : function() {
+    this.validateColumnWidths();
+    
+    for(var allKeys = this._oColumnSet.keys, i=0, len=allKeys.length, col; i<len; i++) {
+        col = allKeys[i];
+        if(col._ddResizer) {
+            col._ddResizer.resetResizerEl();
+        }
+    }
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// RECORDSET FUNCTIONS
+
+/**
+ * Returns Record index for given TR element or page row index.
+ *
+ * @method getRecordIndex
+ * @param row {YAHOO.widget.Record | HTMLElement | Number} Record instance, TR
+ * element reference or page row index.
+ * @return {Number} Record's RecordSet index, or null.
+ */
+getRecordIndex : function(row) {
+    var nTrIndex;
+
+    if(!lang.isNumber(row)) {
+        // By Record
+        if(row instanceof YAHOO.widget.Record) {
+            return this._oRecordSet.getRecordIndex(row);
+        }
+        // By element reference
+        else {
+            // Find the TR element
+            var el = this.getTrEl(row);
+            if(el) {
+                nTrIndex = el.sectionRowIndex;
+            }
+        }
+    }
+    // By page row index
+    else {
+        nTrIndex = row;
+    }
+
+    if(lang.isNumber(nTrIndex)) {
+        var oPaginator = this.get("paginator");
+        if(oPaginator) {
+            return oPaginator.get('recordOffset') + nTrIndex;
+        }
+        else {
+            return nTrIndex;
+        }
+    }
+
+    return null;
+},
+
+/**
+ * For the given identifier, returns the associated Record instance.
+ *
+ * @method getRecord
+ * @param row {HTMLElement | Number | String} DOM reference to a TR element (or
+ * child of a TR element), RecordSet position index, or Record ID.
+ * @return {YAHOO.widget.Record} Record instance.
+ */
+getRecord : function(row) {
+    var oRecord = this._oRecordSet.getRecord(row);
+
+    if(!oRecord) {
+        // Validate TR element
+        var elRow = this.getTrEl(row);
+        if(elRow) {
+            oRecord = this._oRecordSet.getRecord(elRow.id);
+        }
+    }
+
+    if(oRecord instanceof YAHOO.widget.Record) {
+        return this._oRecordSet.getRecord(oRecord);
+    }
+    else {
+        return null;
+    }
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// COLUMN FUNCTIONS
+
+/**
+ * For the given identifier, returns the associated Column instance. Note: For
+ * getting Columns by Column ID string, please use the method getColumnById().
+ *
+ * @method getColumn
+ * @param column {HTMLElement | String | Number} TH/TD element (or child of a
+ * TH/TD element), a Column key, or a ColumnSet key index.
+ * @return {YAHOO.widget.Column} Column instance.
+ */
+getColumn : function(column) {
+    var oColumn = this._oColumnSet.getColumn(column);
+
+    if(!oColumn) {
+        // Validate TD element
+        var elCell = this.getTdEl(column);
+        if(elCell) {
+            oColumn = this._oColumnSet.getColumn(this.getCellIndex(elCell));
+        }
+        // Validate TH element
+        else {
+            elCell = this.getThEl(column);
+            if(elCell) {
+                // Find by TH el ID
+                var allColumns = this._oColumnSet.flat;
+                for(var i=0, len=allColumns.length; i<len; i++) {
+                    if(allColumns[i].getThEl().id === elCell.id) {
+                        oColumn = allColumns[i];
+                    } 
+                }
+            }
+        }
+    }
+    if(!oColumn) {
+    }
+    return oColumn;
+},
+
+/**
+ * For the given Column ID, returns the associated Column instance. Note: For
+ * getting Columns by key, please use the method getColumn().
+ *
+ * @method getColumnById
+ * @param column {String} Column ID string.
+ * @return {YAHOO.widget.Column} Column instance.
+ */
+getColumnById : function(column) {
+    return this._oColumnSet.getColumnById(column);
+},
+
+/**
+ * For the given Column instance, returns next direction to sort.
+ *
+ * @method getColumnSortDir
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param oSortedBy {Object} (optional) Specify the state, or use current state. 
+ * @return {String} YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTableCLASS_DESC.
+ */
+getColumnSortDir : function(oColumn, oSortedBy) {
+    // Backward compatibility
+    if(oColumn.sortOptions && oColumn.sortOptions.defaultDir) {
+        if(oColumn.sortOptions.defaultDir == "asc") {
+            oColumn.sortOptions.defaultDir = DT.CLASS_ASC;
+        }
+        else if (oColumn.sortOptions.defaultDir == "desc") {
+            oColumn.sortOptions.defaultDir = DT.CLASS_DESC;
+        }
+    }
+    
+    // What is the Column's default sort direction?
+    var sortDir = (oColumn.sortOptions && oColumn.sortOptions.defaultDir) ? oColumn.sortOptions.defaultDir : DT.CLASS_ASC;
+
+    // Is the Column currently sorted?
+    var bSorted = false;
+    oSortedBy = oSortedBy || this.get("sortedBy");
+    if(oSortedBy && (oSortedBy.key === oColumn.key)) {
+        bSorted = true;
+        if(oSortedBy.dir) {
+            sortDir = (oSortedBy.dir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
+        }
+        else {
+            sortDir = (sortDir === DT.CLASS_ASC) ? DT.CLASS_DESC : DT.CLASS_ASC;
+        }
+    }
+    return sortDir;
+},
+
+/**
+ * Overridable method gives implementers a hook to show loading message before
+ * sorting Column.
+ *
+ * @method doBeforeSortColumn
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param sSortDir {String} YAHOO.widget.DataTable.CLASS_ASC or
+ * YAHOO.widget.DataTable.CLASS_DESC.
+ * @return {Boolean} Return true to continue sorting Column.
+ */
+doBeforeSortColumn : function(oColumn, sSortDir) {
+    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
+    return true;
+},
+
+/**
+ * Sorts given Column. If "dynamicData" is true, current selections are purged before
+ * a request is sent to the DataSource for data for the new state (using the
+ * request returned by "generateRequest()").
+ *
+ * @method sortColumn
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param sDir {String} (Optional) YAHOO.widget.DataTable.CLASS_ASC or
+ * YAHOO.widget.DataTable.CLASS_DESC
+ */
+sortColumn : function(oColumn, sDir) {
+    if(oColumn && (oColumn instanceof YAHOO.widget.Column)) {
+        if(!oColumn.sortable) {
+            Dom.addClass(this.getThEl(oColumn), DT.CLASS_SORTABLE);
+        }
+        
+        // Validate given direction
+        if(sDir && (sDir !== DT.CLASS_ASC) && (sDir !== DT.CLASS_DESC)) {
+            sDir = null;
+        }
+        
+        // Get the sort dir
+        var sSortDir = sDir || this.getColumnSortDir(oColumn);
+
+        // Is the Column currently sorted?
+        var oSortedBy = this.get("sortedBy") || {};
+        var bSorted = (oSortedBy.key === oColumn.key) ? true : false;
+
+        var ok = this.doBeforeSortColumn(oColumn, sSortDir);
+        if(ok) {
+            // Server-side sort
+            if(this.get("dynamicData")) {
+                // Get current state
+                var oState = this.getState();
+                
+                // Reset record offset, if paginated
+                if(oState.pagination) {
+                    oState.pagination.recordOffset = 0;
+                }
+                
+                // Update sortedBy to new values
+                oState.sortedBy = {
+                    key: oColumn.key,
+                    dir: sSortDir
+                };
+                
+                // Get the request for the new state
+                var request = this.get("generateRequest")(oState, this);
+
+                // Purge selections
+                this.unselectAllRows();
+                this.unselectAllCells();
+
+                // Send request for new data
+                var callback = {
+                    success : this.onDataReturnSetRows,
+                    failure : this.onDataReturnSetRows,
+                    argument : oState, // Pass along the new state to the callback
+                    scope : this
+                };
+                this._oDataSource.sendRequest(request, callback);            
+            }
+            // Client-side sort
+            else {
+                // Is there a custom sort handler function defined?
+                var sortFnc = (oColumn.sortOptions && lang.isFunction(oColumn.sortOptions.sortFunction)) ?
+                        // Custom sort function
+                        oColumn.sortOptions.sortFunction : null;
+                   
+                // Sort the Records
+                if(!bSorted || sDir || sortFnc) {
+                    // Default sort function if necessary
+                    sortFnc = sortFnc || this.get("sortFunction");
+                    // Get the field to sort
+                    var sField = (oColumn.sortOptions && oColumn.sortOptions.field) ? oColumn.sortOptions.field : oColumn.field;
+
+                    // Sort the Records        
+                    this._oRecordSet.sortRecords(sortFnc, ((sSortDir == DT.CLASS_DESC) ? true : false), sField);
+                }
+                // Just reverse the Records
+                else {
+                    this._oRecordSet.reverseRecords();
+                }
+        
+                // Reset to first page if paginated
+                var oPaginator = this.get('paginator');
+                if (oPaginator) {
+                    // Set page silently, so as not to fire change event.
+                    oPaginator.setPage(1,true);
+                }
+        
+                // Update UI via sortedBy
+                this.render();
+                this.set("sortedBy", {key:oColumn.key, dir:sSortDir, column:oColumn}); 
+            }       
+            
+            this.fireEvent("columnSortEvent",{column:oColumn,dir:sSortDir});
+            return;
+        }
+    }
+},
+
+/**
+ * Sets given Column to given pixel width. If new width is less than minimum
+ * width, sets to minimum width. Updates oColumn.width value.
+ *
+ * @method setColumnWidth
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param nWidth {Number} New width in pixels. A null value auto-sizes Column,
+ * subject to minWidth and maxAutoWidth validations. 
+ */
+setColumnWidth : function(oColumn, nWidth) {
+    if(!(oColumn instanceof YAHOO.widget.Column)) {
+        oColumn = this.getColumn(oColumn);
+    }
+    if(oColumn) {
+        // Validate new width against minimum width
+        if(lang.isNumber(nWidth)) {
+            // This is why we must require a Number... :-|
+            nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
+
+            // Save state
+            oColumn.width = nWidth;
+            
+            // Resize the DOM elements
+            this._setColumnWidth(oColumn, nWidth+"px");
+            
+            this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
+        }
+        // Unsets a width to auto-size
+        else if(nWidth === null) {
+            // Save state
+            oColumn.width = nWidth;
+            
+            // Resize the DOM elements
+            this._setColumnWidth(oColumn, "auto");
+            this.validateColumnWidths(oColumn);
+            this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
+        }
+                
+        // Bug 2339454: resize then sort misaligment
+        this._clearTrTemplateEl();
+    }
+    else {
+    }
+},
+
+/**
+ * Sets liner DIV elements of given Column to given width. When value should be
+ * auto-calculated to fit content overflow is set to visible, otherwise overflow
+ * is set to hidden. No validations against minimum width and no updating
+ * Column.width value.
+ *
+ * @method _setColumnWidth
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param sWidth {String} New width value.
+ * @param sOverflow {String} Should be "hidden" when Column width is explicitly
+ * being set to a value, but should be "visible" when Column is meant to auto-fit content.  
+ * @private
+ */
+_setColumnWidth : function(oColumn, sWidth, sOverflow) {
+    if(oColumn && (oColumn.getKeyIndex() !== null)) {
+        sOverflow = sOverflow || (((sWidth === '') || (sWidth === 'auto')) ? 'visible' : 'hidden');
+    
+        // Dynamic style algorithm
+        if(!DT._bDynStylesFallback) {
+            this._setColumnWidthDynStyles(oColumn, sWidth, sOverflow);
+        }
+        // Dynamic function algorithm
+        else {
+            this._setColumnWidthDynFunction(oColumn, sWidth, sOverflow);
+        }
+    }
+    else {
+    }
+},
+
+/**
+ * Updates width of a Column's liner DIV elements by dynamically creating a
+ * STYLE node and writing and updating CSS style rules to it. If this fails during
+ * runtime, the fallback method _setColumnWidthDynFunction() will be called.
+ * Notes: This technique is not performant in IE6. IE7 crashes if DataTable is
+ * nested within another TABLE element. For these cases, it is recommended to
+ * use the method _setColumnWidthDynFunction by setting _bDynStylesFallback to TRUE.
+ *
+ * @method _setColumnWidthDynStyles
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param sWidth {String} New width value.
+ * @private
+ */
+_setColumnWidthDynStyles : function(oColumn, sWidth, sOverflow) {
+    var s = DT._elDynStyleNode,
+        rule;
+    
+    // Create a new STYLE node
+    if(!s) {
+        s = document.createElement('style');
+        s.type = 'text/css';
+        s = document.getElementsByTagName('head').item(0).appendChild(s);
+        DT._elDynStyleNode = s;
+    }
+    
+    // We have a STYLE node to update
+    if(s) {
+        // Use unique classname for this Column instance as a hook for resizing
+        var sClassname = "." + this.getId() + "-col-" + oColumn.getSanitizedKey() + " ." + DT.CLASS_LINER;
+        
+        // Hide for performance
+        if(this._elTbody) {
+            this._elTbody.style.display = 'none';
+        }
+        
+        rule = DT._oDynStyles[sClassname];
+
+        // The Column does not yet have a rule
+        if(!rule) {
+            if(s.styleSheet && s.styleSheet.addRule) {
+                s.styleSheet.addRule(sClassname,"overflow:"+sOverflow);
+                s.styleSheet.addRule(sClassname,'width:'+sWidth);
+                rule = s.styleSheet.rules[s.styleSheet.rules.length-1];
+                DT._oDynStyles[sClassname] = rule;
+            }
+            else if(s.sheet && s.sheet.insertRule) {
+                s.sheet.insertRule(sClassname+" {overflow:"+sOverflow+";width:"+sWidth+";}",s.sheet.cssRules.length);
+                rule = s.sheet.cssRules[s.sheet.cssRules.length-1];
+                DT._oDynStyles[sClassname] = rule;
+            }
+        }
+        // We have a rule to update
+        else {
+            rule.style.overflow = sOverflow;
+            rule.style.width = sWidth;
+        } 
+        
+        // Unhide
+        if(this._elTbody) {
+            this._elTbody.style.display = '';
+        }
+    }
+    
+    // That was not a success, we must call the fallback routine
+    if(!rule) {
+        DT._bDynStylesFallback = true;
+        this._setColumnWidthDynFunction(oColumn, sWidth);
+    }
+},
+
+/**
+ * Updates width of a Column's liner DIV elements by dynamically creating a
+ * function to update all element style properties in one pass. Note: This
+ * technique is not supported in sandboxed environments that prohibit EVALs.    
+ *
+ * @method _setColumnWidthDynFunction
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param sWidth {String} New width value.
+ * @private
+ */
+_setColumnWidthDynFunction : function(oColumn, sWidth, sOverflow) {
+    // TODO: why is this here?
+    if(sWidth == 'auto') {
+        sWidth = ''; 
+    }
+    
+    // Create one function for each value of rows.length
+    var rowslen = this._elTbody ? this._elTbody.rows.length : 0;
+    
+    // Dynamically create the function
+    if (!this._aDynFunctions[rowslen]) {
+        
+        //Compile a custom function to do all the liner div width
+        //assignments at the same time.  A unique function is required
+        //for each unique number of rows in _elTbody.  This will
+        //result in a function declaration like:
+        //function (oColumn,sWidth,sOverflow) {
+        //    var colIdx = oColumn.getKeyIndex();
+        //    oColumn.getThLinerEl().style.overflow =
+        //    this._elTbody.rows[0].cells[colIdx].firstChild.style.overflow =
+        //    this._elTbody.rows[1].cells[colIdx].firstChild.style.overflow =
+        //    ... (for all row indices in this._elTbody.rows.length - 1)
+        //    this._elTbody.rows[99].cells[colIdx].firstChild.style.overflow =
+        //    sOverflow;
+        //    oColumn.getThLinerEl().style.width =
+        //    this._elTbody.rows[0].cells[colIdx].firstChild.style.width =
+        //    this._elTbody.rows[1].cells[colIdx].firstChild.style.width =
+        //    ... (for all row indices in this._elTbody.rows.length - 1)
+        //    this._elTbody.rows[99].cells[colIdx].firstChild.style.width =
+        //    sWidth;
+        //}
+        
+        var i,j,k;
+        var resizerDef = [
+            'var colIdx=oColumn.getKeyIndex();',
+            'oColumn.getThLinerEl().style.overflow='
+        ];
+        for (i=rowslen-1, j=2; i >= 0; --i) {
+            resizerDef[j++] = 'this._elTbody.rows[';
+            resizerDef[j++] = i;
+            resizerDef[j++] = '].cells[colIdx].firstChild.style.overflow=';
+        }
+        resizerDef[j] = 'sOverflow;';
+        resizerDef[j+1] = 'oColumn.getThLinerEl().style.width=';
+        for (i=rowslen-1, k=j+2; i >= 0; --i) {
+            resizerDef[k++] = 'this._elTbody.rows[';
+            resizerDef[k++] = i;
+            resizerDef[k++] = '].cells[colIdx].firstChild.style.width=';
+        }
+        resizerDef[k] = 'sWidth;';
+        this._aDynFunctions[rowslen] =
+            new Function('oColumn','sWidth','sOverflow',resizerDef.join(''));
+    }
+    
+    // Get the function to execute
+    var resizerFn = this._aDynFunctions[rowslen];
+
+    // TODO: Hide TBODY for performance in _setColumnWidthDynFunction?
+    if (resizerFn) {
+        resizerFn.call(this,oColumn,sWidth,sOverflow);
+    }
+},
+
+/**
+ * For one or all Columns, when Column is not hidden, width is not set, and minWidth
+ * and/or maxAutoWidth is set, validates auto-width against minWidth and maxAutoWidth.
+ *
+ * @method validateColumnWidths
+ * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
+ */
+validateColumnWidths : function(oColumn) {
+    var elColgroup = this._elColgroup;
+    var elColgroupClone = elColgroup.cloneNode(true);
+    var bNeedsValidation = false;
+    var allKeys = this._oColumnSet.keys;
+    var elThLiner;
+    // Validate just one Column's minWidth and/or maxAutoWidth
+    if(oColumn && !oColumn.hidden && !oColumn.width && (oColumn.getKeyIndex() !== null)) {
+            elThLiner = oColumn.getThLinerEl();
+            if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
+                elColgroupClone.childNodes[oColumn.getKeyIndex()].style.width = 
+                        oColumn.minWidth + 
+                        (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
+                        (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
+                bNeedsValidation = true;
+            }
+            else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
+                this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
+            }
+    }
+    // Validate all Columns
+    else {
+        for(var i=0, len=allKeys.length; i<len; i++) {
+            oColumn = allKeys[i];
+            if(!oColumn.hidden && !oColumn.width) {
+                elThLiner = oColumn.getThLinerEl();
+                if((oColumn.minWidth > 0) && (elThLiner.offsetWidth < oColumn.minWidth)) {
+                    elColgroupClone.childNodes[i].style.width = 
+                            oColumn.minWidth + 
+                            (parseInt(Dom.getStyle(elThLiner,"paddingLeft"),10)|0) +
+                            (parseInt(Dom.getStyle(elThLiner,"paddingRight"),10)|0) + "px";
+                    bNeedsValidation = true;
+                }
+                else if((oColumn.maxAutoWidth > 0) && (elThLiner.offsetWidth > oColumn.maxAutoWidth)) {
+                    this._setColumnWidth(oColumn, oColumn.maxAutoWidth+"px", "hidden");
+                }
+            }
+        }
+    }
+    if(bNeedsValidation) {
+        elColgroup.parentNode.replaceChild(elColgroupClone, elColgroup);
+        this._elColgroup = elColgroupClone;
+    }
+},
+
+/**
+ * Clears minWidth.
+ *
+ * @method _clearMinWidth
+ * @param oColumn {YAHOO.widget.Column} Which Column.
+ * @private
+ */
+_clearMinWidth : function(oColumn) {
+    if(oColumn.getKeyIndex() !== null) {
+        this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = '';
+    }
+},
+
+/**
+ * Restores minWidth.
+ *
+ * @method _restoreMinWidth
+ * @param oColumn {YAHOO.widget.Column} Which Column.
+ * @private
+ */
+_restoreMinWidth : function(oColumn) {
+    if(oColumn.minWidth && (oColumn.getKeyIndex() !== null)) {
+        this._elColgroup.childNodes[oColumn.getKeyIndex()].style.width = oColumn.minWidth + 'px';
+    }
+},
+
+/**
+ * Hides given Column. NOTE: You cannot hide/show nested Columns. You can only
+ * hide/show non-nested Columns, and top-level parent Columns (which will
+ * hide/show all children Columns).
+ *
+ * @method hideColumn
+ * @param oColumn {YAHOO.widget.Column | HTMLElement | String | Number} Column
+ * instance, TH/TD element (or child of a TH/TD element), a Column key, or a
+ * ColumnSet key index.
+ */
+hideColumn : function(oColumn) {
+    if(!(oColumn instanceof YAHOO.widget.Column)) {
+        oColumn = this.getColumn(oColumn);
+    }
+    // Only top-level Columns can get hidden due to issues in FF2 and SF3
+    if(oColumn && !oColumn.hidden && oColumn.getTreeIndex() !== null) {
+        
+        var allrows = this.getTbodyEl().rows;
+        var l = allrows.length;
+        var allDescendants = this._oColumnSet.getDescendants(oColumn);
+        
+        // Hide each nested Column
+        for(var i=0, len=allDescendants.length; i<len; i++) {
+            var thisColumn = allDescendants[i];
+            thisColumn.hidden = true;
+
+            // Style the head cell
+            Dom.addClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
+            
+            // Does this Column have body cells?
+            var thisKeyIndex = thisColumn.getKeyIndex();
+            if(thisKeyIndex !== null) {                    
+                // Clear minWidth
+                this._clearMinWidth(oColumn);
+                
+                // Style the body cells
+                for(var j=0;j<l;j++) {
+                    Dom.addClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
+                }
+            }
+            
+            this.fireEvent("columnHideEvent",{column:thisColumn});
+        }
+      
+        this._repaintOpera();
+        this._clearTrTemplateEl();
+    }
+    else {
+    }
+},
+
+/**
+ * Shows given Column. NOTE: You cannot hide/show nested Columns. You can only
+ * hide/show non-nested Columns, and top-level parent Columns (which will
+ * hide/show all children Columns).
+ *
+ * @method showColumn
+ * @param oColumn {YAHOO.widget.Column | HTMLElement | String | Number} Column
+ * instance, TH/TD element (or child of a TH/TD element), a Column key, or a
+ * ColumnSet key index.
+ */
+showColumn : function(oColumn) {
+    if(!(oColumn instanceof YAHOO.widget.Column)) {
+        oColumn = this.getColumn(oColumn);
+    }
+    // Only top-level Columns can get hidden
+    if(oColumn && oColumn.hidden && (oColumn.getTreeIndex() !== null)) {
+        var allrows = this.getTbodyEl().rows;
+        var l = allrows.length;
+        var allDescendants = this._oColumnSet.getDescendants(oColumn);
+        
+        // Show each nested Column
+        for(var i=0, len=allDescendants.length; i<len; i++) {
+            var thisColumn = allDescendants[i];
+            thisColumn.hidden = false;
+            
+            // Unstyle the head cell
+            Dom.removeClass(thisColumn.getThEl(), DT.CLASS_HIDDEN);
+
+            // Does this Column have body cells?
+            var thisKeyIndex = thisColumn.getKeyIndex();
+            if(thisKeyIndex !== null) {
+                // Restore minWidth
+                this._restoreMinWidth(oColumn);
+                
+            
+                // Unstyle the body cells
+                for(var j=0;j<l;j++) {
+                    Dom.removeClass(allrows[j].cells[thisKeyIndex],DT.CLASS_HIDDEN);
+                }
+            }
+
+            this.fireEvent("columnShowEvent",{column:thisColumn});
+        }
+        this._clearTrTemplateEl();
+    }
+    else {
+    }
+},
+
+/**
+ * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
+ * non-nested Columns, and top-level parent Columns (which will remove all
+ * children Columns).
+ *
+ * @method removeColumn
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @return oColumn {YAHOO.widget.Column} Removed Column instance.
+ */
+removeColumn : function(oColumn) {
+    // Validate Column
+    if(!(oColumn instanceof YAHOO.widget.Column)) {
+        oColumn = this.getColumn(oColumn);
+    }
+    if(oColumn) {
+        var nColTreeIndex = oColumn.getTreeIndex();
+        if(nColTreeIndex !== null) {
+            // Which key index(es)
+            var i, len,
+                aKeyIndexes = oColumn.getKeyIndex();
+            // Must be a parent Column
+            if(aKeyIndexes === null) {
+                var descKeyIndexes = [];
+                var allDescendants = this._oColumnSet.getDescendants(oColumn);
+                for(i=0, len=allDescendants.length; i<len; i++) {
+                    // Is this descendant a key Column?
+                    var thisKey = allDescendants[i].getKeyIndex();
+                    if(thisKey !== null) {
+                        descKeyIndexes[descKeyIndexes.length] = thisKey;
+                    }
+                }
+                if(descKeyIndexes.length > 0) {
+                    aKeyIndexes = descKeyIndexes;
+                }
+            }
+            // Must be a key Column
+            else {
+                aKeyIndexes = [aKeyIndexes];
+            }
+            
+            if(aKeyIndexes !== null) {
+                // Sort the indexes so we can remove from the right
+                aKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
+                
+                // Destroy previous THEAD
+                this._destroyTheadEl();
+    
+                // Create new THEAD
+                var aOrigColumnDefs = this._oColumnSet.getDefinitions();
+                oColumn = aOrigColumnDefs.splice(nColTreeIndex,1)[0];
+                this._initColumnSet(aOrigColumnDefs);
+                this._initTheadEl();
+                
+                // Remove COL
+                for(i=aKeyIndexes.length-1; i>-1; i--) {
+                    this._removeColgroupColEl(aKeyIndexes[i]);
+                }
+                
+                // Remove TD
+                var allRows = this._elTbody.rows;
+                if(allRows.length > 0) {
+                    var loopN = this.get("renderLoopSize"),
+                        loopEnd = allRows.length;
+                    this._oChainRender.add({
+                        method: function(oArg) {
+                            if((this instanceof DT) && this._sId) {
+                                var i = oArg.nCurrentRow,
+                                    len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
+                                    aIndexes = oArg.aIndexes,
+                                    j;
+                                for(; i < len; ++i) {
+                                    for(j = aIndexes.length-1; j>-1; j--) {
+                                        allRows[i].removeChild(allRows[i].childNodes[aIndexes[j]]);
+                                    }
+                                }
+                                oArg.nCurrentRow = i;
+                            }
+                        },
+                        iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
+                        argument: {nCurrentRow:0, aIndexes:aKeyIndexes},
+                        scope: this,
+                        timeout: (loopN > 0) ? 0 : -1
+                    });
+                    this._runRenderChain();
+                }
+        
+                this.fireEvent("columnRemoveEvent",{column:oColumn});
+                return oColumn;
+            }
+        }
+    }
+},
+
+/**
+ * Inserts given Column at the index if given, otherwise at the end. NOTE: You
+ * can only add non-nested Columns and top-level parent Columns. You cannot add
+ * a nested Column to an existing parent.
+ *
+ * @method insertColumn
+ * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
+ * definition or a Column instance.
+ * @param index {Number} (optional) New tree index.
+ * @return oColumn {YAHOO.widget.Column} Inserted Column instance. 
+ */
+insertColumn : function(oColumn, index) {
+    // Validate Column
+    if(oColumn instanceof YAHOO.widget.Column) {
+        oColumn = oColumn.getDefinition();
+    }
+    else if(oColumn.constructor !== Object) {
+        return;
+    }
+    
+    // Validate index or append new Column to the end of the ColumnSet
+    var oColumnSet = this._oColumnSet;
+    if(!lang.isValue(index) || !lang.isNumber(index)) {
+        index = oColumnSet.tree[0].length;
+    }
+    
+    // Destroy previous THEAD
+    this._destroyTheadEl();
+    
+    // Create new THEAD
+    var aNewColumnDefs = this._oColumnSet.getDefinitions();
+    aNewColumnDefs.splice(index, 0, oColumn);
+    this._initColumnSet(aNewColumnDefs);
+    this._initTheadEl();
+    
+    // Need to refresh the reference
+    oColumnSet = this._oColumnSet;
+    var 
+    
+    // Get key index(es) for new Column
+    var i, len,
+        descKeyIndexes = [];
+    var allDescendants = oColumnSet.getDescendants(oNewColumn);
+    for(i=0, len=allDescendants.length; i<len; i++) {
+        // Is this descendant a key Column?
+        var thisKey = allDescendants[i].getKeyIndex();
+        if(thisKey !== null) {
+            descKeyIndexes[descKeyIndexes.length] = thisKey;
+        }
+    }
+    
+    if(descKeyIndexes.length > 0) {  
+        // Sort the indexes
+        var newIndex = descKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
+        
+        // Add COL
+        for(i=descKeyIndexes.length-1; i>-1; i--) {
+            this._insertColgroupColEl(descKeyIndexes[i]);
+        }
+            
+        // Add TD
+        var allRows = this._elTbody.rows;
+        if(allRows.length > 0) {
+            var loopN = this.get("renderLoopSize"),
+                loopEnd = allRows.length;
+            
+            // Get templates for each new TD
+            var aTdTemplates = [],
+                elTdTemplate;
+            for(i=0, len=descKeyIndexes.length; i<len; i++) {
+                var thisKeyIndex = descKeyIndexes[i];
+                elTdTemplate = this._getTrTemplateEl().childNodes[i].cloneNode(true);
+                elTdTemplate = this._formatTdEl(this._oColumnSet.keys[thisKeyIndex], elTdTemplate, thisKeyIndex, (thisKeyIndex===this._oColumnSet.keys.length-1));
+                aTdTemplates[thisKeyIndex] = elTdTemplate;
+            }
+            
+            this._oChainRender.add({
+                method: function(oArg) {
+                    if((this instanceof DT) && this._sId) {
+                        var i = oArg.nCurrentRow, j,
+                            descKeyIndexes = oArg.descKeyIndexes,
+                            len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
+                            nextSibling;
+                        for(; i < len; ++i) {
+                            nextSibling = allRows[i].childNodes[newIndex] || null;
+                            for(j=descKeyIndexes.length-1; j>-1; j--) {
+                                allRows[i].insertBefore(oArg.aTdTemplates[descKeyIndexes[j]].cloneNode(true), nextSibling);
+                            }
+                        }
+                        oArg.nCurrentRow = i;
+                    }
+                },
+                iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
+                argument: {nCurrentRow:0,aTdTemplates:aTdTemplates,descKeyIndexes:descKeyIndexes},
+                scope: this,
+                timeout: (loopN > 0) ? 0 : -1
+            });
+            this._runRenderChain(); 
+        }
+
+        this.fireEvent("columnInsertEvent",{column:oColumn,index:index});
+        return oNewColumn;
+    }
+},
+
+/**
+ * Removes given Column and inserts into given tree index. NOTE: You
+ * can only reorder non-nested Columns and top-level parent Columns. You cannot
+ * reorder a nested Column to an existing parent.
+ *
+ * @method reorderColumn
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param index {Number} New tree index.
+ * @return oColumn {YAHOO.widget.Column} Reordered Column instance. 
+ */
+reorderColumn : function(oColumn, index) {
+    // Validate Column and new index
+    if(!(oColumn instanceof YAHOO.widget.Column)) {
+        oColumn = this.getColumn(oColumn);
+    }
+    if(oColumn && YAHOO.lang.isNumber(index)) {
+        var nOrigTreeIndex = oColumn.getTreeIndex();
+        if((nOrigTreeIndex !== null) && (nOrigTreeIndex !== index)) {
+            // Which key index(es)
+            var i, len,
+                aOrigKeyIndexes = oColumn.getKeyIndex(),
+                allDescendants,
+                descKeyIndexes = [],
+                thisKey;
+            // Must be a parent Column...
+            if(aOrigKeyIndexes === null) {
+                allDescendants = this._oColumnSet.getDescendants(oColumn);
+                for(i=0, len=allDescendants.length; i<len; i++) {
+                    // Is this descendant a key Column?
+                    thisKey = allDescendants[i].getKeyIndex();
+                    if(thisKey !== null) {
+                        descKeyIndexes[descKeyIndexes.length] = thisKey;
+                    }
+                }
+                if(descKeyIndexes.length > 0) {
+                    aOrigKeyIndexes = descKeyIndexes;
+                }
+            }
+            // ...or else must be a key Column
+            else {
+                aOrigKeyIndexes = [aOrigKeyIndexes];
+            }
+            
+            if(aOrigKeyIndexes !== null) {                   
+                // Sort the indexes
+                aOrigKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);});
+                
+                // Destroy previous THEAD
+                this._destroyTheadEl();
+    
+                // Create new THEAD
+                var aColumnDefs = this._oColumnSet.getDefinitions();
+                var oColumnDef = aColumnDefs.splice(nOrigTreeIndex,1)[0];
+                aColumnDefs.splice(index, 0, oColumnDef);
+                this._initColumnSet(aColumnDefs);
+                this._initTheadEl();
+                
+                // Need to refresh the reference
+                var 
+
+                // What are new key index(es)
+                var aNewKeyIndexes = oNewColumn.getKeyIndex();
+                // Must be a parent Column
+                if(aNewKeyIndexes === null) {
+                    descKeyIndexes = [];
+                    allDescendants = this._oColumnSet.getDescendants(oNewColumn);
+                    for(i=0, len=allDescendants.length; i<len; i++) {
+                        // Is this descendant a key Column?
+                        thisKey = allDescendants[i].getKeyIndex();
+                        if(thisKey !== null) {
+                            descKeyIndexes[descKeyIndexes.length] = thisKey;
+                        }
+                    }
+                    if(descKeyIndexes.length > 0) {
+                        aNewKeyIndexes = descKeyIndexes;
+                    }
+                }
+                // Must be a key Column
+                else {
+                    aNewKeyIndexes = [aNewKeyIndexes];
+                }
+                
+                // Sort the new indexes and grab the first one for the new location
+                var newIndex = aNewKeyIndexes.sort(function(a, b) {return YAHOO.util.Sort.compare(a, b);})[0];
+
+                // Reorder COL
+                this._reorderColgroupColEl(aOrigKeyIndexes, newIndex);
+                
+                // Reorder TD
+                var allRows = this._elTbody.rows;
+                if(allRows.length > 0) {
+                    var loopN = this.get("renderLoopSize"),
+                        loopEnd = allRows.length;
+                    this._oChainRender.add({
+                        method: function(oArg) {
+                            if((this instanceof DT) && this._sId) {
+                                var i = oArg.nCurrentRow, j, tmpTds, nextSibling,
+                                    len = loopN > 0 ? Math.min(i + loopN,allRows.length) : allRows.length,
+                                    aIndexes = oArg.aIndexes, thisTr;
+                                // For each row
+                                for(; i < len; ++i) {
+                                    tmpTds = [];
+                                    thisTr = allRows[i];
+                                    
+                                    // Remove each TD
+                                    for(j=aIndexes.length-1; j>-1; j--) {
+                                        tmpTds.push(thisTr.removeChild(thisTr.childNodes[aIndexes[j]]));
+                                    }
+                                    
+                                    // Insert each TD
+                                    nextSibling = thisTr.childNodes[newIndex] || null;
+                                    for(j=tmpTds.length-1; j>-1; j--) {
+                                        thisTr.insertBefore(tmpTds[j], nextSibling);
+                                    }                                    
+                                }
+                                oArg.nCurrentRow = i;
+                            }
+                        },
+                        iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
+                        argument: {nCurrentRow:0, aIndexes:aOrigKeyIndexes},
+                        scope: this,
+                        timeout: (loopN > 0) ? 0 : -1
+                    });
+                    this._runRenderChain();
+                }
+        
+                this.fireEvent("columnReorderEvent",{column:oNewColumn, oldIndex:nOrigTreeIndex});
+                return oNewColumn;
+            }
+        }
+    }
+},
+
+/**
+ * Selects given Column. NOTE: You cannot select/unselect nested Columns. You can only
+ * select/unselect non-nested Columns, and bottom-level key Columns.
+ *
+ * @method selectColumn
+ * @param column {HTMLElement | String | Number} DOM reference or ID string to a
+ * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
+ */
+selectColumn : function(oColumn) {
+    oColumn = this.getColumn(oColumn);
+    if(oColumn && !oColumn.selected) {
+        // Only bottom-level Columns can get hidden
+        if(oColumn.getKeyIndex() !== null) {
+            oColumn.selected = true;
+            
+            // Update head cell
+            var elTh = oColumn.getThEl();
+            Dom.addClass(elTh,DT.CLASS_SELECTED);
+
+            // Update body cells
+            var allRows = this.getTbodyEl().rows;
+            var oChainRender = this._oChainRender;
+            oChainRender.add({
+                method: function(oArg) {
+                    if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
+                        Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED);                    
+                    }
+                    oArg.rowIndex++;
+                },
+                scope: this,
+                iterations: allRows.length,
+                argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
+            });
+
+            this._clearTrTemplateEl();
+            
+            this._elTbody.style.display = "none";
+            this._runRenderChain();
+            this._elTbody.style.display = "";      
+            
+            this.fireEvent("columnSelectEvent",{column:oColumn});
+        }
+        else {
+        }
+    }
+},
+
+/**
+ * Unselects given Column. NOTE: You cannot select/unselect nested Columns. You can only
+ * select/unselect non-nested Columns, and bottom-level key Columns.
+ *
+ * @method unselectColumn
+ * @param column {HTMLElement | String | Number} DOM reference or ID string to a
+ * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
+ */
+unselectColumn : function(oColumn) {
+    oColumn = this.getColumn(oColumn);
+    if(oColumn && oColumn.selected) {
+        // Only bottom-level Columns can get hidden
+        if(oColumn.getKeyIndex() !== null) {
+            oColumn.selected = false;
+            
+            // Update head cell
+            var elTh = oColumn.getThEl();
+            Dom.removeClass(elTh,DT.CLASS_SELECTED);
+
+            // Update body cells
+            var allRows = this.getTbodyEl().rows;
+            var oChainRender = this._oChainRender;
+            oChainRender.add({
+                method: function(oArg) {
+                    if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
+                        Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_SELECTED); 
+                    }                   
+                    oArg.rowIndex++;
+                },
+                scope: this,
+                iterations:allRows.length,
+                argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()}
+            });
+            
+            this._clearTrTemplateEl();
+
+            this._elTbody.style.display = "none";
+            this._runRenderChain();
+            this._elTbody.style.display = "";      
+            
+            this.fireEvent("columnUnselectEvent",{column:oColumn});
+        }
+        else {
+        }
+    }
+},
+
+/**
+ * Returns an array selected Column instances.
+ *
+ * @method getSelectedColumns
+ * @return {YAHOO.widget.Column[]} Array of Column instances.
+ */
+getSelectedColumns : function(oColumn) {
+    var selectedColumns = [];
+    var aKeys = this._oColumnSet.keys;
+    for(var i=0,len=aKeys.length; i<len; i++) {
+        if(aKeys[i].selected) {
+            selectedColumns[selectedColumns.length] = aKeys[i];
+        }
+    }
+    return selectedColumns;
+},
+
+/**
+ * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
+ * NOTE: You cannot highlight/unhighlight nested Columns. You can only
+ * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
+ *
+ * @method highlightColumn
+ * @param column {HTMLElement | String | Number} DOM reference or ID string to a
+ * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
+ */
+highlightColumn : function(column) {
+    var oColumn = this.getColumn(column);
+    // Only bottom-level Columns can get highlighted
+    if(oColumn && (oColumn.getKeyIndex() !== null)) {            
+        // Update head cell
+        var elTh = oColumn.getThEl();
+        Dom.addClass(elTh,DT.CLASS_HIGHLIGHTED);
+
+        // Update body cells
+        var allRows = this.getTbodyEl().rows;
+        var oChainRender = this._oChainRender;
+        oChainRender.add({
+            method: function(oArg) {
+                if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
+                    Dom.addClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);   
+                }                 
+                oArg.rowIndex++;
+            },
+            scope: this,
+            iterations:allRows.length,
+            argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
+            timeout: -1
+        });
+        this._elTbody.style.display = "none";
+        this._runRenderChain();
+        this._elTbody.style.display = "";      
+            
+        this.fireEvent("columnHighlightEvent",{column:oColumn});
+    }
+    else {
+    }
+},
+
+/**
+ * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to cells of the given Column.
+ * NOTE: You cannot highlight/unhighlight nested Columns. You can only
+ * highlight/unhighlight non-nested Columns, and bottom-level key Columns.
+ *
+ * @method unhighlightColumn
+ * @param column {HTMLElement | String | Number} DOM reference or ID string to a
+ * TH/TD element (or child of a TH/TD element), a Column key, or a ColumnSet key index.
+ */
+unhighlightColumn : function(column) {
+    var oColumn = this.getColumn(column);
+    // Only bottom-level Columns can get highlighted
+    if(oColumn && (oColumn.getKeyIndex() !== null)) {
+        // Update head cell
+        var elTh = oColumn.getThEl();
+        Dom.removeClass(elTh,DT.CLASS_HIGHLIGHTED);
+
+        // Update body cells
+        var allRows = this.getTbodyEl().rows;
+        var oChainRender = this._oChainRender;
+        oChainRender.add({
+            method: function(oArg) {
+                if((this instanceof DT) && this._sId && allRows[oArg.rowIndex] && allRows[oArg.rowIndex].cells[oArg.cellIndex]) {
+                    Dom.removeClass(allRows[oArg.rowIndex].cells[oArg.cellIndex],DT.CLASS_HIGHLIGHTED);
+                }                 
+                oArg.rowIndex++;
+            },
+            scope: this,
+            iterations:allRows.length,
+            argument: {rowIndex:0,cellIndex:oColumn.getKeyIndex()},
+            timeout: -1
+        });
+        this._elTbody.style.display = "none";
+        this._runRenderChain();
+        this._elTbody.style.display = "";     
+            
+        this.fireEvent("columnUnhighlightEvent",{column:oColumn});
+    }
+    else {
+    }
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// ROW FUNCTIONS
+
+/**
+ * Adds one new Record of data into the RecordSet at the index if given,
+ * otherwise at the end. If the new Record is in page view, the
+ * corresponding DOM elements are also updated.
+ *
+ * @method addRow
+ * @param oData {Object} Object literal of data for the row.
+ * @param index {Number} (optional) RecordSet position index at which to add data.
+ */
+addRow : function(oData, index) {
+    if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
+        return;
+    }
+
+    if(oData && lang.isObject(oData)) {
+        var oRecord = this._oRecordSet.addRecord(oData, index);
+        if(oRecord) {
+            var recIndex;
+            var oPaginator = this.get('paginator');
+
+            // Paginated
+            if (oPaginator) {     
+                // Update the paginator's totalRecords
+                var totalRecords = oPaginator.get('totalRecords');
+                if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
+                    oPaginator.set('totalRecords',totalRecords + 1);
+                }
+
+                recIndex = this.getRecordIndex(oRecord);
+                var endRecIndex = (oPaginator.getPageRecords())[1];
+
+                // New record affects the view
+                if (recIndex <= endRecIndex) {
+                    // Defer UI updates to the render method
+                    this.render();
+                }
+                
+                this.fireEvent("rowAddEvent", {record:oRecord});
+                return;
+            }
+            // Not paginated
+            else {
+                recIndex = this.getRecordIndex(oRecord);
+                if(lang.isNumber(recIndex)) {
+                    // Add the TR element
+                    this._oChainRender.add({
+                        method: function(oArg) {
+                            if((this instanceof DT) && this._sId) {
+                                var oRecord = oArg.record;
+                                var recIndex = oArg.recIndex;
+                                var elNewTr = this._addTrEl(oRecord);
+                                if(elNewTr) {
+                                    var elNext = (this._elTbody.rows[recIndex]) ? this._elTbody.rows[recIndex] : null;
+                                    this._elTbody.insertBefore(elNewTr, elNext);
+
+                                    // Set FIRST/LAST
+                                    if(recIndex === 0) {
+                                        this._setFirstRow();
+                                    }
+                                    if(elNext === null) {
+                                        this._setLastRow();
+                                    }
+                                    // Set EVEN/ODD
+                                    this._setRowStripes();                           
+                                    
+                                    this.hideTableMessage();
+            
+                                    this.fireEvent("rowAddEvent", {record:oRecord});
+                                }
+                            }
+                        },
+                        argument: {record: oRecord, recIndex: recIndex},
+                        scope: this,
+                        timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
+                    });
+                    this._runRenderChain();
+                    return;
+                }
+            }            
+        }
+    }
+},
+
+/**
+ * Convenience method to add multiple rows.
+ *
+ * @method addRows
+ * @param aData {Object[]} Array of object literal data for the rows.
+ * @param index {Number} (optional) RecordSet position index at which to add data.
+ */
+addRows : function(aData, index) {
+    if(lang.isNumber(index) && (index < 0 || index > this._oRecordSet.getLength())) {
+        return;
+    }
+
+    if(lang.isArray(aData)) {
+        var aRecords = this._oRecordSet.addRecords(aData, index);
+        if(aRecords) {
+            var recIndex = this.getRecordIndex(aRecords[0]);
+            
+            // Paginated
+            var oPaginator = this.get('paginator');
+            if (oPaginator) {
+                // Update the paginator's totalRecords
+                var totalRecords = oPaginator.get('totalRecords');
+                if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
+                    oPaginator.set('totalRecords',totalRecords + aRecords.length);
+                }
+    
+                var endRecIndex = (oPaginator.getPageRecords())[1];
+
+                // At least one of the new records affects the view
+                if (recIndex <= endRecIndex) {
+                    this.render();
+                }
+                
+                this.fireEvent("rowsAddEvent", {records:aRecords});
+                return;
+            }
+            // Not paginated
+            else {
+                // Add the TR elements
+                var loopN = this.get("renderLoopSize");
+                var loopEnd = recIndex + aData.length;
+                var nRowsNeeded = (loopEnd - recIndex); // how many needed
+                var isLast = (recIndex >= this._elTbody.rows.length);
+                this._oChainRender.add({
+                    method: function(oArg) {
+                        if((this instanceof DT) && this._sId) {
+                            var aRecords = oArg.aRecords,
+                                i = oArg.nCurrentRow,
+                                j = oArg.nCurrentRecord,
+                                len = loopN > 0 ? Math.min(i + loopN,loopEnd) : loopEnd,
+                                df = document.createDocumentFragment(),
+                                elNext = (this._elTbody.rows[i]) ? this._elTbody.rows[i] : null;
+                            for(; i < len; i++, j++) {
+                                df.appendChild(this._addTrEl(aRecords[j]));
+                            }
+                            this._elTbody.insertBefore(df, elNext);
+                            oArg.nCurrentRow = i;
+                            oArg.nCurrentRecord = j;
+                        }
+                    },
+                    iterations: (loopN > 0) ? Math.ceil(loopEnd/loopN) : 1,
+                    argument: {nCurrentRow:recIndex,nCurrentRecord:0,aRecords:aRecords},
+                    scope: this,
+                    timeout: (loopN > 0) ? 0 : -1
+                });
+                this._oChainRender.add({
+                    method: function(oArg) {
+                        var recIndex = oArg.recIndex;
+                        // Set FIRST/LAST
+                        if(recIndex === 0) {
+                            this._setFirstRow();
+                        }
+                        if(oArg.isLast) {
+                            this._setLastRow();
+                        }
+                        // Set EVEN/ODD
+                        this._setRowStripes();                           
+
+                        this.fireEvent("rowsAddEvent", {records:aRecords});
+                    },
+                    argument: {recIndex: recIndex, isLast: isLast},
+                    scope: this,
+                    timeout: -1 // Needs to run immediately after the DOM insertions above
+                });
+                this._runRenderChain();
+                this.hideTableMessage();                
+                return;
+            }            
+        }
+    }
+},
+
+/**
+ * For the given row, updates the associated Record with the given data. If the
+ * row is on current page, the corresponding DOM elements are also updated.
+ *
+ * @method updateRow
+ * @param row {YAHOO.widget.Record | Number | HTMLElement | String}
+ * Which row to update: By Record instance, by Record's RecordSet
+ * position index, by HTMLElement reference to the TR element, or by ID string
+ * of the TR element.
+ * @param oData {Object} Object literal of data for the row.
+ */
+updateRow : function(row, oData) {
+    var index = row;
+    if (!lang.isNumber(index)) {
+        index = this.getRecordIndex(row);
+    }
+
+    // Update the Record
+    if(lang.isNumber(index) && (index >= 0)) {
+        var oRecordSet = this._oRecordSet,
+            oldRecord = oRecordSet.getRecord(index);
+
+        if(oldRecord) {
+            var updatedRecord = this._oRecordSet.setRecord(oData, index),
+                elRow = this.getTrEl(oldRecord),
+                // Copy data from the Record for the event that gets fired later
+                oldData = oldRecord ? oldRecord.getData() : null;
+
+            if(updatedRecord) {
+                // Update selected rows as necessary
+                var tracker = this._aSelections || [],
+                i=0,
+                oldId = oldRecord.getId(),
+                newId = updatedRecord.getId();
+                for(; i<tracker.length; i++) {
+                    if((tracker[i] === oldId)) {
+                        tracker[i] = newId;
+                    }
+                    else if(tracker[i].recordId === oldId) {
+                        tracker[i].recordId = newId;
+                    }
+                }
+
+                // Update anchors as necessary
+                if(this._oAnchorRecord && this._oAnchorRecord.getId() === oldId) {
+                    this._oAnchorRecord = updatedRecord;
+                }
+                if(this._oAnchorCell && this._oAnchorCell.record.getId() === oldId) {
+                    this._oAnchorCell.record = updatedRecord;
+                }
+
+                // Update the TR only if row is on current page
+                this._oChainRender.add({
+                    method: function() {
+                        if((this instanceof DT) && this._sId) {
+                            // Paginated
+                            var oPaginator = this.get('paginator');
+                            if (oPaginator) {
+                                var pageStartIndex = (oPaginator.getPageRecords())[0],
+                                    pageLastIndex = (oPaginator.getPageRecords())[1];
+
+                                // At least one of the new records affects the view
+                                if ((index >= pageStartIndex) || (index <= pageLastIndex)) {
+                                    this.render();
+                                }
+                            }
+                            else {
+                                if(elRow) {
+                                    this._updateTrEl(elRow, updatedRecord);
+                                }
+                                else {
+                                    this.getTbodyEl().appendChild(this._addTrEl(updatedRecord));
+                                }
+                            }
+                            this.fireEvent("rowUpdateEvent", {record:updatedRecord, oldData:oldData});
+                        }
+                    },
+                    scope: this,
+                    timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
+                });
+                this._runRenderChain();
+                return;
+            }
+        }
+    }
+    return;
+},
+
+/**
+ * Starting with the given row, updates associated Records with the given data.
+ * The number of rows to update are determined by the array of data provided.
+ * Undefined data (i.e., not an object literal) causes a row to be skipped. If
+ * any of the rows are on current page, the corresponding DOM elements are also
+ * updated.
+ *
+ * @method updateRows
+ * @param startrow {YAHOO.widget.Record | Number | HTMLElement | String}
+ * Starting row to update: By Record instance, by Record's RecordSet
+ * position index, by HTMLElement reference to the TR element, or by ID string
+ * of the TR element.
+ * @param aData {Object[]} Array of object literal of data for the rows.
+ */
+updateRows : function(startrow, aData) {
+    if(lang.isArray(aData)) {
+        var startIndex = startrow,
+            oRecordSet = this._oRecordSet,
+            lastRowIndex = oRecordSet.getLength();
+
+        if (!lang.isNumber(startrow)) {
+            startIndex = this.getRecordIndex(startrow);
+        }
+            
+        if(lang.isNumber(startIndex) && (startIndex >= 0) && (startIndex < oRecordSet.getLength())) {
+            var lastIndex = startIndex + aData.length,
+                aOldRecords = oRecordSet.getRecords(startIndex, aData.length),
+                aNewRecords = oRecordSet.setRecords(aData, startIndex);
+            if(aNewRecords) {
+                var tracker = this._aSelections || [],
+                    i=0, j, newRecord, newId, oldId,
+                    anchorRecord = this._oAnchorRecord ? this._oAnchorRecord.getId() : null,
+                    anchorCell = this._oAnchorCell ? this._oAnchorCell.record.getId() : null;
+                for(; i<aOldRecords.length; i++) {
+                    oldId = aOldRecords[i].getId();
+                    newRecord = aNewRecords[i];
+                    newId = newRecord.getId();
+
+                    // Update selected rows as necessary
+                    for(j=0; j<tracker.length; j++) {
+                        if((tracker[j] === oldId)) {
+                            tracker[j] = newId;
+                        }
+                        else if(tracker[j].recordId === oldId) {
+                            tracker[j].recordId = newId;
+                        }
+                    }
+
+                    // Update anchors as necessary
+                    if(anchorRecord && anchorRecord === oldId) {
+                        this._oAnchorRecord = newRecord;
+                    }
+                    if(anchorCell && anchorCell === oldId) {
+                        this._oAnchorCell.record = newRecord;
+                    }
+               }
+
+                // Paginated
+                var oPaginator = this.get('paginator');
+                if (oPaginator) {
+                    var pageStartIndex = (oPaginator.getPageRecords())[0],
+                        pageLastIndex = (oPaginator.getPageRecords())[1];
+    
+                    // At least one of the new records affects the view
+                    if ((startIndex >= pageStartIndex) || (lastIndex <= pageLastIndex)) {
+                        this.render();
+                    }
+
+                    this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
+                    return;
+                }
+                // Not paginated
+                else {
+                    // Update the TR elements
+                    var loopN = this.get("renderLoopSize"),
+                        rowCount = aData.length, // how many needed
+                        isLast = (lastIndex >= lastRowIndex),
+                        isAdding = (lastIndex > lastRowIndex);
+                                           
+                    this._oChainRender.add({
+                        method: function(oArg) {
+                            if((this instanceof DT) && this._sId) {
+                                var aRecords = oArg.aRecords,
+                                    i = oArg.nCurrentRow,
+                                    j = oArg.nDataPointer,
+                                    len = loopN > 0 ? Math.min(i+loopN, startIndex+aRecords.length) : startIndex+aRecords.length;
+                                    
+                                for(; i < len; i++,j++) {
+                                    if(isAdding && (i>=lastRowIndex)) {
+                                        this._elTbody.appendChild(this._addTrEl(aRecords[j]));
+                                    }
+                                    else {
+                                        this._updateTrEl(this._elTbody.rows[i], aRecords[j]);
+                                    }
+                                }
+                                oArg.nCurrentRow = i;
+                                oArg.nDataPointer = j;
+                            }
+                        },
+                        iterations: (loopN > 0) ? Math.ceil(rowCount/loopN) : 1,
+                        argument: {nCurrentRow:startIndex,aRecords:aNewRecords,nDataPointer:0,isAdding:isAdding},
+                        scope: this,
+                        timeout: (loopN > 0) ? 0 : -1
+                    });
+                    this._oChainRender.add({
+                        method: function(oArg) {
+                            var recIndex = oArg.recIndex;
+                            // Set FIRST/LAST
+                            if(recIndex === 0) {
+                                this._setFirstRow();
+                            }
+                            if(oArg.isLast) {
+                                this._setLastRow();
+                            }
+                            // Set EVEN/ODD
+                            this._setRowStripes();                           
+    
+                            this.fireEvent("rowsAddEvent", {newRecords:aNewRecords, oldRecords:aOldRecords});
+                        },
+                        argument: {recIndex: startIndex, isLast: isLast},
+                        scope: this,
+                        timeout: -1 // Needs to run immediately after the DOM insertions above
+                    });
+                    this._runRenderChain();
+                    this.hideTableMessage();                
+                    return;
+                }            
+            }
+        }
+    }
+},
+
+/**
+ * Deletes the given row's Record from the RecordSet. If the row is on current page,
+ * the corresponding DOM elements are also deleted.
+ *
+ * @method deleteRow
+ * @param row {HTMLElement | String | Number} DOM element reference or ID string
+ * to DataTable page element or RecordSet index.
+ */
+deleteRow : function(row) {
+    var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
+    if(lang.isNumber(nRecordIndex)) {
+        var oRecord = this.getRecord(nRecordIndex);
+        if(oRecord) {
+            var nTrIndex = this.getTrIndex(nRecordIndex);
+            
+            // Remove from selection tracker if there
+            var sRecordId = oRecord.getId();
+            var tracker = this._aSelections || [];
+            for(var j=tracker.length-1; j>-1; j--) {
+                if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
+                        (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
+                    tracker.splice(j,1);
+                }
+            }
+    
+            // Delete Record from RecordSet
+            var oData = this._oRecordSet.deleteRecord(nRecordIndex);
+    
+            // Update the UI
+            if(oData) {
+                // If paginated and the deleted row was on this or a prior page, just
+                // re-render
+                var oPaginator = this.get('paginator');
+                if (oPaginator) {
+                    // Update the paginator's totalRecords
+                    var totalRecords = oPaginator.get('totalRecords'),
+                        // must capture before the totalRecords change because
+                        // Paginator shifts to previous page automatically
+                        rng = oPaginator.getPageRecords();
+
+                    if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
+                        oPaginator.set('totalRecords',totalRecords - 1);
+                    }
+    
+                    // The deleted record was on this or a prior page, re-render
+                    if (!rng || nRecordIndex <= rng[1]) {
+                        this.render();
+                    }
+
+                    this._oChainRender.add({
+                        method: function() {
+                            if((this instanceof DT) && this._sId) {
+                                this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex, oldData:oData, trElIndex:nTrIndex});
+                            }
+                        },
+                        scope: this,
+                        timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
+                    });
+                    this._runRenderChain();
+                }
+                // Not paginated
+                else {
+                    if(lang.isNumber(nTrIndex)) {
+                        this._oChainRender.add({
+                            method: function() {
+                                if((this instanceof DT) && this._sId) {
+                                    var isLast = (nRecordIndex === this._oRecordSet.getLength());//(nTrIndex == this.getLastTrEl().sectionRowIndex);
+                                    this._deleteTrEl(nTrIndex);
+                    
+                                    // Post-delete tasks
+                                    if(this._elTbody.rows.length > 0) {
+                                        // Set FIRST/LAST
+                                        if(nTrIndex === 0) {
+                                            this._setFirstRow();
+                                        }
+                                        if(isLast) {
+                                            this._setLastRow();
+                                        }
+                                        // Set EVEN/ODD
+                                        if(nTrIndex != this._elTbody.rows.length) {
+                                            this._setRowStripes(nTrIndex);
+                                        }                                
+                                    }
+                    
+                                    this.fireEvent("rowDeleteEvent", {recordIndex:nRecordIndex,oldData:oData, trElIndex:nTrIndex});
+                                }
+                            },
+                            scope: this,
+                            timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
+                        });
+                        this._runRenderChain();
+                        return;
+                    }
+                }
+            }
+        }
+    }
+    return null;
+},
+
+/**
+ * Convenience method to delete multiple rows.
+ *
+ * @method deleteRows
+ * @param row {HTMLElement | String | Number} DOM element reference or ID string
+ * to DataTable page element or RecordSet index.
+ * @param count {Number} (optional) How many rows to delete. A negative value
+ * will delete towards the beginning.
+ */
+deleteRows : function(row, count) {
+    var nRecordIndex = (lang.isNumber(row)) ? row : this.getRecordIndex(row);
+    if(lang.isNumber(nRecordIndex)) {
+        var oRecord = this.getRecord(nRecordIndex);
+        if(oRecord) {
+            var nTrIndex = this.getTrIndex(nRecordIndex);
+            
+            // Remove from selection tracker if there
+            var sRecordId = oRecord.getId();
+            var tracker = this._aSelections || [];
+            for(var j=tracker.length-1; j>-1; j--) {
+                if((lang.isString(tracker[j]) && (tracker[j] === sRecordId)) ||
+                        (lang.isObject(tracker[j]) && (tracker[j].recordId === sRecordId))) {
+                    tracker.splice(j,1);
+                }
+            }
+    
+            // Delete Record from RecordSet
+            var highIndex = nRecordIndex;
+            var lowIndex = nRecordIndex;
+        
+            // Validate count and account for negative value
+            if(count && lang.isNumber(count)) {
+                highIndex = (count > 0) ? nRecordIndex + count -1 : nRecordIndex;
+                lowIndex = (count > 0) ? nRecordIndex : nRecordIndex + count + 1;
+                count = (count > 0) ? count : count*-1;
+                if(lowIndex < 0) {
+                    lowIndex = 0;
+                    count = highIndex - lowIndex + 1;
+                }
+            }
+            else {
+                count = 1;
+            }
+            
+            var aData = this._oRecordSet.deleteRecords(lowIndex, count);
+    
+            // Update the UI
+            if(aData) {
+                var oPaginator = this.get('paginator'),
+                    loopN = this.get("renderLoopSize");
+                // If paginated and the deleted row was on this or a prior page, just
+                // re-render
+                if (oPaginator) {
+                    // Update the paginator's totalRecords
+                    var totalRecords = oPaginator.get('totalRecords'),
+                        // must capture before the totalRecords change because
+                        // Paginator shifts to previous page automatically
+                        rng = oPaginator.getPageRecords();
+
+                    if (totalRecords !== widget.Paginator.VALUE_UNLIMITED) {
+                        oPaginator.set('totalRecords',totalRecords - aData.length);
+                    }
+    
+                    // The records were on this or a prior page, re-render
+                    if (!rng || lowIndex <= rng[1]) {
+                        this.render();
+                    }
+
+                    this._oChainRender.add({
+                        method: function(oArg) {
+                            if((this instanceof DT) && this._sId) {
+                                this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
+                            }
+                        },
+                        scope: this,
+                        timeout: (loopN > 0) ? 0 : -1
+                    });
+                    this._runRenderChain();
+                    return;
+                }
+                // Not paginated
+                else {
+                    if(lang.isNumber(nTrIndex)) {
+                        // Delete the TR elements starting with highest index
+                        var loopEnd = lowIndex;
+                        var nRowsNeeded = count; // how many needed
+                        this._oChainRender.add({
+                            method: function(oArg) {
+                                if((this instanceof DT) && this._sId) {
+                                    var i = oArg.nCurrentRow,
+                                        len = (loopN > 0) ? (Math.max(i - loopN,loopEnd)-1) : loopEnd-1;
+                                    for(; i>len; --i) {
+                                        this._deleteTrEl(i);
+                                    }
+                                    oArg.nCurrentRow = i;
+                                }
+                            },
+                            iterations: (loopN > 0) ? Math.ceil(count/loopN) : 1,
+                            argument: {nCurrentRow:highIndex},
+                            scope: this,
+                            timeout: (loopN > 0) ? 0 : -1
+                        });
+                        this._oChainRender.add({
+                            method: function() {    
+                                // Post-delete tasks
+                                if(this._elTbody.rows.length > 0) {
+                                    this._setFirstRow();
+                                    this._setLastRow();
+                                    this._setRowStripes();
+                                }
+                                
+                                this.fireEvent("rowsDeleteEvent", {recordIndex:lowIndex, oldData:aData, count:count});
+                            },
+                            scope: this,
+                            timeout: -1 // Needs to run immediately after the DOM deletions above
+                        });
+                        this._runRenderChain();
+                        return;
+                    }
+                }
+            }
+        }
+    }
+    return null;
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// CELL FUNCTIONS
+
+/**
+ * Outputs markup into the given TD based on given Record.
+ *
+ * @method formatCell
+ * @param elLiner {HTMLElement} The liner DIV element within the TD.
+ * @param oRecord {YAHOO.widget.Record} (Optional) Record instance.
+ * @param oColumn {YAHOO.widget.Column} (Optional) Column instance.
+ */
+formatCell : function(elLiner, oRecord, oColumn) {
+    if(!oRecord) {
+        oRecord = this.getRecord(elLiner);
+    }
+    if(!oColumn) {
+        oColumn = this.getColumn(this.getCellIndex(elLiner.parentNode));
+    }
+
+    if(oRecord && oColumn) {
+        var sField = oColumn.field;
+        var oData = oRecord.getData(sField);
+
+        var fnFormatter = typeof oColumn.formatter === 'function' ?
+                          oColumn.formatter :
+                          DT.Formatter[oColumn.formatter+''] ||
+                          DT.Formatter.defaultFormatter;
+
+        // Apply special formatter
+        if(fnFormatter) {
+            fnFormatter.call(this, elLiner, oRecord, oColumn, oData);
+        }
+        else {
+            elLiner.innerHTML = oData;
+        }
+
+        this.fireEvent("cellFormatEvent", {record:oRecord, column:oColumn, key:oColumn.key, el:elLiner});
+    }
+    else {
+    }
+},
+
+/**
+ * For the given row and column, updates the Record with the given data. If the
+ * cell is on current page, the corresponding DOM elements are also updated.
+ *
+ * @method updateCell
+ * @param oRecord {YAHOO.widget.Record} Record instance.
+ * @param oColumn {YAHOO.widget.Column | String | Number} A Column key, or a ColumnSet key index.
+ * @param oData {Object} New data value for the cell.
+ * @param skipRender {Boolean} Skips render step. Editors that update multiple
+ * cells in ScrollingDataTable should render only on the last call to updateCell().
+ */
+updateCell : function(oRecord, oColumn, oData, skipRender) {
+    // Validate Column and Record
+    oColumn = (oColumn instanceof YAHOO.widget.Column) ? oColumn : this.getColumn(oColumn);
+    if(oColumn && oColumn.getField() && (oRecord instanceof YAHOO.widget.Record)) {
+        var sKey = oColumn.getField(),
+        
+        // Copy data from the Record for the event that gets fired later
+        //var oldData = YAHOO.widget.DataTable._cloneObject(oRecord.getData());
+            oldData = oRecord.getData(sKey);
+
+        // Update Record with new data
+        this._oRecordSet.updateRecordValue(oRecord, sKey, oData);
+    
+        // Update the TD only if row is on current page
+        var elTd = this.getTdEl({record: oRecord, column: oColumn});
+        if(elTd) {
+            this._oChainRender.add({
+                method: function() {
+                    if((this instanceof DT) && this._sId) {
+                        this.formatCell(elTd.firstChild, oRecord, oColumn);
+                        this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
+                    }
+                },
+                scope: this,
+                timeout: (this.get("renderLoopSize") > 0) ? 0 : -1
+            });
+            // Bug 2529024
+            if(!skipRender) {
+                this._runRenderChain();
+            }
+        }
+        else {
+            this.fireEvent("cellUpdateEvent", {record:oRecord, column: oColumn, oldData:oldData});
+        }
+    }
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// PAGINATION
+/**
+ * Method executed during set() operation for the "paginator" attribute.
+ * Adds and/or severs event listeners between DataTable and Paginator
+ *
+ * @method _updatePaginator
+ * @param newPag {Paginator} Paginator instance (or null) for DataTable to use
+ * @private
+ */
+_updatePaginator : function (newPag) {
+    var oldPag = this.get('paginator');
+    if (oldPag && newPag !== oldPag) {
+        oldPag.unsubscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
+    }
+    if (newPag) {
+        newPag.subscribe('changeRequest', this.onPaginatorChangeRequest, this, true);
+    }
+},
+
+/**
+ * Update the UI infrastructure in response to a "paginator" attribute change.
+ *
+ * @method _handlePaginatorChange
+ * @param e {Object} Change event object containing keys 'type','newValue',
+ *                   and 'prevValue'
+ * @private
+ */
+_handlePaginatorChange : function (e) {
+    if (e.prevValue === e.newValue) { return; }
+
+    var newPag     = e.newValue,
+        oldPag     = e.prevValue,
+        containers = this._defaultPaginatorContainers();
+
+    if (oldPag) {
+        if (oldPag.getContainerNodes()[0] == containers[0]) {
+            oldPag.set('containers',[]);
+        }
+        oldPag.destroy();
+
+        // Convenience: share the default containers if possible.
+        // Otherwise, remove the default containers from the DOM.
+        if (containers[0]) {
+            if (newPag && !newPag.getContainerNodes().length) {
+                newPag.set('containers',containers);
+            } else {
+                // No new Paginator to use existing containers, OR new
+                // Paginator has configured containers.
+                for (var i = containers.length - 1; i >= 0; --i) {
+                    if (containers[i]) {
+                        containers[i].parentNode.removeChild(containers[i]);
+                    }
+                }
+            }
+        }
+    }
+
+    if (!this._bInit) {
+        this.render();
+
+    }
+
+    if (newPag) {
+        this.renderPaginator();
+    }
+
+},
+
+/**
+ * Returns the default containers used for Paginators.  If create param is
+ * passed, the containers will be created and added to the DataTable container.
+ *
+ * @method _defaultPaginatorContainers
+ * @param create {boolean} Create the default containers if not found
+ * @private
+ */
+_defaultPaginatorContainers : function (create) {
+    var above_id = this._sId + '-paginator0',
+        below_id = this._sId + '-paginator1',
+        above    = Dom.get(above_id),
+        below    = Dom.get(below_id);
+
+    if (create && (!above || !below)) {
+        // One above and one below the table
+        if (!above) {
+            above    = document.createElement('div');
+            above.id = above_id;
+            Dom.addClass(above, DT.CLASS_PAGINATOR);
+
+            this._elContainer.insertBefore(above,this._elContainer.firstChild);
+        }
+
+        if (!below) {
+            below    = document.createElement('div');
+            below.id = below_id;
+            Dom.addClass(below, DT.CLASS_PAGINATOR);
+
+            this._elContainer.appendChild(below);
+        }
+    }
+
+    return [above,below];
+},
+
+/**
+ * Calls Paginator's destroy() method
+ *
+ * @method _destroyPaginator
+ * @private
+ */
+_destroyPaginator : function () {
+    var oldPag = this.get('paginator');
+    if (oldPag) {
+        oldPag.destroy();
+    }
+},
+
+/**
+ * Renders the Paginator to the DataTable UI
+ *
+ * @method renderPaginator
+ */
+renderPaginator : function () {
+    var pag = this.get("paginator");
+    if (!pag) { return; }
+
+    // Add the containers if the Paginator is not configured with containers
+    if (!pag.getContainerNodes().length) {
+        pag.set('containers',this._defaultPaginatorContainers(true));
+    }
+
+    pag.render();
+},
+
+/**
+ * Overridable method gives implementers a hook to show loading message before
+ * changing Paginator value.
+ *
+ * @method doBeforePaginatorChange
+ * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
+ * @return {Boolean} Return true to continue changing Paginator value.
+ */
+doBeforePaginatorChange : function(oPaginatorState) {
+    this.showTableMessage(this.get("MSG_LOADING"), DT.CLASS_LOADING);
+    return true;
+},
+
+/**
+ * Responds to new Pagination states. By default, updates the UI to reflect the
+ * new state. If "dynamicData" is true, current selections are purged before
+ * a request is sent to the DataSource for data for the new state (using the
+ * request returned by "generateRequest()").
+ *  
+ * @method onPaginatorChangeRequest
+ * @param oPaginatorState {Object} An object literal describing the proposed pagination state.
+ */
+onPaginatorChangeRequest : function (oPaginatorState) {
+    var ok = this.doBeforePaginatorChange(oPaginatorState);
+    if(ok) {
+        // Server-side pagination
+        if(this.get("dynamicData")) {
+            // Get the current state
+            var oState = this.getState();
+            
+            // Update pagination values
+            oState.pagination = oPaginatorState;
+    
+            // Get the request for the new state
+            var request = this.get("generateRequest")(oState, this);
+            
+            // Purge selections
+            this.unselectAllRows();
+            this.unselectAllCells();
+            
+            // Get the new data from the server
+            var callback = {
+                success : this.onDataReturnSetRows,
+                failure : this.onDataReturnSetRows,
+                argument : oState, // Pass along the new state to the callback
+                scope : this
+            };
+            this._oDataSource.sendRequest(request, callback);
+        }
+        // Client-side pagination
+        else {
+            // Set the core pagination values silently (the second param)
+            // to avoid looping back through the changeRequest mechanism
+            oPaginatorState.paginator.setStartIndex(oPaginatorState.recordOffset,true);
+            oPaginatorState.paginator.setRowsPerPage(oPaginatorState.rowsPerPage,true);
+    
+            // Update the UI
+            this.render();
+        }
+    }
+    else {
+    }
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// SELECTION/HIGHLIGHTING
+
+/*
+ * Reference to last highlighted cell element
+ *
+ * @property _elLastHighlightedTd
+ * @type HTMLElement
+ * @private
+ */
+_elLastHighlightedTd : null,
+
+/*
+ * ID string of last highlighted row element
+ *
+ * @property _sLastHighlightedTrElId
+ * @type String
+ * @private
+ */
+//_sLastHighlightedTrElId : null,
+
+/**
+ * Array to track row selections (by sRecordId) and/or cell selections
+ * (by {recordId:sRecordId, columnKey:sColumnKey})
+ *
+ * @property _aSelections
+ * @type Object[]
+ * @private
+ */
+_aSelections : null,
+
+/**
+ * Record instance of the row selection anchor.
+ *
+ * @property _oAnchorRecord
+ * @type YAHOO.widget.Record
+ * @private
+ */
+_oAnchorRecord : null,
+
+/**
+ * Object literal representing cell selection anchor:
+ * {recordId:sRecordId, columnKey:sColumnKey}.
+ *
+ * @property _oAnchorCell
+ * @type Object
+ * @private
+ */
+_oAnchorCell : null,
+
+/**
+ * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
+ * from all TR elements on the page.
+ *
+ * @method _unselectAllTrEls
+ * @private
+ */
+_unselectAllTrEls : function() {
+    var selectedRows = Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
+    Dom.removeClass(selectedRows, DT.CLASS_SELECTED);
+},
+
+/**
+ * Returns object literal of values that represent the selection trigger. Used
+ * to determine selection behavior resulting from a key event.
+ *
+ * @method _getSelectionTrigger
+ * @private
+ */
+_getSelectionTrigger : function() {
+    var sMode = this.get("selectionMode");
+    var oTrigger = {};
+    var oTriggerCell, oTriggerRecord, nTriggerRecordIndex, elTriggerRow, nTriggerTrIndex;
+
+    // Cell mode
+    if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
+        oTriggerCell = this.getLastSelectedCell();
+        // No selected cells found
+        if(!oTriggerCell) {
+            return null;
+        }
+        else {
+            oTriggerRecord = this.getRecord(oTriggerCell.recordId);
+            nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
+            elTriggerRow = this.getTrEl(oTriggerRecord);
+            nTriggerTrIndex = this.getTrIndex(elTriggerRow);
+
+            // Selected cell not found on this page
+            if(nTriggerTrIndex === null) {
+                return null;
+            }
+            else {
+                oTrigger.record = oTriggerRecord;
+                oTrigger.recordIndex = nTriggerRecordIndex;
+                oTrigger.el = this.getTdEl(oTriggerCell);
+                oTrigger.trIndex = nTriggerTrIndex;
+                oTrigger.column = this.getColumn(oTriggerCell.columnKey);
+                oTrigger.colKeyIndex = oTrigger.column.getKeyIndex();
+                oTrigger.cell = oTriggerCell;
+                return oTrigger;
+            }
+        }
+    }
+    // Row mode
+    else {
+        oTriggerRecord = this.getLastSelectedRecord();
+        // No selected rows found
+        if(!oTriggerRecord) {
+                return null;
+        }
+        else {
+            // Selected row found, but is it on current page?
+            oTriggerRecord = this.getRecord(oTriggerRecord);
+            nTriggerRecordIndex = this.getRecordIndex(oTriggerRecord);
+            elTriggerRow = this.getTrEl(oTriggerRecord);
+            nTriggerTrIndex = this.getTrIndex(elTriggerRow);
+
+            // Selected row not found on this page
+            if(nTriggerTrIndex === null) {
+                return null;
+            }
+            else {
+                oTrigger.record = oTriggerRecord;
+                oTrigger.recordIndex = nTriggerRecordIndex;
+                oTrigger.el = elTriggerRow;
+                oTrigger.trIndex = nTriggerTrIndex;
+                return oTrigger;
+            }
+        }
+    }
+},
+
+/**
+ * Returns object literal of values that represent the selection anchor. Used
+ * to determine selection behavior resulting from a user event.
+ *
+ * @method _getSelectionAnchor
+ * @param oTrigger {Object} (Optional) Object literal of selection trigger values
+ * (for key events).
+ * @private
+ */
+_getSelectionAnchor : function(oTrigger) {
+    var sMode = this.get("selectionMode");
+    var oAnchor = {};
+    var oAnchorRecord, nAnchorRecordIndex, nAnchorTrIndex;
+
+    // Cell mode
+    if((sMode == "cellblock") || (sMode == "cellrange") || (sMode == "singlecell")) {
+        // Validate anchor cell
+        var oAnchorCell = this._oAnchorCell;
+        if(!oAnchorCell) {
+            if(oTrigger) {
+                oAnchorCell = this._oAnchorCell = oTrigger.cell;
+            }
+            else {
+                return null;
+            }
+        }
+        oAnchorRecord = this._oAnchorCell.record;
+        nAnchorRecordIndex = this._oRecordSet.getRecordIndex(oAnchorRecord);
+        nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
+        // If anchor cell is not on this page...
+        if(nAnchorTrIndex === null) {
+            // ...set TR index equal to top TR
+            if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
+                nAnchorTrIndex = 0;
+            }
+            // ...set TR index equal to bottom TR
+            else {
+                nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
+            }
+        }
+
+        oAnchor.record = oAnchorRecord;
+        oAnchor.recordIndex = nAnchorRecordIndex;
+        oAnchor.trIndex = nAnchorTrIndex;
+        oAnchor.column = this._oAnchorCell.column;
+        oAnchor.colKeyIndex = oAnchor.column.getKeyIndex();
+        oAnchor.cell = oAnchorCell;
+        return oAnchor;
+    }
+    // Row mode
+    else {
+        oAnchorRecord = this._oAnchorRecord;
+        if(!oAnchorRecord) {
+            if(oTrigger) {
+                oAnchorRecord = this._oAnchorRecord = oTrigger.record;
+            }
+            else {
+                return null;
+            }
+        }
+
+        nAnchorRecordIndex = this.getRecordIndex(oAnchorRecord);
+        nAnchorTrIndex = this.getTrIndex(oAnchorRecord);
+        // If anchor row is not on this page...
+        if(nAnchorTrIndex === null) {
+            // ...set TR index equal to top TR
+            if(nAnchorRecordIndex < this.getRecordIndex(this.getFirstTrEl())) {
+                nAnchorTrIndex = 0;
+            }
+            // ...set TR index equal to bottom TR
+            else {
+                nAnchorTrIndex = this.getRecordIndex(this.getLastTrEl());
+            }
+        }
+
+        oAnchor.record = oAnchorRecord;
+        oAnchor.recordIndex = nAnchorRecordIndex;
+        oAnchor.trIndex = nAnchorTrIndex;
+        return oAnchor;
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a mouse event when selection mode
+ * is set to "standard".
+ *
+ * @method _handleStandardSelectionByMouse
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ * @private
+ */
+_handleStandardSelectionByMouse : function(oArgs) {
+    var elTarget = oArgs.target;
+
+    // Validate target row
+    var elTargetRow = this.getTrEl(elTarget);
+    if(elTargetRow) {
+        var e = oArgs.event;
+        var bSHIFT = e.shiftKey;
+        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
+
+        var oTargetRecord = this.getRecord(elTargetRow);
+        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
+
+        var oAnchor = this._getSelectionAnchor();
+
+        var i;
+
+        // Both SHIFT and CTRL
+        if(bSHIFT && bCTRL) {
+            // Validate anchor
+            if(oAnchor) {
+                if(this.isSelected(oAnchor.record)) {
+                    // Select all rows between anchor row and target row, including target row
+                    if(oAnchor.recordIndex < nTargetRecordIndex) {
+                        for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex; i++) {
+                            if(!this.isSelected(i)) {
+                                this.selectRow(i);
+                            }
+                        }
+                    }
+                    // Select all rows between target row and anchor row, including target row
+                    else {
+                        for(i=oAnchor.recordIndex-1; i>=nTargetRecordIndex; i--) {
+                            if(!this.isSelected(i)) {
+                                this.selectRow(i);
+                            }
+                        }
+                    }
+                }
+                else {
+                    // Unselect all rows between anchor row and target row
+                    if(oAnchor.recordIndex < nTargetRecordIndex) {
+                        for(i=oAnchor.recordIndex+1; i<=nTargetRecordIndex-1; i++) {
+                            if(this.isSelected(i)) {
+                                this.unselectRow(i);
+                            }
+                        }
+                    }
+                    // Unselect all rows between target row and anchor row
+                    else {
+                        for(i=nTargetRecordIndex+1; i<=oAnchor.recordIndex-1; i++) {
+                            if(this.isSelected(i)) {
+                                this.unselectRow(i);
+                            }
+                        }
+                    }
+                    // Select the target row
+                    this.selectRow(oTargetRecord);
+                }
+            }
+            // Invalid anchor
+            else {
+                // Set anchor
+                this._oAnchorRecord = oTargetRecord;
+
+                // Toggle selection of target
+                if(this.isSelected(oTargetRecord)) {
+                    this.unselectRow(oTargetRecord);
+                }
+                else {
+                    this.selectRow(oTargetRecord);
+                }
+            }
+        }
+         // Only SHIFT
+        else if(bSHIFT) {
+            this.unselectAllRows();
+
+            // Validate anchor
+            if(oAnchor) {
+                // Select all rows between anchor row and target row,
+                // including the anchor row and target row
+                if(oAnchor.recordIndex < nTargetRecordIndex) {
+                    for(i=oAnchor.recordIndex; i<=nTargetRecordIndex; i++) {
+                        this.selectRow(i);
+                    }
+                }
+                // Select all rows between target row and anchor row,
+                // including the target row and anchor row
+                else {
+                    for(i=oAnchor.recordIndex; i>=nTargetRecordIndex; i--) {
+                        this.selectRow(i);
+                    }
+                }
+            }
+            // Invalid anchor
+            else {
+                // Set anchor
+                this._oAnchorRecord = oTargetRecord;
+
+                // Select target row only
+                this.selectRow(oTargetRecord);
+            }
+        }
+        // Only CTRL
+        else if(bCTRL) {
+            // Set anchor
+            this._oAnchorRecord = oTargetRecord;
+
+            // Toggle selection of target
+            if(this.isSelected(oTargetRecord)) {
+                this.unselectRow(oTargetRecord);
+            }
+            else {
+                this.selectRow(oTargetRecord);
+            }
+        }
+        // Neither SHIFT nor CTRL
+        else {
+            this._handleSingleSelectionByMouse(oArgs);
+            return;
+        }
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a key event when selection mode
+ * is set to "standard".
+ *
+ * @method _handleStandardSelectionByKey
+ * @param e {HTMLEvent} Event object.
+ * @private
+ */
+_handleStandardSelectionByKey : function(e) {
+    var nKey = Ev.getCharCode(e);
+
+    if((nKey == 38) || (nKey == 40)) {
+        var bSHIFT = e.shiftKey;
+
+        // Validate trigger
+        var oTrigger = this._getSelectionTrigger();
+        // Arrow selection only works if last selected row is on current page
+        if(!oTrigger) {
+            return null;
+        }
+
+        Ev.stopEvent(e);
+
+        // Validate anchor
+        var oAnchor = this._getSelectionAnchor(oTrigger);
+
+        // Determine which direction we're going to
+        if(bSHIFT) {
+            // Selecting down away from anchor row
+            if((nKey == 40) && (oAnchor.recordIndex <= oTrigger.trIndex)) {
+                this.selectRow(this.getNextTrEl(oTrigger.el));
+            }
+            // Selecting up away from anchor row
+            else if((nKey == 38) && (oAnchor.recordIndex >= oTrigger.trIndex)) {
+                this.selectRow(this.getPreviousTrEl(oTrigger.el));
+            }
+            // Unselect trigger
+            else {
+                this.unselectRow(oTrigger.el);
+            }
+        }
+        else {
+            this._handleSingleSelectionByKey(e);
+        }
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a mouse event when selection mode
+ * is set to "single".
+ *
+ * @method _handleSingleSelectionByMouse
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ * @private
+ */
+_handleSingleSelectionByMouse : function(oArgs) {
+    var elTarget = oArgs.target;
+
+    // Validate target row
+    var elTargetRow = this.getTrEl(elTarget);
+    if(elTargetRow) {
+        var oTargetRecord = this.getRecord(elTargetRow);
+
+        // Set anchor
+        this._oAnchorRecord = oTargetRecord;
+
+        // Select only target
+        this.unselectAllRows();
+        this.selectRow(oTargetRecord);
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a key event when selection mode
+ * is set to "single".
+ *
+ * @method _handleSingleSelectionByKey
+ * @param e {HTMLEvent} Event object.
+ * @private
+ */
+_handleSingleSelectionByKey : function(e) {
+    var nKey = Ev.getCharCode(e);
+
+    if((nKey == 38) || (nKey == 40)) {
+        // Validate trigger
+        var oTrigger = this._getSelectionTrigger();
+        // Arrow selection only works if last selected row is on current page
+        if(!oTrigger) {
+            return null;
+        }
+
+        Ev.stopEvent(e);
+
+        // Determine the new row to select
+        var elNew;
+        if(nKey == 38) { // arrow up
+            elNew = this.getPreviousTrEl(oTrigger.el);
+
+            // Validate new row
+            if(elNew === null) {
+                //TODO: wrap around to last tr on current page
+                //elNew = this.getLastTrEl();
+
+                //TODO: wrap back to last tr of previous page
+
+                // Top row selection is sticky
+                elNew = this.getFirstTrEl();
+            }
+        }
+        else if(nKey == 40) { // arrow down
+            elNew = this.getNextTrEl(oTrigger.el);
+
+            // Validate new row
+            if(elNew === null) {
+                //TODO: wrap around to first tr on current page
+                //elNew = this.getFirstTrEl();
+
+                //TODO: wrap forward to first tr of previous page
+
+                // Bottom row selection is sticky
+                elNew = this.getLastTrEl();
+            }
+        }
+
+        // Unselect all rows
+        this.unselectAllRows();
+
+        // Select the new row
+        this.selectRow(elNew);
+
+        // Set new anchor
+        this._oAnchorRecord = this.getRecord(elNew);
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a mouse event when selection mode
+ * is set to "cellblock".
+ *
+ * @method _handleCellBlockSelectionByMouse
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ * @private
+ */
+_handleCellBlockSelectionByMouse : function(oArgs) {
+    var elTarget = oArgs.target;
+
+    // Validate target cell
+    var elTargetCell = this.getTdEl(elTarget);
+    if(elTargetCell) {
+        var e = oArgs.event;
+        var bSHIFT = e.shiftKey;
+        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
+
+        var elTargetRow = this.getTrEl(elTargetCell);
+        var nTargetTrIndex = this.getTrIndex(elTargetRow);
+        var oTargetColumn = this.getColumn(elTargetCell);
+        var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
+        var oTargetRecord = this.getRecord(elTargetRow);
+        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
+        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
+
+        var oAnchor = this._getSelectionAnchor();
+
+        var allRows = this.getTbodyEl().rows;
+        var startIndex, endIndex, currentRow, i, j;
+
+        // Both SHIFT and CTRL
+        if(bSHIFT && bCTRL) {
+
+            // Validate anchor
+            if(oAnchor) {
+                // Anchor is selected
+                if(this.isSelected(oAnchor.cell)) {
+                    // All cells are on the same row
+                    if(oAnchor.recordIndex === nTargetRecordIndex) {
+                        // Select all cells between anchor cell and target cell, including target cell
+                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
+                            for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
+                                this.selectCell(elTargetRow.cells[i]);
+                            }
+                        }
+                        // Select all cells between target cell and anchor cell, including target cell
+                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
+                            for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
+                                this.selectCell(elTargetRow.cells[i]);
+                            }
+                        }
+                    }
+                    // Anchor row is above target row
+                    else if(oAnchor.recordIndex < nTargetRecordIndex) {
+                        startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
+                        endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
+
+                        // Select all cells from startIndex to endIndex on rows between anchor row and target row
+                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
+                            for(j=startIndex; j<=endIndex; j++) {
+                                this.selectCell(allRows[i].cells[j]);
+                            }
+                        }
+                    }
+                    // Anchor row is below target row
+                    else {
+                        startIndex = Math.min(oAnchor.trIndex, nTargetColKeyIndex);
+                        endIndex = Math.max(oAnchor.trIndex, nTargetColKeyIndex);
+
+                        // Select all cells from startIndex to endIndex on rows between target row and anchor row
+                        for(i=oAnchor.trIndex; i>=nTargetTrIndex; i--) {
+                            for(j=endIndex; j>=startIndex; j--) {
+                                this.selectCell(allRows[i].cells[j]);
+                            }
+                        }
+                    }
+                }
+                // Anchor cell is unselected
+                else {
+                    // All cells are on the same row
+                    if(oAnchor.recordIndex === nTargetRecordIndex) {
+                        // Unselect all cells between anchor cell and target cell
+                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
+                            for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
+                                this.unselectCell(elTargetRow.cells[i]);
+                            }
+                        }
+                        // Select all cells between target cell and anchor cell
+                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
+                            for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
+                                this.unselectCell(elTargetRow.cells[i]);
+                            }
+                        }
+                    }
+                    // Anchor row is above target row
+                    if(oAnchor.recordIndex < nTargetRecordIndex) {
+                        // Unselect all cells from anchor cell to target cell
+                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
+                            currentRow = allRows[i];
+                            for(j=0; j<currentRow.cells.length; j++) {
+                                // This is the anchor row, only unselect cells after the anchor cell
+                                if(currentRow.sectionRowIndex === oAnchor.trIndex) {
+                                    if(j>oAnchor.colKeyIndex) {
+                                        this.unselectCell(currentRow.cells[j]);
+                                    }
+                                }
+                                // This is the target row, only unelect cells before the target cell
+                                else if(currentRow.sectionRowIndex === nTargetTrIndex) {
+                                    if(j<nTargetColKeyIndex) {
+                                        this.unselectCell(currentRow.cells[j]);
+                                    }
+                                }
+                                // Unselect all cells on this row
+                                else {
+                                    this.unselectCell(currentRow.cells[j]);
+                                }
+                            }
+                        }
+                    }
+                    // Anchor row is below target row
+                    else {
+                        // Unselect all cells from target cell to anchor cell
+                        for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
+                            currentRow = allRows[i];
+                            for(j=0; j<currentRow.cells.length; j++) {
+                                // This is the target row, only unselect cells after the target cell
+                                if(currentRow.sectionRowIndex == nTargetTrIndex) {
+                                    if(j>nTargetColKeyIndex) {
+                                        this.unselectCell(currentRow.cells[j]);
+                                    }
+                                }
+                                // This is the anchor row, only unselect cells before the anchor cell
+                                else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
+                                    if(j<oAnchor.colKeyIndex) {
+                                        this.unselectCell(currentRow.cells[j]);
+                                    }
+                                }
+                                // Unselect all cells on this row
+                                else {
+                                    this.unselectCell(currentRow.cells[j]);
+                                }
+                            }
+                        }
+                    }
+
+                    // Select the target cell
+                    this.selectCell(elTargetCell);
+                }
+            }
+            // Invalid anchor
+            else {
+                // Set anchor
+                this._oAnchorCell = oTargetCell;
+
+                // Toggle selection of target
+                if(this.isSelected(oTargetCell)) {
+                    this.unselectCell(oTargetCell);
+                }
+                else {
+                    this.selectCell(oTargetCell);
+                }
+            }
+
+        }
+         // Only SHIFT
+        else if(bSHIFT) {
+            this.unselectAllCells();
+
+            // Validate anchor
+            if(oAnchor) {
+                // All cells are on the same row
+                if(oAnchor.recordIndex === nTargetRecordIndex) {
+                    // Select all cells between anchor cell and target cell,
+                    // including the anchor cell and target cell
+                    if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
+                        for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
+                            this.selectCell(elTargetRow.cells[i]);
+                        }
+                    }
+                    // Select all cells between target cell and anchor cell
+                    // including the target cell and anchor cell
+                    else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
+                        for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
+                            this.selectCell(elTargetRow.cells[i]);
+                        }
+                    }
+                }
+                // Anchor row is above target row
+                else if(oAnchor.recordIndex < nTargetRecordIndex) {
+                    // Select the cellblock from anchor cell to target cell
+                    // including the anchor cell and the target cell
+                    startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
+                    endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
+
+                    for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
+                        for(j=startIndex; j<=endIndex; j++) {
+                            this.selectCell(allRows[i].cells[j]);
+                        }
+                    }
+                }
+                // Anchor row is below target row
+                else {
+                    // Select the cellblock from target cell to anchor cell
+                    // including the target cell and the anchor cell
+                    startIndex = Math.min(oAnchor.colKeyIndex, nTargetColKeyIndex);
+                    endIndex = Math.max(oAnchor.colKeyIndex, nTargetColKeyIndex);
+
+                    for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
+                        for(j=startIndex; j<=endIndex; j++) {
+                            this.selectCell(allRows[i].cells[j]);
+                        }
+                    }
+                }
+            }
+            // Invalid anchor
+            else {
+                // Set anchor
+                this._oAnchorCell = oTargetCell;
+
+                // Select target only
+                this.selectCell(oTargetCell);
+            }
+        }
+        // Only CTRL
+        else if(bCTRL) {
+
+            // Set anchor
+            this._oAnchorCell = oTargetCell;
+
+            // Toggle selection of target
+            if(this.isSelected(oTargetCell)) {
+                this.unselectCell(oTargetCell);
+            }
+            else {
+                this.selectCell(oTargetCell);
+            }
+
+        }
+        // Neither SHIFT nor CTRL
+        else {
+            this._handleSingleCellSelectionByMouse(oArgs);
+        }
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a key event when selection mode
+ * is set to "cellblock".
+ *
+ * @method _handleCellBlockSelectionByKey
+ * @param e {HTMLEvent} Event object.
+ * @private
+ */
+_handleCellBlockSelectionByKey : function(e) {
+    var nKey = Ev.getCharCode(e);
+    var bSHIFT = e.shiftKey;
+    if((nKey == 9) || !bSHIFT) {
+        this._handleSingleCellSelectionByKey(e);
+        return;
+    }
+
+    if((nKey > 36) && (nKey < 41)) {
+        // Validate trigger
+        var oTrigger = this._getSelectionTrigger();
+        // Arrow selection only works if last selected row is on current page
+        if(!oTrigger) {
+            return null;
+        }
+
+        Ev.stopEvent(e);
+
+        // Validate anchor
+        var oAnchor = this._getSelectionAnchor(oTrigger);
+
+        var i, startIndex, endIndex, elNew, elNewRow;
+        var allRows = this.getTbodyEl().rows;
+        var elThisRow = oTrigger.el.parentNode;
+
+        // Determine which direction we're going to
+
+        if(nKey == 40) { // arrow down
+            // Selecting away from anchor cell
+            if(oAnchor.recordIndex <= oTrigger.recordIndex) {
+                // Select the horiz block on the next row...
+                // ...making sure there is room below the trigger row
+                elNewRow = this.getNextTrEl(oTrigger.el);
+                if(elNewRow) {
+                    startIndex = oAnchor.colKeyIndex;
+                    endIndex = oTrigger.colKeyIndex;
+                    // ...going left
+                    if(startIndex > endIndex) {
+                        for(i=startIndex; i>=endIndex; i--) {
+                            elNew = elNewRow.cells[i];
+                            this.selectCell(elNew);
+                        }
+                    }
+                    // ... going right
+                    else {
+                        for(i=startIndex; i<=endIndex; i++) {
+                            elNew = elNewRow.cells[i];
+                            this.selectCell(elNew);
+                        }
+                    }
+                }
+            }
+            // Unselecting towards anchor cell
+            else {
+                startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
+                endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
+                // Unselect the horiz block on this row towards the next row
+                for(i=startIndex; i<=endIndex; i++) {
+                    this.unselectCell(elThisRow.cells[i]);
+                }
+            }
+        }
+        // Arrow up
+        else if(nKey == 38) {
+            // Selecting away from anchor cell
+            if(oAnchor.recordIndex >= oTrigger.recordIndex) {
+                // Select the horiz block on the previous row...
+                // ...making sure there is room
+                elNewRow = this.getPreviousTrEl(oTrigger.el);
+                if(elNewRow) {
+                    // Select in order from anchor to trigger...
+                    startIndex = oAnchor.colKeyIndex;
+                    endIndex = oTrigger.colKeyIndex;
+                    // ...going left
+                    if(startIndex > endIndex) {
+                        for(i=startIndex; i>=endIndex; i--) {
+                            elNew = elNewRow.cells[i];
+                            this.selectCell(elNew);
+                        }
+                    }
+                    // ... going right
+                    else {
+                        for(i=startIndex; i<=endIndex; i++) {
+                            elNew = elNewRow.cells[i];
+                            this.selectCell(elNew);
+                        }
+                    }
+                }
+            }
+            // Unselecting towards anchor cell
+            else {
+                startIndex = Math.min(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
+                endIndex = Math.max(oAnchor.colKeyIndex, oTrigger.colKeyIndex);
+                // Unselect the horiz block on this row towards the previous row
+                for(i=startIndex; i<=endIndex; i++) {
+                    this.unselectCell(elThisRow.cells[i]);
+                }
+            }
+        }
+        // Arrow right
+        else if(nKey == 39) {
+            // Selecting away from anchor cell
+            if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
+                // Select the next vert block to the right...
+                // ...making sure there is room
+                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
+                    // Select in order from anchor to trigger...
+                    startIndex = oAnchor.trIndex;
+                    endIndex = oTrigger.trIndex;
+                    // ...going up
+                    if(startIndex > endIndex) {
+                        for(i=startIndex; i>=endIndex; i--) {
+                            elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
+                            this.selectCell(elNew);
+                        }
+                    }
+                    // ... going down
+                    else {
+                        for(i=startIndex; i<=endIndex; i++) {
+                            elNew = allRows[i].cells[oTrigger.colKeyIndex+1];
+                            this.selectCell(elNew);
+                        }
+                    }
+                }
+            }
+            // Unselecting towards anchor cell
+            else {
+                // Unselect the vert block on this column towards the right
+                startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
+                endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
+                for(i=startIndex; i<=endIndex; i++) {
+                    this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
+                }
+            }
+        }
+        // Arrow left
+        else if(nKey == 37) {
+            // Selecting away from anchor cell
+            if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
+                //Select the previous vert block to the left
+                if(oTrigger.colKeyIndex > 0) {
+                    // Select in order from anchor to trigger...
+                    startIndex = oAnchor.trIndex;
+                    endIndex = oTrigger.trIndex;
+                    // ...going up
+                    if(startIndex > endIndex) {
+                        for(i=startIndex; i>=endIndex; i--) {
+                            elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
+                            this.selectCell(elNew);
+                        }
+                    }
+                    // ... going down
+                    else {
+                        for(i=startIndex; i<=endIndex; i++) {
+                            elNew = allRows[i].cells[oTrigger.colKeyIndex-1];
+                            this.selectCell(elNew);
+                        }
+                    }
+                }
+            }
+            // Unselecting towards anchor cell
+            else {
+                // Unselect the vert block on this column towards the left
+                startIndex = Math.min(oAnchor.trIndex, oTrigger.trIndex);
+                endIndex = Math.max(oAnchor.trIndex, oTrigger.trIndex);
+                for(i=startIndex; i<=endIndex; i++) {
+                    this.unselectCell(allRows[i].cells[oTrigger.colKeyIndex]);
+                }
+            }
+        }
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a mouse event when selection mode
+ * is set to "cellrange".
+ *
+ * @method _handleCellRangeSelectionByMouse
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ * @private
+ */
+_handleCellRangeSelectionByMouse : function(oArgs) {
+    var elTarget = oArgs.target;
+
+    // Validate target cell
+    var elTargetCell = this.getTdEl(elTarget);
+    if(elTargetCell) {
+        var e = oArgs.event;
+        var bSHIFT = e.shiftKey;
+        var bCTRL = e.ctrlKey || ((navigator.userAgent.toLowerCase().indexOf("mac") != -1) && e.metaKey);
+
+        var elTargetRow = this.getTrEl(elTargetCell);
+        var nTargetTrIndex = this.getTrIndex(elTargetRow);
+        var oTargetColumn = this.getColumn(elTargetCell);
+        var nTargetColKeyIndex = oTargetColumn.getKeyIndex();
+        var oTargetRecord = this.getRecord(elTargetRow);
+        var nTargetRecordIndex = this._oRecordSet.getRecordIndex(oTargetRecord);
+        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
+
+        var oAnchor = this._getSelectionAnchor();
+
+        var allRows = this.getTbodyEl().rows;
+        var currentRow, i, j;
+
+        // Both SHIFT and CTRL
+        if(bSHIFT && bCTRL) {
+
+            // Validate anchor
+            if(oAnchor) {
+                // Anchor is selected
+                if(this.isSelected(oAnchor.cell)) {
+                    // All cells are on the same row
+                    if(oAnchor.recordIndex === nTargetRecordIndex) {
+                        // Select all cells between anchor cell and target cell, including target cell
+                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
+                            for(i=oAnchor.colKeyIndex+1; i<=nTargetColKeyIndex; i++) {
+                                this.selectCell(elTargetRow.cells[i]);
+                            }
+                        }
+                        // Select all cells between target cell and anchor cell, including target cell
+                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
+                            for(i=nTargetColKeyIndex; i<oAnchor.colKeyIndex; i++) {
+                                this.selectCell(elTargetRow.cells[i]);
+                            }
+                        }
+                    }
+                    // Anchor row is above target row
+                    else if(oAnchor.recordIndex < nTargetRecordIndex) {
+                        // Select all cells on anchor row from anchor cell to the end of the row
+                        for(i=oAnchor.colKeyIndex+1; i<elTargetRow.cells.length; i++) {
+                            this.selectCell(elTargetRow.cells[i]);
+                        }
+
+                        // Select all cells on all rows between anchor row and target row
+                        for(i=oAnchor.trIndex+1; i<nTargetTrIndex; i++) {
+                            for(j=0; j<allRows[i].cells.length; j++){
+                                this.selectCell(allRows[i].cells[j]);
+                            }
+                        }
+
+                        // Select all cells on target row from first cell to the target cell
+                        for(i=0; i<=nTargetColKeyIndex; i++) {
+                            this.selectCell(elTargetRow.cells[i]);
+                        }
+                    }
+                    // Anchor row is below target row
+                    else {
+                        // Select all cells on target row from target cell to the end of the row
+                        for(i=nTargetColKeyIndex; i<elTargetRow.cells.length; i++) {
+                            this.selectCell(elTargetRow.cells[i]);
+                        }
+
+                        // Select all cells on all rows between target row and anchor row
+                        for(i=nTargetTrIndex+1; i<oAnchor.trIndex; i++) {
+                            for(j=0; j<allRows[i].cells.length; j++){
+                                this.selectCell(allRows[i].cells[j]);
+                            }
+                        }
+
+                        // Select all cells on anchor row from first cell to the anchor cell
+                        for(i=0; i<oAnchor.colKeyIndex; i++) {
+                            this.selectCell(elTargetRow.cells[i]);
+                        }
+                    }
+                }
+                // Anchor cell is unselected
+                else {
+                    // All cells are on the same row
+                    if(oAnchor.recordIndex === nTargetRecordIndex) {
+                        // Unselect all cells between anchor cell and target cell
+                        if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
+                            for(i=oAnchor.colKeyIndex+1; i<nTargetColKeyIndex; i++) {
+                                this.unselectCell(elTargetRow.cells[i]);
+                            }
+                        }
+                        // Select all cells between target cell and anchor cell
+                        else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
+                            for(i=nTargetColKeyIndex+1; i<oAnchor.colKeyIndex; i++) {
+                                this.unselectCell(elTargetRow.cells[i]);
+                            }
+                        }
+                    }
+                    // Anchor row is above target row
+                    if(oAnchor.recordIndex < nTargetRecordIndex) {
+                        // Unselect all cells from anchor cell to target cell
+                        for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
+                            currentRow = allRows[i];
+                            for(j=0; j<currentRow.cells.length; j++) {
+                                // This is the anchor row, only unselect cells after the anchor cell
+                                if(currentRow.sectionRowIndex === oAnchor.trIndex) {
+                                    if(j>oAnchor.colKeyIndex) {
+                                        this.unselectCell(currentRow.cells[j]);
+                                    }
+                                }
+                                // This is the target row, only unelect cells before the target cell
+                                else if(currentRow.sectionRowIndex === nTargetTrIndex) {
+                                    if(j<nTargetColKeyIndex) {
+                                        this.unselectCell(currentRow.cells[j]);
+                                    }
+                                }
+                                // Unselect all cells on this row
+                                else {
+                                    this.unselectCell(currentRow.cells[j]);
+                                }
+                            }
+                        }
+                    }
+                    // Anchor row is below target row
+                    else {
+                        // Unselect all cells from target cell to anchor cell
+                        for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
+                            currentRow = allRows[i];
+                            for(j=0; j<currentRow.cells.length; j++) {
+                                // This is the target row, only unselect cells after the target cell
+                                if(currentRow.sectionRowIndex == nTargetTrIndex) {
+                                    if(j>nTargetColKeyIndex) {
+                                        this.unselectCell(currentRow.cells[j]);
+                                    }
+                                }
+                                // This is the anchor row, only unselect cells before the anchor cell
+                                else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
+                                    if(j<oAnchor.colKeyIndex) {
+                                        this.unselectCell(currentRow.cells[j]);
+                                    }
+                                }
+                                // Unselect all cells on this row
+                                else {
+                                    this.unselectCell(currentRow.cells[j]);
+                                }
+                            }
+                        }
+                    }
+
+                    // Select the target cell
+                    this.selectCell(elTargetCell);
+                }
+            }
+            // Invalid anchor
+            else {
+                // Set anchor
+                this._oAnchorCell = oTargetCell;
+
+                // Toggle selection of target
+                if(this.isSelected(oTargetCell)) {
+                    this.unselectCell(oTargetCell);
+                }
+                else {
+                    this.selectCell(oTargetCell);
+                }
+            }
+        }
+         // Only SHIFT
+        else if(bSHIFT) {
+
+            this.unselectAllCells();
+
+            // Validate anchor
+            if(oAnchor) {
+                // All cells are on the same row
+                if(oAnchor.recordIndex === nTargetRecordIndex) {
+                    // Select all cells between anchor cell and target cell,
+                    // including the anchor cell and target cell
+                    if(oAnchor.colKeyIndex < nTargetColKeyIndex) {
+                        for(i=oAnchor.colKeyIndex; i<=nTargetColKeyIndex; i++) {
+                            this.selectCell(elTargetRow.cells[i]);
+                        }
+                    }
+                    // Select all cells between target cell and anchor cell
+                    // including the target cell and anchor cell
+                    else if(nTargetColKeyIndex < oAnchor.colKeyIndex) {
+                        for(i=nTargetColKeyIndex; i<=oAnchor.colKeyIndex; i++) {
+                            this.selectCell(elTargetRow.cells[i]);
+                        }
+                    }
+                }
+                // Anchor row is above target row
+                else if(oAnchor.recordIndex < nTargetRecordIndex) {
+                    // Select all cells from anchor cell to target cell
+                    // including the anchor cell and target cell
+                    for(i=oAnchor.trIndex; i<=nTargetTrIndex; i++) {
+                        currentRow = allRows[i];
+                        for(j=0; j<currentRow.cells.length; j++) {
+                            // This is the anchor row, only select the anchor cell and after
+                            if(currentRow.sectionRowIndex == oAnchor.trIndex) {
+                                if(j>=oAnchor.colKeyIndex) {
+                                    this.selectCell(currentRow.cells[j]);
+                                }
+                            }
+                            // This is the target row, only select the target cell and before
+                            else if(currentRow.sectionRowIndex == nTargetTrIndex) {
+                                if(j<=nTargetColKeyIndex) {
+                                    this.selectCell(currentRow.cells[j]);
+                                }
+                            }
+                            // Select all cells on this row
+                            else {
+                                this.selectCell(currentRow.cells[j]);
+                            }
+                        }
+                    }
+                }
+                // Anchor row is below target row
+                else {
+                    // Select all cells from target cell to anchor cell,
+                    // including the target cell and anchor cell
+                    for(i=nTargetTrIndex; i<=oAnchor.trIndex; i++) {
+                        currentRow = allRows[i];
+                        for(j=0; j<currentRow.cells.length; j++) {
+                            // This is the target row, only select the target cell and after
+                            if(currentRow.sectionRowIndex == nTargetTrIndex) {
+                                if(j>=nTargetColKeyIndex) {
+                                    this.selectCell(currentRow.cells[j]);
+                                }
+                            }
+                            // This is the anchor row, only select the anchor cell and before
+                            else if(currentRow.sectionRowIndex == oAnchor.trIndex) {
+                                if(j<=oAnchor.colKeyIndex) {
+                                    this.selectCell(currentRow.cells[j]);
+                                }
+                            }
+                            // Select all cells on this row
+                            else {
+                                this.selectCell(currentRow.cells[j]);
+                            }
+                        }
+                    }
+                }
+            }
+            // Invalid anchor
+            else {
+                // Set anchor
+                this._oAnchorCell = oTargetCell;
+
+                // Select target only
+                this.selectCell(oTargetCell);
+            }
+
+
+        }
+        // Only CTRL
+        else if(bCTRL) {
+
+            // Set anchor
+            this._oAnchorCell = oTargetCell;
+
+            // Toggle selection of target
+            if(this.isSelected(oTargetCell)) {
+                this.unselectCell(oTargetCell);
+            }
+            else {
+                this.selectCell(oTargetCell);
+            }
+
+        }
+        // Neither SHIFT nor CTRL
+        else {
+            this._handleSingleCellSelectionByMouse(oArgs);
+        }
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a key event when selection mode
+ * is set to "cellrange".
+ *
+ * @method _handleCellRangeSelectionByKey
+ * @param e {HTMLEvent} Event object.
+ * @private
+ */
+_handleCellRangeSelectionByKey : function(e) {
+    var nKey = Ev.getCharCode(e);
+    var bSHIFT = e.shiftKey;
+    if((nKey == 9) || !bSHIFT) {
+        this._handleSingleCellSelectionByKey(e);
+        return;
+    }
+
+    if((nKey > 36) && (nKey < 41)) {
+        // Validate trigger
+        var oTrigger = this._getSelectionTrigger();
+        // Arrow selection only works if last selected row is on current page
+        if(!oTrigger) {
+            return null;
+        }
+
+        Ev.stopEvent(e);
+
+        // Validate anchor
+        var oAnchor = this._getSelectionAnchor(oTrigger);
+
+        var i, elNewRow, elNew;
+        var allRows = this.getTbodyEl().rows;
+        var elThisRow = oTrigger.el.parentNode;
+
+        // Arrow down
+        if(nKey == 40) {
+            elNewRow = this.getNextTrEl(oTrigger.el);
+
+            // Selecting away from anchor cell
+            if(oAnchor.recordIndex <= oTrigger.recordIndex) {
+                // Select all cells to the end of this row
+                for(i=oTrigger.colKeyIndex+1; i<elThisRow.cells.length; i++){
+                    elNew = elThisRow.cells[i];
+                    this.selectCell(elNew);
+                }
+
+                // Select some of the cells on the next row down
+                if(elNewRow) {
+                    for(i=0; i<=oTrigger.colKeyIndex; i++){
+                        elNew = elNewRow.cells[i];
+                        this.selectCell(elNew);
+                    }
+                }
+            }
+            // Unselecting towards anchor cell
+            else {
+                // Unselect all cells to the end of this row
+                for(i=oTrigger.colKeyIndex; i<elThisRow.cells.length; i++){
+                    this.unselectCell(elThisRow.cells[i]);
+                }
+
+                // Unselect some of the cells on the next row down
+                if(elNewRow) {
+                    for(i=0; i<oTrigger.colKeyIndex; i++){
+                        this.unselectCell(elNewRow.cells[i]);
+                    }
+                }
+            }
+        }
+        // Arrow up
+        else if(nKey == 38) {
+            elNewRow = this.getPreviousTrEl(oTrigger.el);
+
+            // Selecting away from anchor cell
+            if(oAnchor.recordIndex >= oTrigger.recordIndex) {
+                // Select all the cells to the beginning of this row
+                for(i=oTrigger.colKeyIndex-1; i>-1; i--){
+                    elNew = elThisRow.cells[i];
+                    this.selectCell(elNew);
+                }
+
+                // Select some of the cells from the end of the previous row
+                if(elNewRow) {
+                    for(i=elThisRow.cells.length-1; i>=oTrigger.colKeyIndex; i--){
+                        elNew = elNewRow.cells[i];
+                        this.selectCell(elNew);
+                    }
+                }
+            }
+            // Unselecting towards anchor cell
+            else {
+                // Unselect all the cells to the beginning of this row
+                for(i=oTrigger.colKeyIndex; i>-1; i--){
+                    this.unselectCell(elThisRow.cells[i]);
+                }
+
+                // Unselect some of the cells from the end of the previous row
+                if(elNewRow) {
+                    for(i=elThisRow.cells.length-1; i>oTrigger.colKeyIndex; i--){
+                        this.unselectCell(elNewRow.cells[i]);
+                    }
+                }
+            }
+        }
+        // Arrow right
+        else if(nKey == 39) {
+            elNewRow = this.getNextTrEl(oTrigger.el);
+
+            // Selecting away from anchor cell
+            if(oAnchor.recordIndex < oTrigger.recordIndex) {
+                // Select the next cell to the right
+                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
+                    elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
+                    this.selectCell(elNew);
+                }
+                // Select the first cell of the next row
+                else if(elNewRow) {
+                    elNew = elNewRow.cells[0];
+                    this.selectCell(elNew);
+                }
+            }
+            // Unselecting towards anchor cell
+            else if(oAnchor.recordIndex > oTrigger.recordIndex) {
+                this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
+
+                // Unselect this cell towards the right
+                if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
+                }
+                // Unselect this cells towards the first cell of the next row
+                else {
+                }
+            }
+            // Anchor is on this row
+            else {
+                // Selecting away from anchor
+                if(oAnchor.colKeyIndex <= oTrigger.colKeyIndex) {
+                    // Select the next cell to the right
+                    if(oTrigger.colKeyIndex < elThisRow.cells.length-1) {
+                        elNew = elThisRow.cells[oTrigger.colKeyIndex+1];
+                        this.selectCell(elNew);
+                    }
+                    // Select the first cell on the next row
+                    else if(oTrigger.trIndex < allRows.length-1){
+                        elNew = elNewRow.cells[0];
+                        this.selectCell(elNew);
+                    }
+                }
+                // Unselecting towards anchor
+                else {
+                    // Unselect this cell towards the right
+                    this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
+                }
+            }
+        }
+        // Arrow left
+        else if(nKey == 37) {
+            elNewRow = this.getPreviousTrEl(oTrigger.el);
+
+            // Unselecting towards the anchor
+            if(oAnchor.recordIndex < oTrigger.recordIndex) {
+                this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
+
+                // Unselect this cell towards the left
+                if(oTrigger.colKeyIndex > 0) {
+                }
+                // Unselect this cell towards the last cell of the previous row
+                else {
+                }
+            }
+            // Selecting towards the anchor
+            else if(oAnchor.recordIndex > oTrigger.recordIndex) {
+                // Select the next cell to the left
+                if(oTrigger.colKeyIndex > 0) {
+                    elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
+                    this.selectCell(elNew);
+                }
+                // Select the last cell of the previous row
+                else if(oTrigger.trIndex > 0){
+                    elNew = elNewRow.cells[elNewRow.cells.length-1];
+                    this.selectCell(elNew);
+                }
+            }
+            // Anchor is on this row
+            else {
+                // Selecting away from anchor cell
+                if(oAnchor.colKeyIndex >= oTrigger.colKeyIndex) {
+                    // Select the next cell to the left
+                    if(oTrigger.colKeyIndex > 0) {
+                        elNew = elThisRow.cells[oTrigger.colKeyIndex-1];
+                        this.selectCell(elNew);
+                    }
+                    // Select the last cell of the previous row
+                    else if(oTrigger.trIndex > 0){
+                        elNew = elNewRow.cells[elNewRow.cells.length-1];
+                        this.selectCell(elNew);
+                    }
+                }
+                // Unselecting towards anchor cell
+                else {
+                    this.unselectCell(elThisRow.cells[oTrigger.colKeyIndex]);
+
+                    // Unselect this cell towards the left
+                    if(oTrigger.colKeyIndex > 0) {
+                    }
+                    // Unselect this cell towards the last cell of the previous row
+                    else {
+                    }
+                }
+            }
+        }
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a mouse event when selection mode
+ * is set to "singlecell".
+ *
+ * @method _handleSingleCellSelectionByMouse
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ * @private
+ */
+_handleSingleCellSelectionByMouse : function(oArgs) {
+    var elTarget = oArgs.target;
+
+    // Validate target cell
+    var elTargetCell = this.getTdEl(elTarget);
+    if(elTargetCell) {
+        var elTargetRow = this.getTrEl(elTargetCell);
+        var oTargetRecord = this.getRecord(elTargetRow);
+        var oTargetColumn = this.getColumn(elTargetCell);
+        var oTargetCell = {record:oTargetRecord, column:oTargetColumn};
+
+        // Set anchor
+        this._oAnchorCell = oTargetCell;
+
+        // Select only target
+        this.unselectAllCells();
+        this.selectCell(oTargetCell);
+    }
+},
+
+/**
+ * Determines selection behavior resulting from a key event when selection mode
+ * is set to "singlecell".
+ *
+ * @method _handleSingleCellSelectionByKey
+ * @param e {HTMLEvent} Event object.
+ * @private
+ */
+_handleSingleCellSelectionByKey : function(e) {
+    var nKey = Ev.getCharCode(e);
+    if((nKey == 9) || ((nKey > 36) && (nKey < 41))) {
+        var bSHIFT = e.shiftKey;
+
+        // Validate trigger
+        var oTrigger = this._getSelectionTrigger();
+        // Arrow selection only works if last selected row is on current page
+        if(!oTrigger) {
+            return null;
+        }
+
+        // Determine the new cell to select
+        var elNew;
+        if(nKey == 40) { // Arrow down
+            elNew = this.getBelowTdEl(oTrigger.el);
+
+            // Validate new cell
+            if(elNew === null) {
+                //TODO: wrap around to first tr on current page
+
+                //TODO: wrap forward to first tr of next page
+
+                // Bottom selection is sticky
+                elNew = oTrigger.el;
+            }
+        }
+        else if(nKey == 38) { // Arrow up
+            elNew = this.getAboveTdEl(oTrigger.el);
+
+            // Validate new cell
+            if(elNew === null) {
+                //TODO: wrap around to last tr on current page
+
+                //TODO: wrap back to last tr of previous page
+
+                // Top selection is sticky
+                elNew = oTrigger.el;
+            }
+        }
+        else if((nKey == 39) || (!bSHIFT && (nKey == 9))) { // Arrow right or tab
+            elNew = this.getNextTdEl(oTrigger.el);
+
+            // Validate new cell
+            if(elNew === null) {
+                //TODO: wrap around to first td on current page
+
+                //TODO: wrap forward to first td of next page
+
+                // Top-left selection is sticky, and release TAB focus
+                //elNew = oTrigger.el;
+                return;
+            }
+        }
+        else if((nKey == 37) || (bSHIFT && (nKey == 9))) { // Arrow left or shift-tab
+            elNew = this.getPreviousTdEl(oTrigger.el);
+
+            // Validate new cell
+            if(elNew === null) {
+                //TODO: wrap around to last td on current page
+
+                //TODO: wrap back to last td of previous page
+
+                // Bottom-right selection is sticky, and release TAB focus
+                //elNew = oTrigger.el;
+                return;
+            }
+        }
+
+        Ev.stopEvent(e);
+        
+        // Unselect all cells
+        this.unselectAllCells();
+
+        // Select the new cell
+        this.selectCell(elNew);
+
+        // Set new anchor
+        this._oAnchorCell = {record:this.getRecord(elNew), column:this.getColumn(elNew)};
+    }
+},
+
+/**
+ * Returns array of selected TR elements on the page.
+ *
+ * @method getSelectedTrEls
+ * @return {HTMLElement[]} Array of selected TR elements.
+ */
+getSelectedTrEls : function() {
+    return Dom.getElementsByClassName(DT.CLASS_SELECTED,"tr",this._elTbody);
+},
+
+/**
+ * Sets given row to the selected state.
+ *
+ * @method selectRow
+ * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
+ * reference or ID string, Record instance, or RecordSet position index.
+ */
+selectRow : function(row) {
+    var oRecord, elRow;
+
+    if(row instanceof YAHOO.widget.Record) {
+        oRecord = this._oRecordSet.getRecord(row);
+        elRow = this.getTrEl(oRecord);
+    }
+    else if(lang.isNumber(row)) {
+        oRecord = this.getRecord(row);
+        elRow = this.getTrEl(oRecord);
+    }
+    else {
+        elRow = this.getTrEl(row);
+        oRecord = this.getRecord(elRow);
+    }
+
+    if(oRecord) {
+        // Update selection trackers
+        var tracker = this._aSelections || [];
+        var sRecordId = oRecord.getId();
+        var index = -1;
+
+        // Remove if already there:
+        // Use Array.indexOf if available...
+        /*if(tracker.indexOf && (tracker.indexOf(sRecordId) >  -1)) {
+            tracker.splice(tracker.indexOf(sRecordId),1);
+        }*/
+        if(tracker.indexOf) {
+            index = tracker.indexOf(sRecordId);
+            
+        }
+        // ...or do it the old-fashioned way
+        else {
+            for(var j=tracker.length-1; j>-1; j--) {
+                if(tracker[j] === sRecordId){
+                    index = j;
+                    break;
+                }
+            }
+        }
+        if(index > -1) {
+            tracker.splice(index,1);
+        }
+        
+        // Add to the end
+        tracker.push(sRecordId);
+        this._aSelections = tracker;
+
+        // Update trackers
+        if(!this._oAnchorRecord) {
+            this._oAnchorRecord = oRecord;
+        }
+
+        // Update UI
+        if(elRow) {
+            Dom.addClass(elRow, DT.CLASS_SELECTED);
+        }
+
+        this.fireEvent("rowSelectEvent", {record:oRecord, el:elRow});
+    }
+    else {
+    }
+},
+
+/**
+ * Sets given row to the unselected state.
+ *
+ * @method unselectRow
+ * @param row {HTMLElement | String | YAHOO.widget.Record | Number} HTML element
+ * reference or ID string, Record instance, or RecordSet position index.
+ */
+unselectRow : function(row) {
+    var elRow = this.getTrEl(row);
+
+    var oRecord;
+    if(row instanceof YAHOO.widget.Record) {
+        oRecord = this._oRecordSet.getRecord(row);
+    }
+    else if(lang.isNumber(row)) {
+        oRecord = this.getRecord(row);
+    }
+    else {
+        oRecord = this.getRecord(elRow);
+    }
+
+    if(oRecord) {
+        // Update selection trackers
+        var tracker = this._aSelections || [];
+        var sRecordId = oRecord.getId();
+        var index = -1;
+
+        // Use Array.indexOf if available...
+        if(tracker.indexOf) {
+            index = tracker.indexOf(sRecordId);
+        }
+        // ...or do it the old-fashioned way
+        else {
+            for(var j=tracker.length-1; j>-1; j--) {
+                if(tracker[j] === sRecordId){
+                    index = j;
+                    break;
+                }
+            }
+        }
+        if(index > -1) {
+            // Update tracker
+            tracker.splice(index,1);
+            this._aSelections = tracker;
+
+            // Update the UI
+            Dom.removeClass(elRow, DT.CLASS_SELECTED);
+
+            this.fireEvent("rowUnselectEvent", {record:oRecord, el:elRow});
+
+            return;
+        }
+    }
+},
+
+/**
+ * Clears out all row selections.
+ *
+ * @method unselectAllRows
+ */
+unselectAllRows : function() {
+    // Remove all rows from tracker
+    var tracker = this._aSelections || [],
+        recId,
+        removed = [];
+    for(var j=tracker.length-1; j>-1; j--) {
+       if(lang.isString(tracker[j])){
+            recId = tracker.splice(j,1);
+            removed[removed.length] = this.getRecord(lang.isArray(recId) ? recId[0] : recId);
+        }
+    }
+
+    // Update tracker
+    this._aSelections = tracker;
+
+    // Update UI
+    this._unselectAllTrEls();
+
+    this.fireEvent("unselectAllRowsEvent", {records: removed});
+},
+
+/**
+ * Convenience method to remove the class YAHOO.widget.DataTable.CLASS_SELECTED
+ * from all TD elements in the internal tracker.
+ *
+ * @method _unselectAllTdEls
+ * @private
+ */
+_unselectAllTdEls : function() {
+    var selectedCells = Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
+    Dom.removeClass(selectedCells, DT.CLASS_SELECTED);
+},
+
+/**
+ * Returns array of selected TD elements on the page.
+ *
+ * @method getSelectedTdEls
+ * @return {HTMLElement[]} Array of selected TD elements.
+ */
+getSelectedTdEls : function() {
+    return Dom.getElementsByClassName(DT.CLASS_SELECTED,"td",this._elTbody);
+},
+
+/**
+ * Sets given cell to the selected state.
+ *
+ * @method selectCell
+ * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
+ * object literal of syntax {record:oRecord, column:oColumn}.
+ */
+selectCell : function(cell) {
+//TODO: accept {record} in selectCell()
+    var elCell = this.getTdEl(cell);
+
+    if(elCell) {
+        var oRecord = this.getRecord(elCell);
+        var oColumn = this.getColumn(this.getCellIndex(elCell));
+        var sColumnKey = oColumn.getKey();
+
+        if(oRecord && sColumnKey) {
+            // Get Record ID
+            var tracker = this._aSelections || [];
+            var sRecordId = oRecord.getId();
+
+            // Remove if there
+            for(var j=tracker.length-1; j>-1; j--) {
+               if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
+                    tracker.splice(j,1);
+                    break;
+                }
+            }
+
+            // Add to the end
+            tracker.push({recordId:sRecordId, columnKey:sColumnKey});
+
+            // Update trackers
+            this._aSelections = tracker;
+            if(!this._oAnchorCell) {
+                this._oAnchorCell = {record:oRecord, column:oColumn};
+            }
+
+            // Update the UI
+            Dom.addClass(elCell, DT.CLASS_SELECTED);
+
+            this.fireEvent("cellSelectEvent", {record:oRecord, column:oColumn, key: sColumnKey, el:elCell});
+            return;
+        }
+    }
+},
+
+/**
+ * Sets given cell to the unselected state.
+ *
+ * @method unselectCell
+ * @param cell {HTMLElement | String | Object} TD element or child of a TD element, or
+ * object literal of syntax {record:oRecord, column:oColumn}.
+ * @param cell {HTMLElement | String} DOM element reference or ID string
+ * to DataTable page element or RecordSet index.
+ */
+unselectCell : function(cell) {
+    var elCell = this.getTdEl(cell);
+
+    if(elCell) {
+        var oRecord = this.getRecord(elCell);
+        var oColumn = this.getColumn(this.getCellIndex(elCell));
+        var sColumnKey = oColumn.getKey();
+
+        if(oRecord && sColumnKey) {
+            // Get Record ID
+            var tracker = this._aSelections || [];
+            var id = oRecord.getId();
+
+            // Is it selected?
+            for(var j=tracker.length-1; j>-1; j--) {
+                if((tracker[j].recordId === id) && (tracker[j].columnKey === sColumnKey)){
+                    // Remove from tracker
+                    tracker.splice(j,1);
+
+                    // Update tracker
+                    this._aSelections = tracker;
+
+                    // Update the UI
+                    Dom.removeClass(elCell, DT.CLASS_SELECTED);
+
+                    this.fireEvent("cellUnselectEvent", {record:oRecord, column: oColumn, key:sColumnKey, el:elCell});
+                    return;
+                }
+            }
+        }
+    }
+},
+
+/**
+ * Clears out all cell selections.
+ *
+ * @method unselectAllCells
+ */
+unselectAllCells : function() {
+    // Remove all cells from tracker
+    var tracker = this._aSelections || [];
+    for(var j=tracker.length-1; j>-1; j--) {
+       if(lang.isObject(tracker[j])){
+            tracker.splice(j,1);
+        }
+    }
+
+    // Update tracker
+    this._aSelections = tracker;
+
+    // Update UI
+    this._unselectAllTdEls();
+
+    //TODO: send data to unselectAllCellsEvent handler
+    this.fireEvent("unselectAllCellsEvent");
+},
+
+/**
+ * Returns true if given item is selected, false otherwise.
+ *
+ * @method isSelected
+ * @param o {String | HTMLElement | YAHOO.widget.Record | Number
+ * {record:YAHOO.widget.Record, column:YAHOO.widget.Column} } TR or TD element by
+ * reference or ID string, a Record instance, a RecordSet position index,
+ * or an object literal representation
+ * of a cell.
+ * @return {Boolean} True if item is selected.
+ */
+isSelected : function(o) {
+    if(o && (o.ownerDocument == document)) {
+        return (Dom.hasClass(this.getTdEl(o),DT.CLASS_SELECTED) || Dom.hasClass(this.getTrEl(o),DT.CLASS_SELECTED));
+    }
+    else {
+        var oRecord, sRecordId, j;
+        var tracker = this._aSelections;
+        if(tracker && tracker.length > 0) {
+            // Looking for a Record?
+            if(o instanceof YAHOO.widget.Record) {
+                oRecord = o;
+            }
+            else if(lang.isNumber(o)) {
+                oRecord = this.getRecord(o);
+            }
+            if(oRecord) {
+                sRecordId = oRecord.getId();
+
+                // Is it there?
+                // Use Array.indexOf if available...
+                if(tracker.indexOf) {
+                    if(tracker.indexOf(sRecordId) >  -1) {
+                        return true;
+                    }
+                }
+                // ...or do it the old-fashioned way
+                else {
+                    for(j=tracker.length-1; j>-1; j--) {
+                       if(tracker[j] === sRecordId){
+                        return true;
+                       }
+                    }
+                }
+            }
+            // Looking for a cell
+            else if(o.record && o.column){
+                sRecordId = o.record.getId();
+                var sColumnKey = o.column.getKey();
+
+                for(j=tracker.length-1; j>-1; j--) {
+                    if((tracker[j].recordId === sRecordId) && (tracker[j].columnKey === sColumnKey)){
+                        return true;
+                    }
+                }
+            }
+        }
+    }
+    return false;
+},
+
+/**
+ * Returns selected rows as an array of Record IDs.
+ *
+ * @method getSelectedRows
+ * @return {String[]} Array of selected rows by Record ID.
+ */
+getSelectedRows : function() {
+    var aSelectedRows = [];
+    var tracker = this._aSelections || [];
+    for(var j=0; j<tracker.length; j++) {
+       if(lang.isString(tracker[j])){
+            aSelectedRows.push(tracker[j]);
+        }
+    }
+    return aSelectedRows;
+},
+
+/**
+ * Returns selected cells as an array of object literals:
+ *     {recordId:sRecordId, columnKey:sColumnKey}.
+ *
+ * @method getSelectedCells
+ * @return {Object[]} Array of selected cells by Record ID and Column ID.
+ */
+getSelectedCells : function() {
+    var aSelectedCells = [];
+    var tracker = this._aSelections || [];
+    for(var j=0; j<tracker.length; j++) {
+       if(tracker[j] && lang.isObject(tracker[j])){
+            aSelectedCells.push(tracker[j]);
+        }
+    }
+    return aSelectedCells;
+},
+
+/**
+ * Returns last selected Record ID.
+ *
+ * @method getLastSelectedRecord
+ * @return {String} Record ID of last selected row.
+ */
+getLastSelectedRecord : function() {
+    var tracker = this._aSelections;
+    if(tracker && tracker.length > 0) {
+        for(var i=tracker.length-1; i>-1; i--) {
+           if(lang.isString(tracker[i])){
+                return tracker[i];
+            }
+        }
+    }
+},
+
+/**
+ * Returns last selected cell as an object literal:
+ *     {recordId:sRecordId, columnKey:sColumnKey}.
+ *
+ * @method getLastSelectedCell
+ * @return {Object} Object literal representation of a cell.
+ */
+getLastSelectedCell : function() {
+    var tracker = this._aSelections;
+    if(tracker && tracker.length > 0) {
+        for(var i=tracker.length-1; i>-1; i--) {
+           if(tracker[i].recordId && tracker[i].columnKey){
+                return tracker[i];
+            }
+        }
+    }
+},
+
+/**
+ * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given row.
+ *
+ * @method highlightRow
+ * @param row {HTMLElement | String} DOM element reference or ID string.
+ */
+highlightRow : function(row) {
+    var elRow = this.getTrEl(row);
+
+    if(elRow) {
+        // Make sure previous row is unhighlighted
+/*        if(this._sLastHighlightedTrElId) {
+            Dom.removeClass(this._sLastHighlightedTrElId,DT.CLASS_HIGHLIGHTED);
+        }*/
+        var oRecord = this.getRecord(elRow);
+        Dom.addClass(elRow,DT.CLASS_HIGHLIGHTED);
+        //this._sLastHighlightedTrElId = elRow.id;
+        this.fireEvent("rowHighlightEvent", {record:oRecord, el:elRow});
+        return;
+    }
+},
+
+/**
+ * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given row.
+ *
+ * @method unhighlightRow
+ * @param row {HTMLElement | String} DOM element reference or ID string.
+ */
+unhighlightRow : function(row) {
+    var elRow = this.getTrEl(row);
+
+    if(elRow) {
+        var oRecord = this.getRecord(elRow);
+        Dom.removeClass(elRow,DT.CLASS_HIGHLIGHTED);
+        this.fireEvent("rowUnhighlightEvent", {record:oRecord, el:elRow});
+        return;
+    }
+},
+
+/**
+ * Assigns the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED to the given cell.
+ *
+ * @method highlightCell
+ * @param cell {HTMLElement | String} DOM element reference or ID string.
+ */
+highlightCell : function(cell) {
+    var elCell = this.getTdEl(cell);
+
+    if(elCell) {
+        // Make sure previous cell is unhighlighted
+        if(this._elLastHighlightedTd) {
+            this.unhighlightCell(this._elLastHighlightedTd);
+        }
+
+        var oRecord = this.getRecord(elCell);
+        var oColumn = this.getColumn(this.getCellIndex(elCell));
+        var sColumnKey = oColumn.getKey();
+        Dom.addClass(elCell,DT.CLASS_HIGHLIGHTED);
+        this._elLastHighlightedTd = elCell;
+        this.fireEvent("cellHighlightEvent", {record:oRecord, column:oColumn, key:sColumnKey, el:elCell});
+        return;
+    }
+},
+
+/**
+ * Removes the class YAHOO.widget.DataTable.CLASS_HIGHLIGHTED from the given cell.
+ *
+ * @method unhighlightCell
+ * @param cell {HTMLElement | String} DOM element reference or ID string.
+ */
+unhighlightCell : function(cell) {
+    var elCell = this.getTdEl(cell);
+
+    if(elCell) {
+        var oRecord = this.getRecord(elCell);
+        Dom.removeClass(elCell,DT.CLASS_HIGHLIGHTED);
+        this._elLastHighlightedTd = null;
+        this.fireEvent("cellUnhighlightEvent", {record:oRecord, column:this.getColumn(this.getCellIndex(elCell)), key:this.getColumn(this.getCellIndex(elCell)).getKey(), el:elCell});
+        return;
+    }
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// INLINE EDITING
+
+/**
+ * Assigns CellEditor instance to existing Column.
+ * @method addCellEditor
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param oEditor {YAHOO.wdiget.CellEditor} CellEditor instance.
+ */
+addCellEditor : function(oColumn, oEditor) {
+    oColumn.editor = oEditor;
+    oColumn.editor.subscribe("showEvent", this._onEditorShowEvent, this, true);
+    oColumn.editor.subscribe("keydownEvent", this._onEditorKeydownEvent, this, true);
+    oColumn.editor.subscribe("revertEvent", this._onEditorRevertEvent, this, true);
+    oColumn.editor.subscribe("saveEvent", this._onEditorSaveEvent, this, true);
+    oColumn.editor.subscribe("cancelEvent", this._onEditorCancelEvent, this, true);
+    oColumn.editor.subscribe("blurEvent", this._onEditorBlurEvent, this, true);
+    oColumn.editor.subscribe("blockEvent", this._onEditorBlockEvent, this, true);
+    oColumn.editor.subscribe("unblockEvent", this._onEditorUnblockEvent, this, true);
+},
+
+/**
+ * Returns current CellEditor instance, or null.
+ * @method getCellEditor
+ * @return {YAHOO.widget.CellEditor} CellEditor instance.
+ */
+getCellEditor : function() {
+    return this._oCellEditor;
+},
+
+
+/**
+ * Activates and shows CellEditor instance for the given cell while deactivating and
+ * canceling previous CellEditor. It is baked into DataTable that only one CellEditor
+ * can be active at any given time. 
+ *
+ * @method showCellEditor
+ * @param elCell {HTMLElement | String} Cell to edit.
+ */
+showCellEditor : function(elCell, oRecord, oColumn) {
+    // Get a particular CellEditor
+    elCell = this.getTdEl(elCell);
+    if(elCell) {
+        oColumn = this.getColumn(elCell);
+        if(oColumn && oColumn.editor) {
+            var oCellEditor = this._oCellEditor;
+            // Clean up active CellEditor
+            if(oCellEditor) {
+                if(this._oCellEditor.cancel) {
+                    this._oCellEditor.cancel();
+                }
+                else if(oCellEditor.isActive) {
+                    this.cancelCellEditor();
+                }
+            }
+            
+            if(oColumn.editor instanceof YAHOO.widget.BaseCellEditor) {
+                // Get CellEditor
+                oCellEditor = oColumn.editor;
+                var ok = oCellEditor.attach(this, elCell);
+                if(ok) {
+                    oCellEditor.render();
+                    oCellEditor.move();
+                    ok = this.doBeforeShowCellEditor(oCellEditor);
+                    if(ok) {
+                        oCellEditor.show();
+                        this._oCellEditor = oCellEditor;
+                    }
+                }
+            }
+            // Backward compatibility
+            else {
+                    if(!oRecord || !(oRecord instanceof YAHOO.widget.Record)) {
+                        oRecord = this.getRecord(elCell);
+                    }
+                    if(!oColumn || !(oColumn instanceof YAHOO.widget.Column)) {
+                        oColumn = this.getColumn(elCell);
+                    }
+                    if(oRecord && oColumn) {
+                        if(!this._oCellEditor || this._oCellEditor.container) {
+                            this._initCellEditorEl();
+                        }
+                        
+                        // Update Editor values
+                        oCellEditor = this._oCellEditor;
+                        oCellEditor.cell = elCell;
+                        oCellEditor.record = oRecord;
+                        oCellEditor.column = oColumn;
+                        oCellEditor.validator = (oColumn.editorOptions &&
+                                lang.isFunction(oColumn.editorOptions.validator)) ?
+                                oColumn.editorOptions.validator : null;
+                        oCellEditor.value = oRecord.getData(oColumn.key);
+                        oCellEditor.defaultValue = null;
+            
+                        // Move Editor
+                        var elContainer = oCellEditor.container;
+                        var x = Dom.getX(elCell);
+                        var y = Dom.getY(elCell);
+            
+                        // SF doesn't get xy for cells in scrolling table
+                        // when tbody display is set to block
+                        if(isNaN(x) || isNaN(y)) {
+                            x = elCell.offsetLeft + // cell pos relative to table
+                                    Dom.getX(this._elTbody.parentNode) - // plus table pos relative to document
+                                    this._elTbody.scrollLeft; // minus tbody scroll
+                            y = elCell.offsetTop + // cell pos relative to table
+                                    Dom.getY(this._elTbody.parentNode) - // plus table pos relative to document
+                                    this._elTbody.scrollTop + // minus tbody scroll
+                                    this._elThead.offsetHeight; // account for fixed THEAD cells
+                        }
+            
+                        elContainer.style.left = x + "px";
+                        elContainer.style.top = y + "px";
+            
+                        // Hook to customize the UI
+                        this.doBeforeShowCellEditor(this._oCellEditor);
+            
+                        //TODO: This is temporarily up here due so elements can be focused
+                        // Show Editor
+                        elContainer.style.display = "";
+            
+                        // Handle ESC key
+                        Ev.addListener(elContainer, "keydown", function(e, oSelf) {
+                            // ESC hides Cell Editor
+                            if((e.keyCode == 27)) {
+                                oSelf.cancelCellEditor();
+                                oSelf.focusTbodyEl();
+                            }
+                            else {
+                                oSelf.fireEvent("editorKeydownEvent", {editor:oSelf._oCellEditor, event:e});
+                            }
+                        }, this);
+            
+                        // Render Editor markup
+                        var fnEditor;
+                        if(lang.isString(oColumn.editor)) {
+                            switch(oColumn.editor) {
+                                case "checkbox":
+                                    fnEditor = DT.editCheckbox;
+                                    break;
+                                case "date":
+                                    fnEditor = DT.editDate;
+                                    break;
+                                case "dropdown":
+                                    fnEditor = DT.editDropdown;
+                                    break;
+                                case "radio":
+                                    fnEditor = DT.editRadio;
+                                    break;
+                                case "textarea":
+                                    fnEditor = DT.editTextarea;
+                                    break;
+                                case "textbox":
+                                    fnEditor = DT.editTextbox;
+                                    break;
+                                default:
+                                    fnEditor = null;
+                            }
+                        }
+                        else if(lang.isFunction(oColumn.editor)) {
+                            fnEditor = oColumn.editor;
+                        }
+            
+                        if(fnEditor) {
+                            // Create DOM input elements
+                            fnEditor(this._oCellEditor, this);
+            
+                            // Show Save/Cancel buttons
+                            if(!oColumn.editorOptions || !oColumn.editorOptions.disableBtns) {
+                                this.showCellEditorBtns(elContainer);
+                            }
+            
+                            oCellEditor.isActive = true;
+            
+                            //TODO: verify which args to pass
+                            this.fireEvent("editorShowEvent", {editor:oCellEditor});
+                            return;
+                        }
+                    }
+
+
+
+            
+            }
+        }
+    }
+},
+
+/**
+ * Backward compatibility.
+ *
+ * @method _initCellEditorEl
+ * @private
+ * @deprecated Use BaseCellEditor class.
+ */
+_initCellEditorEl : function() {
+    // Attach Cell Editor container element as first child of body
+    var elCellEditor = document.createElement("div");
+    elCellEditor.id = this._sId + "-celleditor";
+    elCellEditor.style.display = "none";
+    elCellEditor.tabIndex = 0;
+    Dom.addClass(elCellEditor, DT.CLASS_EDITOR);
+    var elFirstChild = Dom.getFirstChild(document.body);
+    if(elFirstChild) {
+        elCellEditor = Dom.insertBefore(elCellEditor, elFirstChild);
+    }
+    else {
+        elCellEditor = document.body.appendChild(elCellEditor);
+    }
+    
+    // Internal tracker of Cell Editor values
+    var oCellEditor = {};
+    oCellEditor.container = elCellEditor;
+    oCellEditor.value = null;
+    oCellEditor.isActive = false;
+    this._oCellEditor = oCellEditor;
+},
+
+/**
+ * Overridable abstract method to customize CellEditor before showing.
+ *
+ * @method doBeforeShowCellEditor
+ * @param oCellEditor {YAHOO.widget.CellEditor} The CellEditor instance.
+ * @return {Boolean} Return true to continue showing CellEditor.
+ */
+doBeforeShowCellEditor : function(oCellEditor) {
+    return true;
+},
+
+/**
+ * Saves active CellEditor input to Record and upates DOM UI.
+ *
+ * @method saveCellEditor
+ */
+saveCellEditor : function() {
+    if(this._oCellEditor) {
+        if(this._oCellEditor.save) {
+            this._oCellEditor.save();
+        }
+        // Backward compatibility
+        else if(this._oCellEditor.isActive) {
+            var newData = this._oCellEditor.value;
+            // Copy the data to pass to the event
+            //var oldData = YAHOO.widget.DataTable._cloneObject(this._oCellEditor.record.getData(this._oCellEditor.column.key));
+            var oldData = this._oCellEditor.record.getData(this._oCellEditor.column.key);
+    
+            // Validate input data
+            if(this._oCellEditor.validator) {
+                newData = this._oCellEditor.value = this._oCellEditor.validator.call(this, newData, oldData, this._oCellEditor);
+                if(newData === null ) {
+                    this.resetCellEditor();
+                    this.fireEvent("editorRevertEvent",
+                            {editor:this._oCellEditor, oldData:oldData, newData:newData});
+                    return;
+                }
+            }
+            // Update the Record
+            this._oRecordSet.updateRecordValue(this._oCellEditor.record, this._oCellEditor.column.key, this._oCellEditor.value);
+            // Update the UI
+            this.formatCell(this._oCellEditor.cell.firstChild, this._oCellEditor.record, this._oCellEditor.column);
+            
+            // Bug fix 1764044
+            this._oChainRender.add({
+                method: function() {
+                    this.validateColumnWidths();
+                },
+                scope: this
+            });
+            this._oChainRender.run();
+            // Clear out the Cell Editor
+            this.resetCellEditor();
+    
+            this.fireEvent("editorSaveEvent",
+                    {editor:this._oCellEditor, oldData:oldData, newData:newData});
+        }
+    }   
+},
+
+/**
+ * Cancels active CellEditor.
+ *
+ * @method cancelCellEditor
+ */
+cancelCellEditor : function() {
+    if(this._oCellEditor) {
+        if(this._oCellEditor.cancel) {
+            this._oCellEditor.cancel();
+        }
+        // Backward compatibility
+        else if(this._oCellEditor.isActive) {
+            this.resetCellEditor();
+            //TODO: preserve values for the event?
+            this.fireEvent("editorCancelEvent", {editor:this._oCellEditor});
+        }
+
+    }
+},
+
+/**
+ * Destroys active CellEditor instance and UI.
+ *
+ * @method destroyCellEditor
+ */
+destroyCellEditor : function() {
+    if(this._oCellEditor) {
+        this._oCellEditor.destroy();
+        this._oCellEditor = null;
+    }   
+},
+
+/**
+ * Passes through showEvent of the active CellEditor.
+ *
+ * @method _onEditorShowEvent
+ * @param oArgs {Object}  Custom Event args.
+ * @private 
+ */
+_onEditorShowEvent : function(oArgs) {
+    this.fireEvent("editorShowEvent", oArgs);
+},
+
+/**
+ * Passes through keydownEvent of the active CellEditor.
+ * @param oArgs {Object}  Custom Event args. 
+ *
+ * @method _onEditorKeydownEvent
+ * @private 
+ */
+_onEditorKeydownEvent : function(oArgs) {
+    this.fireEvent("editorKeydownEvent", oArgs);
+},
+
+/**
+ * Passes through revertEvent of the active CellEditor.
+ *
+ * @method _onEditorRevertEvent
+ * @param oArgs {Object}  Custom Event args. 
+ * @private  
+ */
+_onEditorRevertEvent : function(oArgs) {
+    this.fireEvent("editorRevertEvent", oArgs);
+},
+
+/**
+ * Passes through saveEvent of the active CellEditor.
+ *
+ * @method _onEditorSaveEvent
+ * @param oArgs {Object}  Custom Event args.  
+ * @private 
+ */
+_onEditorSaveEvent : function(oArgs) {
+    this.fireEvent("editorSaveEvent", oArgs);
+},
+
+/**
+ * Passes through cancelEvent of the active CellEditor.
+ *
+ * @method _onEditorCancelEvent
+ * @param oArgs {Object}  Custom Event args.
+ * @private   
+ */
+_onEditorCancelEvent : function(oArgs) {
+    this.fireEvent("editorCancelEvent", oArgs);
+},
+
+/**
+ * Passes through blurEvent of the active CellEditor.
+ *
+ * @method _onEditorBlurEvent
+ * @param oArgs {Object}  Custom Event args. 
+ * @private  
+ */
+_onEditorBlurEvent : function(oArgs) {
+    this.fireEvent("editorBlurEvent", oArgs);
+},
+
+/**
+ * Passes through blockEvent of the active CellEditor.
+ *
+ * @method _onEditorBlockEvent
+ * @param oArgs {Object}  Custom Event args. 
+ * @private  
+ */
+_onEditorBlockEvent : function(oArgs) {
+    this.fireEvent("editorBlockEvent", oArgs);
+},
+
+/**
+ * Passes through unblockEvent of the active CellEditor.
+ *
+ * @method _onEditorUnblockEvent
+ * @param oArgs {Object}  Custom Event args. 
+ * @private  
+ */
+_onEditorUnblockEvent : function(oArgs) {
+    this.fireEvent("editorUnblockEvent", oArgs);
+},
+
+/**
+ * Public handler of the editorBlurEvent. By default, saves on blur if
+ * disableBtns is true, otherwise cancels on blur. 
+ *
+ * @method onEditorBlurEvent
+ * @param oArgs {Object}  Custom Event args.  
+ */
+onEditorBlurEvent : function(oArgs) {
+    if(oArgs.editor.disableBtns) {
+        // Save on blur
+        if(oArgs.editor.save) { // Backward incompatible
+            oArgs.editor.save();
+        }
+    }      
+    else if(oArgs.editor.cancel) { // Backward incompatible
+        // Cancel on blur
+        oArgs.editor.cancel();
+    }      
+},
+
+/**
+ * Public handler of the editorBlockEvent. By default, disables DataTable UI.
+ *
+ * @method onEditorBlockEvent
+ * @param oArgs {Object}  Custom Event args.  
+ */
+onEditorBlockEvent : function(oArgs) {
+    this.disable();
+},
+
+/**
+ * Public handler of the editorUnblockEvent. By default, undisables DataTable UI.
+ *
+ * @method onEditorUnblockEvent
+ * @param oArgs {Object}  Custom Event args.  
+ */
+onEditorUnblockEvent : function(oArgs) {
+    this.undisable();
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// ABSTRACT METHODS
+
+/**
+ * Overridable method gives implementers a hook to access data before
+ * it gets added to RecordSet and rendered to the TBODY.
+ *
+ * @method doBeforeLoadData
+ * @param sRequest {String} Original request.
+ * @param oResponse {Object} <a href="" object</a>.
+ * @param oPayload {MIXED} additional arguments
+ * @return {Boolean} Return true to continue loading data into RecordSet and
+ * updating DataTable with new Records, false to cancel.
+ */
+doBeforeLoadData : function(sRequest, oResponse, oPayload) {
+    return true;
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public Custom Event Handlers
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Custom event handler to sort Column.
+ *
+ * @method onEventSortColumn
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventSortColumn : function(oArgs) {
+//TODO: support form elements in sortable columns
+    var evt = oArgs.event;
+    var target = oArgs.target;
+
+    var el = this.getThEl(target) || this.getTdEl(target);
+    if(el) {
+        var oColumn = this.getColumn(el);
+        if(oColumn.sortable) {
+            Ev.stopEvent(evt);
+            this.sortColumn(oColumn);
+        }
+    }
+    else {
+    }
+},
+
+/**
+ * Custom event handler to select Column.
+ *
+ * @method onEventSelectColumn
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventSelectColumn : function(oArgs) {
+    this.selectColumn(oArgs.target);
+},
+
+/**
+ * Custom event handler to highlight Column. Accounts for spurious
+ * caused-by-child events. 
+ *
+ * @method onEventHighlightColumn
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventHighlightColumn : function(oArgs) {
+    this.highlightColumn(oArgs.target);
+},
+
+/**
+ * Custom event handler to unhighlight Column. Accounts for spurious
+ * caused-by-child events. 
+ *
+ * @method onEventUnhighlightColumn
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventUnhighlightColumn : function(oArgs) {
+    this.unhighlightColumn(oArgs.target);
+},
+
+/**
+ * Custom event handler to manage selection according to desktop paradigm.
+ *
+ * @method onEventSelectRow
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventSelectRow : function(oArgs) {
+    var sMode = this.get("selectionMode");
+    if(sMode == "single") {
+        this._handleSingleSelectionByMouse(oArgs);
+    }
+    else {
+        this._handleStandardSelectionByMouse(oArgs);
+    }
+},
+
+/**
+ * Custom event handler to select cell.
+ *
+ * @method onEventSelectCell
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventSelectCell : function(oArgs) {
+    var sMode = this.get("selectionMode");
+    if(sMode == "cellblock") {
+        this._handleCellBlockSelectionByMouse(oArgs);
+    }
+    else if(sMode == "cellrange") {
+        this._handleCellRangeSelectionByMouse(oArgs);
+    }
+    else {
+        this._handleSingleCellSelectionByMouse(oArgs);
+    }
+},
+
+/**
+ * Custom event handler to highlight row. Accounts for spurious
+ * caused-by-child events. 
+ *
+ * @method onEventHighlightRow
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventHighlightRow : function(oArgs) {
+    this.highlightRow(oArgs.target);
+},
+
+/**
+ * Custom event handler to unhighlight row. Accounts for spurious
+ * caused-by-child events. 
+ *
+ * @method onEventUnhighlightRow
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventUnhighlightRow : function(oArgs) {
+    this.unhighlightRow(oArgs.target);
+},
+
+/**
+ * Custom event handler to highlight cell. Accounts for spurious
+ * caused-by-child events. 
+ *
+ * @method onEventHighlightCell
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventHighlightCell : function(oArgs) {
+    this.highlightCell(oArgs.target);
+},
+
+/**
+ * Custom event handler to unhighlight cell. Accounts for spurious
+ * caused-by-child events. 
+ *
+ * @method onEventUnhighlightCell
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventUnhighlightCell : function(oArgs) {
+    this.unhighlightCell(oArgs.target);
+},
+
+/**
+ * Custom event handler to format cell.
+ *
+ * @method onEventFormatCell
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventFormatCell : function(oArgs) {
+    var target = oArgs.target;
+
+    var elCell = this.getTdEl(target);
+    if(elCell) {
+        var oColumn = this.getColumn(this.getCellIndex(elCell));
+        this.formatCell(elCell.firstChild, this.getRecord(elCell), oColumn);
+    }
+    else {
+    }
+},
+
+/**
+ * Custom event handler to edit cell.
+ *
+ * @method onEventShowCellEditor
+ * @param oArgs.event {HTMLEvent} Event object.
+ * @param oArgs.target {HTMLElement} Target element.
+ */
+onEventShowCellEditor : function(oArgs) {
+    if(!this.isDisabled()) {
+        this.showCellEditor(oArgs.target);
+    }
+},
+
+/**
+ * Custom event handler to save active CellEditor input.
+ *
+ * @method onEventSaveCellEditor
+ */
+onEventSaveCellEditor : function(oArgs) {
+    if(this._oCellEditor) {
+        if(this._oCellEditor.save) {
+            this._oCellEditor.save();
+        }
+        // Backward compatibility
+        else {
+            this.saveCellEditor();
+        }
+    }
+},
+
+/**
+ * Custom event handler to cancel active CellEditor.
+ *
+ * @method onEventCancelCellEditor
+ */
+onEventCancelCellEditor : function(oArgs) {
+    if(this._oCellEditor) {
+        if(this._oCellEditor.cancel) {
+            this._oCellEditor.cancel();
+        }
+        // Backward compatibility
+        else {
+            this.cancelCellEditor();
+        }
+    }
+},
+
+/**
+ * Callback function receives data from DataSource and populates an entire
+ * DataTable with Records and TR elements, clearing previous Records, if any.
+ *
+ * @method onDataReturnInitializeTable
+ * @param sRequest {String} Original request.
+ * @param oResponse {Object} <a href="" object</a>.
+ * @param oPayload {MIXED} (optional) Additional argument(s)
+ */
+onDataReturnInitializeTable : function(sRequest, oResponse, oPayload) {
+    if((this instanceof DT) && this._sId) {
+        this.initializeTable();
+    
+        this.onDataReturnSetRows(sRequest,oResponse,oPayload);
+    }
+},
+
+/**
+ * Callback function receives reponse from DataSource, replaces all existing
+ * Records in  RecordSet, updates TR elements with new data, and updates state
+ * UI for pagination and sorting from payload data, if necessary. 
+ *  
+ * @method onDataReturnReplaceRows
+ * @param oRequest {MIXED} Original generated request.
+ * @param oResponse {Object} <a href="" object</a>.
+ * @param oPayload {MIXED} (optional) Additional argument(s)
+ */
+onDataReturnReplaceRows : function(oRequest, oResponse, oPayload) {
+    if((this instanceof DT) && this._sId) {
+        this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
+    
+        // Pass data through abstract method for any transformations
+        var ok    = this.doBeforeLoadData(oRequest, oResponse, oPayload),
+            pag   = this.get('paginator'),
+            index = 0;
+    
+        // Data ok to set
+        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
+            // Update Records
+            this._oRecordSet.reset();
+    
+            if (this.get('dynamicData')) {
+                if (oPayload && oPayload.pagination &&
+                    lang.isNumber(oPayload.pagination.recordOffset)) {
+                    index = oPayload.pagination.recordOffset;
+                } else if (pag) {
+                    index = pag.getStartIndex();
+                }
+            }
+    
+            this._oRecordSet.setRecords(oResponse.results, index | 0);
+            
+            // Update state
+            this._handleDataReturnPayload(oRequest, oResponse, oPayload);
+            
+            // Update UI
+            this.render();    
+        }
+        // Error
+        else if(ok && oResponse.error) {
+            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
+        }
+    }
+},
+
+/**
+ * Callback function receives data from DataSource and appends to an existing
+ * DataTable new Records and, if applicable, creates or updates
+ * corresponding TR elements.
+ *
+ * @method onDataReturnAppendRows
+ * @param sRequest {String} Original request.
+ * @param oResponse {Object} <a href="" object</a>.
+ * @param oPayload {MIXED} (optional) Additional argument(s)
+ */
+onDataReturnAppendRows : function(sRequest, oResponse, oPayload) {
+    if((this instanceof DT) && this._sId) {
+        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
+    
+        // Pass data through abstract method for any transformations
+        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
+    
+        // Data ok to append
+        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {        
+            // Append rows
+            this.addRows(oResponse.results);
+    
+            // Update state
+            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
+        }
+        // Error
+        else if(ok && oResponse.error) {
+            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
+        }
+    }
+},
+
+/**
+ * Callback function receives data from DataSource and inserts new records
+ * starting at the index specified in oPayload.insertIndex. The value for
+ * oPayload.insertIndex can be populated when sending the request to the DataSource,
+ * or by accessing oPayload.insertIndex with the doBeforeLoadData() method at runtime.
+ * If applicable, creates or updates corresponding TR elements.
+ *
+ * @method onDataReturnInsertRows
+ * @param sRequest {String} Original request.
+ * @param oResponse {Object} <a href="" object</a>.
+ * @param oPayload {MIXED} Argument payload, looks in oPayload.insertIndex.
+ */
+onDataReturnInsertRows : function(sRequest, oResponse, oPayload) {
+    if((this instanceof DT) && this._sId) {
+        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
+    
+        // Pass data through abstract method for any transformations
+        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
+    
+        // Data ok to append
+        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
+            // Insert rows
+            this.addRows(oResponse.results, (oPayload ? oPayload.insertIndex : 0));
+    
+            // Update state
+            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
+        }
+        // Error
+        else if(ok && oResponse.error) {
+            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
+        }
+    }
+},
+
+/**
+ * Callback function receives data from DataSource and incrementally updates Records
+ * starting at the index specified in oPayload.updateIndex. The value for
+ * oPayload.updateIndex can be populated when sending the request to the DataSource,
+ * or by accessing oPayload.updateIndex with the doBeforeLoadData() method at runtime.
+ * If applicable, creates or updates corresponding TR elements.
+ *
+ * @method onDataReturnUpdateRows
+ * @param sRequest {String} Original request.
+ * @param oResponse {Object} <a href="" object</a>.
+ * @param oPayload {MIXED} Argument payload, looks in oPayload.updateIndex.
+ */
+onDataReturnUpdateRows : function(sRequest, oResponse, oPayload) {
+    if((this instanceof DT) && this._sId) {
+        this.fireEvent("dataReturnEvent", {request:sRequest,response:oResponse,payload:oPayload});
+    
+        // Pass data through abstract method for any transformations
+        var ok = this.doBeforeLoadData(sRequest, oResponse, oPayload);
+    
+        // Data ok to append
+        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
+            // Insert rows
+            this.updateRows((oPayload ? oPayload.updateIndex : 0), oResponse.results);
+    
+            // Update state
+            this._handleDataReturnPayload(sRequest, oResponse, oPayload);
+        }
+        // Error
+        else if(ok && oResponse.error) {
+            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
+        }
+    }
+},
+
+/**
+ * Callback function receives reponse from DataSource and populates the
+ * RecordSet with the results.
+ *  
+ * @method onDataReturnSetRows
+ * @param oRequest {MIXED} Original generated request.
+ * @param oResponse {Object} <a href="" object</a>.
+ * @param oPayload {MIXED} (optional) Additional argument(s)
+ */
+onDataReturnSetRows : function(oRequest, oResponse, oPayload) {
+    if((this instanceof DT) && this._sId) {
+        this.fireEvent("dataReturnEvent", {request:oRequest,response:oResponse,payload:oPayload});
+    
+        // Pass data through abstract method for any transformations
+        var ok    = this.doBeforeLoadData(oRequest, oResponse, oPayload),
+            pag   = this.get('paginator'),
+            index = 0;
+    
+        // Data ok to set
+        if(ok && oResponse && !oResponse.error && lang.isArray(oResponse.results)) {
+            // Update Records
+            if (this.get('dynamicData')) {
+                if (oPayload && oPayload.pagination &&
+                    lang.isNumber(oPayload.pagination.recordOffset)) {
+                    index = oPayload.pagination.recordOffset;
+                } else if (pag) {
+                    index = pag.getStartIndex();
+                }
+                
+                this._oRecordSet.reset(); // Bug 2290604: dyanmic data shouldn't keep accumulating by default
+            }
+    
+            this._oRecordSet.setRecords(oResponse.results, index | 0);
+    
+            // Update state
+            this._handleDataReturnPayload(oRequest, oResponse, oPayload);
+            
+            // Update UI
+            this.render();
+        }
+        // Error
+        else if(ok && oResponse.error) {
+            this.showTableMessage(this.get("MSG_ERROR"), DT.CLASS_ERROR);
+        }
+    }
+    else {
+    }
+},
+
+/**
+ * Hook to update oPayload before consumption.
+ *  
+ * @method handleDataReturnPayload
+ * @param oRequest {MIXED} Original generated request.
+ * @param oResponse {Object} <a href="" object</a>.
+ * @param oPayload {MIXED} State values.
+ * @return oPayload {MIXED} State values.
+ */
+handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
+    return oPayload || {};
+},
+
+/**
+ * Updates the DataTable with state data sent in an onDataReturn* payload.
+ *  
+ * @method _handleDataReturnPayload
+ * @param oRequest {MIXED} Original generated request.
+ * @param oResponse {Object} <a href="" object</a>.
+ * @param oPayload {MIXED} State values
+ * @private
+ */
+_handleDataReturnPayload : function (oRequest, oResponse, oPayload) {
+    oPayload = this.handleDataReturnPayload(oRequest, oResponse, oPayload);
+    if(oPayload) {
+        // Update pagination
+        var oPaginator = this.get('paginator');
+        if (oPaginator) {
+            // Update totalRecords
+            if(this.get("dynamicData")) {
+                if (widget.Paginator.isNumeric(oPayload.totalRecords)) {
+                    oPaginator.set('totalRecords',oPayload.totalRecords);
+                }
+            }
+            else {
+                oPaginator.set('totalRecords',this._oRecordSet.getLength());
+            }
+            // Update other paginator values
+            if (lang.isObject(oPayload.pagination)) {
+                oPaginator.set('rowsPerPage',oPayload.pagination.rowsPerPage);
+                oPaginator.set('recordOffset',oPayload.pagination.recordOffset);
+            }
+        }
+
+        // Update sorting
+        if (oPayload.sortedBy) {
+            // Set the sorting values in preparation for refresh
+            this.set('sortedBy', oPayload.sortedBy);
+        }
+        // Backwards compatibility for sorting
+        else if (oPayload.sorting) {
+            // Set the sorting values in preparation for refresh
+            this.set('sortedBy', oPayload.sorting);
+        }
+    }
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    /////////////////////////////////////////////////////////////////////////////
+    //
+    // Custom Events
+    //
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Fired when the DataTable's rows are rendered from an initialized state.
+     *
+     * @event initEvent
+     */
+
+    /**
+     * Fired before the DataTable's DOM is rendered or modified.
+     *
+     * @event beforeRenderEvent
+     */
+
+    /**
+     * Fired when the DataTable's DOM is rendered or modified.
+     *
+     * @event renderEvent
+     */
+
+    /**
+     * Fired when the DataTable's post-render routine is complete, including
+     * Column width validations.
+     *
+     * @event postRenderEvent
+     */
+
+    /**
+     * Fired when the DataTable is disabled.
+     *
+     * @event disableEvent
+     */
+
+    /**
+     * Fired when the DataTable is undisabled.
+     *
+     * @event undisableEvent
+     */
+
+    /**
+     * Fired when data is returned from DataSource but before it is consumed by
+     * DataTable.
+     *
+     * @event dataReturnEvent
+     * @param oArgs.request {String} Original request.
+     * @param oArgs.response {Object} Response object.
+     */
+
+    /**
+     * Fired when the DataTable has a focus event.
+     *
+     * @event tableFocusEvent
+     */
+
+    /**
+     * Fired when the DataTable THEAD element has a focus event.
+     *
+     * @event theadFocusEvent
+     */
+
+    /**
+     * Fired when the DataTable TBODY element has a focus event.
+     *
+     * @event tbodyFocusEvent
+     */
+
+    /**
+     * Fired when the DataTable has a blur event.
+     *
+     * @event tableBlurEvent
+     */
+
+    /*TODO implement theadBlurEvent
+     * Fired when the DataTable THEAD element has a blur event.
+     *
+     * @event theadBlurEvent
+     */
+
+    /*TODO: implement tbodyBlurEvent
+     * Fired when the DataTable TBODY element has a blur event.
+     *
+     * @event tbodyBlurEvent
+     */
+
+    /**
+     * Fired when the DataTable has a key event.
+     *
+     * @event tableKeyEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
+     */
+
+    /**
+     * Fired when the DataTable THEAD element has a key event.
+     *
+     * @event theadKeyEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
+     */
+
+    /**
+     * Fired when the DataTable TBODY element has a key event.
+     *
+     * @event tbodyKeyEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
+     */
+
+    /**
+     * Fired when the DataTable has a mouseover.
+     *
+     * @event tableMouseoverEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
+     *
+     */
+
+    /**
+     * Fired when the DataTable has a mouseout.
+     *
+     * @event tableMouseoutEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
+     *
+     */
+
+    /**
+     * Fired when the DataTable has a mousedown.
+     *
+     * @event tableMousedownEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
+     *
+     */
+
+    /**
+     * Fired when the DataTable has a mouseup.
+     *
+     * @event tableMouseupEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
+     *
+     */
+
+    /**
+     * Fired when the DataTable has a click.
+     *
+     * @event tableClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
+     *
+     */
+
+    /**
+     * Fired when the DataTable has a dblclick.
+     *
+     * @event tableDblclickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The DataTable's TABLE element.
+     *
+     */
+
+    /**
+     * Fired when a message is shown in the DataTable's message element.
+     *
+     * @event tableMsgShowEvent
+     * @param oArgs.html {HTML} The HTML displayed.
+     * @param oArgs.className {String} The className assigned.
+     *
+     */
+
+    /**
+     * Fired when the DataTable's message element is hidden.
+     *
+     * @event tableMsgHideEvent
+     */
+
+    /**
+     * Fired when a THEAD row has a mouseover.
+     *
+     * @event theadRowMouseoverEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a THEAD row has a mouseout.
+     *
+     * @event theadRowMouseoutEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a THEAD row has a mousedown.
+     *
+     * @event theadRowMousedownEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a THEAD row has a mouseup.
+     *
+     * @event theadRowMouseupEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a THEAD row has a click.
+     *
+     * @event theadRowClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a THEAD row has a dblclick.
+     *
+     * @event theadRowDblclickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a THEAD cell has a mouseover.
+     *
+     * @event theadCellMouseoverEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TH element.
+     *
+     */
+
+    /**
+     * Fired when a THEAD cell has a mouseout.
+     *
+     * @event theadCellMouseoutEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TH element.
+     *
+     */
+
+    /**
+     * Fired when a THEAD cell has a mousedown.
+     *
+     * @event theadCellMousedownEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TH element.
+     */
+
+    /**
+     * Fired when a THEAD cell has a mouseup.
+     *
+     * @event theadCellMouseupEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TH element.
+     */
+
+    /**
+     * Fired when a THEAD cell has a click.
+     *
+     * @event theadCellClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TH element.
+     */
+
+    /**
+     * Fired when a THEAD cell has a dblclick.
+     *
+     * @event theadCellDblclickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TH element.
+     */
+
+    /**
+     * Fired when a THEAD label has a mouseover.
+     *
+     * @event theadLabelMouseoverEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The SPAN element.
+     *
+     */
+
+    /**
+     * Fired when a THEAD label has a mouseout.
+     *
+     * @event theadLabelMouseoutEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The SPAN element.
+     *
+     */
+
+    /**
+     * Fired when a THEAD label has a mousedown.
+     *
+     * @event theadLabelMousedownEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The SPAN element.
+     */
+
+    /**
+     * Fired when a THEAD label has a mouseup.
+     *
+     * @event theadLabelMouseupEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The SPAN element.
+     */
+
+    /**
+     * Fired when a THEAD label has a click.
+     *
+     * @event theadLabelClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The SPAN element.
+     */
+
+    /**
+     * Fired when a THEAD label has a dblclick.
+     *
+     * @event theadLabelDblclickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The SPAN element.
+     */
+
+    /**
+     * Fired when a column is sorted.
+     *
+     * @event columnSortEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     * @param oArgs.dir {String} Sort direction: YAHOO.widget.DataTable.CLASS_ASC
+     * or YAHOO.widget.DataTable.CLASS_DESC.
+     */
+
+    /**
+     * Fired when a column width is set.
+     *
+     * @event columnSetWidthEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     * @param oArgs.width {Number} The width in pixels.
+     */
+
+    /**
+     * Fired when a column width is unset.
+     *
+     * @event columnUnsetWidthEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     */
+
+    /**
+     * Fired when a column is drag-resized.
+     *
+     * @event columnResizeEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     * @param oArgs.target {HTMLElement} The TH element.
+     * @param oArgs.width {Number} Width in pixels.     
+     */
+
+    /**
+     * Fired when a Column is moved to a new index.
+     *
+     * @event columnReorderEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     * @param oArgs.oldIndex {Number} The previous tree index position.
+     */
+
+    /**
+     * Fired when a column is hidden.
+     *
+     * @event columnHideEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     */
+
+    /**
+     * Fired when a column is shown.
+     *
+     * @event columnShowEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     */
+
+    /**
+     * Fired when a column is selected.
+     *
+     * @event columnSelectEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     */
+
+    /**
+     * Fired when a column is unselected.
+     *
+     * @event columnUnselectEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     */
+    /**
+     * Fired when a column is removed.
+     *
+     * @event columnRemoveEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     */
+
+    /**
+     * Fired when a column is inserted.
+     *
+     * @event columnInsertEvent
+     * @param oArgs.column {YAHOO.widget.Column} The Column instance.
+     * @param oArgs.index {Number} The index position.
+     */
+
+    /**
+     * Fired when a column is highlighted.
+     *
+     * @event columnHighlightEvent
+     * @param oArgs.column {YAHOO.widget.Column} The highlighted Column.
+     */
+
+    /**
+     * Fired when a column is unhighlighted.
+     *
+     * @event columnUnhighlightEvent
+     * @param oArgs.column {YAHOO.widget.Column} The unhighlighted Column.
+     */
+
+
+    /**
+     * Fired when a row has a mouseover.
+     *
+     * @event rowMouseoverEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a row has a mouseout.
+     *
+     * @event rowMouseoutEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a row has a mousedown.
+     *
+     * @event rowMousedownEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a row has a mouseup.
+     *
+     * @event rowMouseupEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a row has a click.
+     *
+     * @event rowClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a row has a dblclick.
+     *
+     * @event rowDblclickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TR element.
+     */
+
+    /**
+     * Fired when a row is added.
+     *
+     * @event rowAddEvent
+     * @param oArgs.record {YAHOO.widget.Record} The added Record.
+     */
+     
+    /**
+     * Fired when rows are added.
+     *
+     * @event rowsAddEvent
+     * @param oArgs.record {YAHOO.widget.Record[]} The added Records.
+     */
+
+    /**
+     * Fired when a row is updated.
+     *
+     * @event rowUpdateEvent
+     * @param oArgs.record {YAHOO.widget.Record} The updated Record.
+     * @param oArgs.oldData {Object} Object literal of the old data.
+     */
+
+    /**
+     * Fired when a row is deleted.
+     *
+     * @event rowDeleteEvent
+     * @param oArgs.oldData {Object} Object literal of the deleted data.
+     * @param oArgs.recordIndex {Number} Index of the deleted Record.
+     * @param oArgs.trElIndex {Number} Index of the deleted TR element, if on current page.
+     */
+     
+    /**
+     * Fired when rows are deleted.
+     *
+     * @event rowsDeleteEvent
+     * @param oArgs.oldData {Object[]} Array of object literals of the deleted data.
+     * @param oArgs.recordIndex {Number} Index of the first deleted Record.
+     * @param oArgs.count {Number} Number of deleted Records.
+     */
+
+    /**
+     * Fired when a row is selected.
+     *
+     * @event rowSelectEvent
+     * @param oArgs.el {HTMLElement} The selected TR element, if applicable.
+     * @param oArgs.record {YAHOO.widget.Record} The selected Record.
+     */
+
+    /**
+     * Fired when a row is unselected.
+     *
+     * @event rowUnselectEvent
+     * @param oArgs.el {HTMLElement} The unselected TR element, if applicable.
+     * @param oArgs.record {YAHOO.widget.Record} The unselected Record.
+     */
+
+    /**
+     * Fired when all row selections are cleared.
+     *
+     * @event unselectAllRowsEvent
+     */
+
+    /**
+     * Fired when a row is highlighted.
+     *
+     * @event rowHighlightEvent
+     * @param oArgs.el {HTMLElement} The highlighted TR element.
+     * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
+     */
+
+    /**
+     * Fired when a row is unhighlighted.
+     *
+     * @event rowUnhighlightEvent
+     * @param oArgs.el {HTMLElement} The highlighted TR element.
+     * @param oArgs.record {YAHOO.widget.Record} The highlighted Record.
+     */
+
+    /**
+     * Fired when a cell is updated.
+     *
+     * @event cellUpdateEvent
+     * @param oArgs.record {YAHOO.widget.Record} The updated Record.
+     * @param oArgs.column {YAHOO.widget.Column} The updated Column.
+     * @param oArgs.oldData {Object} Original data value of the updated cell.
+     */
+
+    /**
+     * Fired when a cell has a mouseover.
+     *
+     * @event cellMouseoverEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TD element.
+     */
+
+    /**
+     * Fired when a cell has a mouseout.
+     *
+     * @event cellMouseoutEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TD element.
+     */
+
+    /**
+     * Fired when a cell has a mousedown.
+     *
+     * @event cellMousedownEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TD element.
+     */
+
+    /**
+     * Fired when a cell has a mouseup.
+     *
+     * @event cellMouseupEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TD element.
+     */
+
+    /**
+     * Fired when a cell has a click.
+     *
+     * @event cellClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TD element.
+     */
+
+    /**
+     * Fired when a cell has a dblclick.
+     *
+     * @event cellDblclickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The TD element.
+     */
+
+    /**
+     * Fired when a cell is formatted.
+     *
+     * @event cellFormatEvent
+     * @param oArgs.el {HTMLElement} The formatted TD element.
+     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
+     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
+     * @param oArgs.key {String} (deprecated) The key of the formatted cell.
+     */
+
+    /**
+     * Fired when a cell is selected.
+     *
+     * @event cellSelectEvent
+     * @param oArgs.el {HTMLElement} The selected TD element.
+     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
+     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
+     * @param oArgs.key {String} (deprecated) The key of the selected cell.
+     */
+
+    /**
+     * Fired when a cell is unselected.
+     *
+     * @event cellUnselectEvent
+     * @param oArgs.el {HTMLElement} The unselected TD element.
+     * @param oArgs.record {YAHOO.widget.Record} The associated Record.
+     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
+     * @param oArgs.key {String} (deprecated) The key of the unselected cell.
+
+     */
+
+    /**
+     * Fired when a cell is highlighted.
+     *
+     * @event cellHighlightEvent
+     * @param oArgs.el {HTMLElement} The highlighted TD element.
+     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
+     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
+     * @param oArgs.key {String} (deprecated) The key of the highlighted cell.
+
+     */
+
+    /**
+     * Fired when a cell is unhighlighted.
+     *
+     * @event cellUnhighlightEvent
+     * @param oArgs.el {HTMLElement} The unhighlighted TD element.
+     * @param oArgs.record {YAHOO.widget.Record} The associated Record instance.
+     * @param oArgs.column {YAHOO.widget.Column} The associated Column instance.
+     * @param oArgs.key {String} (deprecated) The key of the unhighlighted cell.
+
+     */
+
+    /**
+     * Fired when all cell selections are cleared.
+     *
+     * @event unselectAllCellsEvent
+     */
+
+    /**
+     * Fired when a CellEditor is shown.
+     *
+     * @event editorShowEvent
+     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
+     */
+
+    /**
+     * Fired when a CellEditor has a keydown.
+     *
+     * @event editorKeydownEvent
+     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
+     * @param oArgs.event {HTMLEvent} The event object.
+     */
+
+    /**
+     * Fired when a CellEditor input is reverted.
+     *
+     * @event editorRevertEvent
+     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
+     * @param oArgs.newData {Object} New data value from form input field.
+     * @param oArgs.oldData {Object} Old data value.
+     */
+
+    /**
+     * Fired when a CellEditor input is saved.
+     *
+     * @event editorSaveEvent
+     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
+     * @param oArgs.newData {Object} New data value from form input field.
+     * @param oArgs.oldData {Object} Old data value.
+     */
+
+    /**
+     * Fired when a CellEditor input is canceled.
+     *
+     * @event editorCancelEvent
+     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
+     */
+
+    /**
+     * Fired when a CellEditor has a blur event.
+     *
+     * @event editorBlurEvent
+     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
+     */
+
+    /**
+     * Fired when a CellEditor is blocked.
+     *
+     * @event editorBlockEvent
+     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
+     */
+
+    /**
+     * Fired when a CellEditor is unblocked.
+     *
+     * @event editorUnblockEvent
+     * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
+     */
+
+
+
+
+
+    /**
+     * Fired when a link is clicked.
+     *
+     * @event linkClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The A element.
+     */
+
+    /**
+     * Fired when a BUTTON element or INPUT element of type "button", "image",
+     * "submit", "reset" is clicked.
+     *
+     * @event buttonClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The BUTTON element.
+     */
+
+    /**
+     * Fired when a CHECKBOX element is clicked.
+     *
+     * @event checkboxClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The CHECKBOX element.
+     */
+
+    /**
+     * Fired when a SELECT element is changed.
+     *
+     * @event dropdownChangeEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The SELECT element.
+     */
+
+    /**
+     * Fired when a RADIO element is clicked.
+     *
+     * @event radioClickEvent
+     * @param oArgs.event {HTMLEvent} The event object.
+     * @param oArgs.target {HTMLElement} The RADIO element.
+     */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Deprecated APIs
+//
+/////////////////////////////////////////////////////////////////////////////
+  
+/*
+ * @method showCellEditorBtns
+ * @deprecated Use CellEditor.renderBtns() 
+ */
+showCellEditorBtns : function(elContainer) {
+    // Buttons
+    var elBtnsDiv = elContainer.appendChild(document.createElement("div"));
+    Dom.addClass(elBtnsDiv, DT.CLASS_BUTTON);
+
+    // Save button
+    var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
+    Dom.addClass(elSaveBtn, DT.CLASS_DEFAULT);
+    elSaveBtn.innerHTML = "OK";
+    Ev.addListener(elSaveBtn, "click", function(oArgs, oSelf) {
+        oSelf.onEventSaveCellEditor(oArgs, oSelf);
+        oSelf.focusTbodyEl();
+    }, this, true);
+
+    // Cancel button
+    var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
+    elCancelBtn.innerHTML = "Cancel";
+    Ev.addListener(elCancelBtn, "click", function(oArgs, oSelf) {
+        oSelf.onEventCancelCellEditor(oArgs, oSelf);
+        oSelf.focusTbodyEl();
+    }, this, true);
+
+},
+
+/**
+ * @method resetCellEditor
+ * @deprecated Use destroyCellEditor 
+ */
+resetCellEditor : function() {
+    var elContainer = this._oCellEditor.container;
+    elContainer.style.display = "none";
+    Ev.purgeElement(elContainer, true);
+    elContainer.innerHTML = "";
+    this._oCellEditor.value = null;
+    this._oCellEditor.isActive = false;
+
+},
+
+/**
+ * @event editorUpdateEvent
+ * @deprecated Use CellEditor class.
+ */
+
+/**
+ * @method getBody
+ * @deprecated Use getTbodyEl().
+ */
+getBody : function() {
+    // Backward compatibility
+    return this.getTbodyEl();
+},
+
+/**
+ * @method getCell
+ * @deprecated Use getTdEl().
+ */
+getCell : function(index) {
+    // Backward compatibility
+    return this.getTdEl(index);
+},
+
+/**
+ * @method getRow
+ * @deprecated Use getTrEl().
+ */
+getRow : function(index) {
+    // Backward compatibility
+    return this.getTrEl(index);
+},
+
+/**
+ * @method refreshView
+ * @deprecated Use render.
+ */
+refreshView : function() {
+    // Backward compatibility
+    this.render();
+},
+
+/**
+ * @method select
+ * @deprecated Use selectRow.
+ */
+select : function(els) {
+    // Backward compatibility
+    if(!lang.isArray(els)) {
+        els = [els];
+    }
+    for(var i=0; i<els.length; i++) {
+        this.selectRow(els[i]);
+    }
+},
+
+/**
+ * @method onEventEditCell
+ * @deprecated Use onEventShowCellEditor.
+ */
+onEventEditCell : function(oArgs) {
+    // Backward compatibility
+    this.onEventShowCellEditor(oArgs);
+},
+
+/**
+ * @method _syncColWidths
+ * @deprecated Use validateColumnWidths.
+ */
+_syncColWidths : function() {
+    // Backward compatibility
+    this.validateColumnWidths();
+}
+
+/**
+ * @event headerRowMouseoverEvent
+ * @deprecated Use theadRowMouseoverEvent.
+ */
+
+/**
+ * @event headerRowMouseoutEvent
+ * @deprecated Use theadRowMouseoutEvent.
+ */
+
+/**
+ * @event headerRowMousedownEvent
+ * @deprecated Use theadRowMousedownEvent.
+ */
+
+/**
+ * @event headerRowClickEvent
+ * @deprecated Use theadRowClickEvent.
+ */
+
+/**
+ * @event headerRowDblclickEvent
+ * @deprecated Use theadRowDblclickEvent.
+ */
+
+/**
+ * @event headerCellMouseoverEvent
+ * @deprecated Use theadCellMouseoverEvent.
+ */
+
+/**
+ * @event headerCellMouseoutEvent
+ * @deprecated Use theadCellMouseoutEvent.
+ */
+
+/**
+ * @event headerCellMousedownEvent
+ * @deprecated Use theadCellMousedownEvent.
+ */
+
+/**
+ * @event headerCellClickEvent
+ * @deprecated Use theadCellClickEvent.
+ */
+
+/**
+ * @event headerCellDblclickEvent
+ * @deprecated Use theadCellDblclickEvent.
+ */
+
+/**
+ * @event headerLabelMouseoverEvent
+ * @deprecated Use theadLabelMouseoverEvent.
+ */
+
+/**
+ * @event headerLabelMouseoutEvent
+ * @deprecated Use theadLabelMouseoutEvent.
+ */
+
+/**
+ * @event headerLabelMousedownEvent
+ * @deprecated Use theadLabelMousedownEvent.
+ */
+
+/**
+ * @event headerLabelClickEvent
+ * @deprecated Use theadLabelClickEvent.
+ */
+
+/**
+ * @event headerLabelDbllickEvent
+ * @deprecated Use theadLabelDblclickEvent.
+ */
+
+});
+
+/**
+ * Alias for onDataReturnSetRows for backward compatibility
+ * @method onDataReturnSetRecords
+ * @deprecated Use onDataReturnSetRows
+ */
+DT.prototype.
+
+/**
+ * Alias for onPaginatorChange for backward compatibility
+ * @method onPaginatorChange
+ * @deprecated Use onPaginatorChangeRequest
+ */
+DT.prototype.
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Deprecated static APIs
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * @method DataTable.editCheckbox
+ * @deprecated  Use YAHOO.widget.CheckboxCellEditor.
+ */
+DT.editCheckbox = function() {};
+
+/**
+ * @method DataTable.editDate
+ * @deprecated Use YAHOO.widget.DateCellEditor.
+ */
+DT.editDate = function() {};
+
+/**
+ * @method DataTable.editDropdown
+ * @deprecated Use YAHOO.widget.DropdownCellEditor.
+ */
+DT.editDropdown = function() {};
+
+/**
+ * @method DataTable.editRadio
+ * @deprecated Use YAHOO.widget.RadioCellEditor.
+ */
+DT.editRadio = function() {};
+
+/**
+ * @method DataTable.editTextarea
+ * @deprecated Use YAHOO.widget.TextareaCellEditor
+ */
+DT.editTextarea = function() {};
+
+/**
+ * @method DataTable.editTextbox
+ * @deprecated Use YAHOO.widget.TextboxCellEditor
+ */
+DT.editTextbox= function() {};
+
+})();
+
+(function () {
+
+var lang   = YAHOO.lang,
+    util   = YAHOO.util,
+    widget = YAHOO.widget,
+    ua     = YAHOO.env.ua,
+    
+    Dom    = util.Dom,
+    Ev     = util.Event,
+    DS     = util.DataSourceBase,
+    DT     = widget.DataTable,
+    Pag    = widget.Paginator;
+    
+/**
+ * The ScrollingDataTable class extends the DataTable class to provide
+ * functionality for x-scrolling, y-scrolling, and xy-scrolling.
+ *
+ * @namespace YAHOO.widget
+ * @class ScrollingDataTable
+ * @extends YAHOO.widget.DataTable
+ * @constructor
+ * @param elContainer {HTMLElement} Container element for the TABLE.
+ * @param aColumnDefs {Object[]} Array of object literal Column definitions.
+ * @param oDataSource {YAHOO.util.DataSource} DataSource instance.
+ * @param oConfigs {object} (optional) Object literal of configuration values.
+ */
+widget.ScrollingDataTable = function(elContainer,aColumnDefs,oDataSource,oConfigs) {
+    oConfigs = oConfigs || {};
+    
+    // Prevent infinite loop
+    if(oConfigs.scrollable) {
+        oConfigs.scrollable = false;
+    }
+
+    this._init();
+
+    widget.ScrollingDataTable.superclass.constructor.call(this, elContainer,aColumnDefs,oDataSource,oConfigs); 
+
+    // Once per instance
+    this.subscribe("columnShowEvent", this._onColumnChange);
+};
+
+var SDT = widget.ScrollingDataTable;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public constants
+//
+/////////////////////////////////////////////////////////////////////////////
+lang.augmentObject(SDT, {
+
+    /**
+     * Class name assigned to inner DataTable header container.
+     *
+     * @property DataTable.CLASS_HEADER
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-hd"
+     */
+    CLASS_HEADER : "yui-dt-hd",
+    
+    /**
+     * Class name assigned to inner DataTable body container.
+     *
+     * @property DataTable.CLASS_BODY
+     * @type String
+     * @static
+     * @final
+     * @default "yui-dt-bd"
+     */
+    CLASS_BODY : "yui-dt-bd"
+});
+
+lang.extend(SDT, DT, {
+
+/**
+ * Container for fixed header TABLE element.
+ *
+ * @property _elHdContainer
+ * @type HTMLElement
+ * @private
+ */
+_elHdContainer : null,
+
+/**
+ * Fixed header TABLE element.
+ *
+ * @property _elHdTable
+ * @type HTMLElement
+ * @private
+ */
+_elHdTable : null,
+
+/**
+ * Container for scrolling body TABLE element.
+ *
+ * @property _elBdContainer
+ * @type HTMLElement
+ * @private
+ */
+_elBdContainer : null,
+
+/**
+ * Body THEAD element.
+ *
+ * @property _elBdThead
+ * @type HTMLElement
+ * @private
+ */
+_elBdThead : null,
+
+/**
+ * Offscreen container to temporarily clone SDT for auto-width calculation.
+ *
+ * @property _elTmpContainer
+ * @type HTMLElement
+ * @private
+ */
+_elTmpContainer : null,
+
+/**
+ * Offscreen TABLE element for auto-width calculation.
+ *
+ * @property _elTmpTable
+ * @type HTMLElement
+ * @private
+ */
+_elTmpTable : null,
+
+/**
+ * True if x-scrollbar is currently visible.
+ * @property _bScrollbarX
+ * @type Boolean
+ * @private 
+ */
+_bScrollbarX : null,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Superclass methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Implementation of Element's abstract method. Sets up config values.
+ *
+ * @method initAttributes
+ * @param oConfigs {Object} (Optional) Object literal definition of configuration values.
+ * @private
+ */
+
+initAttributes : function(oConfigs) {
+    oConfigs = oConfigs || {};
+    SDT.superclass.initAttributes.call(this, oConfigs);
+
+    /**
+    * @attribute width
+    * @description Table width for scrollable tables (e.g., "40em").
+    * @type String
+    */
+    this.setAttributeConfig("width", {
+        value: null,
+        validator: lang.isString,
+        method: function(oParam) {
+            if(this._elHdContainer && this._elBdContainer) {
+                this._elHdContainer.style.width = oParam;
+                this._elBdContainer.style.width = oParam;            
+                this._syncScrollX();      
+                this._syncScrollOverhang();
+            }
+        }
+    });
+
+    /**
+    * @attribute height
+    * @description Table body height for scrollable tables, not including headers (e.g., "40em").
+    * @type String
+    */
+    this.setAttributeConfig("height", {
+        value: null,
+        validator: lang.isString,
+        method: function(oParam) {
+            if(this._elHdContainer && this._elBdContainer) {
+                this._elBdContainer.style.height = oParam;    
+                this._syncScrollX();   
+                this._syncScrollY();
+                this._syncScrollOverhang();
+            }
+        }
+    });
+
+    /**
+    * @attribute COLOR_COLUMNFILLER
+    * @description CSS color value assigned to header filler on scrollable tables.  
+    * @type String
+    * @default "#F2F2F2"
+    */
+    this.setAttributeConfig("COLOR_COLUMNFILLER", {
+        value: "#F2F2F2",
+        validator: lang.isString,
+        method: function(oParam) {
+            if(this._elHdContainer) {
+                this._elHdContainer.style.backgroundColor = oParam;
+            }
+        }
+    });
+},
+
+/**
+ * Initializes internal variables.
+ *
+ * @method _init
+ * @private
+ */
+_init : function() {
+    this._elHdContainer = null;
+    this._elHdTable = null;
+    this._elBdContainer = null;
+    this._elBdThead = null;
+    this._elTmpContainer = null;
+    this._elTmpTable = null;
+},
+
+/**
+ * Initializes DOM elements for a ScrollingDataTable, including creation of
+ * two separate TABLE elements.
+ *
+ * @method _initDomElements
+ * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID. 
+ * return {Boolean} False in case of error, otherwise true 
+ * @private
+ */
+_initDomElements : function(elContainer) {
+    // Outer and inner containers
+    this._initContainerEl(elContainer);
+    if(this._elContainer && this._elHdContainer && this._elBdContainer) {
+        // TABLEs
+        this._initTableEl();
+        
+        if(this._elHdTable && this._elTable) {
+            // COLGROUPs
+            ///this._initColgroupEl(this._elHdTable, this._elTable);  
+            this._initColgroupEl(this._elHdTable);        
+            
+            // THEADs
+            this._initTheadEl(this._elHdTable, this._elTable);
+            
+            // Primary TBODY
+            this._initTbodyEl(this._elTable);
+            // Message TBODY
+            this._initMsgTbodyEl(this._elTable);            
+        }
+    }
+    if(!this._elContainer || !this._elTable || !this._elColgroup ||  !this._elThead || !this._elTbody || !this._elMsgTbody ||
+            !this._elHdTable || !this._elBdThead) {
+        return false;
+    }
+    else {
+        return true;
+    }
+},
+
+/**
+ * Destroy's the DataTable outer and inner container elements, if available.
+ *
+ * @method _destroyContainerEl
+ * @param elContainer {HTMLElement} Reference to the container element. 
+ * @private
+ */
+_destroyContainerEl : function(elContainer) {
+    Dom.removeClass(elContainer, DT.CLASS_SCROLLABLE);
+    SDT.superclass._destroyContainerEl.call(this, elContainer);
+    this._elHdContainer = null;
+    this._elBdContainer = null;
+},
+
+/**
+ * Initializes the DataTable outer container element and creates inner header
+ * and body container elements.
+ *
+ * @method _initContainerEl
+ * @param elContainer {HTMLElement | String} HTML DIV element by reference or ID.
+ * @private
+ */
+_initContainerEl : function(elContainer) {
+    SDT.superclass._initContainerEl.call(this, elContainer);
+    
+    if(this._elContainer) {
+        elContainer = this._elContainer; // was constructor input, now is DOM ref
+        Dom.addClass(elContainer, DT.CLASS_SCROLLABLE);
+        
+        // Container for header TABLE
+        var elHdContainer = document.createElement("div");
+        elHdContainer.style.width = this.get("width") || "";
+        elHdContainer.style.backgroundColor = this.get("COLOR_COLUMNFILLER");
+        Dom.addClass(elHdContainer, SDT.CLASS_HEADER);
+        this._elHdContainer = elHdContainer;
+        elContainer.appendChild(elHdContainer);
+    
+        // Container for body TABLE
+        var elBdContainer = document.createElement("div");
+        elBdContainer.style.width = this.get("width") || "";
+        elBdContainer.style.height = this.get("height") || "";
+        Dom.addClass(elBdContainer, SDT.CLASS_BODY);
+        Ev.addListener(elBdContainer, "scroll", this._onScroll, this); // to sync horiz scroll headers
+        this._elBdContainer = elBdContainer;
+        elContainer.appendChild(elBdContainer);
+    }
+},
+
+/**
+ * Creates HTML markup CAPTION element.
+ *
+ * @method _initCaptionEl
+ * @param sCaption {String} Text for caption.
+ * @private
+ */
+_initCaptionEl : function(sCaption) {
+    // Not yet supported
+    /*if(this._elHdTable && sCaption) {
+        // Create CAPTION element
+        if(!this._elCaption) { 
+            this._elCaption = this._elHdTable.createCaption();
+        }
+        // Set CAPTION value
+        this._elCaption.innerHTML = sCaption;
+    }
+    else if(this._elCaption) {
+        this._elCaption.parentNode.removeChild(this._elCaption);
+    }*/
+},
+
+/**
+ * Destroy's the DataTable head TABLE element, if available.
+ *
+ * @method _destroyHdTableEl
+ * @private
+ */
+_destroyHdTableEl : function() {
+    var elTable = this._elHdTable;
+    if(elTable) {
+        Ev.purgeElement(elTable, true);
+        elTable.parentNode.removeChild(elTable);
+        
+        // A little out of place, but where else can we null out these extra elements?
+        ///this._elBdColgroup = null;
+        this._elBdThead = null;
+    }
+},
+
+/**
+ * Initializes ScrollingDataTable TABLE elements into the two inner containers.
+ *
+ * @method _initTableEl
+ * @private
+ */
+_initTableEl : function() {
+    // Head TABLE
+    if(this._elHdContainer) {
+        this._destroyHdTableEl();
+    
+        // Create TABLE
+        this._elHdTable = this._elHdContainer.appendChild(document.createElement("table"));   
+
+        // Set up mouseover/mouseout events via mouseenter/mouseleave delegation
+        Ev.delegate(this._elHdTable, "mouseenter", this._onTableMouseover, "thead ."+DT.CLASS_LABEL, this);
+        Ev.delegate(this._elHdTable, "mouseleave", this._onTableMouseout, "thead ."+DT.CLASS_LABEL, this);
+    }
+    // Body TABLE
+    SDT.superclass._initTableEl.call(this, this._elBdContainer);
+},
+
+/**
+ * Initializes ScrollingDataTable THEAD elements into the two inner containers.
+ *
+ * @method _initTheadEl
+ * @param elHdTable {HTMLElement} (optional) Fixed header TABLE element reference.
+ * @param elTable {HTMLElement} (optional) TABLE element reference.
+ * @private
+ */
+_initTheadEl : function(elHdTable, elTable) {
+    elHdTable = elHdTable || this._elHdTable;
+    elTable = elTable || this._elTable;
+    
+    // Scrolling body's THEAD
+    this._initBdTheadEl(elTable);
+    // Standard fixed head THEAD
+    SDT.superclass._initTheadEl.call(this, elHdTable);
+},
+
+/**
+ * SDT changes ID so as not to duplicate the accessibility TH IDs.
+ *
+ * @method _initThEl
+ * @param elTh {HTMLElement} TH element reference.
+ * @param oColumn {YAHOO.widget.Column} Column object.
+ * @private
+ */
+_initThEl : function(elTh, oColumn) {
+    SDT.superclass._initThEl.call(this, elTh, oColumn);
+    elTh.id = this.getId() +"-fixedth-" + oColumn.getSanitizedKey(); // Needed for getColumn by TH and ColumnDD
+},
+
+/**
+ * Destroy's the DataTable body THEAD element, if available.
+ *
+ * @method _destroyBdTheadEl
+ * @private
+ */
+_destroyBdTheadEl : function() {
+    var elBdThead = this._elBdThead;
+    if(elBdThead) {
+        var elTable = elBdThead.parentNode;
+        Ev.purgeElement(elBdThead, true);
+        elTable.removeChild(elBdThead);
+        this._elBdThead = null;
+
+        this._destroyColumnHelpers();
+    }
+},
+
+/**
+ * Initializes body THEAD element.
+ *
+ * @method _initBdTheadEl
+ * @param elTable {HTMLElement} TABLE element into which to create THEAD.
+ * @return {HTMLElement} Initialized THEAD element. 
+ * @private
+ */
+_initBdTheadEl : function(elTable) {
+    if(elTable) {
+        // Destroy previous
+        this._destroyBdTheadEl();
+
+        var elThead = elTable.insertBefore(document.createElement("thead"), elTable.firstChild);
+        
+        // Add TRs to the THEAD;
+        var oColumnSet = this._oColumnSet,
+            colTree = oColumnSet.tree,
+            elTh, elTheadTr, oColumn, i, j, k, len;
+
+        for(i=0, k=colTree.length; i<k; i++) {
+            elTheadTr = elThead.appendChild(document.createElement("tr"));
+    
+            // ...and create TH cells
+            for(j=0, len=colTree[i].length; j<len; j++) {
+                oColumn = colTree[i][j];
+                elTh = elTheadTr.appendChild(document.createElement("th"));
+                this._initBdThEl(elTh,oColumn,i,j);
+            }
+        }
+        this._elBdThead = elThead;
+    }
+},
+
+/**
+ * Populates TH element for the body THEAD element.
+ *
+ * @method _initBdThEl
+ * @param elTh {HTMLElement} TH element reference.
+ * @param oColumn {YAHOO.widget.Column} Column object.
+ * @private
+ */
+_initBdThEl : function(elTh, oColumn) {
+    elTh.id = this.getId()+"-th-" + oColumn.getSanitizedKey(); // Needed for accessibility
+    elTh.rowSpan = oColumn.getRowspan();
+    elTh.colSpan = oColumn.getColspan();
+    // Assign abbr attribute
+    if(oColumn.abbr) {
+        elTh.abbr = oColumn.abbr;
+    }
+
+    // TODO: strip links and form elements
+    var sKey = oColumn.getKey();
+    var sLabel = lang.isValue(oColumn.label) ? oColumn.label : sKey;
+    elTh.innerHTML = sLabel;
+},
+
+/**
+ * Initializes ScrollingDataTable TBODY element for data
+ *
+ * @method _initTbodyEl
+ * @param elTable {HTMLElement} TABLE element into which to create TBODY .
+ * @private
+ */
+_initTbodyEl : function(elTable) {
+    SDT.superclass._initTbodyEl.call(this, elTable);
+    
+    // Bug 2105534 - Safari 3 gap
+    // Bug 2492591 - IE8 offsetTop
+    elTable.style.marginTop = (this._elTbody.offsetTop > 0) ?
+            "-"+this._elTbody.offsetTop+"px" : 0;
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Sets focus on the given element.
+ *
+ * @method _focusEl
+ * @param el {HTMLElement} Element.
+ * @private
+ */
+_focusEl : function(el) {
+    el = el || this._elTbody;
+    var oSelf = this;
+    this._storeScrollPositions();
+    // http://developer.mozilla.org/en/docs/index.php?title=Key-navigable_custom_DHTML_widgets
+    // The timeout is necessary in both IE and Firefox 1.5, to prevent scripts from doing
+    // strange unexpected things as the user clicks on buttons and other controls.
+    
+    // Bug 1921135: Wrap the whole thing in a setTimeout
+    setTimeout(function() {
+        setTimeout(function() {
+            try {
+                el.focus();
+                oSelf._restoreScrollPositions();
+            }
+            catch(e) {
+            }
+        },0);
+    }, 0);
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Internal wrapper calls run() on render Chain instance.
+ *
+ * @method _runRenderChain
+ * @private 
+ */
+_runRenderChain : function() {
+    this._storeScrollPositions();
+    this._oChainRender.run();
+},
+
+/**
+ * Stores scroll positions so they can be restored after a render.
+ *
+ * @method _storeScrollPositions
+ * @private
+ */
+ _storeScrollPositions : function() {
+    this._nScrollTop = this._elBdContainer.scrollTop;
+    this._nScrollLeft = this._elBdContainer.scrollLeft;
+},
+
+/**
+ * Clears stored scroll positions to interrupt the automatic restore mechanism.
+ * Useful for setting scroll positions programmatically rather than as part of
+ * the post-render cleanup process.
+ *
+ * @method clearScrollPositions
+ * @private
+ */
+ clearScrollPositions : function() {
+    this._nScrollTop = 0;
+    this._nScrollLeft = 0;
+},
+
+/**
+ * Restores scroll positions to stored value. 
+ *
+ * @method _retoreScrollPositions
+ * @private 
+ */
+ _restoreScrollPositions : function() {
+    // Reset scroll positions
+    if(this._nScrollTop) {
+        this._elBdContainer.scrollTop = this._nScrollTop;
+        this._nScrollTop = null;
+    } 
+    if(this._nScrollLeft) {
+        this._elBdContainer.scrollLeft = this._nScrollLeft;
+        // Bug 2529024
+        this._elHdContainer.scrollLeft = this._nScrollLeft; 
+        this._nScrollLeft = null;
+    } 
+},
+
+/**
+ * Helper function calculates and sets a validated width for a Column in a ScrollingDataTable.
+ *
+ * @method _validateColumnWidth
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param elTd {HTMLElement} TD element to validate against.
+ * @private
+ */
+_validateColumnWidth : function(oColumn, elTd) {
+    // Only Columns without widths that are not hidden
+    if(!oColumn.width && !oColumn.hidden) {
+        var elTh = oColumn.getThEl();
+        // Unset a calculated auto-width
+        if(oColumn._calculatedWidth) {
+            this._setColumnWidth(oColumn, "auto", "visible");
+        }
+        // Compare auto-widths
+        if(elTh.offsetWidth !== elTd.offsetWidth) {
+            var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
+                    oColumn.getThLinerEl() : elTd.firstChild;               
+
+            // Grab the wider liner width, unless the minWidth is wider
+            var newWidth = Math.max(0,
+                (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
+                oColumn.minWidth);
+                
+            var sOverflow = 'visible';
+            
+            // Now validate against maxAutoWidth
+            if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
+                newWidth = oColumn.maxAutoWidth;
+                sOverflow = "hidden";
+            }
+
+            // Set to the wider auto-width
+            this._elTbody.style.display = "none";
+            this._setColumnWidth(oColumn, newWidth+'px', sOverflow);
+            oColumn._calculatedWidth = newWidth;
+            this._elTbody.style.display = "";
+        }
+    }
+},
+
+/**
+ * For one or all Columns of a ScrollingDataTable, when Column is not hidden,
+ * and width is not set, syncs widths of header and body cells and 
+ * validates that width against minWidth and/or maxAutoWidth as necessary.
+ *
+ * @method validateColumnWidths
+ * @param oArg.column {YAHOO.widget.Column} (optional) One Column to validate. If null, all Columns' widths are validated.
+ */
+validateColumnWidths : function(oColumn) {
+    // Validate there is at least one TR with proper TDs
+    var allKeys   = this._oColumnSet.keys,
+        allKeysLength = allKeys.length,
+        elRow     = this.getFirstTrEl();
+
+    // Reset overhang for IE
+    if(ua.ie) {
+        this._setOverhangValue(1);
+    }
+
+    if(allKeys && elRow && (elRow.childNodes.length === allKeysLength)) {
+        // Temporarily unsnap container since it causes inaccurate calculations
+        var sWidth = this.get("width");
+        if(sWidth) {
+            this._elHdContainer.style.width = "";
+            this._elBdContainer.style.width = "";
+        }
+        this._elContainer.style.width = "";
+        
+        //Validate just one Column
+        if(oColumn && lang.isNumber(oColumn.getKeyIndex())) {
+            this._validateColumnWidth(oColumn, elRow.childNodes[oColumn.getKeyIndex()]);
+        }
+        // Iterate through all Columns to unset calculated widths in one pass
+        else {
+            var elTd, todos = [], thisTodo, i, len;
+            for(i=0; i<allKeysLength; i++) {
+                oColumn = allKeys[i];
+                // Only Columns without widths that are not hidden, unset a calculated auto-width
+                if(!oColumn.width && !oColumn.hidden && oColumn._calculatedWidth) {
+                    todos[todos.length] = oColumn;      
+                }
+            }
+            
+            this._elTbody.style.display = "none";
+            for(i=0, len=todos.length; i<len; i++) {
+                this._setColumnWidth(todos[i], "auto", "visible");
+            }
+            this._elTbody.style.display = "";
+            
+            todos = [];
+
+            // Iterate through all Columns and make the store the adjustments to make in one pass
+            for(i=0; i<allKeysLength; i++) {
+                oColumn = allKeys[i];
+                elTd = elRow.childNodes[i];
+                // Only Columns without widths that are not hidden
+                if(!oColumn.width && !oColumn.hidden) {
+                    var elTh = oColumn.getThEl();
+
+                    // Compare auto-widths
+                    if(elTh.offsetWidth !== elTd.offsetWidth) {
+                        var elWider = (elTh.offsetWidth > elTd.offsetWidth) ?
+                                oColumn.getThLinerEl() : elTd.firstChild;               
+                
+                        // Grab the wider liner width, unless the minWidth is wider
+                        var newWidth = Math.max(0,
+                            (elWider.offsetWidth -(parseInt(Dom.getStyle(elWider,"paddingLeft"),10)|0) - (parseInt(Dom.getStyle(elWider,"paddingRight"),10)|0)),
+                            oColumn.minWidth);
+                            
+                        var sOverflow = 'visible';
+                        
+                        // Now validate against maxAutoWidth
+                        if((oColumn.maxAutoWidth > 0) && (newWidth > oColumn.maxAutoWidth)) {
+                            newWidth = oColumn.maxAutoWidth;
+                            sOverflow = "hidden";
+                        }
+                
+                        todos[todos.length] = [oColumn, newWidth, sOverflow];
+                    }
+                }
+            }
+            
+            this._elTbody.style.display = "none";
+            for(i=0, len=todos.length; i<len; i++) {
+                thisTodo = todos[i];
+                // Set to the wider auto-width
+                this._setColumnWidth(thisTodo[0], thisTodo[1]+"px", thisTodo[2]);
+                thisTodo[0]._calculatedWidth = thisTodo[1];
+            }
+            this._elTbody.style.display = "";
+        }
+    
+        // Resnap unsnapped containers
+        if(sWidth) {
+            this._elHdContainer.style.width = sWidth;
+            this._elBdContainer.style.width = sWidth;
+        } 
+    }
+    
+    this._syncScroll();
+    this._restoreScrollPositions();
+},
+
+/**
+ * Syncs padding around scrollable tables, including Column header right-padding
+ * and container width and height.
+ *
+ * @method _syncScroll
+ * @private 
+ */
+_syncScroll : function() {
+    this._syncScrollX();
+    this._syncScrollY();
+    this._syncScrollOverhang();
+    if(ua.opera) {
+        // Bug 1925874
+        this._elHdContainer.scrollLeft = this._elBdContainer.scrollLeft;
+        if(!this.get("width")) {
+            // Bug 1926125
+            document.body.style += '';
+        }
+    }
+ },
+
+/**
+ * Snaps container width for y-scrolling tables.
+ *
+ * @method _syncScrollY
+ * @private
+ */
+_syncScrollY : function() {
+    var elTbody = this._elTbody,
+        elBdContainer = this._elBdContainer;
+    
+    // X-scrolling not enabled
+    if(!this.get("width")) {
+        // Snap outer container width to content
+        this._elContainer.style.width = 
+                (elBdContainer.scrollHeight > elBdContainer.clientHeight) ?
+                // but account for y-scrollbar since it is visible
+                (elTbody.parentNode.clientWidth + 19) + "px" :
+                // no y-scrollbar, just borders
+                (elTbody.parentNode.clientWidth + 2) + "px";
+    }
+},
+
+/**
+ * Snaps container height for x-scrolling tables in IE. Syncs message TBODY width.
+ *
+ * @method _syncScrollX
+ * @private
+ */
+_syncScrollX : function() {
+    var elTbody = this._elTbody,
+        elBdContainer = this._elBdContainer;
+    
+    // IE 6 and 7 only when y-scrolling not enabled
+    if(!this.get("height") && (ua.ie)) {
+        // Snap outer container height to content
+        elBdContainer.style.height = 
+                // but account for x-scrollbar if it is visible
+                (elBdContainer.scrollWidth > elBdContainer.offsetWidth ) ?
+                (elTbody.parentNode.offsetHeight + 18) + "px" : 
+                elTbody.parentNode.offsetHeight + "px";
+    }
+
+    // Sync message tbody
+    if(this._elTbody.rows.length === 0) {
+        this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
+    }
+    else {
+        this._elMsgTbody.parentNode.style.width = "";
+    }
+},
+
+/**
+ * Adds/removes Column header overhang as necesary.
+ *
+ * @method _syncScrollOverhang
+ * @private
+ */
+_syncScrollOverhang : function() {
+    var elBdContainer = this._elBdContainer,
+        // Overhang should be either 1 (default) or 18px, depending on the location of the right edge of the table
+        nPadding = 1;
+    
+    // Y-scrollbar is visible, which is when the overhang needs to jut out
+    if((elBdContainer.scrollHeight > elBdContainer.clientHeight) &&
+        // X-scrollbar is also visible, which means the right is jagged, not flush with the Column
+        (elBdContainer.scrollWidth > elBdContainer.clientWidth)) {
+        nPadding = 18;
+    }
+    
+    this._setOverhangValue(nPadding);
+    
+},
+
+/**
+ * Sets Column header overhang to given width.
+ *
+ * @method _setOverhangValue
+ * @param nBorderWidth {Number} Value of new border for overhang. 
+ * @private
+ */
+_setOverhangValue : function(nBorderWidth) {
+    var aLastHeaders = this._oColumnSet.headers[this._oColumnSet.headers.length-1] || [],
+        len = aLastHeaders.length,
+        sPrefix = this._sId+"-fixedth-",
+        sValue = nBorderWidth + "px solid " + this.get("COLOR_COLUMNFILLER");
+
+    this._elThead.style.display = "none";
+    for(var i=0; i<len; i++) {
+        Dom.get(sPrefix+aLastHeaders[i]).style.borderRight = sValue;
+    }
+    this._elThead.style.display = "";
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * Returns DOM reference to the DataTable's fixed header container element.
+ *
+ * @method getHdContainerEl
+ * @return {HTMLElement} Reference to DIV element.
+ */
+getHdContainerEl : function() {
+    return this._elHdContainer;
+},
+
+/**
+ * Returns DOM reference to the DataTable's scrolling body container element.
+ *
+ * @method getBdContainerEl
+ * @return {HTMLElement} Reference to DIV element.
+ */
+getBdContainerEl : function() {
+    return this._elBdContainer;
+},
+
+/**
+ * Returns DOM reference to the DataTable's fixed header TABLE element.
+ *
+ * @method getHdTableEl
+ * @return {HTMLElement} Reference to TABLE element.
+ */
+getHdTableEl : function() {
+    return this._elHdTable;
+},
+
+/**
+ * Returns DOM reference to the DataTable's scrolling body TABLE element.
+ *
+ * @method getBdTableEl
+ * @return {HTMLElement} Reference to TABLE element.
+ */
+getBdTableEl : function() {
+    return this._elTable;
+},
+
+/**
+ * Disables ScrollingDataTable UI.
+ *
+ * @method disable
+ */
+disable : function() {
+    var elMask = this._elMask;
+    elMask.style.width = this._elBdContainer.offsetWidth + "px";
+    elMask.style.height = this._elHdContainer.offsetHeight + this._elBdContainer.offsetHeight + "px";
+    elMask.style.display = "";
+    this.fireEvent("disableEvent");
+},
+
+/**
+ * Removes given Column. NOTE: You cannot remove nested Columns. You can only remove
+ * non-nested Columns, and top-level parent Columns (which will remove all
+ * children Columns).
+ *
+ * @method removeColumn
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @return oColumn {YAHOO.widget.Column} Removed Column instance.
+ */
+removeColumn : function(oColumn) {
+    // Store scroll pos
+    var hdPos = this._elHdContainer.scrollLeft;
+    var bdPos = this._elBdContainer.scrollLeft;
+    
+    // Call superclass method
+    oColumn = SDT.superclass.removeColumn.call(this, oColumn);
+    
+    // Restore scroll pos
+    this._elHdContainer.scrollLeft = hdPos;
+    this._elBdContainer.scrollLeft = bdPos;
+    
+    return oColumn;
+},
+
+/**
+ * Inserts given Column at the index if given, otherwise at the end. NOTE: You
+ * can only add non-nested Columns and top-level parent Columns. You cannot add
+ * a nested Column to an existing parent.
+ *
+ * @method insertColumn
+ * @param oColumn {Object | YAHOO.widget.Column} Object literal Column
+ * definition or a Column instance.
+ * @param index {Number} (optional) New tree index.
+ * @return oColumn {YAHOO.widget.Column} Inserted Column instance. 
+ */
+insertColumn : function(oColumn, index) {
+    // Store scroll pos
+    var hdPos = this._elHdContainer.scrollLeft;
+    var bdPos = this._elBdContainer.scrollLeft;
+    
+    // Call superclass method
+    var  oColumn, index);
+    
+    // Restore scroll pos
+    this._elHdContainer.scrollLeft = hdPos;
+    this._elBdContainer.scrollLeft = bdPos;
+    
+    return oNewColumn;
+},
+
+/**
+ * Removes given Column and inserts into given tree index. NOTE: You
+ * can only reorder non-nested Columns and top-level parent Columns. You cannot
+ * reorder a nested Column to an existing parent.
+ *
+ * @method reorderColumn
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param index {Number} New tree index.
+ */
+reorderColumn : function(oColumn, index) {
+    // Store scroll pos
+    var hdPos = this._elHdContainer.scrollLeft;
+    var bdPos = this._elBdContainer.scrollLeft;
+    
+    // Call superclass method
+    var  oColumn, index);
+    
+    // Restore scroll pos
+    this._elHdContainer.scrollLeft = hdPos;
+    this._elBdContainer.scrollLeft = bdPos;
+
+    return oNewColumn;
+},
+
+/**
+ * Sets given Column to given pixel width. If new width is less than minWidth
+ * width, sets to minWidth. Updates oColumn.width value.
+ *
+ * @method setColumnWidth
+ * @param oColumn {YAHOO.widget.Column} Column instance.
+ * @param nWidth {Number} New width in pixels.
+ */
+setColumnWidth : function(oColumn, nWidth) {
+    oColumn = this.getColumn(oColumn);
+    if(oColumn) {
+        this._storeScrollPositions();
+
+        // Validate new width against minWidth
+        if(lang.isNumber(nWidth)) {
+            nWidth = (nWidth > oColumn.minWidth) ? nWidth : oColumn.minWidth;
+
+            // Save state
+            oColumn.width = nWidth;
+            
+            // Resize the DOM elements
+            this._setColumnWidth(oColumn, nWidth+"px");
+            this._syncScroll();
+            
+            this.fireEvent("columnSetWidthEvent",{column:oColumn,width:nWidth});
+        }
+        // Unsets a width to auto-size
+        else if(nWidth === null) {
+            // Save state
+            oColumn.width = nWidth;
+            
+            // Resize the DOM elements
+            this._setColumnWidth(oColumn, "auto");
+            this.validateColumnWidths(oColumn);
+            this.fireEvent("columnUnsetWidthEvent",{column:oColumn});
+        }
+        
+        // Bug 2339454: resize then sort misaligment
+        this._clearTrTemplateEl();
+    }
+    else {
+    }
+},
+
+/**
+ * Scrolls to given row or cell
+ *
+ * @method scrollTo
+ * @param to {YAHOO.widget.Record | HTMLElement } Itme to scroll to.
+ */
+scrollTo : function(to) {
+        var td = this.getTdEl(to);
+        if(td) {
+            this.clearScrollPositions();
+            this.getBdContainerEl().scrollLeft = td.offsetLeft;
+            this.getBdContainerEl().scrollTop = td.parentNode.offsetTop;
+        }
+        else {
+            var tr = this.getTrEl(to);
+            if(tr) {
+                this.clearScrollPositions();
+                this.getBdContainerEl().scrollTop = tr.offsetTop;
+            }
+        }
+},
+
+/**
+ * Displays message within secondary TBODY.
+ *
+ * @method showTableMessage
+ * @param sHTML {String} (optional) Value for innerHTMlang.
+ * @param sClassName {String} (optional) Classname.
+ */
+showTableMessage : function(sHTML, sClassName) {
+    var elCell = this._elMsgTd;
+    if(lang.isString(sHTML)) {
+        elCell.firstChild.innerHTML = sHTML;
+    }
+    if(lang.isString(sClassName)) {
+        Dom.addClass(elCell.firstChild, sClassName);
+    }
+
+    // Needed for SDT only
+    var elThead = this.getTheadEl();
+    var elTable = elThead.parentNode;
+    var newWidth = elTable.offsetWidth;
+    this._elMsgTbody.parentNode.style.width = this.getTheadEl().parentNode.offsetWidth + "px";
+
+    this._elMsgTbody.style.display = "";
+
+    this.fireEvent("tableMsgShowEvent", {html:sHTML, className:sClassName});
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private Custom Event Handlers
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Handles Column mutations
+ *
+ * @method onColumnChange
+ * @param oArgs {Object} Custom Event data.
+ */
+_onColumnChange : function(oArg) {
+    // Figure out which Column changed
+    var oColumn = (oArg.column) ? oArg.column :
+            (oArg.editor) ? oArg.editor.column : null;
+    this._storeScrollPositions();
+    this.validateColumnWidths(oColumn);
+},
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private DOM Event Handlers
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Syncs scrolltop and scrollleft of all TABLEs.
+ *
+ * @method _onScroll
+ * @param e {HTMLEvent} The scroll event.
+ * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
+ * @private
+ */
+_onScroll : function(e, oSelf) {
+    oSelf._elHdContainer.scrollLeft = oSelf._elBdContainer.scrollLeft;
+
+    if(oSelf._oCellEditor && oSelf._oCellEditor.isActive) {
+        oSelf.fireEvent("editorBlurEvent", {editor:oSelf._oCellEditor});
+        oSelf.cancelCellEditor();
+    }
+
+    var elTarget = Ev.getTarget(e);
+    var elTag = elTarget.nodeName.toLowerCase();
+    oSelf.fireEvent("tableScrollEvent", {event:e, target:elTarget});
+},
+
+/**
+ * Handles keydown events on the THEAD element.
+ *
+ * @method _onTheadKeydown
+ * @param e {HTMLEvent} The key event.
+ * @param oSelf {YAHOO.widget.ScrollingDataTable} ScrollingDataTable instance.
+ * @private
+ */
+_onTheadKeydown : function(e, oSelf) {
+    // If tabbing to next TH label link causes THEAD to scroll,
+    // need to sync scrollLeft with TBODY
+    if(Ev.getCharCode(e) === 9) {
+        setTimeout(function() {
+            if((oSelf instanceof SDT) && oSelf._sId) {
+                oSelf._elBdContainer.scrollLeft = oSelf._elHdContainer.scrollLeft;
+            }
+        },0);
+    }
+    
+    var elTarget = Ev.getTarget(e);
+    var elTag = elTarget.nodeName.toLowerCase();
+    var bKeepBubbling = true;
+    while(elTarget && (elTag != "table")) {
+        switch(elTag) {
+            case "body":
+                return;
+            case "input":
+            case "textarea":
+                // TODO: implement textareaKeyEvent
+                break;
+            case "thead":
+                bKeepBubbling = oSelf.fireEvent("theadKeyEvent",{target:elTarget,event:e});
+                break;
+            default:
+                break;
+        }
+        if(bKeepBubbling === false) {
+            return;
+        }
+        else {
+            elTarget = elTarget.parentNode;
+            if(elTarget) {
+                elTag = elTarget.nodeName.toLowerCase();
+            }
+        }
+    }
+    oSelf.fireEvent("tableKeyEvent",{target:(elTarget || oSelf._elContainer),event:e});
+}
+
+
+
+
+/**
+ * Fired when a fixed scrolling DataTable has a scroll.
+ *
+ * @event tableScrollEvent
+ * @param oArgs.event {HTMLEvent} The event object.
+ * @param oArgs.target {HTMLElement} The DataTable's CONTAINER element (in IE)
+ * or the DataTable's TBODY element (everyone else).
+ *
+ */
+
+
+
+
+});
+
+})();
+
+(function () {
+
+var lang   = YAHOO.lang,
+    util   = YAHOO.util,
+    widget = YAHOO.widget,
+    ua     = YAHOO.env.ua,
+    
+    Dom    = util.Dom,
+    Ev     = util.Event,
+    
+    DT     = widget.DataTable;
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+    
+/**
+ * The BaseCellEditor class provides base functionality common to all inline cell
+ * editors for a DataTable widget.
+ *
+ * @namespace YAHOO.widget
+ * @class BaseCellEditor
+ * @uses YAHOO.util.EventProvider 
+ * @constructor
+ * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
+ * @param oConfigs {Object} (Optional) Object literal of configs.
+ */
+widget.BaseCellEditor = function(sType, oConfigs) {
+    this._sId = this._sId || Dom.generateId(null, "yui-ceditor"); // "yui-ceditor" + YAHOO.widget.BaseCellEditor._nCount++;
+    YAHOO.widget.BaseCellEditor._nCount++;
+    this._sType = sType;
+    
+    // Validate inputs
+    this._initConfigs(oConfigs); 
+    
+    // Create Custom Events
+    this._initEvents();
+             
+    // UI needs to be drawn
+    this._needsRender = true;
+};
+
+var BCE = widget.BaseCellEditor;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Static members
+//
+/////////////////////////////////////////////////////////////////////////////
+lang.augmentObject(BCE, {
+
+/**
+ * Global instance counter.
+ *
+ * @property CellEditor._nCount
+ * @type Number
+ * @static
+ * @default 0
+ * @private 
+ */
+_nCount : 0,
+
+/**
+ * Class applied to CellEditor container.
+ *
+ * @property CellEditor.CLASS_CELLEDITOR
+ * @type String
+ * @static
+ * @default "yui-ceditor"
+ */
+CLASS_CELLEDITOR : "yui-ceditor"
+
+});
+
+BCE.prototype = {
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private members
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Unique id assigned to instance "yui-ceditorN", useful prefix for generating unique
+ * DOM ID strings and log messages.
+ *
+ * @property _sId
+ * @type String
+ * @private
+ */
+_sId : null,
+
+/**
+ * Editor type.
+ *
+ * @property _sType
+ * @type String
+ * @private
+ */
+_sType : null,
+
+/**
+ * DataTable instance.
+ *
+ * @property _oDataTable
+ * @type YAHOO.widget.DataTable
+ * @private 
+ */
+_oDataTable : null,
+
+/**
+ * Column instance.
+ *
+ * @property _oColumn
+ * @type YAHOO.widget.Column
+ * @default null
+ * @private 
+ */
+_oColumn : null,
+
+/**
+ * Record instance.
+ *
+ * @property _oRecord
+ * @type YAHOO.widget.Record
+ * @default null
+ * @private 
+ */
+_oRecord : null,
+
+/**
+ * TD element.
+ *
+ * @property _elTd
+ * @type HTMLElement
+ * @default null
+ * @private
+ */
+_elTd : null,
+
+/**
+ * Container for inline editor.
+ *
+ * @property _elContainer
+ * @type HTMLElement
+ * @private 
+ */
+_elContainer : null,
+
+/**
+ * Reference to Cancel button, if available.
+ *
+ * @property _elCancelBtn
+ * @type HTMLElement
+ * @default null
+ * @private 
+ */
+_elCancelBtn : null,
+
+/**
+ * Reference to Save button, if available.
+ *
+ * @property _elSaveBtn
+ * @type HTMLElement
+ * @default null
+ * @private 
+ */
+_elSaveBtn : null,
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Initialize configs.
+ *
+ * @method _initConfigs
+ * @private   
+ */
+_initConfigs : function(oConfigs) {
+    // Object literal defines CellEditor configs
+    if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
+        for(var sConfig in oConfigs) {
+            if(sConfig) {
+                this[sConfig] = oConfigs[sConfig];
+            }
+        }
+    }
+},
+
+/**
+ * Initialize Custom Events.
+ *
+ * @method _initEvents
+ * @private   
+ */
+_initEvents : function() {
+    this.createEvent("showEvent");
+    this.createEvent("keydownEvent");
+    this.createEvent("invalidDataEvent");
+    this.createEvent("revertEvent");
+    this.createEvent("saveEvent");
+    this.createEvent("cancelEvent");
+    this.createEvent("blurEvent");
+    this.createEvent("blockEvent");
+    this.createEvent("unblockEvent");
+},
+
+/**
+ * Initialize container element.
+ *
+ * @method _initContainerEl
+ * @private
+ */
+_initContainerEl : function() {
+    if(this._elContainer) {
+        YAHOO.util.Event.purgeElement(this._elContainer, true);
+        this._elContainer.innerHTML = "";
+    }
+
+    var elContainer = document.createElement("div");
+    elContainer.id = this.getId() + "-container"; // Needed for tracking blur event
+    elContainer.style.display = "none";
+    elContainer.tabIndex = 0;
+    
+    this.className = lang.isArray(this.className) ? this.className : this.className ? [this.className] : [];
+    this.className[this.className.length] = DT.CLASS_EDITOR;
+    elContainer.className = this.className.join(" ");
+    
+    document.body.insertBefore(elContainer, document.body.firstChild);
+    this._elContainer = elContainer;
+},
+
+/**
+ * Initialize container shim element.
+ *
+ * @method _initShimEl
+ * @private
+ */
+_initShimEl : function() {
+    // Iframe shim
+    if(this.useIFrame) {
+        if(!this._elIFrame) {
+            var elIFrame = document.createElement("iframe");
+            elIFrame.src = ""
+            elIFrame.frameBorder = 0;
+            elIFrame.scrolling = "no";
+            elIFrame.style.display = "none";
+            elIFrame.className = DT.CLASS_EDITOR_SHIM;
+            elIFrame.tabIndex = -1;
+            elIFrame.role = "presentation";
+            elIFrame.title = "Presentational iframe shim";
+            document.body.insertBefore(elIFrame, document.body.firstChild);
+            this._elIFrame = elIFrame;
+        }
+    }
+},
+
+/**
+ * Hides CellEditor UI at end of interaction.
+ *
+ * @method _hide
+ */
+_hide : function() {
+    this.getContainerEl().style.display = "none";
+    if(this._elIFrame) {
+        this._elIFrame.style.display = "none";
+    }
+    this.isActive = false;
+    this.getDataTable()._oCellEditor =  null;
+},
+
+
+
+
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Implementer defined function that can submit the input value to a server. This
+ * function must accept the arguments fnCallback and oNewValue. When the submission
+ * is complete, the function must also call fnCallback(bSuccess, oNewValue) to 
+ * finish the save routine in the CellEditor. This function can also be used to 
+ * perform extra validation or input value manipulation. 
+ *
+ * @property asyncSubmitter
+ * @type HTMLFunction
+ */
+asyncSubmitter : null,
+
+/**
+ * Current value.
+ *
+ * @property value
+ * @type MIXED
+ */
+value : null,
+
+/**
+ * Default value in case Record data is undefined. NB: Null values will not trigger
+ * the default value.
+ *
+ * @property defaultValue
+ * @type MIXED
+ * @default null
+ */
+defaultValue : null,
+
+/**
+ * Validator function for input data, called from the DataTable instance scope,
+ * receives the arguments (inputValue, currentValue, editorInstance) and returns
+ * either the validated (or type-converted) value or undefined.
+ *
+ * @property validator
+ * @type HTMLFunction
+ * @default null
+ */
+validator : null,
+
+/**
+ * If validation is enabled, resets input field of invalid data.
+ *
+ * @property resetInvalidData
+ * @type Boolean
+ * @default true
+ */
+resetInvalidData : true,
+
+/**
+ * True if currently active.
+ *
+ * @property isActive
+ * @type Boolean
+ */
+isActive : false,
+
+/**
+ * Text to display on Save button.
+ *
+ * @property LABEL_SAVE
+ * @type HTML
+ * @default "Save"
+ */
+LABEL_SAVE : "Save",
+
+/**
+ * Text to display on Cancel button.
+ *
+ * @property LABEL_CANCEL
+ * @type HTML
+ * @default "Cancel"
+ */
+LABEL_CANCEL : "Cancel",
+
+/**
+ * True if Save/Cancel buttons should not be displayed in the CellEditor.
+ *
+ * @property disableBtns
+ * @type Boolean
+ * @default false
+ */
+disableBtns : false,
+
+/**
+ * True if iframe shim for container element should be enabled.
+ *
+ * @property useIFrame
+ * @type Boolean
+ * @default false
+ */
+useIFrame : false,
+
+/**
+ * Custom CSS class or array of classes applied to the container element.
+ *
+ * @property className
+ * @type String || String[]
+ */
+className : null,
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * CellEditor instance name, for logging.
+ *
+ * @method toString
+ * @return {String} Unique name of the CellEditor instance.
+ */
+
+toString : function() {
+    return "CellEditor instance " + this._sId;
+},
+
+/**
+ * CellEditor unique ID.
+ *
+ * @method getId
+ * @return {String} Unique ID of the CellEditor instance.
+ */
+
+getId : function() {
+    return this._sId;
+},
+
+/**
+ * Returns reference to associated DataTable instance.
+ *
+ * @method getDataTable
+ * @return {YAHOO.widget.DataTable} DataTable instance.
+ */
+
+getDataTable : function() {
+    return this._oDataTable;
+},
+
+/**
+ * Returns reference to associated Column instance.
+ *
+ * @method getColumn
+ * @return {YAHOO.widget.Column} Column instance.
+ */
+
+getColumn : function() {
+    return this._oColumn;
+},
+
+/**
+ * Returns reference to associated Record instance.
+ *
+ * @method getRecord
+ * @return {YAHOO.widget.Record} Record instance.
+ */
+
+getRecord : function() {
+    return this._oRecord;
+},
+
+
+
+/**
+ * Returns reference to associated TD element.
+ *
+ * @method getTdEl
+ * @return {HTMLElement} TD element.
+ */
+
+getTdEl : function() {
+    return this._elTd;
+},
+
+/**
+ * Returns container element.
+ *
+ * @method getContainerEl
+ * @return {HTMLElement} Reference to container element.
+ */
+
+getContainerEl : function() {
+    return this._elContainer;
+},
+
+/**
+ * Nulls out the entire CellEditor instance and related objects, removes attached
+ * event listeners, and clears out DOM elements inside the container, removes
+ * container from the DOM.
+ *
+ * @method destroy
+ */
+destroy : function() {
+    this.unsubscribeAll();
+    
+    // Column is late-binding in attach()
+    var oColumn = this.getColumn();
+    if(oColumn) {
+        oColumn.editor = null;
+    }
+    
+    var elContainer = this.getContainerEl();
+    if (elContainer) {
+        Ev.purgeElement(elContainer, true);
+        elContainer.parentNode.removeChild(elContainer);
+    }
+},
+
+/**
+ * Renders DOM elements and attaches event listeners.
+ *
+ * @method render
+ */
+render : function() {
+    if (!this._needsRender) {
+        return;
+    }
+
+    this._initContainerEl();
+    this._initShimEl();
+
+    // Handle ESC key
+    Ev.addListener(this.getContainerEl(), "keydown", function(e, oSelf) {
+        // ESC cancels Cell Editor
+        if((e.keyCode == 27)) {
+            var target = Ev.getTarget(e);
+            // workaround for Mac FF3 bug that disabled clicks when ESC hit when
+            // select is open. [bug 2273056]
+            if (target.nodeName && target.nodeName.toLowerCase() === 'select') {
+                target.blur();
+            }
+            oSelf.cancel();
+        }
+        // Pass through event
+        oSelf.fireEvent("keydownEvent", {editor:oSelf, event:e});
+    }, this);
+
+    this.renderForm();
+
+    // Show Save/Cancel buttons
+    if(!this.disableBtns) {
+        this.renderBtns();
+    }
+    
+    this.doAfterRender();
+    this._needsRender = false;
+},
+
+/**
+ * Renders Save/Cancel buttons.
+ *
+ * @method renderBtns
+ */
+renderBtns : function() {
+    // Buttons
+    var elBtnsDiv = this.getContainerEl().appendChild(document.createElement("div"));
+    elBtnsDiv.className = DT.CLASS_BUTTON;
+
+    // Save button
+    var elSaveBtn = elBtnsDiv.appendChild(document.createElement("button"));
+    elSaveBtn.className = DT.CLASS_DEFAULT;
+    elSaveBtn.innerHTML = this.LABEL_SAVE;
+    Ev.addListener(elSaveBtn, "click", function(oArgs) {
+        this.save();
+    }, this, true);
+    this._elSaveBtn = elSaveBtn;
+
+    // Cancel button
+    var elCancelBtn = elBtnsDiv.appendChild(document.createElement("button"));
+    elCancelBtn.innerHTML = this.LABEL_CANCEL;
+    Ev.addListener(elCancelBtn, "click", function(oArgs) {
+        this.cancel();
+    }, this, true);
+    this._elCancelBtn = elCancelBtn;
+},
+
+/**
+ * Attach CellEditor for a new interaction.
+ *
+ * @method attach
+ * @param oDataTable {YAHOO.widget.DataTable} Associated DataTable instance.
+ * @param elCell {HTMLElement} Cell to edit.  
+ */
+attach : function(oDataTable, elCell) {
+    // Validate 
+    if(oDataTable instanceof YAHOO.widget.DataTable) {
+        this._oDataTable = oDataTable;
+        
+        // Validate cell
+        elCell = oDataTable.getTdEl(elCell);
+        if(elCell) {
+            this._elTd = elCell;
+
+            // Validate Column
+            var oColumn = oDataTable.getColumn(elCell);
+            if(oColumn) {
+                this._oColumn = oColumn;
+                
+                // Validate Record
+                var oRecord = oDataTable.getRecord(elCell);
+                if(oRecord) {
+                    this._oRecord = oRecord;
+                    var value = oRecord.getData(this.getColumn().getField());
+                    this.value = (value !== undefined) ? value : this.defaultValue;
+                    return true;
+                }
+            }            
+        }
+    }
+    return false;
+},
+
+/**
+ * Moves container into position for display.
+ *
+ * @method move
+ */
+move : function() {
+    // Move Editor
+    var elContainer = this.getContainerEl(),
+        elTd = this.getTdEl(),
+        x = Dom.getX(elTd),
+        y = Dom.getY(elTd);
+
+    //TODO: remove scrolling logic
+    // SF doesn't get xy for cells in scrolling table
+    // when tbody display is set to block
+    if(isNaN(x) || isNaN(y)) {
+        var elTbody = this.getDataTable().getTbodyEl();
+        x = elTd.offsetLeft + // cell pos relative to table
+                Dom.getX(elTbody.parentNode) - // plus table pos relative to document
+                elTbody.scrollLeft; // minus tbody scroll
+        y = elTd.offsetTop + // cell pos relative to table
+                Dom.getY(elTbody.parentNode) - // plus table pos relative to document
+                elTbody.scrollTop + // minus tbody scroll
+                this.getDataTable().getTheadEl().offsetHeight; // account for fixed THEAD cells
+    }
+
+    elContainer.style.left = x + "px";
+    elContainer.style.top = y + "px";
+
+    if(this._elIFrame) {
+        this._elIFrame.style.left = x + "px";
+        this._elIFrame.style.top = y + "px";
+    }
+},
+
+/**
+ * Displays CellEditor UI in the correct position.
+ *
+ * @method show
+ */
+show : function() {
+    var elContainer = this.getContainerEl(),
+        elIFrame = this._elIFrame;
+    this.resetForm();
+    this.isActive = true;
+    elContainer.style.display = "";
+    if(elIFrame) {
+        elIFrame.style.width = elContainer.offsetWidth + "px";
+        elIFrame.style.height = elContainer.offsetHeight + "px";
+        elIFrame.style.display = "";
+    }
+    this.focus();
+    this.fireEvent("showEvent", {editor:this});
+},
+
+/**
+ * Fires blockEvent
+ *
+ * @method block
+ */
+block : function() {
+    this.fireEvent("blockEvent", {editor:this});
+},
+
+/**
+ * Fires unblockEvent
+ *
+ * @method unblock
+ */
+unblock : function() {
+    this.fireEvent("unblockEvent", {editor:this});
+},
+
+/**
+ * Saves value of CellEditor and hides UI.
+ *
+ * @method save
+ */
+save : function() {
+    // Get new value
+    var inputValue = this.getInputValue();
+    var validValue = inputValue;
+    
+    // Validate new value
+    if(this.validator) {
+        validValue = this.validator.call(this.getDataTable(), inputValue, this.value, this);
+        if(validValue === undefined ) {
+            if(this.resetInvalidData) {
+                this.resetForm();
+            }
+            this.fireEvent("invalidDataEvent",
+                    {editor:this, oldData:this.value, newData:inputValue});
+            return;
+        }
+    }
+        
+    var oSelf = this;
+    var finishSave = function(bSuccess, oNewValue) {
+        var oOrigValue = oSelf.value;
+        if(bSuccess) {
+            // Update new value
+            oSelf.value = oNewValue;
+            oSelf.getDataTable().updateCell(oSelf.getRecord(), oSelf.getColumn(), oNewValue);
+            
+            // Hide CellEditor
+            oSelf._hide();
+            
+            oSelf.fireEvent("saveEvent",
+                    {editor:oSelf, oldData:oOrigValue, newData:oSelf.value});
+        }
+        else {
+            oSelf.resetForm();
+            oSelf.fireEvent("revertEvent",
+                    {editor:oSelf, oldData:oOrigValue, newData:oNewValue});
+        }
+        oSelf.unblock();
+    };
+    
+    this.block();
+    if(lang.isFunction(this.asyncSubmitter)) {
+        this.asyncSubmitter.call(this, finishSave, validValue);
+    } 
+    else {   
+        finishSave(true, validValue);
+    }
+},
+
+/**
+ * Cancels CellEditor input and hides UI.
+ *
+ * @method cancel
+ */
+cancel : function() {
+    if(this.isActive) {
+        this._hide();
+        this.fireEvent("cancelEvent", {editor:this});
+    }
+    else {
+    }
+},
+
+/**
+ * Renders form elements.
+ *
+ * @method renderForm
+ */
+renderForm : function() {
+    // To be implemented by subclass
+},
+
+/**
+ * Access to add additional event listeners.
+ *
+ * @method doAfterRender
+ */
+doAfterRender : function() {
+    // To be implemented by subclass
+},
+
+
+/**
+ * After rendering form, if disabledBtns is set to true, then sets up a mechanism
+ * to save input without them. 
+ *
+ * @method handleDisabledBtns
+ */
+handleDisabledBtns : function() {
+    // To be implemented by subclass
+},
+
+/**
+ * Resets CellEditor UI to initial state.
+ *
+ * @method resetForm
+ */
+resetForm : function() {
+    // To be implemented by subclass
+},
+
+/**
+ * Sets focus in CellEditor.
+ *
+ * @method focus
+ */
+focus : function() {
+    // To be implemented by subclass
+},
+
+/**
+ * Retrieves input value from CellEditor.
+ *
+ * @method getInputValue
+ */
+getInputValue : function() {
+    // To be implemented by subclass
+}
+
+};
+
+lang.augmentProto(BCE, util.EventProvider);
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Custom Events
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Fired when a CellEditor is shown.
+ *
+ * @event showEvent
+ * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance.
+ */
+
+/**
+ * Fired when a CellEditor has a keydown.
+ *
+ * @event keydownEvent
+ * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
+ * @param oArgs.event {HTMLEvent} The event object.
+ */
+
+/**
+ * Fired when a CellEditor input is reverted due to invalid data.
+ *
+ * @event invalidDataEvent
+ * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
+ * @param oArgs.newData {Object} New data value from form input field.
+ * @param oArgs.oldData {Object} Old data value.
+ */
+
+/**
+ * Fired when a CellEditor input is reverted due to asyncSubmitter failure.
+ *
+ * @event revertEvent
+ * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
+ * @param oArgs.newData {Object} New data value from form input field.
+ * @param oArgs.oldData {Object} Old data value.
+ */
+
+/**
+ * Fired when a CellEditor input is saved.
+ *
+ * @event saveEvent
+ * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
+ * @param oArgs.newData {Object} New data value from form input field.
+ * @param oArgs.oldData {Object} Old data value.
+ */
+
+/**
+ * Fired when a CellEditor input is canceled.
+ *
+ * @event cancelEvent
+ * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
+ */
+
+/**
+ * Fired when a CellEditor has a blur event.
+ *
+ * @event blurEvent
+ * @param oArgs.editor {YAHOO.widget.CellEditor} The CellEditor instance. 
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+    
+/**
+ * The CheckboxCellEditor class provides functionality for inline editing
+ * DataTable cell data with checkboxes.
+ *
+ * @namespace YAHOO.widget
+ * @class CheckboxCellEditor
+ * @extends YAHOO.widget.BaseCellEditor
+ * @constructor
+ * @param oConfigs {Object} (Optional) Object literal of configs.
+ */
+widget.CheckboxCellEditor = function(oConfigs) {
+    oConfigs = oConfigs || {};
+    this._sId = this._sId || Dom.generateId(null, "yui-checkboxceditor"); // "yui-checkboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
+    YAHOO.widget.BaseCellEditor._nCount++;
+    widget.CheckboxCellEditor.superclass.constructor.call(this, oConfigs.type || "checkbox", oConfigs);
+};
+
+// CheckboxCellEditor extends BaseCellEditor
+lang.extend(widget.CheckboxCellEditor, BCE, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// CheckboxCellEditor public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Array of checkbox values. Can either be a simple array (e.g., ["red","green","blue"])
+ * or a an array of objects (e.g., [{label:"red", value:"#FF0000"},
+ * {label:"green", value:"#00FF00"}, {label:"blue", value:"#0000FF"}]). String
+ * values are treated as markup and inserted into the DOM as innerHTML.
+ *
+ * @property checkboxOptions
+ * @type HTML[] | Object[]
+ */
+checkboxOptions : null,
+
+/**
+ * Reference to the checkbox elements.
+ *
+ * @property checkboxes
+ * @type HTMLElement[] 
+ */
+checkboxes : null,
+
+/**
+ * Array of checked values
+ *
+ * @property value
+ * @type String[] 
+ */
+value : null,
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// CheckboxCellEditor public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Render a form with input(s) type=checkbox.
+ *
+ * @method renderForm
+ */
+renderForm : function() {
+    if(lang.isArray(this.checkboxOptions)) {
+        var checkboxOption, checkboxValue, checkboxId, elLabel, j, len;
+        
+        // Create the checkbox buttons in an IE-friendly way...
+        for(j=0,len=this.checkboxOptions.length; j<len; j++) {
+            checkboxOption = this.checkboxOptions[j];
+            checkboxValue = lang.isValue(checkboxOption.value) ?
+                    checkboxOption.value : checkboxOption;
+
+            checkboxId = this.getId() + "-chk" + j;
+            this.getContainerEl().innerHTML += "<input type=\"checkbox\"" +
+                    " id=\"" + checkboxId + "\"" + // Needed for label
+                    " value=\"" + checkboxValue + "\" />";
+            
+            // Create the labels in an IE-friendly way
+            elLabel = this.getContainerEl().appendChild(document.createElement("label"));
+            elLabel.htmlFor = checkboxId;
+            elLabel.innerHTML = lang.isValue(checkboxOption.label) ?
+                    checkboxOption.label : checkboxOption;
+        }
+        
+        // Store the reference to the checkbox elements
+        var allCheckboxes = [];
+        for(j=0; j<len; j++) {
+            allCheckboxes[allCheckboxes.length] = this.getContainerEl().childNodes[j*2];
+        }
+        this.checkboxes = allCheckboxes;
+
+        if(this.disableBtns) {
+            this.handleDisabledBtns();
+        }
+    }
+    else {
+    }
+},
+
+/**
+ * After rendering form, if disabledBtns is set to true, then sets up a mechanism
+ * to save input without them. 
+ *
+ * @method handleDisabledBtns
+ */
+handleDisabledBtns : function() {
+    Ev.addListener(this.getContainerEl(), "click", function(v){
+        if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
+            // Save on blur
+            this.save();
+        }
+    }, this, true);
+},
+
+/**
+ * Resets CheckboxCellEditor UI to initial state.
+ *
+ * @method resetForm
+ */
+resetForm : function() {
+    // Normalize to array
+    var originalValues = lang.isArray(this.value) ? this.value : [this.value];
+    
+    // Match checks to value
+    for(var i=0, j=this.checkboxes.length; i<j; i++) {
+        this.checkboxes[i].checked = false;
+        for(var k=0, len=originalValues.length; k<len; k++) {
+            if(this.checkboxes[i].value == originalValues[k]) {
+                this.checkboxes[i].checked = true;
+            }
+        }
+    }
+},
+
+/**
+ * Sets focus in CheckboxCellEditor.
+ *
+ * @method focus
+ */
+focus : function() {
+    this.checkboxes[0].focus();
+},
+
+/**
+ * Retrieves input value from CheckboxCellEditor.
+ *
+ * @method getInputValue
+ */
+getInputValue : function() {
+    var checkedValues = [];
+    for(var i=0, j=this.checkboxes.length; i<j; i++) {
+        if(this.checkboxes[i].checked) {
+            checkedValues[checkedValues.length] = this.checkboxes[i].value;
+        }
+    }  
+    return checkedValues;
+}
+
+});
+
+// Copy static members to CheckboxCellEditor class
+lang.augmentObject(widget.CheckboxCellEditor, BCE);
+
+
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+    
+/**
+ * The DataCellEditor class provides functionality for inline editing
+ * DataTable cell data with a YUI Calendar.
+ *
+ * @namespace YAHOO.widget
+ * @class DateCellEditor
+ * @extends YAHOO.widget.BaseCellEditor 
+ * @constructor
+ * @param oConfigs {Object} (Optional) Object literal of configs.
+ */
+widget.DateCellEditor = function(oConfigs) {
+    oConfigs = oConfigs || {};
+    this._sId = this._sId || Dom.generateId(null, "yui-dateceditor"); // "yui-dateceditor" + YAHOO.widget.BaseCellEditor._nCount++;
+    YAHOO.widget.BaseCellEditor._nCount++;
+    widget.DateCellEditor.superclass.constructor.call(this, oConfigs.type || "date", oConfigs);
+};
+
+// CheckboxCellEditor extends BaseCellEditor
+lang.extend(widget.DateCellEditor, BCE, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DateCellEditor public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Reference to Calendar instance.
+ *
+ * @property calendar
+ * @type YAHOO.widget.Calendar
+ */
+calendar : null,
+
+/**
+ * Configs for the calendar instance, to be passed to Calendar constructor.
+ *
+ * @property calendarOptions
+ * @type Object
+ */
+calendarOptions : null,
+
+/**
+ * Default value.
+ *
+ * @property defaultValue
+ * @type Date
+ * @default new Date()
+ */
+defaultValue : new Date(),
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DateCellEditor public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Render a Calendar.
+ *
+ * @method renderForm
+ */
+renderForm : function() {
+    // Calendar widget
+    if(YAHOO.widget.Calendar) {
+        var calContainer = this.getContainerEl().appendChild(document.createElement("div"));
+        calContainer.id = this.getId() + "-dateContainer"; // Needed for Calendar constructor
+        var calendar =
+                new YAHOO.widget.Calendar(this.getId() + "-date",
+                calContainer.id, this.calendarOptions);
+        calendar.render();
+        calContainer.style.cssFloat = "none";
+        
+        // Bug 2528576
+        calendar.hideEvent.subscribe(function() {this.cancel();}, this, true);
+
+        if(ua.ie) {
+            var calFloatClearer = this.getContainerEl().appendChild(document.createElement("div"));
+            calFloatClearer.style.clear = "both";
+        }
+        
+        this.calendar = calendar;
+
+        if(this.disableBtns) {
+            this.handleDisabledBtns();
+        }
+    }
+    else {
+    }
+    
+},
+
+/**
+ * After rendering form, if disabledBtns is set to true, then sets up a mechanism
+ * to save input without them. 
+ *
+ * @method handleDisabledBtns
+ */
+handleDisabledBtns : function() {
+    this.calendar.selectEvent.subscribe(function(v){
+        // Save on select
+        this.save();
+    }, this, true);
+},
+
+/**
+ * Resets DateCellEditor UI to initial state.
+ *
+ * @method resetForm
+ */
+resetForm : function() {
+    var value = this.value || (new Date());
+    this.calendar.select(value);
+    this.calendar.cfg.setProperty("pagedate",value,false);
+	this.calendar.render();
+	// Bug 2528576
+	this.calendar.show();
+},
+
+/**
+ * Sets focus in DateCellEditor.
+ *
+ * @method focus
+ */
+focus : function() {
+    // To be impmlemented by subclass
+},
+
+/**
+ * Retrieves input value from DateCellEditor.
+ *
+ * @method getInputValue
+ */
+getInputValue : function() {
+    return this.calendar.getSelectedDates()[0];
+}
+
+});
+
+// Copy static members to DateCellEditor class
+lang.augmentObject(widget.DateCellEditor, BCE);
+
+
+
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+    
+/**
+ * The DropdownCellEditor class provides functionality for inline editing
+ * DataTable cell data a SELECT element.
+ *
+ * @namespace YAHOO.widget
+ * @class DropdownCellEditor
+ * @extends YAHOO.widget.BaseCellEditor 
+ * @constructor
+ * @param oConfigs {Object} (Optional) Object literal of configs.
+ */
+widget.DropdownCellEditor = function(oConfigs) {
+    oConfigs = oConfigs || {};
+    this._sId = this._sId || Dom.generateId(null, "yui-dropdownceditor"); // "yui-dropdownceditor" + YAHOO.widget.BaseCellEditor._nCount++;
+    YAHOO.widget.BaseCellEditor._nCount++;
+    widget.DropdownCellEditor.superclass.constructor.call(this, oConfigs.type || "dropdown", oConfigs);
+};
+
+// DropdownCellEditor extends BaseCellEditor
+lang.extend(widget.DropdownCellEditor, BCE, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DropdownCellEditor public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Array of dropdown values. Can either be a simple array (e.g.,
+ * ["Alabama","Alaska","Arizona","Arkansas"]) or a an array of objects (e.g., 
+ * [{label:"Alabama", value:"AL"}, {label:"Alaska", value:"AK"},
+ * {label:"Arizona", value:"AZ"}, {label:"Arkansas", value:"AR"}]). String
+ * values are treated as markup and inserted into the DOM as innerHTML.
+ *
+ * @property dropdownOptions
+ * @type HTML[] | Object[]
+ */
+dropdownOptions : null,
+
+/**
+ * Reference to Dropdown element.
+ *
+ * @property dropdown
+ * @type HTMLElement
+ */
+dropdown : null,
+
+/**
+ * Enables multi-select.
+ *
+ * @property multiple
+ * @type Boolean
+ */
+multiple : false,
+
+/**
+ * Specifies number of visible options.
+ *
+ * @property size
+ * @type Number
+ */
+size : null,
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DropdownCellEditor public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Render a form with select element.
+ *
+ * @method renderForm
+ */
+renderForm : function() {
+    var elDropdown = this.getContainerEl().appendChild(document.createElement("select"));
+    elDropdown.style.zoom = 1;
+    if(this.multiple) {
+        elDropdown.multiple = "multiple";
+    }
+    if(lang.isNumber(this.size)) {
+        elDropdown.size = this.size;
+    }
+    this.dropdown = elDropdown;
+    
+    if(lang.isArray(this.dropdownOptions)) {
+        var dropdownOption, elOption;
+        for(var i=0, j=this.dropdownOptions.length; i<j; i++) {
+            dropdownOption = this.dropdownOptions[i];
+            elOption = document.createElement("option");
+            elOption.value = (lang.isValue(dropdownOption.value)) ?
+                    dropdownOption.value : dropdownOption;
+            elOption.innerHTML = (lang.isValue(dropdownOption.label)) ?
+                    dropdownOption.label : dropdownOption;
+            elOption = elDropdown.appendChild(elOption);
+        }
+        
+        if(this.disableBtns) {
+            this.handleDisabledBtns();
+        }
+    }
+},
+
+/**
+ * After rendering form, if disabledBtns is set to true, then sets up a mechanism
+ * to save input without them. 
+ *
+ * @method handleDisabledBtns
+ */
+handleDisabledBtns : function() {
+    // Save on blur for multi-select
+    if(this.multiple) {
+        Ev.addListener(this.dropdown, "blur", function(v){
+            // Save on change
+            this.save();
+        }, this, true);
+    }
+    // Save on change for single-select
+    else {
+        if(!ua.ie) {
+            Ev.addListener(this.dropdown, "change", function(v){
+                // Save on change
+                this.save();
+            }, this, true);
+        }
+        else {
+            // Bug 2529274: "change" event is not keyboard accessible in IE6
+            Ev.addListener(this.dropdown, "blur", function(v){
+                this.save();
+            }, this, true);
+            Ev.addListener(this.dropdown, "click", function(v){
+                this.save();
+            }, this, true);
+        }
+    }
+},
+
+/**
+ * Resets DropdownCellEditor UI to initial state.
+ *
+ * @method resetForm
+ */
+resetForm : function() {
+    var allOptions = this.dropdown.options,
+        i=0, j=allOptions.length;
+
+    // Look for multi-select selections
+    if(lang.isArray(this.value)) {
+        var allValues = this.value,
+            m=0, n=allValues.length,
+            hash = {};
+        // Reset all selections and stash options in a value hash
+        for(; i<j; i++) {
+            allOptions[i].selected = false;
+            hash[allOptions[i].value] = allOptions[i];
+        }
+        for(; m<n; m++) {
+            if(hash[allValues[m]]) {
+                hash[allValues[m]].selected = true;
+            }
+        }
+    }
+    // Only need to look for a single selection
+    else {
+        for(; i<j; i++) {
+            if(this.value == allOptions[i].value) {
+                allOptions[i].selected = true;
+            }
+        }
+    }
+},
+
+/**
+ * Sets focus in DropdownCellEditor.
+ *
+ * @method focus
+ */
+focus : function() {
+    this.getDataTable()._focusEl(this.dropdown);
+},
+
+/**
+ * Retrieves input value from DropdownCellEditor.
+ *
+ * @method getInputValue
+ */
+getInputValue : function() {
+    var allOptions = this.dropdown.options;
+    
+    // Look for multiple selections
+    if(this.multiple) {
+        var values = [],
+            i=0, j=allOptions.length;
+        for(; i<j; i++) {
+            if(allOptions[i].selected) {
+                values.push(allOptions[i].value);
+            }
+        }
+        return values;
+    }
+    // Only need to look for single selection
+    else {
+        return allOptions[allOptions.selectedIndex].value;
+    }
+}
+
+});
+
+// Copy static members to DropdownCellEditor class
+lang.augmentObject(widget.DropdownCellEditor, BCE);
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+    
+/**
+ * The RadioCellEditor class provides functionality for inline editing
+ * DataTable cell data with radio buttons.
+ *
+ * @namespace YAHOO.widget
+ * @class RadioCellEditor
+ * @extends YAHOO.widget.BaseCellEditor 
+ * @constructor
+ * @param oConfigs {Object} (Optional) Object literal of configs.
+ */
+widget.RadioCellEditor = function(oConfigs) {
+    oConfigs = oConfigs || {};
+    this._sId = this._sId || Dom.generateId(null, "yui-radioceditor"); // "yui-radioceditor" + YAHOO.widget.BaseCellEditor._nCount++;
+    YAHOO.widget.BaseCellEditor._nCount++;
+    widget.RadioCellEditor.superclass.constructor.call(this, oConfigs.type || "radio", oConfigs);
+};
+
+// RadioCellEditor extends BaseCellEditor
+lang.extend(widget.RadioCellEditor, BCE, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RadioCellEditor public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Reference to radio elements.
+ *
+ * @property radios
+ * @type HTMLElement[]
+ */
+radios : null,
+
+/**
+ * Array of radio values. Can either be a simple array (e.g., ["yes","no","maybe"])
+ * or a an array of objects (e.g., [{label:"yes", value:1}, {label:"no", value:-1},
+ * {label:"maybe", value:0}]). String values are treated as markup and inserted
+ * into the DOM as innerHTML.
+ *
+ * @property radioOptions
+ * @type HTML[] | Object[]
+ */
+radioOptions : null,
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RadioCellEditor public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Render a form with input(s) type=radio.
+ *
+ * @method renderForm
+ */
+renderForm : function() {
+    if(lang.isArray(this.radioOptions)) {
+        var radioOption, radioValue, radioId, elLabel;
+        
+        // Create the radio buttons in an IE-friendly way
+        for(var i=0, len=this.radioOptions.length; i<len; i++) {
+            radioOption = this.radioOptions[i];
+            radioValue = lang.isValue(radioOption.value) ?
+                    radioOption.value : radioOption;
+            radioId = this.getId() + "-radio" + i;
+            this.getContainerEl().innerHTML += "<input type=\"radio\"" +
+                    " name=\"" + this.getId() + "\"" +
+                    " value=\"" + radioValue + "\"" +
+                    " id=\"" +  radioId + "\" />"; // Needed for label
+            
+            // Create the labels in an IE-friendly way
+            elLabel = this.getContainerEl().appendChild(document.createElement("label"));
+            elLabel.htmlFor = radioId;
+            elLabel.innerHTML = (lang.isValue(radioOption.label)) ?
+                    radioOption.label : radioOption;
+        }
+        
+        // Store the reference to the checkbox elements
+        var allRadios = [],
+            elRadio;
+        for(var j=0; j<len; j++) {
+            elRadio = this.getContainerEl().childNodes[j*2];
+            allRadios[allRadios.length] = elRadio;
+        }
+        this.radios = allRadios;
+
+        if(this.disableBtns) {
+            this.handleDisabledBtns();
+        }
+    }
+    else {
+    }
+},
+
+/**
+ * After rendering form, if disabledBtns is set to true, then sets up a mechanism
+ * to save input without them. 
+ *
+ * @method handleDisabledBtns
+ */
+handleDisabledBtns : function() {
+    Ev.addListener(this.getContainerEl(), "click", function(v){
+        if(Ev.getTarget(v).tagName.toLowerCase() === "input") {
+            // Save on blur
+            this.save();
+        }
+    }, this, true);
+},
+
+/**
+ * Resets RadioCellEditor UI to initial state.
+ *
+ * @method resetForm
+ */
+resetForm : function() {
+    for(var i=0, j=this.radios.length; i<j; i++) {
+        var elRadio = this.radios[i];
+        if(this.value == elRadio.value) {
+            elRadio.checked = true;
+            return;
+        }
+    }
+},
+
+/**
+ * Sets focus in RadioCellEditor.
+ *
+ * @method focus
+ */
+focus : function() {
+    for(var i=0, j=this.radios.length; i<j; i++) {
+        if(this.radios[i].checked) {
+            this.radios[i].focus();
+            return;
+        }
+    }
+},
+
+/**
+ * Retrieves input value from RadioCellEditor.
+ *
+ * @method getInputValue
+ */
+getInputValue : function() {
+    for(var i=0, j=this.radios.length; i<j; i++) {
+        if(this.radios[i].checked) {
+            return this.radios[i].value;
+        }
+    }
+}
+
+});
+
+// Copy static members to RadioCellEditor class
+lang.augmentObject(widget.RadioCellEditor, BCE);
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+    
+/**
+ * The TextareaCellEditor class provides functionality for inline editing
+ * DataTable cell data with a TEXTAREA element.
+ *
+ * @namespace YAHOO.widget
+ * @class TextareaCellEditor
+ * @extends YAHOO.widget.BaseCellEditor 
+ * @constructor
+ * @param oConfigs {Object} (Optional) Object literal of configs.
+ */
+widget.TextareaCellEditor = function(oConfigs) {
+    oConfigs = oConfigs || {};
+    this._sId = this._sId || Dom.generateId(null, "yui-textareaceditor");// "yui-textareaceditor" + ;
+    YAHOO.widget.BaseCellEditor._nCount++;
+    widget.TextareaCellEditor.superclass.constructor.call(this, oConfigs.type || "textarea", oConfigs);
+};
+
+// TextareaCellEditor extends BaseCellEditor
+lang.extend(widget.TextareaCellEditor, BCE, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// TextareaCellEditor public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Reference to textarea element.
+ *
+ * @property textarea
+ * @type HTMLElement
+ */
+textarea : null,
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// TextareaCellEditor public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Render a form with textarea.
+ *
+ * @method renderForm
+ */
+renderForm : function() {
+    var elTextarea = this.getContainerEl().appendChild(document.createElement("textarea"));
+    this.textarea = elTextarea;
+
+    if(this.disableBtns) {
+        this.handleDisabledBtns();
+    }
+},
+
+/**
+ * After rendering form, if disabledBtns is set to true, then sets up a mechanism
+ * to save input without them. 
+ *
+ * @method handleDisabledBtns
+ */
+handleDisabledBtns : function() {
+    Ev.addListener(this.textarea, "blur", function(v){
+        // Save on blur
+        this.save();
+    }, this, true);        
+},
+
+/**
+ * Moves TextareaCellEditor UI to a cell.
+ *
+ * @method move
+ */
+move : function() {
+    this.textarea.style.width = this.getTdEl().offsetWidth + "px";
+    this.textarea.style.height = "3em";
+    YAHOO.widget.TextareaCellEditor.superclass.move.call(this);
+},
+
+/**
+ * Resets TextareaCellEditor UI to initial state.
+ *
+ * @method resetForm
+ */
+resetForm : function() {
+    this.textarea.value = this.value;
+},
+
+/**
+ * Sets focus in TextareaCellEditor.
+ *
+ * @method focus
+ */
+focus : function() {
+    // Bug 2303181, Bug 2263600
+    this.getDataTable()._focusEl(this.textarea);
+    this.textarea.select();
+},
+
+/**
+ * Retrieves input value from TextareaCellEditor.
+ *
+ * @method getInputValue
+ */
+getInputValue : function() {
+    return this.textarea.value;
+}
+
+});
+
+// Copy static members to TextareaCellEditor class
+lang.augmentObject(widget.TextareaCellEditor, BCE);
+
+
+
+
+
+
+
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+    
+/**
+ * The TextboxCellEditor class provides functionality for inline editing
+ * DataTable cell data with an INPUT TYPE=TEXT element.
+ *
+ * @namespace YAHOO.widget
+ * @class TextboxCellEditor
+ * @extends YAHOO.widget.BaseCellEditor 
+ * @constructor
+ * @param oConfigs {Object} (Optional) Object literal of configs.
+ */
+widget.TextboxCellEditor = function(oConfigs) {
+    oConfigs = oConfigs || {};
+    this._sId = this._sId || Dom.generateId(null, "yui-textboxceditor");// "yui-textboxceditor" + YAHOO.widget.BaseCellEditor._nCount++;
+    YAHOO.widget.BaseCellEditor._nCount++;
+    widget.TextboxCellEditor.superclass.constructor.call(this, oConfigs.type || "textbox", oConfigs);
+};
+
+// TextboxCellEditor extends BaseCellEditor
+lang.extend(widget.TextboxCellEditor, BCE, {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// TextboxCellEditor public properties
+//
+/////////////////////////////////////////////////////////////////////////////
+/**
+ * Reference to the textbox element.
+ *
+ * @property textbox
+ */
+textbox : null,
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// TextboxCellEditor public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Render a form with input type=text.
+ *
+ * @method renderForm
+ */
+renderForm : function() {
+    var elTextbox;
+    // Bug 1802582: SF3/Mac needs a form element wrapping the input
+    if(ua.webkit>420) {
+        elTextbox = this.getContainerEl().appendChild(document.createElement("form")).appendChild(document.createElement("input"));
+    }
+    else {
+        elTextbox = this.getContainerEl().appendChild(document.createElement("input"));
+    }
+    elTextbox.type = "text";
+    this.textbox = elTextbox;
+
+    // Save on enter by default
+    // Bug: 1802582 Set up a listener on each textbox to track on keypress
+    // since SF/OP can't preventDefault on keydown
+    Ev.addListener(elTextbox, "keypress", function(v){
+        if((v.keyCode === 13)) {
+            // Prevent form submit
+            YAHOO.util.Event.preventDefault(v);
+            this.save();
+        }
+    }, this, true);
+
+    if(this.disableBtns) {
+        // By default this is no-op since enter saves by default
+        this.handleDisabledBtns();
+    }
+},
+
+/**
+ * Moves TextboxCellEditor UI to a cell.
+ *
+ * @method move
+ */
+move : function() {
+    this.textbox.style.width = this.getTdEl().offsetWidth + "px";
+    widget.TextboxCellEditor.superclass.move.call(this);
+},
+
+/**
+ * Resets TextboxCellEditor UI to initial state.
+ *
+ * @method resetForm
+ */
+resetForm : function() {
+    this.textbox.value = lang.isValue(this.value) ? this.value.toString() : "";
+},
+
+/**
+ * Sets focus in TextboxCellEditor.
+ *
+ * @method focus
+ */
+focus : function() {
+    // Bug 2303181, Bug 2263600
+    this.getDataTable()._focusEl(this.textbox);
+    this.textbox.select();
+},
+
+/**
+ * Returns new value for TextboxCellEditor.
+ *
+ * @method getInputValue
+ */
+getInputValue : function() {
+    return this.textbox.value;
+}
+
+});
+
+// Copy static members to TextboxCellEditor class
+lang.augmentObject(widget.TextboxCellEditor, BCE);
+
+
+
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DataTable extension
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * CellEditor subclasses.
+ * @property DataTable.Editors
+ * @type Object
+ * @static
+ */
+DT.Editors = {
+    checkbox : widget.CheckboxCellEditor,
+    "date"   : widget.DateCellEditor,
+    dropdown : widget.DropdownCellEditor,
+    radio    : widget.RadioCellEditor,
+    textarea : widget.TextareaCellEditor,
+    textbox  : widget.TextboxCellEditor
+};
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+    
+/**
+ * Factory class for instantiating a BaseCellEditor subclass.
+ *
+ * @namespace YAHOO.widget
+ * @class CellEditor
+ * @extends YAHOO.widget.BaseCellEditor 
+ * @constructor
+ * @param sType {String} Type indicator, to map to YAHOO.widget.DataTable.Editors.
+ * @param oConfigs {Object} (Optional) Object literal of configs.
+ */
+widget.CellEditor = function(sType, oConfigs) {
+    // Point to one of the subclasses
+    if(sType && DT.Editors[sType]) {
+        lang.augmentObject(BCE, DT.Editors[sType]);
+        return new DT.Editors[sType](oConfigs);
+    }
+    else {
+        return new BCE(null, oConfigs);
+    }
+};
+
+var CE = widget.CellEditor;
+
+// Copy static members to CellEditor class
+lang.augmentObject(CE, BCE);
+
+
+})();
+
+YAHOO.register("datatable", YAHOO.widget.DataTable, {version: "2.9.0", build: "2800"});

Added: branches/wf4ever/public/_javascript_s/yui/element.js (0 => 3265)


--- branches/wf4ever/public/_javascript_s/yui/element.js	                        (rev 0)
+++ branches/wf4ever/public/_javascript_s/yui/element.js	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,1099 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+/**
+ * Provides Attribute configurations.
+ * @namespace YAHOO.util
+ * @class Attribute
+ * @constructor
+ * @param hash {Object} The intial Attribute.
+ * @param {YAHOO.util.AttributeProvider} The owner of the Attribute instance.
+ */
+
+YAHOO.util.Attribute = function(hash, owner) {
+    if (owner) { 
+        this.owner = owner;
+        this.configure(hash, true);
+    }
+};
+
+YAHOO.util.Attribute.INVALID_VALUE = {};
+
+YAHOO.util.Attribute.prototype = {
+    /**
+     * The name of the attribute.
+     * @property name
+     * @type String
+     */
+    name: undefined,
+    
+    /**
+     * The value of the attribute.
+     * @property value
+     * @type String
+     */
+    value: null,
+    
+    /**
+     * The owner of the attribute.
+     * @property owner
+     * @type YAHOO.util.AttributeProvider
+     */
+    owner: null,
+    
+    /**
+     * Whether or not the attribute is read only.
+     * @property readOnly
+     * @type Boolean
+     */
+    readOnly: false,
+    
+    /**
+     * Whether or not the attribute can only be written once.
+     * @property writeOnce
+     * @type Boolean
+     */
+    writeOnce: false,
+
+    /**
+     * The attribute's initial configuration.
+     * @private
+     * @property _initialConfig
+     * @type Object
+     */
+    _initialConfig: null,
+    
+    /**
+     * Whether or not the attribute's value has been set.
+     * @private
+     * @property _written
+     * @type Boolean
+     */
+    _written: false,
+    
+    /**
+     * A function to call when setting the attribute's value.
+     * The method receives the new value as the first arg and the attribute name as the 2nd
+     * @property method
+     * @type Function
+     */
+    method: null,
+    
+    /**
+     * The function to use when setting the attribute's value.
+     * The setter receives the new value as the first arg and the attribute name as the 2nd
+     * The return value of the setter replaces the value passed to set(). 
+     * @property setter
+     * @type Function
+     */
+    setter: null,
+    
+    /**
+     * The function to use when getting the attribute's value.
+     * The getter receives the new value as the first arg and the attribute name as the 2nd
+     * The return value of the getter will be used as the return from get().
+     * @property getter
+     * @type Function
+     */
+    getter: null,
+
+    /**
+     * The validator to use when setting the attribute's value.
+     * @property validator
+     * @type Function
+     * @return Boolean
+     */
+    validator: null,
+    
+    /**
+     * Retrieves the current value of the attribute.
+     * @method getValue
+     * @return {any} The current value of the attribute.
+     */
+    getValue: function() {
+        var val = this.value;
+
+        if (this.getter) {
+            val = this.getter.call(this.owner, this.name, val);
+        }
+
+        return val;
+    },
+    
+    /**
+     * Sets the value of the attribute and fires beforeChange and change events.
+     * @method setValue
+     * @param {Any} value The value to apply to the attribute.
+     * @param {Boolean} silent If true the change events will not be fired.
+     * @return {Boolean} Whether or not the value was set.
+     */
+    setValue: function(value, silent) {
+        var beforeRetVal,
+            owner = this.owner,
+            name = this.name,
+            invalidValue = YAHOO.util.Attribute.INVALID_VALUE,
+        
+            event = {
+                type: name, 
+                prevValue: this.getValue(),
+                newValue: value
+        };
+        
+        if (this.readOnly || ( this.writeOnce && this._written) ) {
+            return false; // write not allowed
+        }
+        
+        if (this.validator && !this.validator.call(owner, value) ) {
+            return false; // invalid value
+        }
+
+        if (!silent) {
+            beforeRetVal = owner.fireBeforeChangeEvent(event);
+            if (beforeRetVal === false) {
+                return false;
+            }
+        }
+
+        if (this.setter) {
+            value = this.setter.call(owner, value, this.name);
+            if (value === undefined) {
+            }
+
+            if (value === invalidValue) {
+                return false;
+            }
+        }
+        
+        if (this.method) {
+            if (this.method.call(owner, value, this.name) === invalidValue) {
+                return false; 
+            }
+        }
+        
+        this.value = value; // TODO: set before calling setter/method?
+        this._written = true;
+        
+        event.type = name;
+        
+        if (!silent) {
+            this.owner.fireChangeEvent(event);
+        }
+        
+        return true;
+    },
+    
+    /**
+     * Allows for configuring the Attribute's properties.
+     * @method configure
+     * @param {Object} map A key-value map of Attribute properties.
+     * @param {Boolean} init Whether or not this should become the initial config.
+     */
+    configure: function(map, init) {
+        map = map || {};
+
+        if (init) {
+            this._written = false; // reset writeOnce
+        }
+
+        this._initialConfig = this._initialConfig || {};
+        
+        for (var key in map) {
+            if ( map.hasOwnProperty(key) ) {
+                this[key] = map[key];
+                if (init) {
+                    this._initialConfig[key] = map[key];
+                }
+            }
+        }
+    },
+    
+    /**
+     * Resets the value to the initial config value.
+     * @method resetValue
+     * @return {Boolean} Whether or not the value was set.
+     */
+    resetValue: function() {
+        return this.setValue(this._initialConfig.value);
+    },
+    
+    /**
+     * Resets the attribute config to the initial config state.
+     * @method resetConfig
+     */
+    resetConfig: function() {
+        this.configure(this._initialConfig, true);
+    },
+    
+    /**
+     * Resets the value to the current value.
+     * Useful when values may have gotten out of sync with actual properties.
+     * @method refresh
+     * @return {Boolean} Whether or not the value was set.
+     */
+    refresh: function(silent) {
+        this.setValue(this.value, silent);
+    }
+};
+
+(function() {
+    var Lang = YAHOO.util.Lang;
+
+    /*
+    Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+    Code licensed under the BSD License:
+    http://developer.yahoo.net/yui/license.txt
+    */
+    
+    /**
+     * Provides and manages YAHOO.util.Attribute instances
+     * @namespace YAHOO.util
+     * @class AttributeProvider
+     * @uses YAHOO.util.EventProvider
+     */
+    YAHOO.util.AttributeProvider = function() {};
+
+    YAHOO.util.AttributeProvider.prototype = {
+        
+        /**
+         * A key-value map of Attribute configurations
+         * @property _configs
+         * @protected (may be used by subclasses and augmentors)
+         * @private
+         * @type {Object}
+         */
+        _configs: null,
+        /**
+         * Returns the current value of the attribute.
+         * @method get
+         * @param {String} key The attribute whose value will be returned.
+         * @return {Any} The current value of the attribute.
+         */
+        get: function(key){
+            this._configs = this._configs || {};
+            var config = this._configs[key];
+            
+            if (!config || !this._configs.hasOwnProperty(key)) {
+                return null;
+            }
+            
+            return config.getValue();
+        },
+        
+        /**
+         * Sets the value of a config.
+         * @method set
+         * @param {String} key The name of the attribute
+         * @param {Any} value The value to apply to the attribute
+         * @param {Boolean} silent Whether or not to suppress change events
+         * @return {Boolean} Whether or not the value was set.
+         */
+        set: function(key, value, silent){
+            this._configs = this._configs || {};
+            var config = this._configs[key];
+            
+            if (!config) {
+                return false;
+            }
+            
+            return config.setValue(value, silent);
+        },
+    
+        /**
+         * Returns an array of attribute names.
+         * @method getAttributeKeys
+         * @return {Array} An array of attribute names.
+         */
+        getAttributeKeys: function(){
+            this._configs = this._configs;
+            var keys = [], key;
+
+            for (key in this._configs) {
+                if ( Lang.hasOwnProperty(this._configs, key) && 
+                        !Lang.isUndefined(this._configs[key]) ) {
+                    keys[keys.length] = key;
+                }
+            }
+            
+            return keys;
+        },
+        
+        /**
+         * Sets multiple attribute values.
+         * @method setAttributes
+         * @param {Object} map  A key-value map of attributes
+         * @param {Boolean} silent Whether or not to suppress change events
+         */
+        setAttributes: function(map, silent){
+            for (var key in map) {
+                if ( Lang.hasOwnProperty(map, key) ) {
+                    this.set(key, map[key], silent);
+                }
+            }
+        },
+    
+        /**
+         * Resets the specified attribute's value to its initial value.
+         * @method resetValue
+         * @param {String} key The name of the attribute
+         * @param {Boolean} silent Whether or not to suppress change events
+         * @return {Boolean} Whether or not the value was set
+         */
+        resetValue: function(key, silent){
+            this._configs = this._configs || {};
+            if (this._configs[key]) {
+                this.set(key, this._configs[key]._initialConfig.value, silent);
+                return true;
+            }
+            return false;
+        },
+    
+        /**
+         * Sets the attribute's value to its current value.
+         * @method refresh
+         * @param {String | Array} key The attribute(s) to refresh
+         * @param {Boolean} silent Whether or not to suppress change events
+         */
+        refresh: function(key, silent) {
+            this._configs = this._configs || {};
+            var configs = this._configs;
+            
+            key = ( ( Lang.isString(key) ) ? [key] : key ) || 
+                    this.getAttributeKeys();
+            
+            for (var i = 0, len = key.length; i < len; ++i) { 
+                if (configs.hasOwnProperty(key[i])) {
+                    this._configs[key[i]].refresh(silent);
+                }
+            }
+        },
+    
+        /**
+         * Adds an Attribute to the AttributeProvider instance. 
+         * @method register
+         * @param {String} key The attribute's name
+         * @param {Object} map A key-value map containing the
+         * attribute's properties.
+         * @deprecated Use setAttributeConfig
+         */
+        register: function(key, map) {
+            this.setAttributeConfig(key, map);
+        },
+        
+        
+        /**
+         * Returns the attribute's properties.
+         * @method getAttributeConfig
+         * @param {String} key The attribute's name
+         * @private
+         * @return {object} A key-value map containing all of the
+         * attribute's properties.
+         */
+        getAttributeConfig: function(key) {
+            this._configs = this._configs || {};
+            var config = this._configs[key] || {};
+            var map = {}; // returning a copy to prevent overrides
+            
+            for (key in config) {
+                if ( Lang.hasOwnProperty(config, key) ) {
+                    map[key] = config[key];
+                }
+            }
+    
+            return map;
+        },
+        
+        /**
+         * Sets or updates an Attribute instance's properties. 
+         * @method setAttributeConfig
+         * @param {String} key The attribute's name.
+         * @param {Object} map A key-value map of attribute properties
+         * @param {Boolean} init Whether or not this should become the intial config.
+         */
+        setAttributeConfig: function(key, map, init) {
+            this._configs = this._configs || {};
+            map = map || {};
+            if (!this._configs[key]) {
+                map.name = key;
+                this._configs[key] = this.createAttribute(map);
+            } else {
+                this._configs[key].configure(map, init);
+            }
+        },
+        
+        /**
+         * Sets or updates an Attribute instance's properties. 
+         * @method configureAttribute
+         * @param {String} key The attribute's name.
+         * @param {Object} map A key-value map of attribute properties
+         * @param {Boolean} init Whether or not this should become the intial config.
+         * @deprecated Use setAttributeConfig
+         */
+        configureAttribute: function(key, map, init) {
+            this.setAttributeConfig(key, map, init);
+        },
+        
+        /**
+         * Resets an attribute to its intial configuration. 
+         * @method resetAttributeConfig
+         * @param {String} key The attribute's name.
+         * @private
+         */
+        resetAttributeConfig: function(key){
+            this._configs = this._configs || {};
+            this._configs[key].resetConfig();
+        },
+        
+        // wrapper for EventProvider.subscribe
+        // to create events on the fly
+        subscribe: function(type, callback) {
+            this._events = this._events || {};
+
+            if ( !(type in this._events) ) {
+                this._events[type] = this.createEvent(type);
+            }
+
+            YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments);
+        },
+
+        on: function() {
+            this.subscribe.apply(this, arguments);
+        },
+
+        addListener: function() {
+            this.subscribe.apply(this, arguments);
+        },
+
+        /**
+         * Fires the attribute's beforeChange event. 
+         * @method fireBeforeChangeEvent
+         * @param {String} key The attribute's name.
+         * @param {Obj} e The event object to pass to handlers.
+         */
+        fireBeforeChangeEvent: function(e) {
+            var type = 'before';
+            type += e.type.charAt(0).toUpperCase() + e.type.substr(1) + 'Change';
+            e.type = type;
+            return this.fireEvent(e.type, e);
+        },
+        
+        /**
+         * Fires the attribute's change event. 
+         * @method fireChangeEvent
+         * @param {String} key The attribute's name.
+         * @param {Obj} e The event object to pass to the handlers.
+         */
+        fireChangeEvent: function(e) {
+            e.type += 'Change';
+            return this.fireEvent(e.type, e);
+        },
+
+        createAttribute: function(map) {
+            return new YAHOO.util.Attribute(map, this);
+        }
+    };
+    
+    YAHOO.augment(YAHOO.util.AttributeProvider, YAHOO.util.EventProvider);
+})();
+
+(function() {
+// internal shorthand
+var Dom = YAHOO.util.Dom,
+    AttributeProvider = YAHOO.util.AttributeProvider,
+	specialTypes = {
+		mouseenter: true,
+		mouseleave: true
+	};
+
+/**
+ * Element provides an wrapper object to simplify adding
+ * event listeners, using dom methods, and managing attributes. 
+ * @module element
+ * @namespace YAHOO.util
+ * @requires yahoo, dom, event
+ */
+
+/**
+ * Element provides an wrapper object to simplify adding
+ * event listeners, using dom methods, and managing attributes. 
+ * @class Element
+ * @uses YAHOO.util.AttributeProvider
+ * @constructor
+ * @param el {HTMLElement | String} The html element that 
+ * represents the Element.
+ * @param {Object} map A key-value map of initial config names and values
+ */
+var Element = function(el, map) {
+    this.init.apply(this, arguments);
+};
+
+Element.DOM_EVENTS = {
+    'click': true,
+    'dblclick': true,
+    'keydown': true,
+    'keypress': true,
+    'keyup': true,
+    'mousedown': true,
+    'mousemove': true,
+    'mouseout': true, 
+    'mouseover': true, 
+    'mouseup': true,
+    'mouseenter': true, 
+    'mouseleave': true,
+    'focus': true,
+    'blur': true,
+    'submit': true,
+    'change': true
+};
+
+Element.prototype = {
+    /**
+     * Dom events supported by the Element instance.
+     * @property DOM_EVENTS
+     * @type Object
+     */
+    DOM_EVENTS: null,
+
+    DEFAULT_HTML_SETTER: function(value, key) {
+        var el = this.get('element');
+        
+        if (el) {
+            el[key] = value;
+        }
+
+		return value;
+
+    },
+
+    DEFAULT_HTML_GETTER: function(key) {
+        var el = this.get('element'),
+            val;
+
+        if (el) {
+            val = el[key];
+        }
+
+        return val;
+    },
+
+    /**
+     * Wrapper for HTMLElement method.
+     * @method appendChild
+     * @param {YAHOO.util.Element || HTMLElement} child The element to append. 
+     * @return {HTMLElement} The appended DOM element. 
+     */
+    appendChild: function(child) {
+        child = child.get ? child.get('element') : child;
+        return this.get('element').appendChild(child);
+    },
+    
+    /**
+     * Wrapper for HTMLElement method.
+     * @method getElementsByTagName
+     * @param {String} tag The tagName to collect
+     * @return {HTMLCollection} A collection of DOM elements. 
+     */
+    getElementsByTagName: function(tag) {
+        return this.get('element').getElementsByTagName(tag);
+    },
+    
+    /**
+     * Wrapper for HTMLElement method.
+     * @method hasChildNodes
+     * @return {Boolean} Whether or not the element has childNodes
+     */
+    hasChildNodes: function() {
+        return this.get('element').hasChildNodes();
+    },
+    
+    /**
+     * Wrapper for HTMLElement method.
+     * @method insertBefore
+     * @param {HTMLElement} element The HTMLElement to insert
+     * @param {HTMLElement} before The HTMLElement to insert
+     * the element before.
+     * @return {HTMLElement} The inserted DOM element. 
+     */
+    insertBefore: function(element, before) {
+        element = element.get ? element.get('element') : element;
+        before = (before && before.get) ? before.get('element') : before;
+        
+        return this.get('element').insertBefore(element, before);
+    },
+    
+    /**
+     * Wrapper for HTMLElement method.
+     * @method removeChild
+     * @param {HTMLElement} child The HTMLElement to remove
+     * @return {HTMLElement} The removed DOM element. 
+     */
+    removeChild: function(child) {
+        child = child.get ? child.get('element') : child;
+        return this.get('element').removeChild(child);
+    },
+    
+    /**
+     * Wrapper for HTMLElement method.
+     * @method replaceChild
+     * @param {HTMLElement} newNode The HTMLElement to insert
+     * @param {HTMLElement} oldNode The HTMLElement to replace
+     * @return {HTMLElement} The replaced DOM element. 
+     */
+    replaceChild: function(newNode, oldNode) {
+        newNode = newNode.get ? newNode.get('element') : newNode;
+        oldNode = oldNode.get ? oldNode.get('element') : oldNode;
+        return this.get('element').replaceChild(newNode, oldNode);
+    },
+
+    
+    /**
+     * Registers Element specific attributes.
+     * @method initAttributes
+     * @param {Object} map A key-value map of initial attribute configs
+     */
+    initAttributes: function(map) {
+    },
+
+    /**
+     * Adds a listener for the given event.  These may be DOM or 
+     * customEvent listeners.  Any event that is fired via fireEvent
+     * can be listened for.  All handlers receive an event object. 
+     * @method addListener
+     * @param {String} type The name of the event to listen for
+     * @param {Function} fn The handler to call when the event fires
+     * @param {Any} obj A variable to pass to the handler
+     * @param {Object} scope The object to use for the scope of the handler 
+     */
+    addListener: function(type, fn, obj, scope) {
+
+        scope = scope || this;
+
+        var Event = YAHOO.util.Event,
+			el = this.get('element') || this.get('id'),
+        	self = this;
+
+
+		if (specialTypes[type] && !Event._createMouseDelegate) {
+	        return false;	
+		}
+
+
+        if (!this._events[type]) { // create on the fly
+
+            if (el && this.DOM_EVENTS[type]) {
+				Event.on(el, type, function(e, matchedEl) {
+
+					// Supplement IE with target, currentTarget relatedTarget
+
+	                if (e.srcElement && !e.target) { 
+	                    e.target = e.srcElement;
+	                }
+
+					if ((e.toElement && !e.relatedTarget) || (e.fromElement && !e.relatedTarget)) {
+						e.relatedTarget = Event.getRelatedTarget(e);
+					}
+					
+					if (!e.currentTarget) {
+						e.currentTarget = el;
+					}
+
+					//	Note: matchedEl el is passed back for delegated listeners
+		            self.fireEvent(type, e, matchedEl);
+
+		        }, obj, scope);
+            }
+            this.createEvent(type, {scope: this});
+        }
+        
+        return YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments); // notify via customEvent
+    },
+
+
+    /**
+     * Alias for addListener
+     * @method on
+     * @param {String} type The name of the event to listen for
+     * @param {Function} fn The function call when the event fires
+     * @param {Any} obj A variable to pass to the handler
+     * @param {Object} scope The object to use for the scope of the handler 
+     */
+    on: function() {
+        return this.addListener.apply(this, arguments);
+    },
+    
+    /**
+     * Alias for addListener
+     * @method subscribe
+     * @param {String} type The name of the event to listen for
+     * @param {Function} fn The function call when the event fires
+     * @param {Any} obj A variable to pass to the handler
+     * @param {Object} scope The object to use for the scope of the handler 
+     */
+    subscribe: function() {
+        return this.addListener.apply(this, arguments);
+    },
+    
+    /**
+     * Remove an event listener
+     * @method removeListener
+     * @param {String} type The name of the event to listen for
+     * @param {Function} fn The function call when the event fires
+     */
+    removeListener: function(type, fn) {
+        return this.unsubscribe.apply(this, arguments);
+    },
+    
+    /**
+     * Wrapper for Dom method.
+     * @method addClass
+     * @param {String} className The className to add
+     */
+    addClass: function(className) {
+        Dom.addClass(this.get('element'), className);
+    },
+    
+    /**
+     * Wrapper for Dom method.
+     * @method getElementsByClassName
+     * @param {String} className The className to collect
+     * @param {String} tag (optional) The tag to use in
+     * conjunction with class name
+     * @return {Array} Array of HTMLElements
+     */
+    getElementsByClassName: function(className, tag) {
+        return Dom.getElementsByClassName(className, tag,
+                this.get('element') );
+    },
+    
+    /**
+     * Wrapper for Dom method.
+     * @method hasClass
+     * @param {String} className The className to add
+     * @return {Boolean} Whether or not the element has the class name
+     */
+    hasClass: function(className) {
+        return Dom.hasClass(this.get('element'), className); 
+    },
+    
+    /**
+     * Wrapper for Dom method.
+     * @method removeClass
+     * @param {String} className The className to remove
+     */
+    removeClass: function(className) {
+        return Dom.removeClass(this.get('element'), className);
+    },
+    
+    /**
+     * Wrapper for Dom method.
+     * @method replaceClass
+     * @param {String} oldClassName The className to replace
+     * @param {String} newClassName The className to add
+     */
+    replaceClass: function(oldClassName, newClassName) {
+        return Dom.replaceClass(this.get('element'), 
+                oldClassName, newClassName);
+    },
+    
+    /**
+     * Wrapper for Dom method.
+     * @method setStyle
+     * @param {String} property The style property to set
+     * @param {String} value The value to apply to the style property
+     */
+    setStyle: function(property, value) {
+        return Dom.setStyle(this.get('element'),  property, value); // TODO: always queuing?
+    },
+    
+    /**
+     * Wrapper for Dom method.
+     * @method getStyle
+     * @param {String} property The style property to retrieve
+     * @return {String} The current value of the property
+     */
+    getStyle: function(property) {
+        return Dom.getStyle(this.get('element'),  property);
+    },
+    
+    /**
+     * Apply any queued set calls.
+     * @method fireQueue
+     */
+    fireQueue: function() {
+        var queue = this._queue;
+        for (var i = 0, len = queue.length; i < len; ++i) {
+            this[queue[i][0]].apply(this, queue[i][1]);
+        }
+    },
+    
+    /**
+     * Appends the HTMLElement into either the supplied parentNode.
+     * @method appendTo
+     * @param {HTMLElement | Element} parentNode The node to append to
+     * @param {HTMLElement | Element} before An optional node to insert before
+     * @return {HTMLElement} The appended DOM element. 
+     */
+    appendTo: function(parent, before) {
+        parent = (parent.get) ?  parent.get('element') : Dom.get(parent);
+        
+        this.fireEvent('beforeAppendTo', {
+            type: 'beforeAppendTo',
+            target: parent
+        });
+        
+        
+        before = (before && before.get) ? 
+                before.get('element') : Dom.get(before);
+        var element = this.get('element');
+        
+        if (!element) {
+            return false;
+        }
+        
+        if (!parent) {
+            return false;
+        }
+        
+        if (element.parent != parent) {
+            if (before) {
+                parent.insertBefore(element, before);
+            } else {
+                parent.appendChild(element);
+            }
+        }
+        
+        
+        this.fireEvent('appendTo', {
+            type: 'appendTo',
+            target: parent
+        });
+
+        return element;
+    },
+    
+    get: function(key) {
+        var configs = this._configs || {},
+            el = configs.element; // avoid loop due to 'element'
+
+        if (el && !configs[key] && !YAHOO.lang.isUndefined(el.value[key]) ) {
+            this._setHTMLAttrConfig(key);
+        }
+
+        return AttributeProvider.prototype.get.call(this, key);
+    },
+
+    setAttributes: function(map, silent) {
+        // set based on configOrder
+        var done = {},
+            configOrder = this._configOrder;
+
+        // set based on configOrder
+        for (var i = 0, len = configOrder.length; i < len; ++i) {
+            if (map[configOrder[i]] !== undefined) {
+                done[configOrder[i]] = true;
+                this.set(configOrder[i], map[configOrder[i]], silent);
+            }
+        }
+
+        // unconfigured (e.g. Dom attributes)
+        for (var att in map) {
+            if (map.hasOwnProperty(att) && !done[att]) {
+                this.set(att, map[att], silent);
+            }
+        }
+    },
+
+    set: function(key, value, silent) {
+        var el = this.get('element');
+        if (!el) {
+            this._queue[this._queue.length] = ['set', arguments];
+            if (this._configs[key]) {
+                this._configs[key].value = value; // so "get" works while queueing
+            
+            }
+            return;
+        }
+        
+        // set it on the element if not configured and is an HTML attribute
+        if ( !this._configs[key] && !YAHOO.lang.isUndefined(el[key]) ) {
+            this._setHTMLAttrConfig(key);
+        }
+
+        return AttributeProvider.prototype.set.apply(this, arguments);
+    },
+    
+    setAttributeConfig: function(key, map, init) {
+        this._configOrder.push(key);
+        AttributeProvider.prototype.setAttributeConfig.apply(this, arguments);
+    },
+
+    createEvent: function(type, config) {
+        this._events[type] = true;
+        return AttributeProvider.prototype.createEvent.apply(this, arguments);
+    },
+    
+    init: function(el, attr) {
+        this._initElement(el, attr); 
+    },
+
+    destroy: function() {
+        var el = this.get('element');
+        YAHOO.util.Event.purgeElement(el, true); // purge DOM listeners recursively
+        this.unsubscribeAll(); // unsubscribe all custom events
+
+        if (el && el.parentNode) {
+            el.parentNode.removeChild(el); // pull from the DOM
+        }
+
+        // revert initial configs
+        this._queue = [];
+        this._events = {};
+        this._configs = {};
+        this._configOrder = []; 
+    },
+
+    _initElement: function(el, attr) {
+        this._queue = this._queue || [];
+        this._events = this._events || {};
+        this._configs = this._configs || {};
+        this._configOrder = []; 
+        attr = attr || {};
+        attr.element = attr.element || el || null;
+
+        var isReady = false;  // to determine when to init HTMLElement and content
+
+        var DOM_EVENTS = Element.DOM_EVENTS;
+        this.DOM_EVENTS = this.DOM_EVENTS || {};
+
+        for (var event in DOM_EVENTS) {
+            if (DOM_EVENTS.hasOwnProperty(event)) {
+                this.DOM_EVENTS[event] = DOM_EVENTS[event];
+            }
+        }
+
+        if (typeof attr.element === 'string') { // register ID for get() access
+            this._setHTMLAttrConfig('id', { value: attr.element });
+        }
+
+        if (Dom.get(attr.element)) {
+            isReady = true;
+            this._initHTMLElement(attr);
+            this._initContent(attr);
+        }
+
+        YAHOO.util.Event.onAvailable(attr.element, function() {
+            if (!isReady) { // otherwise already done
+                this._initHTMLElement(attr);
+            }
+
+            this.fireEvent('available', { type: 'available', target: Dom.get(attr.element) });  
+        }, this, true);
+        
+        YAHOO.util.Event.onContentReady(attr.element, function() {
+            if (!isReady) { // otherwise already done
+                this._initContent(attr);
+            }
+            this.fireEvent('contentReady', { type: 'contentReady', target: Dom.get(attr.element) });  
+        }, this, true);
+    },
+
+    _initHTMLElement: function(attr) {
+        /**
+         * The HTMLElement the Element instance refers to.
+         * @attribute element
+         * @type HTMLElement
+         */
+        this.setAttributeConfig('element', {
+            value: Dom.get(attr.element),
+            readOnly: true
+         });
+    },
+
+    _initContent: function(attr) {
+        this.initAttributes(attr);
+        this.setAttributes(attr, true);
+        this.fireQueue();
+
+    },
+
+    /**
+     * Sets the value of the property and fires beforeChange and change events.
+     * @private
+     * @method _setHTMLAttrConfig
+     * @param {YAHOO.util.Element} element The Element instance to
+     * register the config to.
+     * @param {String} key The name of the config to register
+     * @param {Object} map A key-value map of the config's params
+     */
+    _setHTMLAttrConfig: function(key, map) {
+        var el = this.get('element');
+        map = map || {};
+        map.name = key;
+
+        map.setter = map.setter || this.DEFAULT_HTML_SETTER;
+        map.getter = map.getter || this.DEFAULT_HTML_GETTER;
+
+        map.value = map.value || el[key];
+        this._configs[key] = new YAHOO.util.Attribute(map, this);
+    }
+};
+
+/**
+ * Fires when the Element's HTMLElement can be retrieved by Id.
+ * <p>See: <a href=""
+ * <p><strong>Event fields:</strong><br>
+ * <code>&lt;String&gt; type</code> available<br>
+ * <code>&lt;HTMLElement&gt;
+ * target</code> the HTMLElement bound to this Element instance<br>
+ * <p><strong>Usage:</strong><br>
+ * <code>var handler = function(e) {var target = e.target};<br>
+ * myTabs.addListener('available', handler);</code></p>
+ * @event available
+ */
+ 
+/**
+ * Fires when the Element's HTMLElement subtree is rendered.
+ * <p>See: <a href=""
+ * <p><strong>Event fields:</strong><br>
+ * <code>&lt;String&gt; type</code> contentReady<br>
+ * <code>&lt;HTMLElement&gt;
+ * target</code> the HTMLElement bound to this Element instance<br>
+ * <p><strong>Usage:</strong><br>
+ * <code>var handler = function(e) {var target = e.target};<br>
+ * myTabs.addListener('contentReady', handler);</code></p>
+ * @event contentReady
+ */
+
+/**
+ * Fires before the Element is appended to another Element.
+ * <p>See: <a href=""
+ * <p><strong>Event fields:</strong><br>
+ * <code>&lt;String&gt; type</code> beforeAppendTo<br>
+ * <code>&lt;HTMLElement/Element&gt;
+ * target</code> the HTMLElement/Element being appended to 
+ * <p><strong>Usage:</strong><br>
+ * <code>var handler = function(e) {var target = e.target};<br>
+ * myTabs.addListener('beforeAppendTo', handler);</code></p>
+ * @event beforeAppendTo
+ */
+
+/**
+ * Fires after the Element is appended to another Element.
+ * <p>See: <a href=""
+ * <p><strong>Event fields:</strong><br>
+ * <code>&lt;String&gt; type</code> appendTo<br>
+ * <code>&lt;HTMLElement/Element&gt;
+ * target</code> the HTMLElement/Element being appended to 
+ * <p><strong>Usage:</strong><br>
+ * <code>var handler = function(e) {var target = e.target};<br>
+ * myTabs.addListener('appendTo', handler);</code></p>
+ * @event appendTo
+ */
+
+YAHOO.augment(Element, AttributeProvider);
+YAHOO.util.Element = Element;
+})();
+
+YAHOO.register("element", YAHOO.util.Element, {version: "2.9.0", build: "2800"});

Added: branches/wf4ever/public/_javascript_s/yui/tabview.js (0 => 3265)


--- branches/wf4ever/public/_javascript_s/yui/tabview.js	                        (rev 0)
+++ branches/wf4ever/public/_javascript_s/yui/tabview.js	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,1004 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+(function() {
+
+    /**
+     * The tabview module provides a widget for managing content bound to tabs.
+     * @module tabview
+     * @requires yahoo, dom, event, element
+     *
+     */
+
+    var Y = YAHOO.util,
+        Dom = Y.Dom,
+        Event = Y.Event,
+        document = window.document,
+    
+        // STRING CONSTANTS
+        ACTIVE = 'active',
+        ACTIVE_INDEX = 'activeIndex',
+        ACTIVE_TAB = 'activeTab',
+        DISABLED = 'disabled',
+        CONTENT_EL = 'contentEl',
+        ELEMENT = 'element',
+    
+    /**
+     * A widget to control tabbed views.
+     * @namespace YAHOO.widget
+     * @class TabView
+     * @extends YAHOO.util.Element
+     * @constructor
+     * @param {HTMLElement | String | Object} el(optional) The html 
+     * element that represents the TabView, or the attribute object to use. 
+     * An element will be created if none provided.
+     * @param {Object} attr (optional) A key map of the tabView's 
+     * initial attributes.  Ignored if first arg is attributes object.
+     */
+    TabView = function(el, attr) {
+        attr = attr || {};
+        if (arguments.length == 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
+            attr = el; // treat first arg as attr object
+            el = attr.element || null;
+        }
+        
+        if (!el && !attr.element) { // create if we dont have one
+            el = this._createTabViewElement(attr);
+        }
+        TabView.superclass.constructor.call(this, el, attr); 
+    };
+
+    YAHOO.extend(TabView, Y.Element, {
+        /**
+         * The className to add when building from scratch. 
+         * @property CLASSNAME
+         * @default "navset"
+         */
+        CLASSNAME: 'yui-navset',
+        
+        /**
+         * The className of the HTMLElement containing the TabView's tab elements
+         * to look for when building from existing markup, or to add when building
+         * from scratch. 
+         * All childNodes of the tab container are treated as Tabs when building
+         * from existing markup.
+         * @property TAB_PARENT_CLASSNAME
+         * @default "nav"
+         */
+        TAB_PARENT_CLASSNAME: 'yui-nav',
+        
+        /**
+         * The className of the HTMLElement containing the TabView's label elements
+         * to look for when building from existing markup, or to add when building
+         * from scratch. 
+         * All childNodes of the content container are treated as content elements when
+         * building from existing markup.
+         * @property CONTENT_PARENT_CLASSNAME
+         * @default "nav-content"
+         */
+        CONTENT_PARENT_CLASSNAME: 'yui-content',
+        
+        _tabParent: null,
+        _contentParent: null,
+        
+        /**
+         * Adds a Tab to the TabView instance.  
+         * If no index is specified, the tab is added to the end of the tab list.
+         * @method addTab
+         * @param {YAHOO.widget.Tab} tab A Tab instance to add.
+         * @param {Integer} index The position to add the tab. 
+         * @return void
+         */
+        addTab: function(tab, index) {
+            var tabs = this.get('tabs'),
+                tabParent = this._tabParent,
+                contentParent = this._contentParent,
+                tabElement = tab.get(ELEMENT),
+                contentEl = tab.get(CONTENT_EL),
+                activeIndex = this.get(ACTIVE_INDEX),
+                before;
+
+            if (!tabs) { // not ready yet
+                this._queue[this._queue.length] = ['addTab', arguments];
+                return false;
+            }
+            
+            before = this.getTab(index);
+            index = (index === undefined) ? tabs.length : index;
+            
+            tabs.splice(index, 0, tab);
+
+            if (before) {
+                tabParent.insertBefore(tabElement, before.get(ELEMENT));
+                if (contentEl) {
+                    contentParent.appendChild(contentEl);
+                }
+            } else {
+                tabParent.appendChild(tabElement);
+                if (contentEl) {
+                    contentParent.appendChild(contentEl);
+                }
+            }
+
+            if ( !tab.get(ACTIVE) ) {
+                tab.set('contentVisible', false, true); /* hide if not active */
+                if (index <= activeIndex) {
+                    this.set(ACTIVE_INDEX, activeIndex + 1, true);
+                }  
+            } else {
+                this.set(ACTIVE_TAB, tab, true);
+                this.set('activeIndex', index, true);
+            }
+
+            this._initTabEvents(tab);
+        },
+
+        _initTabEvents: function(tab) {
+            tab.addListener( tab.get('activationEvent'), tab._onActivate, this, tab);
+            tab.addListener('activationEventChange', tab._onActivationEventChange, this, tab);
+        },
+
+        _removeTabEvents: function(tab) {
+            tab.removeListener(tab.get('activationEvent'), tab._onActivate, this, tab);
+            tab.removeListener('activationEventChange', tab._onActivationEventChange, this, tab);
+        },
+
+        /**
+         * Routes childNode events.
+         * @method DOMEventHandler
+         * @param {event} e The Dom event that is being handled.
+         * @return void
+         */
+        DOMEventHandler: function(e) {
+            var target = Event.getTarget(e),
+                tabParent = this._tabParent,
+                tabs = this.get('tabs'),
+                tab,
+                tabEl,
+                contentEl;
+
+            
+            if (Dom.isAncestor(tabParent, target) ) {
+                for (var i = 0, len = tabs.length; i < len; i++) {
+                    tabEl = tabs[i].get(ELEMENT);
+                    contentEl = tabs[i].get(CONTENT_EL);
+
+                    if ( target == tabEl || Dom.isAncestor(tabEl, target) ) {
+                        tab = tabs[i];
+                        break; // note break
+                    }
+                } 
+                
+                if (tab) {
+                    tab.fireEvent(e.type, e);
+                }
+            }
+        },
+        
+        /**
+         * Returns the Tab instance at the specified index.
+         * @method getTab
+         * @param {Integer} index The position of the Tab.
+         * @return YAHOO.widget.Tab
+         */
+        getTab: function(index) {
+            return this.get('tabs')[index];
+        },
+        
+        /**
+         * Returns the index of given tab.
+         * @method getTabIndex
+         * @param {YAHOO.widget.Tab} tab The tab whose index will be returned.
+         * @return int
+         */
+        getTabIndex: function(tab) {
+            var index = null,
+                tabs = this.get('tabs');
+            for (var i = 0, len = tabs.length; i < len; ++i) {
+                if (tab == tabs[i]) {
+                    index = i;
+                    break;
+                }
+            }
+            
+            return index;
+        },
+        
+        /**
+         * Removes the specified Tab from the TabView.
+         * @method removeTab
+         * @param {YAHOO.widget.Tab} item The Tab instance to be removed.
+         * @return void
+         */
+        removeTab: function(tab) {
+            var tabCount = this.get('tabs').length,
+                activeIndex = this.get(ACTIVE_INDEX),
+                index = this.getTabIndex(tab);
+
+            if ( tab === this.get(ACTIVE_TAB) ) { 
+                if (tabCount > 1) { // select another tab
+                    if (index + 1 === tabCount) { // if last, activate previous
+                        this.set(ACTIVE_INDEX, index - 1);
+                    } else { // activate next tab
+                        this.set(ACTIVE_INDEX, index + 1);
+                    }
+                } else { // no more tabs
+                    this.set(ACTIVE_TAB, null);
+                }
+            } else if (index < activeIndex) {
+                this.set(ACTIVE_INDEX, activeIndex - 1, true);
+            }
+            
+            this._removeTabEvents(tab);
+            this._tabParent.removeChild( tab.get(ELEMENT) );
+            this._contentParent.removeChild( tab.get(CONTENT_EL) );
+            this._configs.tabs.value.splice(index, 1);
+
+            tab.fireEvent('remove', { type: 'remove', tabview: this });
+        },
+        
+        /**
+         * Provides a readable name for the TabView instance.
+         * @method toString
+         * @return String
+         */
+        toString: function() {
+            var name = this.get('id') || this.get('tagName');
+            return "TabView " + name; 
+        },
+        
+        /**
+         * The transiton to use when switching between tabs.
+         * @method contentTransition
+         */
+        contentTransition: function(newTab, oldTab) {
+            if (newTab) {
+                newTab.set('contentVisible', true);
+            }
+            if (oldTab) {
+                oldTab.set('contentVisible', false);
+            }
+        },
+        
+        /**
+         * setAttributeConfigs TabView specific properties.
+         * @method initAttributes
+         * @param {Object} attr Hash of initial attributes
+         */
+        initAttributes: function(attr) {
+            TabView.superclass.initAttributes.call(this, attr);
+            
+            if (!attr.orientation) {
+                attr.orientation = 'top';
+            }
+            
+            var el = this.get(ELEMENT);
+
+            if (!this.hasClass(this.CLASSNAME)) {
+                this.addClass(this.CLASSNAME);        
+            }
+            
+            /**
+             * The Tabs belonging to the TabView instance.
+             * @attribute tabs
+             * @type Array
+             */
+            this.setAttributeConfig('tabs', {
+                value: [],
+                readOnly: true
+            });
+
+            /**
+             * The container of the tabView's label elements.
+             * @property _tabParent
+             * @private
+             * @type HTMLElement
+             */
+            this._tabParent = 
+                    this.getElementsByClassName(this.TAB_PARENT_CLASSNAME,
+                            'ul' )[0] || this._createTabParent();
+                
+            /**
+             * The container of the tabView's content elements.
+             * @property _contentParent
+             * @type HTMLElement
+             * @private
+             */
+            this._contentParent = 
+                    this.getElementsByClassName(this.CONTENT_PARENT_CLASSNAME,
+                            'div')[0] ||  this._createContentParent();
+            
+            /**
+             * How the Tabs should be oriented relative to the TabView.
+             * Valid orientations are "top", "left", "bottom", and "right"
+             * @attribute orientation
+             * @type String
+             * @default "top"
+             */
+            this.setAttributeConfig('orientation', {
+                value: attr.orientation,
+                method: function(value) {
+                    var current = this.get('orientation');
+                    this.addClass('yui-navset-' + value);
+                    
+                    if (current != value) {
+                        this.removeClass('yui-navset-' + current);
+                    }
+                    
+                    if (value === 'bottom') {
+                        this.appendChild(this._tabParent);
+                    }
+                }
+            });
+            
+            /**
+             * The index of the tab currently active.
+             * @attribute activeIndex
+             * @type Int
+             */
+            this.setAttributeConfig(ACTIVE_INDEX, {
+                value: attr.activeIndex,
+                validator: function(value) {
+                    var ret = true,
+                        tab;
+                    if (value) { // cannot activate if disabled
+                        tab = this.getTab(value);
+                        if (tab && tab.get(DISABLED)) {
+                            ret = false;
+                        }
+                    }
+                    return ret;
+                }
+            });
+            
+            /**
+             * The tab currently active.
+             * @attribute activeTab
+             * @type YAHOO.widget.Tab
+             */
+            this.setAttributeConfig(ACTIVE_TAB, {
+                value: attr[ACTIVE_TAB],
+                method: function(tab) {
+                    var activeTab = this.get(ACTIVE_TAB);
+                    
+                    if (tab) {
+                        tab.set(ACTIVE, true);
+                    }
+                    
+                    if (activeTab && activeTab !== tab) {
+                        activeTab.set(ACTIVE, false);
+                    }
+                    
+                    if (activeTab && tab !== activeTab) { // no transition if only 1
+                        this.contentTransition(tab, activeTab);
+                    } else if (tab) {
+                        tab.set('contentVisible', true);
+                    }
+                },
+                validator: function(value) {
+                    var ret = true;
+                    if (value && value.get(DISABLED)) { // cannot activate if disabled
+                        ret = false;
+                    }
+                    return ret;
+                }
+            });
+
+            this.on('activeTabChange', this._onActiveTabChange);
+            this.on('activeIndexChange', this._onActiveIndexChange);
+
+            if ( this._tabParent ) {
+                this._initTabs();
+            }
+            
+            // Due to delegation we add all DOM_EVENTS to the TabView container
+            // but IE will leak when unsupported events are added, so remove these
+            this.DOM_EVENTS.submit = false;
+            this.DOM_EVENTS.focus = false;
+            this.DOM_EVENTS.blur = false;
+            this.DOM_EVENTS.change = false;
+
+            for (var type in this.DOM_EVENTS) {
+                if ( YAHOO.lang.hasOwnProperty(this.DOM_EVENTS, type) ) {
+                    this.addListener.call(this, type, this.DOMEventHandler);
+                }
+            }
+        },
+
+        /**
+         * Removes selected state from the given tab if it is the activeTab
+         * @method deselectTab
+         * @param {Int} index The tab index to deselect 
+         */
+        deselectTab: function(index) {
+            if (this.getTab(index) === this.get(ACTIVE_TAB)) {
+                this.set(ACTIVE_TAB, null);
+            }
+        },
+
+        /**
+         * Makes the tab at the given index the active tab
+         * @method selectTab
+         * @param {Int} index The tab index to be made active
+         */
+        selectTab: function(index) {
+            this.set(ACTIVE_TAB, this.getTab(index));
+        },
+
+        _onActiveTabChange: function(e) {
+            var activeIndex = this.get(ACTIVE_INDEX),
+                newIndex = this.getTabIndex(e.newValue);
+
+            if (activeIndex !== newIndex) {
+                if (!(this.set(ACTIVE_INDEX, newIndex)) ) { // NOTE: setting
+                     // revert if activeIndex update fails (cancelled via beforeChange) 
+                    this.set(ACTIVE_TAB, e.prevValue);
+                }
+            }
+        },
+        
+        _onActiveIndexChange: function(e) {
+            // no set if called from ActiveTabChange event
+            if (e.newValue !== this.getTabIndex(this.get(ACTIVE_TAB))) {
+                if (!(this.set(ACTIVE_TAB, this.getTab(e.newValue))) ) { // NOTE: setting
+                     // revert if activeTab update fails (cancelled via beforeChange) 
+                    this.set(ACTIVE_INDEX, e.prevValue);
+                }
+            }
+        },
+
+        /**
+         * Creates Tab instances from a collection of HTMLElements.
+         * @method _initTabs
+         * @private
+         * @return void
+         */
+        _initTabs: function() {
+            var tabs = Dom.getChildren(this._tabParent),
+                contentElements = Dom.getChildren(this._contentParent),
+                activeIndex = this.get(ACTIVE_INDEX),
+                tab,
+                attr,
+                active;
+
+            for (var i = 0, len = tabs.length; i < len; ++i) {
+                attr = {};
+                
+                if (contentElements[i]) {
+                    attr.contentEl = contentElements[i];
+                }
+
+                tab = new YAHOO.widget.Tab(tabs[i], attr);
+                this.addTab(tab);
+                
+                if (tab.hasClass(tab.ACTIVE_CLASSNAME) ) {
+                    active = tab;
+                }
+            }
+            if (activeIndex != undefined) { // not null or undefined
+                this.set(ACTIVE_TAB, this.getTab(activeIndex));
+            } else {
+                this._configs[ACTIVE_TAB].value = active; // dont invoke method
+                this._configs[ACTIVE_INDEX].value = this.getTabIndex(active);
+            }
+        },
+
+        _createTabViewElement: function(attr) {
+            var el = document.createElement('div');
+
+            if ( this.CLASSNAME ) {
+                el.className = this.CLASSNAME;
+            }
+            
+            return el;
+        },
+
+        _createTabParent: function(attr) {
+            var el = document.createElement('ul');
+
+            if ( this.TAB_PARENT_CLASSNAME ) {
+                el.className = this.TAB_PARENT_CLASSNAME;
+            }
+            
+            this.get(ELEMENT).appendChild(el);
+            
+            return el;
+        },
+        
+        _createContentParent: function(attr) {
+            var el = document.createElement('div');
+
+            if ( this.CONTENT_PARENT_CLASSNAME ) {
+                el.className = this.CONTENT_PARENT_CLASSNAME;
+            }
+            
+            this.get(ELEMENT).appendChild(el);
+            
+            return el;
+        }
+    });
+    
+    
+    YAHOO.widget.TabView = TabView;
+})();
+
+(function() {
+    var Y = YAHOO.util, 
+        Dom = Y.Dom,
+        Lang = YAHOO.lang,
+    
+
+    // STRING CONSTANTS
+        ACTIVE_TAB = 'activeTab',
+        LABEL = 'label',
+        LABEL_EL = 'labelEl',
+        CONTENT = 'content',
+        CONTENT_EL = 'contentEl',
+        ELEMENT = 'element',
+        CACHE_DATA = 'cacheData',
+        DATA_SRC = 'dataSrc',
+        DATA_LOADED = 'dataLoaded',
+        DATA_TIMEOUT = 'dataTimeout',
+        LOAD_METHOD = 'loadMethod',
+        POST_DATA = 'postData',
+        DISABLED = 'disabled',
+    
+    /**
+     * A representation of a Tab's label and content.
+     * @namespace YAHOO.widget
+     * @class Tab
+     * @extends YAHOO.util.Element
+     * @constructor
+     * @param element {HTMLElement | String} (optional) The html element that 
+     * represents the Tab. An element will be created if none provided.
+     * @param {Object} properties A key map of initial properties
+     */
+    Tab = function(el, attr) {
+        attr = attr || {};
+        if (arguments.length == 1 && !Lang.isString(el) && !el.nodeName) {
+            attr = el;
+            el = attr.element;
+        }
+
+        if (!el && !attr.element) {
+            el = this._createTabElement(attr);
+        }
+
+        this.loadHandler =  {
+            success: function(o) {
+                this.set(CONTENT, o.responseText);
+            },
+            failure: function(o) {
+            }
+        };
+        
+        Tab.superclass.constructor.call(this, el, attr);
+        
+        this.DOM_EVENTS = {}; // delegating to tabView
+    };
+
+    YAHOO.extend(Tab, YAHOO.util.Element, {
+        /**
+         * The default tag name for a Tab's inner element.
+         * @property LABEL_INNER_TAGNAME
+         * @type String
+         * @default "em"
+         */
+        LABEL_TAGNAME: 'em',
+        
+        /**
+         * The class name applied to active tabs.
+         * @property ACTIVE_CLASSNAME
+         * @type String
+         * @default "selected"
+         */
+        ACTIVE_CLASSNAME: 'selected',
+        
+        /**
+         * The class name applied to active tabs.
+         * @property HIDDEN_CLASSNAME
+         * @type String
+         * @default "yui-hidden"
+         */
+        HIDDEN_CLASSNAME: 'yui-hidden',
+        
+        /**
+         * The title applied to active tabs.
+         * @property ACTIVE_TITLE
+         * @type String
+         * @default "active"
+         */
+        ACTIVE_TITLE: 'active',
+
+        /**
+         * The class name applied to disabled tabs.
+         * @property DISABLED_CLASSNAME
+         * @type String
+         * @default "disabled"
+         */
+        DISABLED_CLASSNAME: DISABLED,
+        
+        /**
+         * The class name applied to dynamic tabs while loading.
+         * @property LOADING_CLASSNAME
+         * @type String
+         * @default "disabled"
+         */
+        LOADING_CLASSNAME: 'loading',
+
+        /**
+         * Provides a reference to the connection request object when data is
+         * loaded dynamically.
+         * @property dataConnection
+         * @type Object
+         */
+        dataConnection: null,
+        
+        /**
+         * Object containing success and failure callbacks for loading data.
+         * @property loadHandler
+         * @type object
+         */
+        loadHandler: null,
+
+        _loading: false,
+        
+        /**
+         * Provides a readable name for the tab.
+         * @method toString
+         * @return String
+         */
+        toString: function() {
+            var el = this.get(ELEMENT),
+                id = el.id || el.tagName;
+            return "Tab " + id; 
+        },
+        
+        /**
+         * setAttributeConfigs Tab specific properties.
+         * @method initAttributes
+         * @param {Object} attr Hash of initial attributes
+         */
+        initAttributes: function(attr) {
+            attr = attr || {};
+            Tab.superclass.initAttributes.call(this, attr);
+            
+            /**
+             * The event that triggers the tab's activation.
+             * @attribute activationEvent
+             * @type String
+             */
+            this.setAttributeConfig('activationEvent', {
+                value: attr.activationEvent || 'click'
+            });        
+
+            /**
+             * The element that contains the tab's label.
+             * @attribute labelEl
+             * @type HTMLElement
+             */
+            this.setAttributeConfig(LABEL_EL, {
+                value: attr[LABEL_EL] || this._getLabelEl(),
+                method: function(value) {
+                    value = Dom.get(value);
+                    var current = this.get(LABEL_EL);
+
+                    if (current) {
+                        if (current == value) {
+                            return false; // already set
+                        }
+                        
+                        current.parentNode.replaceChild(value, current);
+                        this.set(LABEL, value.innerHTML);
+                    }
+                } 
+            });
+
+            /**
+             * The tab's label text (or innerHTML).
+             * @attribute label
+             * @type String
+             */
+            this.setAttributeConfig(LABEL, {
+                value: attr.label || this._getLabel(),
+                method: function(value) {
+                    var labelEl = this.get(LABEL_EL);
+                    if (!labelEl) { // create if needed
+                        this.set(LABEL_EL, this._createLabelEl());
+                    }
+                    
+                    labelEl.innerHTML = value;
+                }
+            });
+            
+            /**
+             * The HTMLElement that contains the tab's content.
+             * @attribute contentEl
+             * @type HTMLElement
+             */
+            this.setAttributeConfig(CONTENT_EL, {
+                value: attr[CONTENT_EL] || document.createElement('div'),
+                method: function(value) {
+                    value = Dom.get(value);
+                    var current = this.get(CONTENT_EL);
+
+                    if (current) {
+                        if (current === value) {
+                            return false; // already set
+                        }
+                        if (!this.get('selected')) {
+                            Dom.addClass(value, this.HIDDEN_CLASSNAME);
+                        }
+                        current.parentNode.replaceChild(value, current);
+                        this.set(CONTENT, value.innerHTML);
+                    }
+                }
+            });
+            
+            /**
+             * The tab's content.
+             * @attribute content
+             * @type String
+             */
+            this.setAttributeConfig(CONTENT, {
+                value: attr[CONTENT] || this.get(CONTENT_EL).innerHTML,
+                method: function(value) {
+                    this.get(CONTENT_EL).innerHTML = value;
+                }
+            });
+
+            /**
+             * The tab's data source, used for loading content dynamically.
+             * @attribute dataSrc
+             * @type String
+             */
+            this.setAttributeConfig(DATA_SRC, {
+                value: attr.dataSrc
+            });
+            
+            /**
+             * Whether or not content should be reloaded for every view.
+             * @attribute cacheData
+             * @type Boolean
+             * @default false
+             */
+            this.setAttributeConfig(CACHE_DATA, {
+                value: attr.cacheData || false,
+                validator: Lang.isBoolean
+            });
+            
+            /**
+             * The method to use for the data request.
+             * @attribute loadMethod
+             * @type String
+             * @default "GET"
+             */
+            this.setAttributeConfig(LOAD_METHOD, {
+                value: attr.loadMethod || 'GET',
+                validator: Lang.isString
+            });
+
+            /**
+             * Whether or not any data has been loaded from the server.
+             * @attribute dataLoaded
+             * @type Boolean
+             */        
+            this.setAttributeConfig(DATA_LOADED, {
+                value: false,
+                validator: Lang.isBoolean,
+                writeOnce: true
+            });
+            
+            /**
+             * Number if milliseconds before aborting and calling failure handler.
+             * @attribute dataTimeout
+             * @type Number
+             * @default null
+             */
+            this.setAttributeConfig(DATA_TIMEOUT, {
+                value: attr.dataTimeout || null,
+                validator: Lang.isNumber
+            });
+            
+            /**
+             * Arguments to pass when POST method is used 
+             * @attribute postData
+             * @default null
+             */
+            this.setAttributeConfig(POST_DATA, {
+                value: attr.postData || null
+            });
+
+            /**
+             * Whether or not the tab is currently active.
+             * If a dataSrc is set for the tab, the content will be loaded from
+             * the given source.
+             * @attribute active
+             * @type Boolean
+             */
+            this.setAttributeConfig('active', {
+                value: attr.active || this.hasClass(this.ACTIVE_CLASSNAME),
+                method: function(value) {
+                    if (value === true) {
+                        this.addClass(this.ACTIVE_CLASSNAME);
+                        this.set('title', this.ACTIVE_TITLE);
+                    } else {
+                        this.removeClass(this.ACTIVE_CLASSNAME);
+                        this.set('title', '');
+                    }
+                },
+                validator: function(value) {
+                    return Lang.isBoolean(value) && !this.get(DISABLED) ;
+                }
+            });
+            
+            /**
+             * Whether or not the tab is disabled.
+             * @attribute disabled
+             * @type Boolean
+             */
+            this.setAttributeConfig(DISABLED, {
+                value: attr.disabled || this.hasClass(this.DISABLED_CLASSNAME),
+                method: function(value) {
+                    if (value === true) {
+                        this.addClass(this.DISABLED_CLASSNAME);
+                    } else {
+                        this.removeClass(this.DISABLED_CLASSNAME);
+                    }
+                },
+                validator: Lang.isBoolean
+            });
+            
+            /**
+             * The href of the tab's anchor element.
+             * @attribute href
+             * @type String
+             * @default '#'
+             */
+            this.setAttributeConfig('href', {
+                value: attr.href ||
+                        this.getElementsByTagName('a')[0].getAttribute('href', 2) || '#',
+                method: function(value) {
+                    this.getElementsByTagName('a')[0].href = ""
+                },
+                validator: Lang.isString
+            });
+            
+            /**
+             * The Whether or not the tab's content is visible.
+             * @attribute contentVisible
+             * @type Boolean
+             * @default false
+             */
+            this.setAttributeConfig('contentVisible', {
+                value: attr.contentVisible,
+                method: function(value) {
+                    if (value) {
+                        Dom.removeClass(this.get(CONTENT_EL), this.HIDDEN_CLASSNAME);
+                        
+                        if ( this.get(DATA_SRC) ) {
+                         // load dynamic content unless already loading or loaded and caching
+                            if ( !this._loading && !(this.get(DATA_LOADED) && this.get(CACHE_DATA)) ) {
+                                this._dataConnect();
+                            }
+                        }
+                    } else {
+                        Dom.addClass(this.get(CONTENT_EL), this.HIDDEN_CLASSNAME);
+                    }
+                },
+                validator: Lang.isBoolean
+            });
+        },
+        
+        _dataConnect: function() {
+            if (!Y.Connect) {
+                return false;
+            }
+
+            Dom.addClass(this.get(CONTENT_EL).parentNode, this.LOADING_CLASSNAME);
+            this._loading = true; 
+            this.dataConnection = Y.Connect.asyncRequest(
+                this.get(LOAD_METHOD),
+                this.get(DATA_SRC), 
+                {
+                    success: function(o) {
+                        this.loadHandler.success.call(this, o);
+                        this.set(DATA_LOADED, true);
+                        this.dataConnection = null;
+                        Dom.removeClass(this.get(CONTENT_EL).parentNode,
+                                this.LOADING_CLASSNAME);
+                        this._loading = false;
+                    },
+                    failure: function(o) {
+                        this.loadHandler.failure.call(this, o);
+                        this.dataConnection = null;
+                        Dom.removeClass(this.get(CONTENT_EL).parentNode,
+                                this.LOADING_CLASSNAME);
+                        this._loading = false;
+                    },
+                    scope: this,
+                    timeout: this.get(DATA_TIMEOUT)
+                },
+
+                this.get(POST_DATA)
+            );
+        },
+        _createTabElement: function(attr) {
+            var el = document.createElement('li'),
+                a = document.createElement('a'),
+                label = attr.label || null,
+                labelEl = attr.labelEl || null;
+            
+            a.href = "" || '#'; // TODO: Use Dom.setAttribute?
+            el.appendChild(a);
+            
+            if (labelEl) { // user supplied labelEl
+                if (!label) { // user supplied label
+                    label = this._getLabel();
+                }
+            } else {
+                labelEl = this._createLabelEl();
+            }
+            
+            a.appendChild(labelEl);
+            
+            return el;
+        },
+
+        _getLabelEl: function() {
+            return this.getElementsByTagName(this.LABEL_TAGNAME)[0];
+        },
+
+        _createLabelEl: function() {
+            var el = document.createElement(this.LABEL_TAGNAME);
+            return el;
+        },
+    
+        
+        _getLabel: function() {
+            var el = this.get(LABEL_EL);
+                
+                if (!el) {
+                    return undefined;
+                }
+            
+            return el.innerHTML;
+        },
+
+        _onActivate: function(e, tabview) {
+            var tab = this,
+                silent = false;
+
+            Y.Event.preventDefault(e);
+            if (tab === tabview.get(ACTIVE_TAB)) {
+                silent = true; // dont fire activeTabChange if already active
+            }
+            tabview.set(ACTIVE_TAB, tab, silent);
+        },
+
+        _onActivationEventChange: function(e) {
+            var tab = this;
+
+            if (e.prevValue != e.newValue) {
+                tab.removeListener(e.prevValue, tab._onActivate);
+                tab.addListener(e.newValue, tab._onActivate, this, tab);
+            }
+        }
+    });
+    
+    
+    /**
+     * Fires when a tab is removed from the tabview
+     * @event remove
+     * @type CustomEvent
+     * @param {Event} An event object with fields for "type" ("remove")
+     * and "tabview" (the tabview instance it was removed from) 
+     */
+    
+    YAHOO.widget.Tab = Tab;
+})();
+
+YAHOO.register("tabview", YAHOO.widget.TabView, {version: "2.9.0", build: "2800"});

Added: branches/wf4ever/public/_javascript_s/yui/treeview.js (0 => 3265)


--- branches/wf4ever/public/_javascript_s/yui/treeview.js	                        (rev 0)
+++ branches/wf4ever/public/_javascript_s/yui/treeview.js	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,4043 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+(function () {
+    var Dom = YAHOO.util.Dom,
+        Event = YAHOO.util.Event,
+        Lang = YAHOO.lang,
+        Widget = YAHOO.widget;
+
+
+
+/**
+ * The treeview widget is a generic tree building tool.
+ * @module treeview
+ * @title TreeView Widget
+ * @requires yahoo, dom, event
+ * @optional animation, json, calendar
+ * @namespace YAHOO.widget
+ */
+
+/**
+ * Contains the tree view state data and the root node.
+ *
+ * @class TreeView
+ * @uses YAHOO.util.EventProvider
+ * @constructor
+ * @param {string|HTMLElement} id The id of the element, or the element itself that the tree will be inserted into.
+ *        Existing markup in this element, if valid, will be used to build the tree
+ * @param {Array|Object|String}  oConfig (optional)  If present, it will be used to build the tree via method <a href=""
+ *
+ */
+YAHOO.widget.TreeView = function(id, oConfig) {
+    if (id) { this.init(id); }
+    if (oConfig) {
+        this.buildTreeFromObject(oConfig);
+    } else if (Lang.trim(this._el.innerHTML)) {
+        this.buildTreeFromMarkup(id);
+    }
+};
+
+var TV = Widget.TreeView;
+
+TV.prototype = {
+
+    /**
+     * The id of tree container element
+     * @property id
+     * @type String
+     */
+    id: null,
+
+    /**
+     * The host element for this tree
+     * @property _el
+     * @private
+     * @type HTMLelement
+     */
+    _el: null,
+
+     /**
+     * Flat collection of all nodes in this tree.  This is a sparse
+     * array, so the length property can't be relied upon for a
+     * node count for the tree.
+     * @property _nodes
+     * @type Node[]
+     * @private
+     */
+    _nodes: null,
+
+    /**
+     * We lock the tree control while waiting for the dynamic loader to return
+     * @property locked
+     * @type boolean
+     */
+    locked: false,
+
+    /**
+     * The animation to use for expanding children, if any
+     * @property _expandAnim
+     * @type string
+     * @private
+     */
+    _expandAnim: null,
+
+    /**
+     * The animation to use for collapsing children, if any
+     * @property _collapseAnim
+     * @type string
+     * @private
+     */
+    _collapseAnim: null,
+
+    /**
+     * The current number of animations that are executing
+     * @property _animCount
+     * @type int
+     * @private
+     */
+    _animCount: 0,
+
+    /**
+     * The maximum number of animations to run at one time.
+     * @property maxAnim
+     * @type int
+     */
+    maxAnim: 2,
+
+    /**
+     * Whether there is any subscriber to dblClickEvent
+     * @property _hasDblClickSubscriber
+     * @type boolean
+     * @private
+     */
+    _hasDblClickSubscriber: false,
+
+    /**
+     * Stores the timer used to check for double clicks
+     * @property _dblClickTimer
+     * @type window.timer object
+     * @private
+     */
+    _dblClickTimer: null,
+
+  /**
+     * A reference to the Node currently having the focus or null if none.
+     * @property currentFocus
+     * @type YAHOO.widget.Node
+     */
+    currentFocus: null,
+
+    /**
+    * If true, only one Node can be highlighted at a time
+    * @property singleNodeHighlight
+    * @type boolean
+    * @default false
+    */
+
+    singleNodeHighlight: false,
+
+    /**
+    * A reference to the Node that is currently highlighted.
+    * It is only meaningful if singleNodeHighlight is enabled
+    * @property _currentlyHighlighted
+    * @type YAHOO.widget.Node
+    * @default null
+    * @private
+    */
+
+    _currentlyHighlighted: null,
+
+    /**
+     * Sets up the animation for expanding children
+     * @method setExpandAnim
+     * @param {string} type the type of animation (acceptable values defined
+     * in YAHOO.widget.TVAnim)
+     */
+    setExpandAnim: function(type) {
+        this._expandAnim = (Widget.TVAnim.isValid(type)) ? type : null;
+    },
+
+    /**
+     * Sets up the animation for collapsing children
+     * @method setCollapseAnim
+     * @param {string} type of animation (acceptable values defined in
+     * YAHOO.widget.TVAnim)
+     */
+    setCollapseAnim: function(type) {
+        this._collapseAnim = (Widget.TVAnim.isValid(type)) ? type : null;
+    },
+
+    /**
+     * Perform the expand animation if configured, or just show the
+     * element if not configured or too many animations are in progress
+     * @method animateExpand
+     * @param el {HTMLElement} the element to animate
+     * @param node {YAHOO.util.Node} the node that was expanded
+     * @return {boolean} true if animation could be invoked, false otherwise
+     */
+    animateExpand: function(el, node) {
+
+        if (this._expandAnim && this._animCount < this.maxAnim) {
+            // this.locked = true;
+            var tree = this;
+            var a = Widget.TVAnim.getAnim(this._expandAnim, el,
+                            function() { tree.expandComplete(node); });
+            if (a) {
+                ++this._animCount;
+                this.fireEvent("animStart", {
+                        "node": node,
+                        "type": "expand"
+                    });
+                a.animate();
+            }
+
+            return true;
+        }
+
+        return false;
+    },
+
+    /**
+     * Perform the collapse animation if configured, or just show the
+     * element if not configured or too many animations are in progress
+     * @method animateCollapse
+     * @param el {HTMLElement} the element to animate
+     * @param node {YAHOO.util.Node} the node that was expanded
+     * @return {boolean} true if animation could be invoked, false otherwise
+     */
+    animateCollapse: function(el, node) {
+
+        if (this._collapseAnim && this._animCount < this.maxAnim) {
+            // this.locked = true;
+            var tree = this;
+            var a = Widget.TVAnim.getAnim(this._collapseAnim, el,
+                            function() { tree.collapseComplete(node); });
+            if (a) {
+                ++this._animCount;
+                this.fireEvent("animStart", {
+                        "node": node,
+                        "type": "collapse"
+                    });
+                a.animate();
+            }
+
+            return true;
+        }
+
+        return false;
+    },
+
+    /**
+     * Function executed when the expand animation completes
+     * @method expandComplete
+     */
+    expandComplete: function(node) {
+        --this._animCount;
+        this.fireEvent("animComplete", {
+                "node": node,
+                "type": "expand"
+            });
+        // this.locked = false;
+    },
+
+    /**
+     * Function executed when the collapse animation completes
+     * @method collapseComplete
+     */
+    collapseComplete: function(node) {
+        --this._animCount;
+        this.fireEvent("animComplete", {
+                "node": node,
+                "type": "collapse"
+            });
+        // this.locked = false;
+    },
+
+    /**
+     * Initializes the tree
+     * @method init
+     * @parm {string|HTMLElement} id the id of the element that will hold the tree
+     * @private
+     */
+    init: function(id) {
+        this._el = Dom.get(id);
+        this.id = Dom.generateId(this._el,"yui-tv-auto-id-");
+
+    /**
+         * When animation is enabled, this event fires when the animation
+         * starts
+         * @event animStart
+         * @type CustomEvent
+         * @param {YAHOO.widget.Node} oArgs.node the node that is expanding/collapsing
+         * @param {String} oArgs.type the type of animation ("expand" or "collapse")
+         */
+        this.createEvent("animStart", this);
+
+        /**
+         * When animation is enabled, this event fires when the animation
+         * completes
+         * @event animComplete
+         * @type CustomEvent
+         * @param {YAHOO.widget.Node} oArgs.node the node that is expanding/collapsing
+         * @param {String} oArgs.type the type of animation ("expand" or "collapse")
+         */
+        this.createEvent("animComplete", this);
+
+        /**
+         * Fires when a node is going to be collapsed.  Return false to stop
+         * the collapse.
+         * @event collapse
+         * @type CustomEvent
+         * @param {YAHOO.widget.Node} node the node that is collapsing
+         */
+        this.createEvent("collapse", this);
+
+        /**
+         * Fires after a node is successfully collapsed.  This event will not fire
+         * if the "collapse" event was cancelled.
+         * @event collapseComplete
+         * @type CustomEvent
+         * @param {YAHOO.widget.Node} node the node that was collapsed
+         */
+        this.createEvent("collapseComplete", this);
+
+        /**
+         * Fires when a node is going to be expanded.  Return false to stop
+         * the collapse.
+         * @event expand
+         * @type CustomEvent
+         * @param {YAHOO.widget.Node} node the node that is expanding
+         */
+        this.createEvent("expand", this);
+
+        /**
+         * Fires after a node is successfully expanded.  This event will not fire
+         * if the "expand" event was cancelled.
+         * @event expandComplete
+         * @type CustomEvent
+         * @param {YAHOO.widget.Node} node the node that was expanded
+         */
+        this.createEvent("expandComplete", this);
+
+    /**
+         * Fires when the Enter key is pressed on a node that has the focus
+         * @event enterKeyPressed
+         * @type CustomEvent
+         * @param {YAHOO.widget.Node} node the node that has the focus
+         */
+        this.createEvent("enterKeyPressed", this);
+
+    /**
+         * Fires when the label in a TextNode or MenuNode or content in an HTMLNode receives a Click.
+    * The listener may return false to cancel toggling and focusing on the node.
+         * @event clickEvent
+         * @type CustomEvent
+         * @param oArgs.event  {HTMLEvent} The event object
+         * @param oArgs.node {YAHOO.widget.Node} node the node that was clicked
+         */
+        this.createEvent("clickEvent", this);
+
+    /**
+         * Fires when the focus receives the focus, when it changes from a Node
+    * to another Node or when it is completely lost (blurred)
+         * @event focusChanged
+         * @type CustomEvent
+         * @param oArgs.oldNode  {YAHOO.widget.Node} Node that had the focus or null if none
+         * @param oArgs.newNode {YAHOO.widget.Node} Node that receives the focus or null if none
+         */
+
+        this.createEvent('focusChanged',this);
+
+    /**
+         * Fires when the label in a TextNode or MenuNode or content in an HTMLNode receives a double Click
+         * @event dblClickEvent
+         * @type CustomEvent
+         * @param oArgs.event  {HTMLEvent} The event object
+         * @param oArgs.node {YAHOO.widget.Node} node the node that was clicked
+         */
+        var self = this;
+        this.createEvent("dblClickEvent", {
+            scope:this,
+            onSubscribeCallback: function() {
+                self._hasDblClickSubscriber = true;
+            }
+        });
+
+    /**
+         * Custom event that is fired when the text node label is clicked.
+         *  The node clicked is  provided as an argument
+         *
+         * @event labelClick
+         * @type CustomEvent
+         * @param {YAHOO.widget.Node} node the node clicked
+    * @deprecated use clickEvent or dblClickEvent
+         */
+        this.createEvent("labelClick", this);
+
+    /**
+     * Custom event fired when the highlight of a node changes.
+     * The node that triggered the change is provided as an argument:
+     * The status of the highlight can be checked in
+     * <a href=""
+     * Depending on <a href="" other nodes might have changed
+     * @event highlightEvent
+     * @type CustomEvent
+     * @param node {YAHOO.widget.Node} the node that started the change in highlighting state
+    */
+        this.createEvent("highlightEvent",this);
+
+
+        this._nodes = [];
+
+        // store a global reference
+        TV.trees[this.id] = this;
+
+        // Set up the root node
+        this.root = new Widget.RootNode(this);
+
+        var LW = Widget.LogWriter;
+
+
+
+        if (this._initEditor) {
+            this._initEditor();
+        }
+
+        // YAHOO.util.Event.onContentReady(this.id, this.handleAvailable, this, true);
+        // YAHOO.util.Event.on(this.id, "click", this.handleClick, this, true);
+    },
+
+    //handleAvailable: function() {
+        //var Event = YAHOO.util.Event;
+        //Event.on(this.id,
+    //},
+ /**
+     * Builds the TreeView from an object.
+     * This is the method called by the constructor to build the tree when it has a second argument.
+     *  A tree can be described by an array of objects, each object corresponding to a node.
+     *  Node descriptions may contain values for any property of a node plus the following extra properties: <ul>
+     * <li>type:  can be one of the following:<ul>
+     *    <li> A shortname for a node type (<code>'text','menu','html'</code>) </li>
+     *    <li>The name of a Node class under YAHOO.widget (<code>'TextNode', 'MenuNode', 'DateNode'</code>, etc) </li>
+     *    <li>a reference to an actual class: <code>YAHOO.widget.DateNode</code></li>
+     * </ul></li>
+     * <li>children: an array containing further node definitions</li></ul>
+     * A string instead of an object will produce a node of type 'text' with the given string as its label.
+     * @method buildTreeFromObject
+     * @param  oConfig {Array|Object|String}  array containing a full description of the tree.
+     *        An object or a string will be turned into an array with the given object or string as its only element.
+     *
+     */
+    buildTreeFromObject: function (oConfig) {
+        var build = function (parent, oConfig) {
+            var i, item, node, children, type, NodeType, ThisType;
+            for (i = 0; i < oConfig.length; i++) {
+                item = oConfig[i];
+                if (Lang.isString(item)) {
+                    node = new Widget.TextNode(item, parent);
+                } else if (Lang.isObject(item)) {
+                    children = item.children;
+                    delete item.children;
+                    type = item.type || 'text';
+                    delete item.type;
+                    switch (Lang.isString(type) && type.toLowerCase()) {
+                        case 'text':
+                            node = new Widget.TextNode(item, parent);
+                            break;
+                        case 'menu':
+                            node = new Widget.MenuNode(item, parent);
+                            break;
+                        case 'html':
+                            node = new Widget.HTMLNode(item, parent);
+                            break;
+                        default:
+                            if (Lang.isString(type)) {
+                                NodeType = Widget[type];
+                            } else {
+                                NodeType = type;
+                            }
+                            if (Lang.isObject(NodeType)) {
+                                for (ThisType = NodeType; ThisType && ThisType !== Widget.Node; ThisType = ThisType.superclass.constructor) {}
+                                if (ThisType) {
+                                    node = new NodeType(item, parent);
+                                } else {
+                                }
+                            } else {
+                            }
+                    }
+                    if (children) {
+                        build(node,children);
+                    }
+                } else {
+                }
+            }
+        };
+        if (!Lang.isArray(oConfig)) {
+            oConfig = [oConfig];
+        }
+
+
+        build(this.root,oConfig);
+    },
+/**
+     * Builds the TreeView from existing markup.   Markup should consist of &lt;UL&gt; or &lt;OL&gt; elements containing &lt;LI&gt; elements.
+     * Each &lt;LI&gt; can have one element used as label and a second optional element which is to be a &lt;UL&gt; or &lt;OL&gt;
+     * containing nested nodes.
+     * Depending on what the first element of the &lt;LI&gt; element is, the following Nodes will be created: <ul>
+     *           <li>plain text:  a regular TextNode</li>
+     *           <li>anchor &lt;A&gt;: a TextNode with its <code>href</code> and <code>target</code> taken from the anchor</li>
+     *           <li>anything else: an HTMLNode</li></ul>
+     * Only the first  outermost (un-)ordered list in the markup and its children will be parsed.
+     * Nodes will be collapsed unless  an  &lt;LI&gt;  tag has a className called 'expanded'.
+     * All other className attributes will be copied over to the Node className property.
+     * If the &lt;LI&gt; element contains an attribute called <code>yuiConfig</code>, its contents should be a JSON-encoded object
+     * as the one used in method <a href=""
+     * @method buildTreeFromMarkup
+     * @param  id {string|HTMLElement} The id of the element that contains the markup or a reference to it.
+     */
+    buildTreeFromMarkup: function (id) {
+        var build = function (markup) {
+            var el, child, branch = [], config = {}, label, yuiConfig;
+            // Dom's getFirstChild and getNextSibling skip over text elements
+            for (el = Dom.getFirstChild(markup); el; el = Dom.getNextSibling(el)) {
+                switch (el.tagName.toUpperCase()) {
+                    case 'LI':
+                        label = '';
+                        config = {
+                            expanded: Dom.hasClass(el,'expanded'),
+                            title: el.title || el.alt || null,
+                            className: Lang.trim(el.className.replace(/\bexpanded\b/,'')) || null
+                        };
+                        // I cannot skip over text elements here because I want them for labels
+                        child = el.firstChild;
+                        if (child.nodeType == 3) {
+                            // nodes with only whitespace, tabs and new lines don't count, they are probably just formatting.
+                            label = Lang.trim(child.nodeValue.replace(/[\n\t\r]*/g,''));
+                            if (label) {
+                                config.type = 'text';
+                                config.label = label;
+                            } else {
+                                child = Dom.getNextSibling(child);
+                            }
+                        }
+                        if (!label) {
+                            if (child.tagName.toUpperCase() == 'A') {
+                                config.type = 'text';
+                                config.label = child.innerHTML;
+                                config.href = ""
+                                config.target = child.target;
+                                config.title = child.title || child.alt || config.title;
+                            } else {
+                                config.type = 'html';
+                                var d = document.createElement('div');
+                                d.appendChild(child.cloneNode(true));
+                                config.html = d.innerHTML;
+                                config.hasIcon = true;
+                            }
+                        }
+                        // see if after the label it has a further list which will become children of this node.
+                        child = Dom.getNextSibling(child);
+                        switch (child && child.tagName.toUpperCase()) {
+                            case 'UL':
+                            case 'OL':
+                                config.children = build(child);
+                                break;
+                        }
+                        // if there are further elements or text, it will be ignored.
+
+                        if (YAHOO.lang.JSON) {
+                            yuiConfig = el.getAttribute('yuiConfig');
+                            if (yuiConfig) {
+                                yuiConfig = YAHOO.lang.JSON.parse(yuiConfig);
+                                config = YAHOO.lang.merge(config,yuiConfig);
+                            }
+                        }
+
+                        branch.push(config);
+                        break;
+                    case 'UL':
+                    case 'OL':
+                        config = {
+                            type: 'text',
+                            label: '',
+                            children: build(child)
+                        };
+                        branch.push(config);
+                        break;
+                }
+            }
+            return branch;
+        };
+
+        var markup = Dom.getChildrenBy(Dom.get(id),function (el) {
+            var tag = el.tagName.toUpperCase();
+            return  tag == 'UL' || tag == 'OL';
+        });
+        if (markup.length) {
+            this.buildTreeFromObject(build(markup[0]));
+        } else {
+        }
+    },
+  /**
+     * Returns the TD element where the event has occurred
+     * @method _getEventTargetTdEl
+     * @private
+     */
+    _getEventTargetTdEl: function (ev) {
+        var target = Event.getTarget(ev);
+        // go up looking for a TD with a className with a ygtv prefix
+        while (target && !(target.tagName.toUpperCase() == 'TD' && Dom.hasClass(target.parentNode,'ygtvrow'))) {
+            target = Dom.getAncestorByTagName(target,'td');
+        }
+        if (Lang.isNull(target)) { return null; }
+        // If it is a spacer cell, do nothing
+        if (/\bygtv(blank)?depthcell/.test(target.className)) { return null;}
+        // If it has an id, search for the node number and see if it belongs to a node in this tree.
+        if (target.id) {
+            var m = target.id.match(/\bygtv([^\d]*)(.*)/);
+            if (m && m[2] && this._nodes[m[2]]) {
+                return target;
+            }
+        }
+        return null;
+    },
+  /**
+     * Event listener for click events
+     * @method _onClickEvent
+     * @private
+     */
+    _onClickEvent: function (ev) {
+        var self = this,
+            td = this._getEventTargetTdEl(ev),
+            node,
+            target,
+            toggle = function (force) {
+                node.focus();
+                if (force || !node.href) {
+                    node.toggle();
+                    try {
+                        Event.preventDefault(ev);
+                    } catch (e) {
+                        // @TODO
+                        // For some reason IE8 is providing an event object with
+                        // most of the fields missing, but only when clicking on
+                        // the node's label, and only when working with inline
+                        // editing.  This generates a "Member not found" error
+                        // in that browser.  Determine if this is a browser
+                        // bug, or a problem with this code.  Already checked to
+                        // see if the problem has to do with access the event
+                        // in the outer scope, and that isn't the problem.
+                        // Maybe the markup for inline editing is broken.
+                    }
+                }
+            };
+
+        if (!td) {
+            return;
+        }
+
+        node = this.getNodeByElement(td);
+        if (!node) {
+            return;
+        }
+
+        // exception to handle deprecated event labelClick
+        // @TODO take another look at this deprecation.  It is common for people to
+        // only be interested in the label click, so why make them have to test
+        // the node type to figure out whether the click was on the label?
+        target = Event.getTarget(ev);
+        if (Dom.hasClass(target, node.labelStyle) || Dom.getAncestorByClassName(target,node.labelStyle)) {
+            this.fireEvent('labelClick',node);
+        }
+        // http://yuilibrary.com/projects/yui2/ticket/2528946
+        // Ensures that any open editor is closed.
+        // Since the editor is in a separate source which might not be included,
+        // we first need to ensure we have the _closeEditor method available
+        if (this._closeEditor) { this._closeEditor(false); }
+
+        //  If it is a toggle cell, toggle
+        if (/\bygtv[tl][mp]h?h?/.test(td.className)) {
+            toggle(true);
+        } else {
+            if (this._dblClickTimer) {
+                window.clearTimeout(this._dblClickTimer);
+                this._dblClickTimer = null;
+            } else {
+                if (this._hasDblClickSubscriber) {
+                    this._dblClickTimer = window.setTimeout(function () {
+                        self._dblClickTimer = null;
+                        if (self.fireEvent('clickEvent', {event:ev,node:node}) !== false) {
+                            toggle();
+                        }
+                    }, 200);
+                } else {
+                    if (self.fireEvent('clickEvent', {event:ev,node:node}) !== false) {
+                        toggle();
+                    }
+                }
+            }
+        }
+    },
+
+  /**
+     * Event listener for double-click events
+     * @method _onDblClickEvent
+     * @private
+     */
+    _onDblClickEvent: function (ev) {
+        if (!this._hasDblClickSubscriber) { return; }
+        var td = this._getEventTargetTdEl(ev);
+        if (!td) {return;}
+
+        if (!(/\bygtv[tl][mp]h?h?/.test(td.className))) {
+            this.fireEvent('dblClickEvent', {event:ev, node:this.getNodeByElement(td)});
+            if (this._dblClickTimer) {
+                window.clearTimeout(this._dblClickTimer);
+                this._dblClickTimer = null;
+            }
+        }
+    },
+  /**
+     * Event listener for mouse over events
+     * @method _onMouseOverEvent
+     * @private
+     */
+    _onMouseOverEvent:function (ev) {
+        var target;
+        if ((target = this._getEventTargetTdEl(ev)) && (target = this.getNodeByElement(target)) && (target = target.getToggleEl())) {
+            target.className = target.className.replace(/\bygtv([lt])([mp])\b/gi,'ygtv$1$2h');
+        }
+    },
+  /**
+     * Event listener for mouse out events
+     * @method _onMouseOutEvent
+     * @private
+     */
+    _onMouseOutEvent: function (ev) {
+        var target;
+        if ((target = this._getEventTargetTdEl(ev)) && (target = this.getNodeByElement(target)) && (target = target.getToggleEl())) {
+            target.className = target.className.replace(/\bygtv([lt])([mp])h\b/gi,'ygtv$1$2');
+        }
+    },
+  /**
+     * Event listener for key down events
+     * @method _onKeyDownEvent
+     * @private
+     */
+    _onKeyDownEvent: function (ev) {
+        var target = Event.getTarget(ev),
+            node = this.getNodeByElement(target),
+            newNode = node,
+            KEY = YAHOO.util.KeyListener.KEY;
+
+        switch(ev.keyCode) {
+            case KEY.UP:
+                do {
+                    if (newNode.previousSibling) {
+                        newNode = newNode.previousSibling;
+                    } else {
+                        newNode = newNode.parent;
+                    }
+                } while (newNode && !newNode._canHaveFocus());
+                if (newNode) { newNode.focus(); }
+                Event.preventDefault(ev);
+                break;
+            case KEY.DOWN:
+                do {
+                    if (newNode.nextSibling) {
+                        newNode = newNode.nextSibling;
+                    } else {
+                        newNode.expand();
+                        newNode = (newNode.children.length || null) && newNode.children[0];
+                    }
+                } while (newNode && !newNode._canHaveFocus);
+                if (newNode) { newNode.focus();}
+                Event.preventDefault(ev);
+                break;
+            case KEY.LEFT:
+                do {
+                    if (newNode.parent) {
+                        newNode = newNode.parent;
+                    } else {
+                        newNode = newNode.previousSibling;
+                    }
+                } while (newNode && !newNode._canHaveFocus());
+                if (newNode) { newNode.focus();}
+                Event.preventDefault(ev);
+                break;
+            case KEY.RIGHT:
+                var self = this,
+                    moveFocusRight,
+                    focusOnExpand = function (newNode) {
+                        self.unsubscribe('expandComplete',focusOnExpand);
+                        moveFocusRight(newNode);
+                    };
+                moveFocusRight = function (newNode) {
+                    do {
+                        if (newNode.isDynamic() && !newNode.childrenRendered) {
+                            self.subscribe('expandComplete',focusOnExpand);
+                            newNode.expand();
+                            newNode = null;
+                            break;
+                        } else {
+                            newNode.expand();
+                            if (newNode.children.length) {
+                                newNode = newNode.children[0];
+                            } else {
+                                newNode = newNode.nextSibling;
+                            }
+                        }
+                    } while (newNode && !newNode._canHaveFocus());
+                    if (newNode) { newNode.focus();}
+                };
+
+                moveFocusRight(newNode);
+                Event.preventDefault(ev);
+                break;
+            case KEY.ENTER:
+                if (node.href) {
+                    if (node.target) {
+                        window.open(node.href,node.target);
+                    } else {
+                        window.location(node.href);
+                    }
+                } else {
+                    node.toggle();
+                }
+                this.fireEvent('enterKeyPressed',node);
+                Event.preventDefault(ev);
+                break;
+            case KEY.HOME:
+                newNode = this.getRoot();
+                if (newNode.children.length) {newNode = newNode.children[0];}
+                if (newNode._canHaveFocus()) { newNode.focus(); }
+                Event.preventDefault(ev);
+                break;
+            case KEY.END:
+                newNode = newNode.parent.children;
+                newNode = newNode[newNode.length -1];
+                if (newNode._canHaveFocus()) { newNode.focus(); }
+                Event.preventDefault(ev);
+                break;
+            // case KEY.PAGE_UP:
+                // break;
+            // case KEY.PAGE_DOWN:
+                // break;
+            case 107:  // plus key
+            case 187:  // plus key
+                if (ev.shiftKey) {
+                    node.parent.expandAll();
+                } else {
+                    node.expand();
+                }
+                break;
+            case 109: // minus key
+            case 189: // minus key
+                if (ev.shiftKey) {
+                    node.parent.collapseAll();
+                } else {
+                    node.collapse();
+                }
+                break;
+            default:
+                break;
+        }
+    },
+    /**
+     * Renders the tree boilerplate and visible nodes
+     * @method render
+     */
+    render: function() {
+        var html = this.root.getHtml(),
+            el = this.getEl();
+        el.innerHTML = html;
+        if (!this._hasEvents) {
+            Event.on(el, 'click', this._onClickEvent, this, true);
+            Event.on(el, 'dblclick', this._onDblClickEvent, this, true);
+            Event.on(el, 'mouseover', this._onMouseOverEvent, this, true);
+            Event.on(el, 'mouseout', this._onMouseOutEvent, this, true);
+            Event.on(el, 'keydown', this._onKeyDownEvent, this, true);
+        }
+        this._hasEvents = true;
+    },
+
+  /**
+     * Returns the tree's host element
+     * @method getEl
+     * @return {HTMLElement} the host element
+     */
+    getEl: function() {
+        if (! this._el) {
+            this._el = Dom.get(this.id);
+        }
+        return this._el;
+    },
+
+    /**
+     * Nodes register themselves with the tree instance when they are created.
+     * @method regNode
+     * @param node {Node} the node to register
+     * @private
+     */
+    regNode: function(node) {
+        this._nodes[node.index] = node;
+    },
+
+    /**
+     * Returns the root node of this tree
+     * @method getRoot
+     * @return {Node} the root node
+     */
+    getRoot: function() {
+        return this.root;
+    },
+
+    /**
+     * Configures this tree to dynamically load all child data
+     * @method setDynamicLoad
+     * @param {function} fnDataLoader the function that will be called to get the data
+     * @param iconMode {int} configures the icon that is displayed when a dynamic
+     * load node is expanded the first time without children.  By default, the
+     * "collapse" icon will be used.  If set to 1, the leaf node icon will be
+     * displayed.
+     */
+    setDynamicLoad: function(fnDataLoader, iconMode) {
+        this.root.setDynamicLoad(fnDataLoader, iconMode);
+    },
+
+    /**
+     * Expands all child nodes.  Note: this conflicts with the "multiExpand"
+     * node property.  If expand all is called in a tree with nodes that
+     * do not allow multiple siblings to be displayed, only the last sibling
+     * will be expanded.
+     * @method expandAll
+     */
+    expandAll: function() {
+        if (!this.locked) {
+            this.root.expandAll();
+        }
+    },
+
+    /**
+     * Collapses all expanded child nodes in the entire tree.
+     * @method collapseAll
+     */
+    collapseAll: function() {
+        if (!this.locked) {
+            this.root.collapseAll();
+        }
+    },
+
+    /**
+     * Returns a node in the tree that has the specified index (this index
+     * is created internally, so this function probably will only be used
+     * in html generated for a given node.)
+     * @method getNodeByIndex
+     * @param {int} nodeIndex the index of the node wanted
+     * @return {Node} the node with index=nodeIndex, null if no match
+     */
+    getNodeByIndex: function(nodeIndex) {
+        var n = this._nodes[nodeIndex];
+        return (n) ? n : null;
+    },
+
+    /**
+     * Returns a node that has a matching property and value in the data
+     * object that was passed into its constructor.
+     * @method getNodeByProperty
+     * @param {object} property the property to search (usually a string)
+     * @param {object} value the value we want to find (usuall an int or string)
+     * @return {Node} the matching node, null if no match
+     */
+    getNodeByProperty: function(property, value) {
+        for (var i in this._nodes) {
+            if (this._nodes.hasOwnProperty(i)) {
+                var n = this._nodes[i];
+                if ((property in n && n[property] == value) || (n.data && value == n.data[property])) {
+                    return n;
+                }
+            }
+        }
+
+        return null;
+    },
+
+    /**
+     * Returns a collection of nodes that have a matching property
+     * and value in the data object that was passed into its constructor.
+     * @method getNodesByProperty
+     * @param {object} property the property to search (usually a string)
+     * @param {object} value the value we want to find (usuall an int or string)
+     * @return {Array} the matching collection of nodes, null if no match
+     */
+    getNodesByProperty: function(property, value) {
+        var values = [];
+        for (var i in this._nodes) {
+            if (this._nodes.hasOwnProperty(i)) {
+                var n = this._nodes[i];
+                if ((property in n && n[property] == value) || (n.data && value == n.data[property])) {
+                    values.push(n);
+                }
+            }
+        }
+
+        return (values.length) ? values : null;
+    },
+
+
+    /**
+     * Returns a collection of nodes that have passed the test function
+     * passed as its only argument.
+     * The function will receive a reference to each node to be tested.
+     * @method getNodesBy
+     * @param {function} a boolean function that receives a Node instance and returns true to add the node to the results list
+     * @return {Array} the matching collection of nodes, null if no match
+     */
+    getNodesBy: function(fn) {
+        var values = [];
+        for (var i in this._nodes) {
+            if (this._nodes.hasOwnProperty(i)) {
+                var n = this._nodes[i];
+                if (fn(n)) {
+                    values.push(n);
+                }
+            }
+        }
+        return (values.length) ? values : null;
+    },
+    /**
+     * Returns the treeview node reference for an ancestor element
+     * of the node, or null if it is not contained within any node
+     * in this tree.
+     * @method getNodeByElement
+     * @param el {HTMLElement} the element to test
+     * @return {YAHOO.widget.Node} a node reference or null
+     */
+    getNodeByElement: function(el) {
+
+        var p=el, m, re=/ygtv([^\d]*)(.*)/;
+
+        do {
+
+            if (p && p.id) {
+                m = p.id.match(re);
+                if (m && m[2]) {
+                    return this.getNodeByIndex(m[2]);
+                }
+            }
+
+            p = p.parentNode;
+
+            if (!p || !p.tagName) {
+                break;
+            }
+
+        }
+        while (p.id !== this.id && p.tagName.toLowerCase() !== "body");
+
+        return null;
+    },
+
+    /**
+     * When in singleNodeHighlight it returns the node highlighted
+     * or null if none.  Returns null if singleNodeHighlight is false.
+     * @method getHighlightedNode
+     * @return {YAHOO.widget.Node} a node reference or null
+     */
+    getHighlightedNode: function() {
+        return this._currentlyHighlighted;
+    },
+
+
+    /**
+     * Removes the node and its children, and optionally refreshes the
+     * branch of the tree that was affected.
+     * @method removeNode
+     * @param {Node} node to remove
+     * @param {boolean} autoRefresh automatically refreshes branch if true
+     * @return {boolean} False is there was a problem, true otherwise.
+     */
+    removeNode: function(node, autoRefresh) {
+
+        // Don't delete the root node
+        if (node.isRoot()) {
+            return false;
+        }
+
+        // Get the branch that we may need to refresh
+        var p = node.parent;
+        if (p.parent) {
+            p = p.parent;
+        }
+
+        // Delete the node and its children
+        this._deleteNode(node);
+
+        // Refresh the parent of the parent
+        if (autoRefresh && p && p.childrenRendered) {
+            p.refresh();
+        }
+
+        return true;
+    },
+
+    /**
+     * wait until the animation is complete before deleting
+     * to avoid _javascript_ errors
+     * @method _removeChildren_animComplete
+     * @param o the custom event payload
+     * @private
+     */
+    _removeChildren_animComplete: function(o) {
+        this.unsubscribe(this._removeChildren_animComplete);
+        this.removeChildren(o.node);
+    },
+
+    /**
+     * Deletes this nodes child collection, recursively.  Also collapses
+     * the node, and resets the dynamic load flag.  The primary use for
+     * this method is to purge a node and allow it to fetch its data
+     * dynamically again.
+     * @method removeChildren
+     * @param {Node} node the node to purge
+     */
+    removeChildren: function(node) {
+
+        if (node.expanded) {
+            // wait until the animation is complete before deleting to
+            // avoid _javascript_ errors
+            if (this._collapseAnim) {
+                this.subscribe("animComplete",
+                        this._removeChildren_animComplete, this, true);
+                Widget.Node.prototype.collapse.call(node);
+                return;
+            }
+
+            node.collapse();
+        }
+
+        while (node.children.length) {
+            this._deleteNode(node.children[0]);
+        }
+
+        if (node.isRoot()) {
+            Widget.Node.prototype.expand.call(node);
+        }
+
+        node.childrenRendered = false;
+        node.dynamicLoadComplete = false;
+
+        node.updateIcon();
+    },
+
+    /**
+     * Deletes the node and recurses children
+     * @method _deleteNode
+     * @private
+     */
+    _deleteNode: function(node) {
+        // Remove all the child nodes first
+        this.removeChildren(node);
+
+        // Remove the node from the tree
+        this.popNode(node);
+    },
+
+    /**
+     * Removes the node from the tree, preserving the child collection
+     * to make it possible to insert the branch into another part of the
+     * tree, or another tree.
+     * @method popNode
+     * @param {Node} node to remove
+     */
+    popNode: function(node) {
+        var p = node.parent;
+
+        // Update the parent's collection of children
+        var a = [];
+
+        for (var i=0, len=p.children.length;i<len;++i) {
+            if (p.children[i] != node) {
+                a[a.length] = p.children[i];
+            }
+        }
+
+        p.children = a;
+
+        // reset the childrenRendered flag for the parent
+        p.childrenRendered = false;
+
+         // Update the sibling relationship
+        if (node.previousSibling) {
+            node.previousSibling.nextSibling = node.nextSibling;
+        }
+
+        if (node.nextSibling) {
+            node.nextSibling.previousSibling = node.previousSibling;
+        }
+
+        if (this.currentFocus == node) {
+            this.currentFocus = null;
+        }
+        if (this._currentlyHighlighted == node) {
+            this._currentlyHighlighted = null;
+        }
+
+        node.parent = null;
+        node.previousSibling = null;
+        node.nextSibling = null;
+        node.tree = null;
+
+        // Update the tree's node collection
+        delete this._nodes[node.index];
+    },
+
+    /**
+    * Nulls out the entire TreeView instance and related objects, removes attached
+    * event listeners, and clears out DOM elements inside the container. After
+    * calling this method, the instance reference should be expliclitly nulled by
+    * implementer, as in myDataTable = null. Use with caution!
+    *
+    * @method destroy
+    */
+    destroy : function() {
+        // Since the label editor can be separated from the main TreeView control
+        // the destroy method for it might not be there.
+        if (this._destroyEditor) { this._destroyEditor(); }
+        var el = this.getEl();
+        Event.removeListener(el,'click');
+        Event.removeListener(el,'dblclick');
+        Event.removeListener(el,'mouseover');
+        Event.removeListener(el,'mouseout');
+        Event.removeListener(el,'keydown');
+        for (var i = 0 ; i < this._nodes.length; i++) {
+            var node = this._nodes[i];
+            if (node && node.destroy) {node.destroy(); }
+        }
+        el.innerHTML = '';
+        this._hasEvents = false;
+    },
+
+
+
+
+    /**
+     * TreeView instance toString
+     * @method toString
+     * @return {string} string representation of the tree
+     */
+    toString: function() {
+        return "TreeView " + this.id;
+    },
+
+    /**
+     * Count of nodes in tree
+     * @method getNodeCount
+     * @return {int} number of nodes in the tree
+     */
+    getNodeCount: function() {
+        return this.getRoot().getNodeCount();
+    },
+
+    /**
+     * Returns an object which could be used to rebuild the tree.
+     * It can be passed to the tree constructor to reproduce the same tree.
+     * It will return false if any node loads dynamically, regardless of whether it is loaded or not.
+     * @method getTreeDefinition
+     * @return {Object | false}  definition of the tree or false if any node is defined as dynamic
+     */
+    getTreeDefinition: function() {
+        return this.getRoot().getNodeDefinition();
+    },
+
+    /**
+     * Abstract method that is executed when a node is expanded
+     * @method onExpand
+     * @param node {Node} the node that was expanded
+     * @deprecated use treeobj.subscribe("expand") instead
+     */
+    onExpand: function(node) { },
+
+    /**
+     * Abstract method that is executed when a node is collapsed.
+     * @method onCollapse
+     * @param node {Node} the node that was collapsed.
+     * @deprecated use treeobj.subscribe("collapse") instead
+     */
+    onCollapse: function(node) { },
+
+    /**
+    * Sets the value of a property for all loaded nodes in the tree.
+    * @method setNodesProperty
+    * @param name {string} Name of the property to be set
+    * @param value {any} value to be set
+    * @param refresh {boolean} if present and true, it does a refresh
+    */
+    setNodesProperty: function(name, value, refresh) {
+        this.root.setNodesProperty(name,value);
+        if (refresh) {
+            this.root.refresh();
+        }
+    },
+    /**
+    * Event listener to toggle node highlight.
+    * Can be assigned as listener to clickEvent, dblClickEvent and enterKeyPressed.
+    * It returns false to prevent the default action.
+    * @method onEventToggleHighlight
+    * @param oArgs {any} it takes the arguments of any of the events mentioned above
+    * @return {false} Always cancels the default action for the event
+    */
+    onEventToggleHighlight: function (oArgs) {
+        var node;
+        if ('node' in oArgs && oArgs.node instanceof Widget.Node) {
+            node = oArgs.node;
+        } else if (oArgs instanceof Widget.Node) {
+            node = oArgs;
+        } else {
+            return false;
+        }
+        node.toggleHighlight();
+        return false;
+    }
+
+
+};
+
+/* Backwards compatibility aliases */
+var PROT = TV.prototype;
+ /**
+     * Renders the tree boilerplate and visible nodes.
+     *  Alias for render
+     * @method draw
+     * @deprecated Use render instead
+     */
+PROT.draw = PROT.render;
+
+/* end backwards compatibility aliases */
+
+YAHOO.augment(TV, YAHOO.util.EventProvider);
+
+/**
+ * Running count of all nodes created in all trees.  This is
+ * used to provide unique identifies for all nodes.  Deleting
+ * nodes does not change the nodeCount.
+ * @property YAHOO.widget.TreeView.nodeCount
+ * @type int
+ * @static
+ */
+TV.nodeCount = 0;
+
+/**
+ * Global cache of tree instances
+ * @property YAHOO.widget.TreeView.trees
+ * @type Array
+ * @static
+ * @private
+ */
+TV.trees = [];
+
+/**
+ * Global method for getting a tree by its id.  Used in the generated
+ * tree html.
+ * @method YAHOO.widget.TreeView.getTree
+ * @param treeId {String} the id of the tree instance
+ * @return {TreeView} the tree instance requested, null if not found.
+ * @static
+ */
+TV.getTree = function(treeId) {
+    var t = TV.trees[treeId];
+    return (t) ? t : null;
+};
+
+
+/**
+ * Global method for getting a node by its id.  Used in the generated
+ * tree html.
+ * @method YAHOO.widget.TreeView.getNode
+ * @param treeId {String} the id of the tree instance
+ * @param nodeIndex {String} the index of the node to return
+ * @return {Node} the node instance requested, null if not found
+ * @static
+ */
+TV.getNode = function(treeId, nodeIndex) {
+    var t = TV.getTree(treeId);
+    return (t) ? t.getNodeByIndex(nodeIndex) : null;
+};
+
+
+/**
+     * Class name assigned to elements that have the focus
+     *
+     * @property TreeView.FOCUS_CLASS_NAME
+     * @type String
+     * @static
+     * @final
+     * @default "ygtvfocus"
+
+    */
+TV.FOCUS_CLASS_NAME = 'ygtvfocus';
+
+
+
+})();
+(function () {
+    var Dom = YAHOO.util.Dom,
+        Lang = YAHOO.lang,
+        Event = YAHOO.util.Event;
+/**
+ * The base class for all tree nodes.  The node's presentation and behavior in
+ * response to mouse events is handled in Node subclasses.
+ * @namespace YAHOO.widget
+ * @class Node
+ * @uses YAHOO.util.EventProvider
+ * @param oData {object} a string or object containing the data that will
+ * be used to render this node, and any custom attributes that should be
+ * stored with the node (which is available in noderef.data).
+ * All values in oData will be used to set equally named properties in the node
+ * as long as the node does have such properties, they are not undefined, private or functions,
+ * the rest of the values will be stored in noderef.data
+ * @param oParent {Node} this node's parent node
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated, use oData.expanded)
+ * @constructor
+ */
+YAHOO.widget.Node = function(oData, oParent, expanded) {
+    if (oData) { this.init(oData, oParent, expanded); }
+};
+
+YAHOO.widget.Node.prototype = {
+
+    /**
+     * The index for this instance obtained from global counter in YAHOO.widget.TreeView.
+     * @property index
+     * @type int
+     */
+    index: 0,
+
+    /**
+     * This node's child node collection.
+     * @property children
+     * @type Node[]
+     */
+    children: null,
+
+    /**
+     * Tree instance this node is part of
+     * @property tree
+     * @type TreeView
+     */
+    tree: null,
+
+    /**
+     * The data linked to this node.  This can be any object or primitive
+     * value, and the data can be used in getNodeHtml().
+     * @property data
+     * @type object
+     */
+    data: null,
+
+    /**
+     * Parent node
+     * @property parent
+     * @type Node
+     */
+    parent: null,
+
+    /**
+     * The depth of this node.  We start at -1 for the root node.
+     * @property depth
+     * @type int
+     */
+    depth: -1,
+
+    /**
+     * The node's expanded/collapsed state
+     * @property expanded
+     * @type boolean
+     */
+    expanded: false,
+
+    /**
+     * Can multiple children be expanded at once?
+     * @property multiExpand
+     * @type boolean
+     */
+    multiExpand: true,
+
+    /**
+     * Should we render children for a collapsed node?  It is possible that the
+     * implementer will want to render the hidden data...  @todo verify that we
+     * need this, and implement it if we do.
+     * @property renderHidden
+     * @type boolean
+     */
+    renderHidden: false,
+
+    /**
+     * This flag is set to true when the html is generated for this node's
+     * children, and set to false when new children are added.
+     * @property childrenRendered
+     * @type boolean
+     */
+    childrenRendered: false,
+
+    /**
+     * Dynamically loaded nodes only fetch the data the first time they are
+     * expanded.  This flag is set to true once the data has been fetched.
+     * @property dynamicLoadComplete
+     * @type boolean
+     */
+    dynamicLoadComplete: false,
+
+    /**
+     * This node's previous sibling
+     * @property previousSibling
+     * @type Node
+     */
+    previousSibling: null,
+
+    /**
+     * This node's next sibling
+     * @property nextSibling
+     * @type Node
+     */
+    nextSibling: null,
+
+    /**
+     * We can set the node up to call an external method to get the child
+     * data dynamically.
+     * @property _dynLoad
+     * @type boolean
+     * @private
+     */
+    _dynLoad: false,
+
+    /**
+     * Function to execute when we need to get this node's child data.
+     * @property dataLoader
+     * @type function
+     */
+    dataLoader: null,
+
+    /**
+     * This is true for dynamically loading nodes while waiting for the
+     * callback to return.
+     * @property isLoading
+     * @type boolean
+     */
+    isLoading: false,
+
+    /**
+     * The toggle/branch icon will not show if this is set to false.  This
+     * could be useful if the implementer wants to have the child contain
+     * extra info about the parent, rather than an actual node.
+     * @property hasIcon
+     * @type boolean
+     */
+    hasIcon: true,
+
+    /**
+     * Used to configure what happens when a dynamic load node is expanded
+     * and we discover that it does not have children.  By default, it is
+     * treated as if it still could have children (plus/minus icon).  Set
+     * iconMode to have it display like a leaf node instead.
+     * @property iconMode
+     * @type int
+     */
+    iconMode: 0,
+
+    /**
+     * Specifies whether or not the content area of the node should be allowed
+     * to wrap.
+     * @property nowrap
+     * @type boolean
+     * @default false
+     */
+    nowrap: false,
+
+ /**
+     * If true, the node will alway be rendered as a leaf node.  This can be
+     * used to override the presentation when dynamically loading the entire
+     * tree.  Setting this to true also disables the dynamic load call for the
+     * node.
+     * @property isLeaf
+     * @type boolean
+     * @default false
+     */
+    isLeaf: false,
+
+/**
+     * The CSS class for the html content container.  Defaults to ygtvhtml, but
+     * can be overridden to provide a custom presentation for a specific node.
+     * @property contentStyle
+     * @type string
+     */
+    contentStyle: "",
+
+
+    /**
+     * The generated id that will contain the data passed in by the implementer.
+     * @property contentElId
+     * @type string
+     */
+    contentElId: null,
+
+/**
+ * Enables node highlighting.  If true, the node can be highlighted and/or propagate highlighting
+ * @property enableHighlight
+ * @type boolean
+ * @default true
+ */
+    enableHighlight: true,
+
+/**
+ * Stores the highlight state.  Can be any of:
+ * <ul>
+ * <li>0 - not highlighted</li>
+ * <li>1 - highlighted</li>
+ * <li>2 - some children highlighted</li>
+ * </ul>
+ * @property highlightState
+ * @type integer
+ * @default 0
+ */
+
+ highlightState: 0,
+
+ /**
+ * Tells whether highlighting will be propagated up to the parents of the clicked node
+ * @property propagateHighlightUp
+ * @type boolean
+ * @default false
+ */
+
+ propagateHighlightUp: false,
+
+ /**
+ * Tells whether highlighting will be propagated down to the children of the clicked node
+ * @property propagateHighlightDown
+ * @type boolean
+ * @default false
+ */
+
+ propagateHighlightDown: false,
+
+ /**
+  * User-defined className to be added to the Node
+  * @property className
+  * @type string
+  * @default null
+  */
+
+ className: null,
+
+ /**
+     * The node type
+     * @property _type
+     * @private
+     * @type string
+     * @default "Node"
+*/
+    _type: "Node",
+
+    /*
+    spacerPath: "http://l.yimg.com/a/i/space.gif",
+    expandedText: "Expanded",
+    collapsedText: "Collapsed",
+    loadingText: "Loading",
+    */
+
+    /**
+     * Initializes this node, gets some of the properties from the parent
+     * @method init
+     * @param oData {object} a string or object containing the data that will
+     * be used to render this node
+     * @param oParent {Node} this node's parent node
+     * @param expanded {boolean} the initial expanded/collapsed state
+     */
+    init: function(oData, oParent, expanded) {
+
+        this.data = ""
+        this.children   = [];
+        this.index      = YAHOO.widget.TreeView.nodeCount;
+        ++YAHOO.widget.TreeView.nodeCount;
+        this.contentElId = "ygtvcontentel" + this.index;
+
+        if (Lang.isObject(oData)) {
+            for (var property in oData) {
+                if (oData.hasOwnProperty(property)) {
+                    if (property.charAt(0) != '_'  && !Lang.isUndefined(this[property]) && !Lang.isFunction(this[property]) ) {
+                        this[property] = oData[property];
+                    } else {
+                        this.data[property] = oData[property];
+                    }
+                }
+            }
+        }
+        if (!Lang.isUndefined(expanded) ) { this.expanded  = expanded;  }
+
+
+        /**
+         * The parentChange event is fired when a parent element is applied
+         * to the node.  This is useful if you need to apply tree-level
+         * properties to a tree that need to happen if a node is moved from
+         * one tree to another.
+         *
+         * @event parentChange
+         * @type CustomEvent
+         */
+        this.createEvent("parentChange", this);
+
+        // oParent should never be null except when we create the root node.
+        if (oParent) {
+            oParent.appendChild(this);
+        }
+    },
+
+    /**
+     * Certain properties for the node cannot be set until the parent
+     * is known. This is called after the node is inserted into a tree.
+     * the parent is also applied to this node's children in order to
+     * make it possible to move a branch from one tree to another.
+     * @method applyParent
+     * @param {Node} parentNode this node's parent node
+     * @return {boolean} true if the application was successful
+     */
+    applyParent: function(parentNode) {
+        if (!parentNode) {
+            return false;
+        }
+
+        this.tree   = parentNode.tree;
+        this.parent = parentNode;
+        this.depth  = parentNode.depth + 1;
+
+        // @todo why was this put here.  This causes new nodes added at the
+        // root level to lose the menu behavior.
+        // if (! this.multiExpand) {
+            // this.multiExpand = parentNode.multiExpand;
+        // }
+
+        this.tree.regNode(this);
+        parentNode.childrenRendered = false;
+
+        // cascade update existing children
+        for (var i=0, len=this.children.length;i<len;++i) {
+            this.children[i].applyParent(this);
+        }
+
+        this.fireEvent("parentChange");
+
+        return true;
+    },
+
+    /**
+     * Appends a node to the child collection.
+     * @method appendChild
+     * @param childNode {Node} the new node
+     * @return {Node} the child node
+     * @private
+     */
+    appendChild: function(childNode) {
+        if (this.hasChildren()) {
+            var sib = this.children[this.children.length - 1];
+            sib.nextSibling = childNode;
+            childNode.previousSibling = sib;
+        }
+        this.children[this.children.length] = childNode;
+        childNode.applyParent(this);
+
+        // part of the IE display issue workaround. If child nodes
+        // are added after the initial render, and the node was
+        // instantiated with expanded = true, we need to show the
+        // children div now that the node has a child.
+        if (this.childrenRendered && this.expanded) {
+            this.getChildrenEl().style.display = "";
+        }
+
+        return childNode;
+    },
+
+    /**
+     * Appends this node to the supplied node's child collection
+     * @method appendTo
+     * @param parentNode {Node} the node to append to.
+     * @return {Node} The appended node
+     */
+    appendTo: function(parentNode) {
+        return parentNode.appendChild(this);
+    },
+
+    /**
+    * Inserts this node before this supplied node
+    * @method insertBefore
+    * @param node {Node} the node to insert this node before
+    * @return {Node} the inserted node
+    */
+    insertBefore: function(node) {
+        var p = node.parent;
+        if (p) {
+
+            if (this.tree) {
+                this.tree.popNode(this);
+            }
+
+            var refIndex = node.isChildOf(p);
+            p.children.splice(refIndex, 0, this);
+            if (node.previousSibling) {
+                node.previousSibling.nextSibling = this;
+            }
+            this.previousSibling = node.previousSibling;
+            this.nextSibling = node;
+            node.previousSibling = this;
+
+            this.applyParent(p);
+        }
+
+        return this;
+    },
+
+    /**
+    * Inserts this node after the supplied node
+    * @method insertAfter
+    * @param node {Node} the node to insert after
+    * @return {Node} the inserted node
+    */
+    insertAfter: function(node) {
+        var p = node.parent;
+        if (p) {
+
+            if (this.tree) {
+                this.tree.popNode(this);
+            }
+
+            var refIndex = node.isChildOf(p);
+
+            if (!node.nextSibling) {
+                this.nextSibling = null;
+                return this.appendTo(p);
+            }
+
+            p.children.splice(refIndex + 1, 0, this);
+
+            node.nextSibling.previousSibling = this;
+            this.previousSibling = node;
+            this.nextSibling = node.nextSibling;
+            node.nextSibling = this;
+
+            this.applyParent(p);
+        }
+
+        return this;
+    },
+
+    /**
+    * Returns true if the Node is a child of supplied Node
+    * @method isChildOf
+    * @param parentNode {Node} the Node to check
+    * @return {boolean} The node index if this Node is a child of
+    *                   supplied Node, else -1.
+    * @private
+    */
+    isChildOf: function(parentNode) {
+        if (parentNode && parentNode.children) {
+            for (var i=0, len=parentNode.children.length; i<len ; ++i) {
+                if (parentNode.children[i] === this) {
+                    return i;
+                }
+            }
+        }
+
+        return -1;
+    },
+
+    /**
+     * Returns a node array of this node's siblings, null if none.
+     * @method getSiblings
+     * @return Node[]
+     */
+    getSiblings: function() {
+        var sib =  this.parent.children.slice(0);
+        for (var i=0;i < sib.length && sib[i] != this;i++) {}
+        sib.splice(i,1);
+        if (sib.length) { return sib; }
+        return null;
+    },
+
+    /**
+     * Shows this node's children
+     * @method showChildren
+     */
+    showChildren: function() {
+        if (!this.tree.animateExpand(this.getChildrenEl(), this)) {
+            if (this.hasChildren()) {
+                this.getChildrenEl().style.display = "";
+            }
+        }
+    },
+
+    /**
+     * Hides this node's children
+     * @method hideChildren
+     */
+    hideChildren: function() {
+
+        if (!this.tree.animateCollapse(this.getChildrenEl(), this)) {
+            this.getChildrenEl().style.display = "none";
+        }
+    },
+
+    /**
+     * Returns the id for this node's container div
+     * @method getElId
+     * @return {string} the element id
+     */
+    getElId: function() {
+        return "ygtv" + this.index;
+    },
+
+    /**
+     * Returns the id for this node's children div
+     * @method getChildrenElId
+     * @return {string} the element id for this node's children div
+     */
+    getChildrenElId: function() {
+        return "ygtvc" + this.index;
+    },
+
+    /**
+     * Returns the id for this node's toggle element
+     * @method getToggleElId
+     * @return {string} the toggel element id
+     */
+    getToggleElId: function() {
+        return "ygtvt" + this.index;
+    },
+
+
+    /*
+     * Returns the id for this node's spacer image.  The spacer is positioned
+     * over the toggle and provides feedback for screen readers.
+     * @method getSpacerId
+     * @return {string} the id for the spacer image
+     */
+    /*
+    getSpacerId: function() {
+        return "ygtvspacer" + this.index;
+    },
+    */
+
+    /**
+     * Returns this node's container html element
+     * @method getEl
+     * @return {HTMLElement} the container html element
+     */
+    getEl: function() {
+        return Dom.get(this.getElId());
+    },
+
+    /**
+     * Returns the div that was generated for this node's children
+     * @method getChildrenEl
+     * @return {HTMLElement} this node's children div
+     */
+    getChildrenEl: function() {
+        return Dom.get(this.getChildrenElId());
+    },
+
+    /**
+     * Returns the element that is being used for this node's toggle.
+     * @method getToggleEl
+     * @return {HTMLElement} this node's toggle html element
+     */
+    getToggleEl: function() {
+        return Dom.get(this.getToggleElId());
+    },
+    /**
+    * Returns the outer html element for this node's content
+    * @method getContentEl
+    * @return {HTMLElement} the element
+    */
+    getContentEl: function() {
+        return Dom.get(this.contentElId);
+    },
+
+
+    /*
+     * Returns the element that is being used for this node's spacer.
+     * @method getSpacer
+     * @return {HTMLElement} this node's spacer html element
+     */
+    /*
+    getSpacer: function() {
+        return document.getElementById( this.getSpacerId() ) || {};
+    },
+    */
+
+    /*
+    getStateText: function() {
+        if (this.isLoading) {
+            return this.loadingText;
+        } else if (this.hasChildren(true)) {
+            if (this.expanded) {
+                return this.expandedText;
+            } else {
+                return this.collapsedText;
+            }
+        } else {
+            return "";
+        }
+    },
+    */
+
+  /**
+     * Hides this nodes children (creating them if necessary), changes the toggle style.
+     * @method collapse
+     */
+    collapse: function() {
+        // Only collapse if currently expanded
+        if (!this.expanded) { return; }
+
+        // fire the collapse event handler
+        var ret = this.tree.onCollapse(this);
+
+        if (false === ret) {
+            return;
+        }
+
+        ret = this.tree.fireEvent("collapse", this);
+
+        if (false === ret) {
+            return;
+        }
+
+
+        if (!this.getEl()) {
+            this.expanded = false;
+        } else {
+            // hide the child div
+            this.hideChildren();
+            this.expanded = false;
+
+            this.updateIcon();
+        }
+
+        // this.getSpacer().title = this.getStateText();
+
+        ret = this.tree.fireEvent("collapseComplete", this);
+
+    },
+
+    /**
+     * Shows this nodes children (creating them if necessary), changes the
+     * toggle style, and collapses its siblings if multiExpand is not set.
+     * @method expand
+     */
+    expand: function(lazySource) {
+        // Only expand if currently collapsed.
+        if (this.isLoading || (this.expanded && !lazySource)) {
+            return;
+        }
+
+        var ret = true;
+
+        // When returning from the lazy load handler, expand is called again
+        // in order to render the new children.  The "expand" event already
+        // fired before fething the new data, so we need to skip it now.
+        if (!lazySource) {
+            // fire the expand event handler
+            ret = this.tree.onExpand(this);
+
+            if (false === ret) {
+                return;
+            }
+
+            ret = this.tree.fireEvent("expand", this);
+        }
+
+        if (false === ret) {
+            return;
+        }
+
+        if (!this.getEl()) {
+            this.expanded = true;
+            return;
+        }
+
+        if (!this.childrenRendered) {
+            this.getChildrenEl().innerHTML = this.renderChildren();
+        } else {
+        }
+
+        this.expanded = true;
+
+        this.updateIcon();
+
+        // this.getSpacer().title = this.getStateText();
+
+        // We do an extra check for children here because the lazy
+        // load feature can expose nodes that have no children.
+
+        // if (!this.hasChildren()) {
+        if (this.isLoading) {
+            this.expanded = false;
+            return;
+        }
+
+        if (! this.multiExpand) {
+            var sibs = this.getSiblings();
+            for (var i=0; sibs && i<sibs.length; ++i) {
+                if (sibs[i] != this && sibs[i].expanded) {
+                    sibs[i].collapse();
+                }
+            }
+        }
+
+        this.showChildren();
+
+        ret = this.tree.fireEvent("expandComplete", this);
+    },
+
+    updateIcon: function() {
+        if (this.hasIcon) {
+            var el = this.getToggleEl();
+            if (el) {
+                el.className = el.className.replace(/\bygtv(([tl][pmn]h?)|(loading))\b/gi,this.getStyle());
+            }
+        }
+        el = Dom.get('ygtvtableel' + this.index);
+        if (el) {
+            if (this.expanded) {
+                Dom.replaceClass(el,'ygtv-collapsed','ygtv-expanded');
+            } else {
+                Dom.replaceClass(el,'ygtv-expanded','ygtv-collapsed');
+            }
+        }
+    },
+
+    /**
+     * Returns the css style name for the toggle
+     * @method getStyle
+     * @return {string} the css class for this node's toggle
+     */
+    getStyle: function() {
+        if (this.isLoading) {
+            return "ygtvloading";
+        } else {
+            // location top or bottom, middle nodes also get the top style
+            var loc = (this.nextSibling) ? "t" : "l";
+
+            // type p=plus(expand), m=minus(collapase), n=none(no children)
+            var type = "n";
+            if (this.hasChildren(true) || (this.isDynamic() && !this.getIconMode())) {
+            // if (this.hasChildren(true)) {
+                type = (this.expanded) ? "m" : "p";
+            }
+
+            return "ygtv" + loc + type;
+        }
+    },
+
+    /**
+     * Returns the hover style for the icon
+     * @return {string} the css class hover state
+     * @method getHoverStyle
+     */
+    getHoverStyle: function() {
+        var s = this.getStyle();
+        if (this.hasChildren(true) && !this.isLoading) {
+            s += "h";
+        }
+        return s;
+    },
+
+    /**
+     * Recursively expands all of this node's children.
+     * @method expandAll
+     */
+    expandAll: function() {
+        var l = this.children.length;
+        for (var i=0;i<l;++i) {
+            var c = this.children[i];
+            if (c.isDynamic()) {
+                break;
+            } else if (! c.multiExpand) {
+                break;
+            } else {
+                c.expand();
+                c.expandAll();
+            }
+        }
+    },
+
+    /**
+     * Recursively collapses all of this node's children.
+     * @method collapseAll
+     */
+    collapseAll: function() {
+        for (var i=0;i<this.children.length;++i) {
+            this.children[i].collapse();
+            this.children[i].collapseAll();
+        }
+    },
+
+    /**
+     * Configures this node for dynamically obtaining the child data
+     * when the node is first expanded.  Calling it without the callback
+     * will turn off dynamic load for the node.
+     * @method setDynamicLoad
+     * @param fmDataLoader {function} the function that will be used to get the data.
+     * @param iconMode {int} configures the icon that is displayed when a dynamic
+     * load node is expanded the first time without children.  By default, the
+     * "collapse" icon will be used.  If set to 1, the leaf node icon will be
+     * displayed.
+     */
+    setDynamicLoad: function(fnDataLoader, iconMode) {
+        if (fnDataLoader) {
+            this.dataLoader = fnDataLoader;
+            this._dynLoad = true;
+        } else {
+            this.dataLoader = null;
+            this._dynLoad = false;
+        }
+
+        if (iconMode) {
+            this.iconMode = iconMode;
+        }
+    },
+
+    /**
+     * Evaluates if this node is the root node of the tree
+     * @method isRoot
+     * @return {boolean} true if this is the root node
+     */
+    isRoot: function() {
+        return (this == this.tree.root);
+    },
+
+    /**
+     * Evaluates if this node's children should be loaded dynamically.  Looks for
+     * the property both in this instance and the root node.  If the tree is
+     * defined to load all children dynamically, the data callback function is
+     * defined in the root node
+     * @method isDynamic
+     * @return {boolean} true if this node's children are to be loaded dynamically
+     */
+    isDynamic: function() {
+        if (this.isLeaf) {
+            return false;
+        } else {
+            return (!this.isRoot() && (this._dynLoad || this.tree.root._dynLoad));
+            // return lazy;
+        }
+    },
+
+    /**
+     * Returns the current icon mode.  This refers to the way childless dynamic
+     * load nodes appear (this comes into play only after the initial dynamic
+     * load request produced no children).
+     * @method getIconMode
+     * @return {int} 0 for collapse style, 1 for leaf node style
+     */
+    getIconMode: function() {
+        return (this.iconMode || this.tree.root.iconMode);
+    },
+
+    /**
+     * Checks if this node has children.  If this node is lazy-loading and the
+     * children have not been rendered, we do not know whether or not there
+     * are actual children.  In most cases, we need to assume that there are
+     * children (for instance, the toggle needs to show the expandable
+     * presentation state).  In other times we want to know if there are rendered
+     * children.  For the latter, "checkForLazyLoad" should be false.
+     * @method hasChildren
+     * @param checkForLazyLoad {boolean} should we check for unloaded children?
+     * @return {boolean} true if this has children or if it might and we are
+     * checking for this condition.
+     */
+    hasChildren: function(checkForLazyLoad) {
+        if (this.isLeaf) {
+            return false;
+        } else {
+            return ( this.children.length > 0 ||
+                (checkForLazyLoad && this.isDynamic() && !this.dynamicLoadComplete)
+            );
+        }
+    },
+
+    /**
+     * Expands if node is collapsed, collapses otherwise.
+     * @method toggle
+     */
+    toggle: function() {
+        if (!this.tree.locked && ( this.hasChildren(true) || this.isDynamic()) ) {
+            if (this.expanded) { this.collapse(); } else { this.expand(); }
+        }
+    },
+
+    /**
+     * Returns the markup for this node and its children.
+     * @method getHtml
+     * @return {string} the markup for this node and its expanded children.
+     */
+    getHtml: function() {
+
+        this.childrenRendered = false;
+
+        return ['<div class="ygtvitem" id="' , this.getElId() , '">' ,this.getNodeHtml() , this.getChildrenHtml() ,'</div>'].join("");
+    },
+
+    /**
+     * Called when first rendering the tree.  We always build the div that will
+     * contain this nodes children, but we don't render the children themselves
+     * unless this node is expanded.
+     * @method getChildrenHtml
+     * @return {string} the children container div html and any expanded children
+     * @private
+     */
+    getChildrenHtml: function() {
+
+
+        var sb = [];
+        sb[sb.length] = '<div class="ygtvchildren" id="' + this.getChildrenElId() + '"';
+
+        // This is a workaround for an IE rendering issue, the child div has layout
+        // in IE, creating extra space if a leaf node is created with the expanded
+        // property set to true.
+        if (!this.expanded || !this.hasChildren()) {
+            sb[sb.length] = ' style="display:none;"';
+        }
+        sb[sb.length] = '>';
+
+
+        // Don't render the actual child node HTML unless this node is expanded.
+        if ( (this.hasChildren(true) && this.expanded) ||
+                (this.renderHidden && !this.isDynamic()) ) {
+            sb[sb.length] = this.renderChildren();
+        }
+
+        sb[sb.length] = '</div>';
+
+        return sb.join("");
+    },
+
+    /**
+     * Generates the markup for the child nodes.  This is not done until the node
+     * is expanded.
+     * @method renderChildren
+     * @return {string} the html for this node's children
+     * @private
+     */
+    renderChildren: function() {
+
+
+        var node = this;
+
+        if (this.isDynamic() && !this.dynamicLoadComplete) {
+            this.isLoading = true;
+            this.tree.locked = true;
+
+            if (this.dataLoader) {
+
+                setTimeout(
+                    function() {
+                        node.dataLoader(node,
+                            function() {
+                                node.loadComplete();
+                            });
+                    }, 10);
+
+            } else if (this.tree.root.dataLoader) {
+
+                setTimeout(
+                    function() {
+                        node.tree.root.dataLoader(node,
+                            function() {
+                                node.loadComplete();
+                            });
+                    }, 10);
+
+            } else {
+                return "Error: data loader not found or not specified.";
+            }
+
+            return "";
+
+        } else {
+            return this.completeRender();
+        }
+    },
+
+    /**
+     * Called when we know we have all the child data.
+     * @method completeRender
+     * @return {string} children html
+     */
+    completeRender: function() {
+        var sb = [];
+
+        for (var i=0; i < this.children.length; ++i) {
+            // this.children[i].childrenRendered = false;
+            sb[sb.length] = this.children[i].getHtml();
+        }
+
+        this.childrenRendered = true;
+
+        return sb.join("");
+    },
+
+    /**
+     * Load complete is the callback function we pass to the data provider
+     * in dynamic load situations.
+     * @method loadComplete
+     */
+    loadComplete: function() {
+        this.getChildrenEl().innerHTML = this.completeRender();
+        if (this.propagateHighlightDown) {
+            if (this.highlightState === 1 && !this.tree.singleNodeHighlight) {
+                for (var i = 0; i < this.children.length; i++) {
+                this.children[i].highlight(true);
+            }
+            } else if (this.highlightState === 0 || this.tree.singleNodeHighlight) {
+                for (i = 0; i < this.children.length; i++) {
+                    this.children[i].unhighlight(true);
+                }
+            } // if (highlighState == 2) leave child nodes with whichever highlight state they are set
+        }
+
+        this.dynamicLoadComplete = true;
+        this.isLoading = false;
+        this.expand(true);
+        this.tree.locked = false;
+    },
+
+    /**
+     * Returns this node's ancestor at the specified depth.
+     * @method getAncestor
+     * @param {int} depth the depth of the ancestor.
+     * @return {Node} the ancestor
+     */
+    getAncestor: function(depth) {
+        if (depth >= this.depth || depth < 0)  {
+            return null;
+        }
+
+        var p = this.parent;
+
+        while (p.depth > depth) {
+            p = p.parent;
+        }
+
+        return p;
+    },
+
+    /**
+     * Returns the css class for the spacer at the specified depth for
+     * this node.  If this node's ancestor at the specified depth
+     * has a next sibling the presentation is different than if it
+     * does not have a next sibling
+     * @method getDepthStyle
+     * @param {int} depth the depth of the ancestor.
+     * @return {string} the css class for the spacer
+     */
+    getDepthStyle: function(depth) {
+        return (this.getAncestor(depth).nextSibling) ?
+            "ygtvdepthcell" : "ygtvblankdepthcell";
+    },
+
+    /**
+     * Get the markup for the node.  This may be overrided so that we can
+     * support different types of nodes.
+     * @method getNodeHtml
+     * @return {string} The HTML that will render this node.
+     */
+    getNodeHtml: function() {
+        var sb = [];
+
+        sb[sb.length] = '<table id="ygtvtableel' + this.index + '" border="0" cellpadding="0" cellspacing="0" class="ygtvtable ygtvdepth' + this.depth;
+        sb[sb.length] = ' ygtv-' + (this.expanded?'expanded':'collapsed');
+        if (this.enableHighlight) {
+            sb[sb.length] = ' ygtv-highlight' + this.highlightState;
+        }
+        if (this.className) {
+            sb[sb.length] = ' ' + this.className;
+        }
+        sb[sb.length] = '"><tr class="ygtvrow">';
+
+        for (var i=0;i<this.depth;++i) {
+            sb[sb.length] = '<td class="ygtvcell ' + this.getDepthStyle(i) + '"><div class="ygtvspacer"></div></td>';
+        }
+
+        if (this.hasIcon) {
+            sb[sb.length] = '<td id="' + this.getToggleElId();
+            sb[sb.length] = '" class="ygtvcell ';
+            sb[sb.length] = this.getStyle() ;
+            sb[sb.length] = '"><a href="" class="ygtvspacer">&#160;</a></td>';
+        }
+
+        sb[sb.length] = '<td id="' + this.contentElId;
+        sb[sb.length] = '" class="ygtvcell ';
+        sb[sb.length] = this.contentStyle  + ' ygtvcontent" ';
+        sb[sb.length] = (this.nowrap) ? ' nowrap="nowrap" ' : '';
+        sb[sb.length] = ' >';
+        sb[sb.length] = this.getContentHtml();
+        sb[sb.length] = '</td></tr></table>';
+
+        return sb.join("");
+
+    },
+    /**
+     * Get the markup for the contents of the node.  This is designed to be overrided so that we can
+     * support different types of nodes.
+     * @method getContentHtml
+     * @return {string} The HTML that will render the content of this node.
+     */
+    getContentHtml: function () {
+        return "";
+    },
+
+    /**
+     * Regenerates the html for this node and its children.  To be used when the
+     * node is expanded and new children have been added.
+     * @method refresh
+     */
+    refresh: function() {
+        // this.loadComplete();
+        this.getChildrenEl().innerHTML = this.completeRender();
+
+        if (this.hasIcon) {
+            var el = this.getToggleEl();
+            if (el) {
+                el.className = el.className.replace(/\bygtv[lt][nmp]h*\b/gi,this.getStyle());
+            }
+        }
+    },
+
+    /**
+     * Node toString
+     * @method toString
+     * @return {string} string representation of the node
+     */
+    toString: function() {
+        return this._type + " (" + this.index + ")";
+    },
+    /**
+    * array of items that had the focus set on them
+    * so that they can be cleaned when focus is lost
+    * @property _focusHighlightedItems
+    * @type Array of DOM elements
+    * @private
+    */
+    _focusHighlightedItems: [],
+    /**
+    * DOM element that actually got the browser focus
+    * @property _focusedItem
+    * @type DOM element
+    * @private
+    */
+    _focusedItem: null,
+
+    /**
+    * Returns true if there are any elements in the node that can
+    * accept the real actual browser focus
+    * @method _canHaveFocus
+    * @return {boolean} success
+    * @private
+    */
+    _canHaveFocus: function() {
+        return this.getEl().getElementsByTagName('a').length > 0;
+    },
+    /**
+    * Removes the focus of previously selected Node
+    * @method _removeFocus
+    * @private
+    */
+    _removeFocus:function () {
+        if (this._focusedItem) {
+            Event.removeListener(this._focusedItem,'blur');
+            this._focusedItem = null;
+        }
+        var el;
+        while ((el = this._focusHighlightedItems.shift())) {  // yes, it is meant as an assignment, really
+            Dom.removeClass(el,YAHOO.widget.TreeView.FOCUS_CLASS_NAME );
+        }
+    },
+    /**
+    * Sets the focus on the node element.
+    * It will only be able to set the focus on nodes that have anchor elements in it.
+    * Toggle or branch icons have anchors and can be focused on.
+    * If will fail in nodes that have no anchor
+    * @method focus
+    * @return {boolean} success
+    */
+    focus: function () {
+        var focused = false, self = this;
+
+        if (this.tree.currentFocus) {
+            this.tree.currentFocus._removeFocus();
+        }
+
+        var  expandParent = function (node) {
+            if (node.parent) {
+                expandParent(node.parent);
+                node.parent.expand();
+            }
+        };
+        expandParent(this);
+
+        Dom.getElementsBy  (
+            function (el) {
+                return (/ygtv(([tl][pmn]h?)|(content))/).test(el.className);
+            } ,
+            'td' ,
+            self.getEl().firstChild ,
+            function (el) {
+                Dom.addClass(el, YAHOO.widget.TreeView.FOCUS_CLASS_NAME );
+                if (!focused) {
+                    var aEl = el.getElementsByTagName('a');
+                    if (aEl.length) {
+                        aEl = aEl[0];
+                        aEl.focus();
+                        self._focusedItem = aEl;
+                        Event.on(aEl,'blur',function () {
+                            self.tree.fireEvent('focusChanged',{oldNode:self.tree.currentFocus,newNode:null});
+                            self.tree.currentFocus = null;
+                            self._removeFocus();
+                        });
+                        focused = true;
+                    }
+                }
+                self._focusHighlightedItems.push(el);
+            }
+        );
+        if (focused) {
+            this.tree.fireEvent('focusChanged',{oldNode:this.tree.currentFocus,newNode:this});
+            this.tree.currentFocus = this;
+        } else {
+            this.tree.fireEvent('focusChanged',{oldNode:self.tree.currentFocus,newNode:null});
+            this.tree.currentFocus = null;
+            this._removeFocus();
+        }
+        return focused;
+    },
+
+  /**
+     * Count of nodes in a branch
+     * @method getNodeCount
+     * @return {int} number of nodes in the branch
+     */
+    getNodeCount: function() {
+        for (var i = 0, count = 0;i< this.children.length;i++) {
+            count += this.children[i].getNodeCount();
+        }
+        return count + 1;
+    },
+
+      /**
+     * Returns an object which could be used to build a tree out of this node and its children.
+     * It can be passed to the tree constructor to reproduce this node as a tree.
+     * It will return false if the node or any children loads dynamically, regardless of whether it is loaded or not.
+     * @method getNodeDefinition
+     * @return {Object | false}  definition of the tree or false if the node or any children is defined as dynamic
+     */
+    getNodeDefinition: function() {
+
+        if (this.isDynamic()) { return false; }
+
+        var def, defs = Lang.merge(this.data), children = [];
+
+
+
+        if (this.expanded) {defs.expanded = this.expanded; }
+        if (!this.multiExpand) { defs.multiExpand = this.multiExpand; }
+        if (this.renderHidden) { defs.renderHidden = this.renderHidden; }
+        if (!this.hasIcon) { defs.hasIcon = this.hasIcon; }
+        if (this.nowrap) { defs.nowrap = this.nowrap; }
+        if (this.className) { defs.className = this.className; }
+        if (this.editable) { defs.editable = this.editable; }
+        if (!this.enableHighlight) { defs.enableHighlight = this.enableHighlight; }
+        if (this.highlightState) { defs.highlightState = this.highlightState; }
+        if (this.propagateHighlightUp) { defs.propagateHighlightUp = this.propagateHighlightUp; }
+        if (this.propagateHighlightDown) { defs.propagateHighlightDown = this.propagateHighlightDown; }
+        defs.type = this._type;
+
+
+
+        for (var i = 0; i < this.children.length;i++) {
+            def = this.children[i].getNodeDefinition();
+            if (def === false) { return false;}
+            children.push(def);
+        }
+        if (children.length) { defs.children = children; }
+        return defs;
+    },
+
+
+    /**
+     * Generates the link that will invoke this node's toggle method
+     * @method getToggleLink
+     * @return {string} the _javascript_ url for toggling this node
+     */
+    getToggleLink: function() {
+        return 'return false;';
+    },
+
+    /**
+    * Sets the value of property for this node and all loaded descendants.
+    * Only public and defined properties can be set, not methods.
+    * Values for unknown properties will be assigned to the refNode.data object
+    * @method setNodesProperty
+    * @param name {string} Name of the property to be set
+    * @param value {any} value to be set
+    * @param refresh {boolean} if present and true, it does a refresh
+    */
+    setNodesProperty: function(name, value, refresh) {
+        if (name.charAt(0) != '_'  && !Lang.isUndefined(this[name]) && !Lang.isFunction(this[name]) ) {
+            this[name] = value;
+        } else {
+            this.data[name] = value;
+        }
+        for (var i = 0; i < this.children.length;i++) {
+            this.children[i].setNodesProperty(name,value);
+        }
+        if (refresh) {
+            this.refresh();
+        }
+    },
+    /**
+    * Toggles the highlighted state of a Node
+    * @method toggleHighlight
+    */
+    toggleHighlight: function() {
+        if (this.enableHighlight) {
+            // unhighlights only if fully highligthed.  For not or partially highlighted it will highlight
+            if (this.highlightState == 1) {
+                this.unhighlight();
+            } else {
+                this.highlight();
+            }
+        }
+    },
+
+    /**
+    * Turns highlighting on node.
+    * @method highlight
+    * @param _silent {boolean} optional, don't fire the highlightEvent
+    */
+    highlight: function(_silent) {
+        if (this.enableHighlight) {
+            if (this.tree.singleNodeHighlight) {
+                if (this.tree._currentlyHighlighted) {
+                    this.tree._currentlyHighlighted.unhighlight(_silent);
+                }
+                this.tree._currentlyHighlighted = this;
+            }
+            this.highlightState = 1;
+            this._setHighlightClassName();
+            if (!this.tree.singleNodeHighlight) {
+                if (this.propagateHighlightDown) {
+                    for (var i = 0;i < this.children.length;i++) {
+                        this.children[i].highlight(true);
+                    }
+                }
+                if (this.propagateHighlightUp) {
+                    if (this.parent) {
+                        this.parent._childrenHighlighted();
+                    }
+                }
+            }
+            if (!_silent) {
+                this.tree.fireEvent('highlightEvent',this);
+            }
+        }
+    },
+    /**
+    * Turns highlighting off a node.
+    * @method unhighlight
+    * @param _silent {boolean} optional, don't fire the highlightEvent
+    */
+    unhighlight: function(_silent) {
+        if (this.enableHighlight) {
+            // might have checked singleNodeHighlight but it wouldn't really matter either way
+            this.tree._currentlyHighlighted = null;
+            this.highlightState = 0;
+            this._setHighlightClassName();
+            if (!this.tree.singleNodeHighlight) {
+                if (this.propagateHighlightDown) {
+                    for (var i = 0;i < this.children.length;i++) {
+                        this.children[i].unhighlight(true);
+                    }
+                }
+                if (this.propagateHighlightUp) {
+                    if (this.parent) {
+                        this.parent._childrenHighlighted();
+                    }
+                }
+            }
+            if (!_silent) {
+                this.tree.fireEvent('highlightEvent',this);
+            }
+        }
+    },
+    /**
+    * Checks whether all or part of the children of a node are highlighted and
+    * sets the node highlight to full, none or partial highlight.
+    * If set to propagate it will further call the parent
+    * @method _childrenHighlighted
+    * @private
+    */
+    _childrenHighlighted: function() {
+        var yes = false, no = false;
+        if (this.enableHighlight) {
+            for (var i = 0;i < this.children.length;i++) {
+                switch(this.children[i].highlightState) {
+                    case 0:
+                        no = true;
+                        break;
+                    case 1:
+                        yes = true;
+                        break;
+                    case 2:
+                        yes = no = true;
+                        break;
+                }
+            }
+            if (yes && no) {
+                this.highlightState = 2;
+            } else if (yes) {
+                this.highlightState = 1;
+            } else {
+                this.highlightState = 0;
+            }
+            this._setHighlightClassName();
+            if (this.propagateHighlightUp) {
+                if (this.parent) {
+                    this.parent._childrenHighlighted();
+                }
+            }
+        }
+    },
+
+    /**
+    * Changes the classNames on the toggle and content containers to reflect the current highlighting
+    * @method _setHighlightClassName
+    * @private
+    */
+    _setHighlightClassName: function() {
+        var el = Dom.get('ygtvtableel' + this.index);
+        if (el) {
+            el.className = el.className.replace(/\bygtv-highlight\d\b/gi,'ygtv-highlight' + this.highlightState);
+        }
+    }
+
+};
+
+YAHOO.augment(YAHOO.widget.Node, YAHOO.util.EventProvider);
+})();
+/**
+ * A custom YAHOO.widget.Node that handles the unique nature of
+ * the virtual, presentationless root node.
+ * @namespace YAHOO.widget
+ * @class RootNode
+ * @extends YAHOO.widget.Node
+ * @param oTree {YAHOO.widget.TreeView} The tree instance this node belongs to
+ * @constructor
+ */
+YAHOO.widget.RootNode = function(oTree) {
+    // Initialize the node with null params.  The root node is a
+    // special case where the node has no presentation.  So we have
+    // to alter the standard properties a bit.
+    this.init(null, null, true);
+
+    /*
+     * For the root node, we get the tree reference from as a param
+     * to the constructor instead of from the parent element.
+     */
+    this.tree = oTree;
+};
+
+YAHOO.extend(YAHOO.widget.RootNode, YAHOO.widget.Node, {
+
+   /**
+     * The node type
+     * @property _type
+      * @type string
+     * @private
+     * @default "RootNode"
+     */
+    _type: "RootNode",
+
+    // overrides YAHOO.widget.Node
+    getNodeHtml: function() {
+        return "";
+    },
+
+    toString: function() {
+        return this._type;
+    },
+
+    loadComplete: function() {
+        this.tree.draw();
+    },
+
+   /**
+     * Count of nodes in tree.
+    * It overrides Nodes.getNodeCount because the root node should not be counted.
+     * @method getNodeCount
+     * @return {int} number of nodes in the tree
+     */
+    getNodeCount: function() {
+        for (var i = 0, count = 0;i< this.children.length;i++) {
+            count += this.children[i].getNodeCount();
+        }
+        return count;
+    },
+
+  /**
+     * Returns an object which could be used to build a tree out of this node and its children.
+     * It can be passed to the tree constructor to reproduce this node as a tree.
+     * Since the RootNode is automatically created by treeView,
+     * its own definition is excluded from the returned node definition
+     * which only contains its children.
+     * @method getNodeDefinition
+     * @return {Object | false}  definition of the tree or false if any child node is defined as dynamic
+     */
+    getNodeDefinition: function() {
+
+        for (var def, defs = [], i = 0; i < this.children.length;i++) {
+            def = this.children[i].getNodeDefinition();
+            if (def === false) { return false;}
+            defs.push(def);
+        }
+        return defs;
+    },
+
+    collapse: function() {},
+    expand: function() {},
+    getSiblings: function() { return null; },
+    focus: function () {}
+
+});
+(function () {
+    var Dom = YAHOO.util.Dom,
+        Lang = YAHOO.lang,
+        Event = YAHOO.util.Event;
+/**
+ * The default node presentation.  The first parameter should be
+ * either a string that will be used as the node's label, or an object
+ * that has at least a string property called label.  By default,  clicking the
+ * label will toggle the expanded/collapsed state of the node.  By
+ * setting the href property of the instance, this behavior can be
+ * changed so that the label will go to the specified href.
+ * @namespace YAHOO.widget
+ * @class TextNode
+ * @extends YAHOO.widget.Node
+ * @constructor
+ * @param oData {object} a string or object containing the data that will
+ * be used to render this node.
+ * Providing a string is the same as providing an object with a single property named label.
+ * All values in the oData will be used to set equally named properties in the node
+ * as long as the node does have such properties, they are not undefined, private or functions.
+ * All attributes are made available in noderef.data, which
+ * can be used to store custom attributes.  TreeView.getNode(s)ByProperty
+ * can be used to retrieve a node by one of the attributes.
+ * @param oParent {YAHOO.widget.Node} this node's parent node
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded)
+ */
+YAHOO.widget.TextNode = function(oData, oParent, expanded) {
+
+    if (oData) {
+        if (Lang.isString(oData)) {
+            oData = { label: oData };
+        }
+        this.init(oData, oParent, expanded);
+        this.setUpLabel(oData);
+    }
+
+};
+
+YAHOO.extend(YAHOO.widget.TextNode, YAHOO.widget.Node, {
+
+    /**
+     * The CSS class for the label href.  Defaults to ygtvlabel, but can be
+     * overridden to provide a custom presentation for a specific node.
+     * @property labelStyle
+     * @type string
+     */
+    labelStyle: "ygtvlabel",
+
+    /**
+     * The derived element id of the label for this node
+     * @property labelElId
+     * @type string
+     */
+    labelElId: null,
+
+    /**
+     * The text for the label.  It is assumed that the oData parameter will
+     * either be a string that will be used as the label, or an object that
+     * has a property called "label" that we will use.
+     * @property label
+     * @type string
+     */
+    label: null,
+
+    /**
+     * The text for the title (tooltip) for the label element
+     * @property title
+     * @type string
+     */
+    title: null,
+
+    /**
+     * The href for the node's label.  If one is not specified, the href will
+     * be set so that it toggles the node.
+     * @property href
+     * @type string
+     */
+    href: null,
+
+    /**
+     * The label href target, defaults to current window
+     * @property target
+     * @type string
+     */
+    target: "_self",
+
+    /**
+     * The node type
+     * @property _type
+     * @private
+     * @type string
+     * @default "TextNode"
+     */
+    _type: "TextNode",
+
+
+    /**
+     * Sets up the node label
+     * @method setUpLabel
+     * @param oData string containing the label, or an object with a label property
+     */
+    setUpLabel: function(oData) {
+
+        if (Lang.isString(oData)) {
+            oData = {
+                label: oData
+            };
+        } else {
+            if (oData.style) {
+                this.labelStyle = oData.style;
+            }
+        }
+
+        this.label = oData.label;
+
+        this.labelElId = "ygtvlabelel" + this.index;
+
+    },
+
+    /**
+     * Returns the label element
+     * @for YAHOO.widget.TextNode
+     * @method getLabelEl
+     * @return {object} the element
+     */
+    getLabelEl: function() {
+        return Dom.get(this.labelElId);
+    },
+
+    // overrides YAHOO.widget.Node
+    getContentHtml: function() {
+        var sb = [];
+        sb[sb.length] = this.href ? '<a' : '<span';
+        sb[sb.length] = ' id="' + Lang.escapeHTML(this.labelElId) + '"';
+        sb[sb.length] = ' class="' + Lang.escapeHTML(this.labelStyle)  + '"';
+        if (this.href) {
+            sb[sb.length] = ' href="" + Lang.escapeHTML(this.href) + '"';
+            sb[sb.length] = ' target="' + Lang.escapeHTML(this.target) + '"';
+        }
+        if (this.title) {
+            sb[sb.length] = ' title="' + Lang.escapeHTML(this.title) + '"';
+        }
+        sb[sb.length] = ' >';
+        sb[sb.length] = Lang.escapeHTML(this.label);
+        sb[sb.length] = this.href?'</a>':'</span>';
+        return sb.join("");
+    },
+
+
+
+  /**
+     * Returns an object which could be used to build a tree out of this node and its children.
+     * It can be passed to the tree constructor to reproduce this node as a tree.
+     * It will return false if the node or any descendant loads dynamically, regardless of whether it is loaded or not.
+     * @method getNodeDefinition
+     * @return {Object | false}  definition of the tree or false if this node or any descendant is defined as dynamic
+     */
+    getNodeDefinition: function() {
+        var def = YAHOO.widget.TextNode.superclass.getNodeDefinition.call(this);
+        if (def === false) { return false; }
+
+        // Node specific properties
+        def.label = this.label;
+        if (this.labelStyle != 'ygtvlabel') { def.style = this.labelStyle; }
+        if (this.title) { def.title = this.title; }
+        if (this.href) { def.href = "" }
+        if (this.target != '_self') { def.target = this.target; }
+
+        return def;
+
+    },
+
+    toString: function() {
+        return YAHOO.widget.TextNode.superclass.toString.call(this) + ": " + this.label;
+    },
+
+    // deprecated
+    onLabelClick: function() {
+        return false;
+    },
+    refresh: function() {
+        YAHOO.widget.TextNode.superclass.refresh.call(this);
+        var label = this.getLabelEl();
+        label.innerHTML = this.label;
+        if (label.tagName.toUpperCase() == 'A') {
+            label.href = ""
+            label.target = this.target;
+        }
+    }
+
+
+
+
+});
+})();
+/**
+ * A menu-specific implementation that differs from TextNode in that only
+ * one sibling can be expanded at a time.
+ * @namespace YAHOO.widget
+ * @class MenuNode
+ * @extends YAHOO.widget.TextNode
+ * @param oData {object} a string or object containing the data that will
+ * be used to render this node.
+ * Providing a string is the same as providing an object with a single property named label.
+ * All values in the oData will be used to set equally named properties in the node
+ * as long as the node does have such properties, they are not undefined, private or functions.
+ * All attributes are made available in noderef.data, which
+ * can be used to store custom attributes.  TreeView.getNode(s)ByProperty
+ * can be used to retrieve a node by one of the attributes.
+ * @param oParent {YAHOO.widget.Node} this node's parent node
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded)
+ * @constructor
+ */
+YAHOO.widget.MenuNode = function(oData, oParent, expanded) {
+    YAHOO.widget.MenuNode.superclass.constructor.call(this,oData,oParent,expanded);
+
+   /*
+     * Menus usually allow only one branch to be open at a time.
+     */
+    this.multiExpand = false;
+
+};
+
+YAHOO.extend(YAHOO.widget.MenuNode, YAHOO.widget.TextNode, {
+
+    /**
+     * The node type
+     * @property _type
+     * @private
+    * @default "MenuNode"
+     */
+    _type: "MenuNode"
+
+});
+(function () {
+    var Dom = YAHOO.util.Dom,
+        Lang = YAHOO.lang,
+        Event = YAHOO.util.Event;
+
+/**
+ * This implementation takes either a string or object for the
+ * oData argument.  If is it a string, it will use it for the display
+ * of this node (and it can contain any html code).  If the parameter
+ * is an object,it looks for a parameter called "html" that will be
+ * used for this node's display.
+ * @namespace YAHOO.widget
+ * @class HTMLNode
+ * @extends YAHOO.widget.Node
+ * @constructor
+ * @param oData {object} a string or object containing the data that will
+ * be used to render this node.
+ * Providing a string is the same as providing an object with a single property named html.
+ * All values in the oData will be used to set equally named properties in the node
+ * as long as the node does have such properties, they are not undefined, private or functions.
+ * All other attributes are made available in noderef.data, which
+ * can be used to store custom attributes.  TreeView.getNode(s)ByProperty
+ * can be used to retrieve a node by one of the attributes.
+ * @param oParent {YAHOO.widget.Node} this node's parent node
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded)
+ * @param hasIcon {boolean} specifies whether or not leaf nodes should
+ * be rendered with or without a horizontal line and/or toggle icon. If the icon
+ * is not displayed, the content fills the space it would have occupied.
+ * This option operates independently of the leaf node presentation logic
+ * for dynamic nodes.
+ * (deprecated; use oData.hasIcon)
+ */
+var HN =  function(oData, oParent, expanded, hasIcon) {
+    if (oData) {
+        this.init(oData, oParent, expanded);
+        this.initContent(oData, hasIcon);
+    }
+};
+
+
+YAHOO.widget.HTMLNode = HN;
+YAHOO.extend(HN, YAHOO.widget.Node, {
+
+    /**
+     * The CSS class for the html content container.  Defaults to ygtvhtml, but
+     * can be overridden to provide a custom presentation for a specific node.
+     * @property contentStyle
+     * @type string
+     */
+    contentStyle: "ygtvhtml",
+
+
+    /**
+     * The HTML content to use for this node's display
+     * @property html
+     * @type string
+     */
+    html: null,
+
+/**
+     * The node type
+     * @property _type
+     * @private
+     * @type string
+     * @default "HTMLNode"
+     */
+    _type: "HTMLNode",
+
+    /**
+     * Sets up the node label
+     * @method initContent
+     * @param oData {object} An html string or object containing an html property
+     * @param hasIcon {boolean} determines if the node will be rendered with an
+     * icon or not
+     */
+    initContent: function(oData, hasIcon) {
+        this.setHtml(oData);
+        this.contentElId = "ygtvcontentel" + this.index;
+        if (!Lang.isUndefined(hasIcon)) { this.hasIcon  = hasIcon; }
+
+    },
+
+    /**
+     * Synchronizes the node.html, and the node's content
+     * @method setHtml
+     * @param o {object |string | HTMLElement } An html string, an object containing an html property or an HTML element
+     */
+    setHtml: function(o) {
+        this.html = (Lang.isObject(o) && 'html' in o) ? o.html : o;
+
+        var el = this.getContentEl();
+        if (el) {
+            if (o.nodeType && o.nodeType == 1 && o.tagName) {
+                el.innerHTML = "";
+            } else {
+                el.innerHTML = this.html;
+            }
+        }
+
+    },
+
+    // overrides YAHOO.widget.Node
+    // If property html is a string, it sets the innerHTML for the node
+    // If it is an HTMLElement, it defers appending it to the tree until the HTML basic structure is built
+    getContentHtml: function() {
+        if (typeof this.html === "string") {
+            return this.html;
+        } else {
+
+            HN._deferredNodes.push(this);
+            if (!HN._timer) {
+                HN._timer = window.setTimeout(function () {
+                    var n;
+                    while((n = HN._deferredNodes.pop())) {
+                        n.getContentEl().appendChild(n.html);
+                    }
+                    HN._timer = null;
+                },0);
+            }
+            return "";
+        }
+    },
+
+      /**
+     * Returns an object which could be used to build a tree out of this node and its children.
+     * It can be passed to the tree constructor to reproduce this node as a tree.
+     * It will return false if any node loads dynamically, regardless of whether it is loaded or not.
+     * @method getNodeDefinition
+     * @return {Object | false}  definition of the tree or false if any node is defined as dynamic
+     */
+    getNodeDefinition: function() {
+        var def = HN.superclass.getNodeDefinition.call(this);
+        if (def === false) { return false; }
+        def.html = this.html;
+        return def;
+
+    }
+});
+
+    /**
+    * An array of HTMLNodes created with HTML Elements that had their rendering
+    * deferred until the basic tree structure is rendered.
+    * @property _deferredNodes
+    * @type YAHOO.widget.HTMLNode[]
+    * @default []
+    * @private
+    * @static
+    */
+HN._deferredNodes = [];
+    /**
+    * A system timer value used to mark whether a deferred operation is pending.
+    * @property _timer
+    * @type System Timer
+    * @default null
+    * @private
+    * @static
+    */
+HN._timer = null;
+})();
+(function () {
+    var Dom = YAHOO.util.Dom,
+        Lang = YAHOO.lang,
+        Event = YAHOO.util.Event,
+        Calendar = YAHOO.widget.Calendar;
+
+/**
+ * A Date-specific implementation that differs from TextNode in that it uses
+ * YAHOO.widget.Calendar as an in-line editor, if available
+ * If Calendar is not available, it behaves as a plain TextNode.
+ * @namespace YAHOO.widget
+ * @class DateNode
+ * @extends YAHOO.widget.TextNode
+ * @param oData {object} a string or object containing the data that will
+ * be used to render this node.
+ * Providing a string is the same as providing an object with a single property named label.
+ * All values in the oData will be used to set equally named properties in the node
+ * as long as the node does have such properties, they are not undefined, private nor functions.
+ * All attributes are made available in noderef.data, which
+ * can be used to store custom attributes.  TreeView.getNode(s)ByProperty
+ * can be used to retrieve a node by one of the attributes.
+ * @param oParent {YAHOO.widget.Node} this node's parent node
+ * @param expanded {boolean} the initial expanded/collapsed state (deprecated; use oData.expanded)
+ * @constructor
+ */
+YAHOO.widget.DateNode = function(oData, oParent, expanded) {
+    YAHOO.widget.DateNode.superclass.constructor.call(this,oData, oParent, expanded);
+};
+
+YAHOO.extend(YAHOO.widget.DateNode, YAHOO.widget.TextNode, {
+
+    /**
+     * The node type
+     * @property _type
+     * @type string
+     * @private
+     * @default  "DateNode"
+     */
+    _type: "DateNode",
+
+    /**
+    * Configuration object for the Calendar editor, if used.
+    * See <a href=""
+    * @property calendarConfig
+    */
+    calendarConfig: null,
+
+
+
+    /**
+     *  If YAHOO.widget.Calendar is available, it will pop up a Calendar to enter a new date.  Otherwise, it falls back to a plain &lt;input&gt;  textbox
+     * @method fillEditorContainer
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @return void
+     */
+    fillEditorContainer: function (editorData) {
+
+        var cal, container = editorData.inputContainer;
+
+        if (Lang.isUndefined(Calendar)) {
+            Dom.replaceClass(editorData.editorPanel,'ygtv-edit-DateNode','ygtv-edit-TextNode');
+            YAHOO.widget.DateNode.superclass.fillEditorContainer.call(this, editorData);
+            return;
+        }
+
+        if (editorData.nodeType != this._type) {
+            editorData.nodeType = this._type;
+            editorData.saveOnEnter = false;
+
+            editorData.node.destroyEditorContents(editorData);
+
+            editorData.inputObject = cal = new Calendar(container.appendChild(document.createElement('div')));
+            if (this.calendarConfig) {
+                cal.cfg.applyConfig(this.calendarConfig,true);
+                cal.cfg.fireQueue();
+            }
+            cal.selectEvent.subscribe(function () {
+                this.tree._closeEditor(true);
+            },this,true);
+        } else {
+            cal = editorData.inputObject;
+        }
+
+        editorData.oldValue = this.label;
+        cal.cfg.setProperty("selected",this.label, false);
+
+        var delim = cal.cfg.getProperty('DATE_FIELD_DELIMITER');
+        var pageDate = this.label.split(delim);
+        cal.cfg.setProperty('pagedate',pageDate[cal.cfg.getProperty('MDY_MONTH_POSITION') -1] + delim + pageDate[cal.cfg.getProperty('MDY_YEAR_POSITION') -1]);
+        cal.cfg.fireQueue();
+
+        cal.render();
+        cal.oDomContainer.focus();
+    },
+     /**
+    * Returns the value from the input element.
+    * Overrides Node.getEditorValue.
+    * @method getEditorValue
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @return {string} date entered
+     */
+
+    getEditorValue: function (editorData) {
+        if (Lang.isUndefined(Calendar)) {
+            return editorData.inputElement.value;
+        } else {
+            var cal = editorData.inputObject,
+                date = cal.getSelectedDates()[0],
+                dd = [];
+
+            dd[cal.cfg.getProperty('MDY_DAY_POSITION') -1] = date.getDate();
+            dd[cal.cfg.getProperty('MDY_MONTH_POSITION') -1] = date.getMonth() + 1;
+            dd[cal.cfg.getProperty('MDY_YEAR_POSITION') -1] = date.getFullYear();
+            return dd.join(cal.cfg.getProperty('DATE_FIELD_DELIMITER'));
+        }
+    },
+
+    /**
+     * Finally displays the newly entered date in the tree.
+     * Overrides Node.displayEditedValue.
+     * @method displayEditedValue
+     * @param value {HTML} date to be displayed and stored in the node.
+     * This data is added to the node unescaped via the innerHTML property.
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     */
+    displayEditedValue: function (value,editorData) {
+        var node = editorData.node;
+        node.label = value;
+        node.getLabelEl().innerHTML = value;
+    },
+
+   /**
+     * Returns an object which could be used to build a tree out of this node and its children.
+     * It can be passed to the tree constructor to reproduce this node as a tree.
+     * It will return false if the node or any descendant loads dynamically, regardless of whether it is loaded or not.
+     * @method getNodeDefinition
+     * @return {Object | false}  definition of the node or false if this node or any descendant is defined as dynamic
+     */
+    getNodeDefinition: function() {
+        var def = YAHOO.widget.DateNode.superclass.getNodeDefinition.call(this);
+        if (def === false) { return false; }
+        if (this.calendarConfig) { def.calendarConfig = this.calendarConfig; }
+        return def;
+    }
+
+
+});
+})();
+(function () {
+    var Dom = YAHOO.util.Dom,
+        Lang = YAHOO.lang,
+        Event = YAHOO.util.Event,
+        TV = YAHOO.widget.TreeView,
+        TVproto = TV.prototype;
+
+    /**
+     * An object to store information used for in-line editing
+     * for all Nodes of all TreeViews. It contains:
+     * <ul>
+    * <li>active {boolean}, whether there is an active cell editor </li>
+    * <li>whoHasIt {YAHOO.widget.TreeView} TreeView instance that is currently using the editor</li>
+    * <li>nodeType {string} value of static Node._type property, allows reuse of input element if node is of the same type.</li>
+    * <li>editorPanel {HTMLelement (&lt;div&gt;)} element holding the in-line editor</li>
+    * <li>inputContainer {HTMLelement (&lt;div&gt;)} element which will hold the type-specific input element(s) to be filled by the fillEditorContainer method</li>
+    * <li>buttonsContainer {HTMLelement (&lt;div&gt;)} element which holds the &lt;button&gt; elements for Ok/Cancel.  If you don't want any of the buttons, hide it via CSS styles, don't destroy it</li>
+    * <li>node {YAHOO.widget.Node} reference to the Node being edited</li>
+    * <li>saveOnEnter {boolean}, whether the Enter key should be accepted as a Save command (Esc. is always taken as Cancel), disable for multi-line input elements </li>
+    * <li>oldValue {any}  value before editing</li>
+    * </ul>
+    *  Editors are free to use this object to store additional data.
+     * @property editorData
+     * @static
+     * @for YAHOO.widget.TreeView
+     */
+    TV.editorData = {
+        active:false,
+        whoHasIt:null, // which TreeView has it
+        nodeType:null,
+        editorPanel:null,
+        inputContainer:null,
+        buttonsContainer:null,
+        node:null, // which Node is being edited
+        saveOnEnter:true,
+        oldValue:undefined
+        // Each node type is free to add its own properties to this as it sees fit.
+    };
+
+    /**
+    * Validator function for edited data, called from the TreeView instance scope,
+    * receives the arguments (newValue, oldValue, nodeInstance)
+    * and returns either the validated (or type-converted) value or undefined.
+    * An undefined return will prevent the editor from closing
+    * @property validator
+    * @type function
+    * @default null
+     * @for YAHOO.widget.TreeView
+     */
+    TVproto.validator = null;
+
+    /**
+    * Entry point for initializing the editing plug-in.
+    * TreeView will call this method on initializing if it exists
+    * @method _initEditor
+     * @for YAHOO.widget.TreeView
+     * @private
+    */
+
+    TVproto._initEditor = function () {
+        /**
+        * Fires when the user clicks on the ok button of a node editor
+        * @event editorSaveEvent
+        * @type CustomEvent
+        * @param oArgs.newValue {mixed} the new value just entered
+        * @param oArgs.oldValue {mixed} the value originally in the tree
+        * @param oArgs.node {YAHOO.widget.Node} the node that has the focus
+            * @for YAHOO.widget.TreeView
+        */
+        this.createEvent("editorSaveEvent", this);
+
+        /**
+        * Fires when the user clicks on the cancel button of a node editor
+        * @event editorCancelEvent
+        * @type CustomEvent
+        * @param {YAHOO.widget.Node} node the node that has the focus
+            * @for YAHOO.widget.TreeView
+        */
+        this.createEvent("editorCancelEvent", this);
+
+    };
+
+    /**
+    * Entry point of the editing plug-in.
+    * TreeView will call this method if it exists when a node label is clicked
+    * @method _nodeEditing
+    * @param node {YAHOO.widget.Node} the node to be edited
+    * @return {Boolean} true to indicate that the node is editable and prevent any further bubbling of the click.
+     * @for YAHOO.widget.TreeView
+     * @private
+    */
+
+
+
+    TVproto._nodeEditing = function (node) {
+        if (node.fillEditorContainer && node.editable) {
+            var ed, topLeft, buttons, button, editorData = TV.editorData;
+            editorData.active = true;
+            editorData.whoHasIt = this;
+            if (!editorData.nodeType) {
+                // Fixes: http://yuilibrary.com/projects/yui2/ticket/2528945
+                editorData.editorPanel = ed = this.getEl().appendChild(document.createElement('div'));
+                Dom.addClass(ed,'ygtv-label-editor');
+                ed.tabIndex = 0;
+
+                buttons = editorData.buttonsContainer = ed.appendChild(document.createElement('div'));
+                Dom.addClass(buttons,'ygtv-button-container');
+                button = buttons.appendChild(document.createElement('button'));
+                Dom.addClass(button,'ygtvok');
+                button.innerHTML = ' ';
+                button = buttons.appendChild(document.createElement('button'));
+                Dom.addClass(button,'ygtvcancel');
+                button.innerHTML = ' ';
+                Event.on(buttons, 'click', function (ev) {
+                    var target = Event.getTarget(ev),
+                        editorData = TV.editorData,
+                        node = editorData.node,
+                        self = editorData.whoHasIt;
+                    if (Dom.hasClass(target,'ygtvok')) {
+                        Event.stopEvent(ev);
+                        self._closeEditor(true);
+                    }
+                    if (Dom.hasClass(target,'ygtvcancel')) {
+                        Event.stopEvent(ev);
+                        self._closeEditor(false);
+                    }
+                });
+
+                editorData.inputContainer = ed.appendChild(document.createElement('div'));
+                Dom.addClass(editorData.inputContainer,'ygtv-input');
+
+                Event.on(ed,'keydown',function (ev) {
+                    var editorData = TV.editorData,
+                        KEY = YAHOO.util.KeyListener.KEY,
+                        self = editorData.whoHasIt;
+                    switch (ev.keyCode) {
+                        case KEY.ENTER:
+                            Event.stopEvent(ev);
+                            if (editorData.saveOnEnter) {
+                                self._closeEditor(true);
+                            }
+                            break;
+                        case KEY.ESCAPE:
+                            Event.stopEvent(ev);
+                            self._closeEditor(false);
+                            break;
+                    }
+                });
+
+
+
+            } else {
+                ed = editorData.editorPanel;
+            }
+            editorData.node = node;
+            if (editorData.nodeType) {
+                Dom.removeClass(ed,'ygtv-edit-' + editorData.nodeType);
+            }
+            Dom.addClass(ed,' ygtv-edit-' + node._type);
+            // Fixes: http://yuilibrary.com/projects/yui2/ticket/2528945
+            Dom.setStyle(ed,'display','block');
+            Dom.setXY(ed,Dom.getXY(node.getContentEl()));
+            // up to here
+            ed.focus();
+            node.fillEditorContainer(editorData);
+
+            return true;  // If inline editor available, don't do anything else.
+        }
+    };
+
+    /**
+    * Method to be associated with an event (clickEvent, dblClickEvent or enterKeyPressed) to pop up the contents editor
+    *  It calls the corresponding node editNode method.
+    * @method onEventEditNode
+    * @param oArgs {object} Object passed as arguments to TreeView event listeners
+    * @for YAHOO.widget.TreeView
+    */
+
+    TVproto. (oArgs) {
+        if (oArgs instanceof YAHOO.widget.Node) {
+            oArgs.editNode();
+        } else if (oArgs.node instanceof YAHOO.widget.Node) {
+            oArgs.node.editNode();
+        }
+        return false;
+    };
+
+    /**
+    * Method to be called when the inline editing is finished and the editor is to be closed
+    * @method _closeEditor
+    * @param save {Boolean} true if the edited value is to be saved, false if discarded
+    * @private
+     * @for YAHOO.widget.TreeView
+    */
+
+    TVproto._closeEditor = function (save) {
+        var ed = TV.editorData,
+            node = ed.node,
+            close = true;
+        // http://yuilibrary.com/projects/yui2/ticket/2528946
+        // _closeEditor might now be called at any time, even when there is no label editor open
+        // so we need to ensure there is one.
+        if (!node || !ed.active) { return; }
+        if (save) {
+            close = ed.node.saveEditorValue(ed) !== false;
+        } else {
+            this.fireEvent( 'editorCancelEvent', node);
+        }
+
+        if (close) {
+            Dom.setStyle(ed.editorPanel,'display','none');
+            ed.active = false;
+            node.focus();
+        }
+    };
+
+    /**
+    *  Entry point for TreeView's destroy method to destroy whatever the editing plug-in has created
+    * @method _destroyEditor
+    * @private
+     * @for YAHOO.widget.TreeView
+    */
+    TVproto._destroyEditor = function() {
+        var ed = TV.editorData;
+        if (ed && ed.nodeType && (!ed.active || ed.whoHasIt === this)) {
+            Event.removeListener(ed.editorPanel,'keydown');
+            Event.removeListener(ed.buttonContainer,'click');
+            ed.node.destroyEditorContents(ed);
+            document.body.removeChild(ed.editorPanel);
+            ed.nodeType = ed.editorPanel = ed.inputContainer = ed.buttonsContainer = ed.whoHasIt = ed.node = null;
+            ed.active = false;
+        }
+    };
+
+    var Nproto = YAHOO.widget.Node.prototype;
+
+    /**
+    * Signals if the label is editable.  (Ignored on TextNodes with href set.)
+    * @property editable
+    * @type boolean
+         * @for YAHOO.widget.Node
+    */
+    Nproto.editable = false;
+
+    /**
+    * pops up the contents editor, if there is one and the node is declared editable
+    * @method editNode
+     * @for YAHOO.widget.Node
+    */
+
+    Nproto.editNode = function () {
+        this.tree._nodeEditing(this);
+    };
+
+
+    /** Placeholder for a function that should provide the inline node label editor.
+     *   Leaving it set to null will indicate that this node type is not editable.
+     * It should be overridden by nodes that provide inline editing.
+     *  The Node-specific editing element (input box, textarea or whatever) should be inserted into editorData.inputContainer.
+     * @method fillEditorContainer
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @return void
+     * @for YAHOO.widget.Node
+     */
+    Nproto.fillEditorContainer = null;
+
+
+    /**
+    * Node-specific destroy function to empty the contents of the inline editor panel.
+    * This function is the worst case alternative that will purge all possible events and remove the editor contents.
+    * Method Event.purgeElement is somewhat costly so if it can be replaced by specifc Event.removeListeners, it is better to do so.
+    * @method destroyEditorContents
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @for YAHOO.widget.Node
+     */
+    Nproto.destroyEditorContents = function (editorData) {
+        // In the worst case, if the input editor (such as the Calendar) has no destroy method
+        // we can only try to remove all possible events on it.
+        Event.purgeElement(editorData.inputContainer,true);
+        editorData.inputContainer.innerHTML = '';
+    };
+
+    /**
+    * Saves the value entered into the editor.
+    * @method saveEditorValue
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @return {false or none} a return of exactly false will prevent the editor from closing
+     * @for YAHOO.widget.Node
+     */
+    Nproto.saveEditorValue = function (editorData) {
+        var node = editorData.node,
+            value,
+            validator = node.tree.validator;
+
+        value = this.getEditorValue(editorData);
+
+        if (Lang.isFunction(validator)) {
+            value = validator(value,editorData.oldValue,node);
+            if (Lang.isUndefined(value)) {
+                return false;
+            }
+        }
+
+        if (this.tree.fireEvent( 'editorSaveEvent', {
+            newValue:value,
+            oldValue:editorData.oldValue,
+            node:node
+        }) !== false) {
+            this.displayEditedValue(value,editorData);
+        }
+    };
+
+
+    /**
+     * Returns the value(s) from the input element(s) .
+     * Should be overridden by each node type.
+     * @method getEditorValue
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @return {any} value entered
+     * @for YAHOO.widget.Node
+     */
+
+     Nproto.getEditorValue = function (editorData) {
+    };
+
+    /**
+     * Finally displays the newly edited value(s) in the tree.
+     * Should be overridden by each node type.
+     * @method displayEditedValue
+     * @param value {HTML} value to be displayed and stored in the node
+     * This data is added to the node unescaped via the innerHTML property.
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @for YAHOO.widget.Node
+     */
+    Nproto.displayEditedValue = function (value,editorData) {
+    };
+
+    var TNproto = YAHOO.widget.TextNode.prototype;
+
+
+
+    /**
+     *  Places an &lt;input&gt;  textbox in the input container and loads the label text into it.
+     * @method fillEditorContainer
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @return void
+     * @for YAHOO.widget.TextNode
+     */
+    TNproto.fillEditorContainer = function (editorData) {
+
+        var input;
+        // If last node edited is not of the same type as this one, delete it and fill it with our editor
+        if (editorData.nodeType != this._type) {
+            editorData.nodeType = this._type;
+            editorData.saveOnEnter = true;
+            editorData.node.destroyEditorContents(editorData);
+
+            editorData.inputElement = input = editorData.inputContainer.appendChild(document.createElement('input'));
+
+        } else {
+            // if the last node edited was of the same time, reuse the input element.
+            input = editorData.inputElement;
+        }
+        editorData.oldValue = this.label;
+        input.value = this.label;
+        input.focus();
+        input.select();
+    };
+
+    /**
+     * Returns the value from the input element.
+     * Overrides Node.getEditorValue.
+     * @method getEditorValue
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @return {string} value entered
+     * @for YAHOO.widget.TextNode
+     */
+
+    TNproto.getEditorValue = function (editorData) {
+        return editorData.inputElement.value;
+    };
+
+    /**
+     * Finally displays the newly edited value in the tree.
+     * Overrides Node.displayEditedValue.
+     * @method displayEditedValue
+     * @param value {string} value to be displayed and stored in the node
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @for YAHOO.widget.TextNode
+     */
+    TNproto.displayEditedValue = function (value,editorData) {
+        var node = editorData.node;
+        node.label = value;
+        node.getLabelEl().innerHTML = value;
+    };
+
+    /**
+    * Destroys the contents of the inline editor panel.
+    * Overrides Node.destroyEditorContent.
+    * Since we didn't set any event listeners on this inline editor, it is more efficient to avoid the generic method in Node.
+    * @method destroyEditorContents
+     * @param editorData {YAHOO.widget.TreeView.editorData}  a shortcut to the static object holding editing information
+     * @for YAHOO.widget.TextNode
+     */
+    TNproto.destroyEditorContents = function (editorData) {
+        editorData.inputContainer.innerHTML = '';
+    };
+})();
+/**
+ * A static factory class for tree view expand/collapse animations
+ * @class TVAnim
+ * @static
+ */
+YAHOO.widget.TVAnim = function() {
+    return {
+        /**
+         * Constant for the fade in animation
+         * @property FADE_IN
+         * @type string
+         * @static
+         */
+        FADE_IN: "TVFadeIn",
+
+        /**
+         * Constant for the fade out animation
+         * @property FADE_OUT
+         * @type string
+         * @static
+         */
+        FADE_OUT: "TVFadeOut",
+
+        /**
+         * Returns a ygAnim instance of the given type
+         * @method getAnim
+         * @param type {string} the type of animation
+         * @param el {HTMLElement} the element to element (probably the children div)
+         * @param callback {function} function to invoke when the animation is done.
+         * @return {YAHOO.util.Animation} the animation instance
+         * @static
+         */
+        getAnim: function(type, el, callback) {
+            if (YAHOO.widget[type]) {
+                return new YAHOO.widget[type](el, callback);
+            } else {
+                return null;
+            }
+        },
+
+        /**
+         * Returns true if the specified animation class is available
+         * @method isValid
+         * @param type {string} the type of animation
+         * @return {boolean} true if valid, false if not
+         * @static
+         */
+        isValid: function(type) {
+            return (YAHOO.widget[type]);
+        }
+    };
+} ();
+/**
+ * A 1/2 second fade-in animation.
+ * @class TVFadeIn
+ * @constructor
+ * @param el {HTMLElement} the element to animate
+ * @param callback {function} function to invoke when the animation is finished
+ */
+YAHOO.widget.TVFadeIn = function(el, callback) {
+    /**
+     * The element to animate
+     * @property el
+     * @type HTMLElement
+     */
+    this.el = el;
+
+    /**
+     * the callback to invoke when the animation is complete
+     * @property callback
+     * @type function
+     */
+    this.callback = callback;
+
+};
+
+YAHOO.widget.TVFadeIn.prototype = {
+    /**
+     * Performs the animation
+     * @method animate
+     */
+    animate: function() {
+        var tvanim = this;
+
+        var s = this.el.style;
+        s.opacity = 0.1;
+        s.filter = "alpha(opacity=10)";
+        s.display = "";
+
+        var dur = 0.4; 
+        var a = new YAHOO.util.Anim(this.el, {opacity: {from: 0.1, to: 1, unit:""}}, dur);
+        a.onComplete.subscribe( function() { tvanim.onComplete(); } );
+        a.animate();
+    },
+
+    /**
+     * Clean up and invoke callback
+     * @method onComplete
+     */
+    onComplete: function() {
+        this.callback();
+    },
+
+    /**
+     * toString
+     * @method toString
+     * @return {string} the string representation of the instance
+     */
+    toString: function() {
+        return "TVFadeIn";
+    }
+};
+/**
+ * A 1/2 second fade out animation.
+ * @class TVFadeOut
+ * @constructor
+ * @param el {HTMLElement} the element to animate
+ * @param callback {Function} function to invoke when the animation is finished
+ */
+YAHOO.widget.TVFadeOut = function(el, callback) {
+    /**
+     * The element to animate
+     * @property el
+     * @type HTMLElement
+     */
+    this.el = el;
+
+    /**
+     * the callback to invoke when the animation is complete
+     * @property callback
+     * @type function
+     */
+    this.callback = callback;
+
+};
+
+YAHOO.widget.TVFadeOut.prototype = {
+    /**
+     * Performs the animation
+     * @method animate
+     */
+    animate: function() {
+        var tvanim = this;
+        var dur = 0.4;
+        var a = new YAHOO.util.Anim(this.el, {opacity: {from: 1, to: 0.1, unit:""}}, dur);
+        a.onComplete.subscribe( function() { tvanim.onComplete(); } );
+        a.animate();
+    },
+
+    /**
+     * Clean up and invoke callback
+     * @method onComplete
+     */
+    onComplete: function() {
+        var s = this.el.style;
+        s.display = "none";
+        s.opacity = 1;
+        s.filter = "alpha(opacity=100)";
+        this.callback();
+    },
+
+    /**
+     * toString
+     * @method toString
+     * @return {string} the string representation of the instance
+     */
+    toString: function() {
+        return "TVFadeOut";
+    }
+};
+YAHOO.register("treeview", YAHOO.widget.TreeView, {version: "2.9.0", build: "2800"});

Added: branches/wf4ever/public/_javascript_s/yui/yahoo-dom-event.js (0 => 3265)


--- branches/wf4ever/public/_javascript_s/yui/yahoo-dom-event.js	                        (rev 0)
+++ branches/wf4ever/public/_javascript_s/yui/yahoo-dom-event.js	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,14 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+if(typeof YAHOO=="undefined"||!YAHOO){var YAHOO={};}YAHOO.namespace=function(){var b=arguments,g=null,e,c,f;for(e=0;e<b.length;e=e+1){f=(""+b[e]).split(".");g=YAHOO;for(c=(f[0]=="YAHOO")?1:0;c<f.length;c=c+1){g[f[c]]=g[f[c]]||{};g=g[f[c]];}}return g;};YAHOO.log=function(d,a,c){var b=YAHOO.widget.Logger;if(b&&b.log){return b.log(d,a,c);}else{return false;}};YAHOO.register=function(a,f,e){var k=YAHOO.env.modules,c,j,h,g,d;if(!k[a]){k[a]={versions:[],builds:[]};}c=k[a];j=e.version;h=e.build;g=YAHOO.env.listeners;c.name=a;c.version=j;c.build=h;c.versions.push(j);c.builds.push(h);c.mainClass=f;for(d=0;d<g.length;d=d+1){g[d](c);}if(f){f.VERSION=j;f.BUILD=h;}else{YAHOO.log("mainClass is undefined for module "+a,"warn");}};YAHOO.env=YAHOO.env||{modules:[],listeners:[]};YAHOO.env.getVersion=function(a){return YAHOO.env.modules[a]||null;};YAHOO.env.parseUA=function(d){var e=function(i){var j=0;return parseFloat(i.replace(/\./g,function(){return(j++==1)?"":".";}));},h=navigator,g={ie:0,opera:0,gecko:0,webkit:0,chrome:0,mobile:null,air:0,ipad:0,iphone:0,ipod:0,ios:null,android:0,webos:0,caja:h&&h.cajaVersion,secure:false,os:null},c=d||(navigator&&navigator.userAgent),f=window&&window.location,b=f&&f.href,a;g.secure=b&&(b.toLowerCase().indexOf("https")===0);if(c){if((/windows|win32/i).test(c)){g.os="windows";}else{if((/macintosh/i).test(c)){g.os="macintosh";}else{if((/rhino/i).test(c)){g.os="rhino";}}}if((/KHTML/).test(c)){g.webkit=1;}a=c.match(/AppleWebKit\/([^\s]*)/);if(a&&a[1]){g.webkit=e(a[1]);if(/ Mobile\//.test(c)){g.mobile="Apple";a=c.match(/OS ([^\s]*)/);if(a&&a[1]){a=e(a[1].replace("_","."));}g.ios=a;g.ipad=g.ipod=g.iphone=0;a=c.match(/iPad|iPod|iPhone/);if(a&&a[0]){g[a[0].toLowerCase()]=g.ios;}}else{a=c.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);if(a){g.mobile=a[0];}if(/webOS/.test(c)){g.mobile="WebOS";a=c.match(/webOS\/([^\s]*);/);if(a&&a[1]){g.webos=e(a[1]);}}if(/ Android/.test(c)){g.mobile="Android";a=c.match(/Android ([^\s]*);/);if(a&&a[1]){g.android=e(a[1]);}}}a=c.match(/Chrome\/([^\s]*)/);if(a&&a[1]){g.chrome=e(a[1]);}else{a=c.match(/AdobeAIR\/([^\s]*)/);if(a){g.air=a[0];}}}if(!g.webkit){a=c.match(/Opera[\s\/]([^\s]*)/);if(a&&a[1]){g.opera=e(a[1]);a=c.match(/Version\/([^\s]*)/);if(a&&a[1]){g.opera=e(a[1]);}a=c.match(/Opera Mini[^;]*/);if(a){g.mobile=a[0];}}else{a=c.match(/MSIE\s([^;]*)/);if(a&&a[1]){g.ie=e(a[1]);}else{a=c.match(/Gecko\/([^\s]*)/);if(a){g.gecko=1;a=c.match(/rv:([^\s\)]*)/);if(a&&a[1]){g.gecko=e(a[1]);}}}}}}return g;};YAHOO.env.ua=YAHOO.env.parseUA();(function(){YAHOO.namespace("util","widget","example");if("undefined"!==typeof YAHOO_config){var b=YAHOO_config.listener,a=YAHOO.env.listeners,d=true,c;if(b){for(c=0;c<a.length;c++){if(a[c]==b){d=false;break;}}if(d){a.push(b);}}}})();YAHOO.lang=YAHOO.lang||{};(function(){var f=YAHOO.lang,a=Object.prototype,c="[object Array]",h="[object Function]",i="[object Object]",b=[],g={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","/":"&#x2F;","`":"&#x60;"},d=["toString","valueOf"],e={isArray:function(j){return a.toString.apply(j)===c;},isBoolean:function(j){return typeof j==="boolean";},isFunction:function(j){return(typeof j==="function")||a.toString.apply(j)===h;},isNull:function(j){return j===null;},isNumber:function(j){return typeof j==="number"&&isFinite(j);},isObject:function(j){return(j&&(typeof j==="object"||f.isFunction(j)))||false;},isString:function(j){return typeof j==="string";},isUndefined:function(j){return typeof j==="undefined";},_IEEnumFix:(YAHOO.env.ua.ie)?function(l,k){var j,n,m;for(j=0;j<d.length;j=j+1){n=d[j];m=k[n];if(f.isFunction(m)&&m!=a[n]){l[n]=m;}}}:function(){},escapeHTML:function(j){return j.replace(/[&<>"'\/`]/g,function(k){return g[k];});},extend:function(m,n,l){if(!n||!m){throw new Error("extend failed, please check that "+"all dependencies are included.");}var k=function(){},j;k.prototype=n.prototype;m.prototype=new k();m.prototype.constructor=m;m.superclass=n.prototype;if(n.prototype.constructor==a.constructor){n.prototype.constructor=n;}if(l){for(j in l){if(f.hasOwnProperty(l,j)){m.prototype[j]=l[j];}}f._IEEnumFix(m.prototype,l);}},augmentObject:function(n,m){if(!m||!n){throw new Error("Absorb failed, verify dependencies.");}var j=arguments,l,o,k=j[2];if(k&&k!==true){for(l=2;l<j.length;l=l+1){n[j[l]]=m[j[l]];}}else{for(o in m){if(k||!(o in n)){n[o]=m[o];}}f._IEEnumFix(n,m);}return n;},augmentProto:function(m,l){if(!l||!m){throw new Error("Augment failed, verify dependencies.");}var j=[m.prototype,l.prototype],k;for(k=2;k<arguments.length;k=k+1){j.push(arguments[k]);}f.augmentObject.apply(this,j);return m;},dump:function(j,p){var l,n,r=[],t="{...}",k="f(){...}",q=", ",m=" => ";if(!f.isObject(j)){return j+"";}else{if(j instanceof Date||("nodeType" in j&&"tagName" in j)){return j;}else{if(f.isFunction(j)){return k;}}}p=(f.isNumber(p))?p:3;if(f.isArray(j)){r.push("[");for(l=0,n=j.length;l<n;l=l+1){if(f.isObject(j[l])){r.push((p>0)?f.dump(j[l],p-1):t);}else{r.push(j[l]);}r.push(q);}if(r.length>1){r.pop();}r.push("]");}else{r.push("{");for(l in j){if(f.hasOwnProperty(j,l)){r.push(l+m);if(f.isObject(j[l])){r.push((p>0)?f.dump(j[l],p-1):t);}else{r.push(j[l]);}r.push(q);}}if(r.length>1){r.pop();}r.push("}");}return r.join("");},substitute:function(x,y,E,l){var D,C,B,G,t,u,F=[],p,z=x.length,A="dump",r=" ",q="{",m="}",n,w;for(;;){D=x.lastIndexOf(q,z);if(D<0){break;}C=x.indexOf(m,D);if(D+1>C){break;}p=x.substring(D+1,C);G=p;u=null;B=G.indexOf(r);if(B>-1){u=G.substring(B+1);G=G.substring(0,B);}t=y[G];if(E){t=E(G,t,u);}if(f.isObject(t)){if(f.isArray(t)){t=f.dump(t,parseInt(u,10));}else{u=u||"";n=u.indexOf(A);if(n>-1){u=u.substring(4);}w=t.toString();if(w===i||n>-1){t=f.dump(t,parseInt(u,10));}else{t=w;}}}else{if(!f.isString(t)&&!f.isNumber(t)){t="~-"+F.length+"-~";F[F.length]=p;}}x=x.substring(0,D)+t+x.substring(C+1);if(l===false){z=D-1;}}for(D=F.length-1;D>=0;D=D-1){x=x.replace(new RegExp("~-"+D+"-~"),"{"+F[D]+"}","g");}return x;},trim:function(j){try{return j.replace(/^\s+|\s+$/g,"");}catch(k){return j;
+}},merge:function(){var n={},k=arguments,j=k.length,m;for(m=0;m<j;m=m+1){f.augmentObject(n,k[m],true);}return n;},later:function(t,k,u,n,p){t=t||0;k=k||{};var l=u,s=n,q,j;if(f.isString(u)){l=k[u];}if(!l){throw new TypeError("method undefined");}if(!f.isUndefined(n)&&!f.isArray(s)){s=[n];}q=function(){l.apply(k,s||b);};j=(p)?setInterval(q,t):setTimeout(q,t);return{interval:p,cancel:function(){if(this.interval){clearInterval(j);}else{clearTimeout(j);}}};},isValue:function(j){return(f.isObject(j)||f.isString(j)||f.isNumber(j)||f.isBoolean(j));}};f.hasOwnProperty=(a.hasOwnProperty)?function(j,k){return j&&j.hasOwnProperty&&j.hasOwnProperty(k);}:function(j,k){return !f.isUndefined(j[k])&&j.constructor.prototype[k]!==j[k];};e.augmentObject(f,e,true);YAHOO.util.Lang=f;f.augment=f.augmentProto;YAHOO.augment=f.augmentProto;YAHOO.extend=f.extend;})();YAHOO.register("yahoo",YAHOO,{version:"2.9.0",build:"2800"});(function(){YAHOO.env._id_counter=YAHOO.env._id_counter||0;var e=YAHOO.util,k=YAHOO.lang,L=YAHOO.env.ua,a=YAHOO.lang.trim,B={},F={},m=/^t(?:able|d|h)$/i,w=/color$/i,j=window.document,v=j.documentElement,C="ownerDocument",M="defaultView",U="documentElement",S="compatMode",z="offsetLeft",o="offsetTop",T="offsetParent",x="parentNode",K="nodeType",c="tagName",n="scrollLeft",H="scrollTop",p="getBoundingClientRect",V="getComputedStyle",y="currentStyle",l="CSS1Compat",A="BackCompat",E="class",f="className",i="",b=" ",R="(?:^|\\s)",J="(?= |$)",t="g",O="position",D="fixed",u="relative",I="left",N="top",Q="medium",P="borderLeftWidth",q="borderTopWidth",d=L.opera,h=L.webkit,g=L.gecko,s=L.ie;e.Dom={CUSTOM_ATTRIBUTES:(!v.hasAttribute)?{"for":"htmlFor","class":f}:{"htmlFor":"for","className":E},DOT_ATTRIBUTES:{checked:true},get:function(aa){var ac,X,ab,Z,W,G,Y=null;if(aa){if(typeof aa=="string"||typeof aa=="number"){ac=aa+"";aa=j.getElementById(aa);G=(aa)?aa.attributes:null;if(aa&&G&&G.id&&G.id.value===ac){return aa;}else{if(aa&&j.all){aa=null;X=j.all[ac];if(X&&X.length){for(Z=0,W=X.length;Z<W;++Z){if(X[Z].id===ac){return X[Z];}}}}}}else{if(e.Element&&aa instanceof e.Element){aa=aa.get("element");}else{if(!aa.nodeType&&"length" in aa){ab=[];for(Z=0,W=aa.length;Z<W;++Z){ab[ab.length]=e.Dom.get(aa[Z]);}aa=ab;}}}Y=aa;}return Y;},getComputedStyle:function(G,W){if(window[V]){return G[C][M][V](G,null)[W];}else{if(G[y]){return e.Dom.IE_ComputedStyle.get(G,W);}}},getStyle:function(G,W){return e.Dom.batch(G,e.Dom._getStyle,W);},_getStyle:function(){if(window[V]){return function(G,Y){Y=(Y==="float")?Y="cssFloat":e.Dom._toCamel(Y);var X=G.style[Y],W;if(!X){W=G[C][M][V](G,null);if(W){X=W[Y];}}return X;};}else{if(v[y]){return function(G,Y){var X;switch(Y){case"opacity":X=100;try{X=G.filters["DXImageTransform.Microsoft.Alpha"].opacity;}catch(Z){try{X=G.filters("alpha").opacity;}catch(W){}}return X/100;case"float":Y="styleFloat";default:Y=e.Dom._toCamel(Y);X=G[y]?G[y][Y]:null;return(G.style[Y]||X);}};}}}(),setStyle:function(G,W,X){e.Dom.batch(G,e.Dom._setStyle,{prop:W,val:X});},_setStyle:function(){if(!window.getComputedStyle&&j.documentElement.currentStyle){return function(W,G){var X=e.Dom._toCamel(G.prop),Y=G.val;if(W){switch(X){case"opacity":if(Y===""||Y===null||Y===1){W.style.removeAttribute("filter");}else{if(k.isString(W.style.filter)){W.style.filter="alpha(opacity="+Y*100+")";if(!W[y]||!W[y].hasLayout){W.style.zoom=1;}}}break;case"float":X="styleFloat";default:W.style[X]=Y;}}else{}};}else{return function(W,G){var X=e.Dom._toCamel(G.prop),Y=G.val;if(W){if(X=="float"){X="cssFloat";}W.style[X]=Y;}else{}};}}(),getXY:function(G){return e.Dom.batch(G,e.Dom._getXY);},_canPosition:function(G){return(e.Dom._getStyle(G,"display")!=="none"&&e.Dom._inDoc(G));},_getXY:function(W){var X,G,Z,ab,Y,aa,ac=Math.round,ad=false;if(e.Dom._canPosition(W)){Z=W[p]();ab=W[C];X=e.Dom.getDocumentScrollLeft(ab);G=e.Dom.getDocumentScrollTop(ab);ad=[Z[I],Z[N]];if(Y||aa){ad[0]-=aa;ad[1]-=Y;}if((G||X)){ad[0]+=X;ad[1]+=G;}ad[0]=ac(ad[0]);ad[1]=ac(ad[1]);}else{}return ad;},getX:function(G){var W=function(X){return e.Dom.getXY(X)[0];};return e.Dom.batch(G,W,e.Dom,true);},getY:function(G){var W=function(X){return e.Dom.getXY(X)[1];};return e.Dom.batch(G,W,e.Dom,true);},setXY:function(G,X,W){e.Dom.batch(G,e.Dom._setXY,{pos:X,noRetry:W});},_setXY:function(G,Z){var aa=e.Dom._getStyle(G,O),Y=e.Dom.setStyle,ad=Z.pos,W=Z.noRetry,ab=[parseInt(e.Dom.getComputedStyle(G,I),10),parseInt(e.Dom.getComputedStyle(G,N),10)],ac,X;ac=e.Dom._getXY(G);if(!ad||ac===false){return false;}if(aa=="static"){aa=u;Y(G,O,aa);}if(isNaN(ab[0])){ab[0]=(aa==u)?0:G[z];}if(isNaN(ab[1])){ab[1]=(aa==u)?0:G[o];}if(ad[0]!==null){Y(G,I,ad[0]-ac[0]+ab[0]+"px");}if(ad[1]!==null){Y(G,N,ad[1]-ac[1]+ab[1]+"px");}if(!W){X=e.Dom._getXY(G);if((ad[0]!==null&&X[0]!=ad[0])||(ad[1]!==null&&X[1]!=ad[1])){e.Dom._setXY(G,{pos:ad,noRetry:true});}}},setX:function(W,G){e.Dom.setXY(W,[G,null]);},setY:function(G,W){e.Dom.setXY(G,[null,W]);},getRegion:function(G){var W=function(X){var Y=false;if(e.Dom._canPosition(X)){Y=e.Region.getRegion(X);}else{}return Y;};return e.Dom.batch(G,W,e.Dom,true);},getClientWidth:function(){return e.Dom.getViewportWidth();},getClientHeight:function(){return e.Dom.getViewportHeight();},getElementsByClassName:function(ab,af,ac,ae,X,ad){af=af||"*";ac=(ac)?e.Dom.get(ac):null||j;if(!ac){return[];}var W=[],G=ac.getElementsByTagName(af),Z=e.Dom.hasClass;for(var Y=0,aa=G.length;Y<aa;++Y){if(Z(G[Y],ab)){W[W.length]=G[Y];}}if(ae){e.Dom.batch(W,ae,X,ad);}return W;},hasClass:function(W,G){return e.Dom.batch(W,e.Dom._hasClass,G);},_hasClass:function(X,W){var G=false,Y;if(X&&W){Y=e.Dom._getAttribute(X,f)||i;if(Y){Y=Y.replace(/\s+/g,b);}if(W.exec){G=W.test(Y);}else{G=W&&(b+Y+b).indexOf(b+W+b)>-1;}}else{}return G;},addClass:function(W,G){return e.Dom.batch(W,e.Dom._addClass,G);},_addClass:function(X,W){var G=false,Y;if(X&&W){Y=e.Dom._getAttribute(X,f)||i;if(!e.Dom._hasClass(X,W)){e.Dom.setAttribute(X,f,a(Y+b+W));G=true;}}else{}return G;},removeClass:function(W,G){return e.Dom.batch(W,e.Dom._removeClass,G);},_removeClass:function(Y,X){var W=false,aa,Z,G;if(Y&&X){aa=e.Dom._getAttribute(Y,f)||i;e.Dom.setAttribute(Y,f,aa.replace(e.Dom._getClassRegex(X),i));Z=e.Dom._getAttribute(Y,f);if(aa!==Z){e.Dom.setAttribute(Y,f,a(Z));W=true;if(e.Dom._getAttribute(Y,f)===""){G=(Y.hasAttribute&&Y.hasAttribute(E))?E:f;Y.removeAttribute(G);}}}else{}return W;},replaceClass:function(X,W,G){return e.Dom.batch(X,e.Dom._replaceClass,{from:W,to:G});},_replaceClass:function(Y,X){var W,ab,aa,G=false,Z;if(Y&&X){ab=X.from;aa=X.to;if(!aa){G=false;}else{if(!ab){G=e.Dom._addClass(Y,X.to);}else{if(ab!==aa){Z=e.Dom._getAttribute(Y,f)||i;W=(b+Z.replace(e.Dom._getClassRegex(ab),b+aa).replace(/\s+/g,b)).split(e.Dom._getClassRegex(aa));W.splice(1,0,b+aa);e.Dom.setAttribute(Y,f,a(W.join(i)));G=true;}}}}else{}return G;},generateId:function(G,X){X=X||"yui-gen";var W=function(Y){if(Y&&Y.id){return Y.id;}var Z=X+YAHOO.env._id_counter++;
+if(Y){if(Y[C]&&Y[C].getElementById(Z)){return e.Dom.generateId(Y,Z+X);}Y.id=Z;}return Z;};return e.Dom.batch(G,W,e.Dom,true)||W.apply(e.Dom,arguments);},isAncestor:function(W,X){W=e.Dom.get(W);X=e.Dom.get(X);var G=false;if((W&&X)&&(W[K]&&X[K])){if(W.contains&&W!==X){G=W.contains(X);}else{if(W.compareDocumentPosition){G=!!(W.compareDocumentPosition(X)&16);}}}else{}return G;},inDocument:function(G,W){return e.Dom._inDoc(e.Dom.get(G),W);},_inDoc:function(W,X){var G=false;if(W&&W[c]){X=X||W[C];G=e.Dom.isAncestor(X[U],W);}else{}return G;},getElementsBy:function(W,af,ab,ad,X,ac,ae){af=af||"*";ab=(ab)?e.Dom.get(ab):null||j;var aa=(ae)?null:[],G;if(ab){G=ab.getElementsByTagName(af);for(var Y=0,Z=G.length;Y<Z;++Y){if(W(G[Y])){if(ae){aa=G[Y];break;}else{aa[aa.length]=G[Y];}}}if(ad){e.Dom.batch(aa,ad,X,ac);}}return aa;},getElementBy:function(X,G,W){return e.Dom.getElementsBy(X,G,W,null,null,null,true);},batch:function(X,ab,aa,Z){var Y=[],W=(Z)?aa:null;X=(X&&(X[c]||X.item))?X:e.Dom.get(X);if(X&&ab){if(X[c]||X.length===undefined){return ab.call(W,X,aa);}for(var G=0;G<X.length;++G){Y[Y.length]=ab.call(W||X[G],X[G],aa);}}else{return false;}return Y;},getDocumentHeight:function(){var W=(j[S]!=l||h)?j.body.scrollHeight:v.scrollHeight,G=Math.max(W,e.Dom.getViewportHeight());return G;},getDocumentWidth:function(){var W=(j[S]!=l||h)?j.body.scrollWidth:v.scrollWidth,G=Math.max(W,e.Dom.getViewportWidth());return G;},getViewportHeight:function(){var G=self.innerHeight,W=j[S];if((W||s)&&!d){G=(W==l)?v.clientHeight:j.body.clientHeight;}return G;},getViewportWidth:function(){var G=self.innerWidth,W=j[S];if(W||s){G=(W==l)?v.clientWidth:j.body.clientWidth;}return G;},getAncestorBy:function(G,W){while((G=G[x])){if(e.Dom._testElement(G,W)){return G;}}return null;},getAncestorByClassName:function(W,G){W=e.Dom.get(W);if(!W){return null;}var X=function(Y){return e.Dom.hasClass(Y,G);};return e.Dom.getAncestorBy(W,X);},getAncestorByTagName:function(W,G){W=e.Dom.get(W);if(!W){return null;}var X=function(Y){return Y[c]&&Y[c].toUpperCase()==G.toUpperCase();};return e.Dom.getAncestorBy(W,X);},getPreviousSiblingBy:function(G,W){while(G){G=G.previousSibling;if(e.Dom._testElement(G,W)){return G;}}return null;},getPreviousSibling:function(G){G=e.Dom.get(G);if(!G){return null;}return e.Dom.getPreviousSiblingBy(G);},getNextSiblingBy:function(G,W){while(G){G=G.nextSibling;if(e.Dom._testElement(G,W)){return G;}}return null;},getNextSibling:function(G){G=e.Dom.get(G);if(!G){return null;}return e.Dom.getNextSiblingBy(G);},getFirstChildBy:function(G,X){var W=(e.Dom._testElement(G.firstChild,X))?G.firstChild:null;return W||e.Dom.getNextSiblingBy(G.firstChild,X);},getFirstChild:function(G,W){G=e.Dom.get(G);if(!G){return null;}return e.Dom.getFirstChildBy(G);},getLastChildBy:function(G,X){if(!G){return null;}var W=(e.Dom._testElement(G.lastChild,X))?G.lastChild:null;return W||e.Dom.getPreviousSiblingBy(G.lastChild,X);},getLastChild:function(G){G=e.Dom.get(G);return e.Dom.getLastChildBy(G);},getChildrenBy:function(W,Y){var X=e.Dom.getFirstChildBy(W,Y),G=X?[X]:[];e.Dom.getNextSiblingBy(X,function(Z){if(!Y||Y(Z)){G[G.length]=Z;}return false;});return G;},getChildren:function(G){G=e.Dom.get(G);if(!G){}return e.Dom.getChildrenBy(G);},getDocumentScrollLeft:function(G){G=G||j;return Math.max(G[U].scrollLeft,G.body.scrollLeft);},getDocumentScrollTop:function(G){G=G||j;return Math.max(G[U].scrollTop,G.body.scrollTop);},insertBefore:function(W,G){W=e.Dom.get(W);G=e.Dom.get(G);if(!W||!G||!G[x]){return null;}return G[x].insertBefore(W,G);},insertAfter:function(W,G){W=e.Dom.get(W);G=e.Dom.get(G);if(!W||!G||!G[x]){return null;}if(G.nextSibling){return G[x].insertBefore(W,G.nextSibling);}else{return G[x].appendChild(W);}},getClientRegion:function(){var X=e.Dom.getDocumentScrollTop(),W=e.Dom.getDocumentScrollLeft(),Y=e.Dom.getViewportWidth()+W,G=e.Dom.getViewportHeight()+X;return new e.Region(X,Y,G,W);},setAttribute:function(W,G,X){e.Dom.batch(W,e.Dom._setAttribute,{attr:G,val:X});},_setAttribute:function(X,W){var G=e.Dom._toCamel(W.attr),Y=W.val;if(X&&X.setAttribute){if(e.Dom.DOT_ATTRIBUTES[G]&&X.tagName&&X.tagName!="BUTTON"){X[G]=Y;}else{G=e.Dom.CUSTOM_ATTRIBUTES[G]||G;X.setAttribute(G,Y);}}else{}},getAttribute:function(W,G){return e.Dom.batch(W,e.Dom._getAttribute,G);},_getAttribute:function(W,G){var X;G=e.Dom.CUSTOM_ATTRIBUTES[G]||G;if(e.Dom.DOT_ATTRIBUTES[G]){X=W[G];}else{if(W&&"getAttribute" in W){if(/^(?:href|src)$/.test(G)){X=W.getAttribute(G,2);}else{X=W.getAttribute(G);}}else{}}return X;},_toCamel:function(W){var X=B;function G(Y,Z){return Z.toUpperCase();}return X[W]||(X[W]=W.indexOf("-")===-1?W:W.replace(/-([a-z])/gi,G));},_getClassRegex:function(W){var G;if(W!==undefined){if(W.exec){G=W;}else{G=F[W];if(!G){W=W.replace(e.Dom._patterns.CLASS_RE_TOKENS,"\\$1");W=W.replace(/\s+/g,b);G=F[W]=new RegExp(R+W+J,t);}}}return G;},_patterns:{ROOT_TAG:/^body|html$/i,CLASS_RE_TOKENS:/([\.\(\)\^\$\*\+\?\|\[\]\{\}\\])/g},_testElement:function(G,W){return G&&G[K]==1&&(!W||W(G));},_calcBorders:function(X,Y){var W=parseInt(e.Dom[V](X,q),10)||0,G=parseInt(e.Dom[V](X,P),10)||0;if(g){if(m.test(X[c])){W=0;G=0;}}Y[0]+=G;Y[1]+=W;return Y;}};var r=e.Dom[V];if(L.opera){e.Dom[V]=function(W,G){var X=r(W,G);if(w.test(G)){X=e.Dom.Color.toRGB(X);}return X;};}if(L.webkit){e.Dom[V]=function(W,G){var X=r(W,G);if(X==="rgba(0, 0, 0, 0)"){X="transparent";}return X;};}if(L.ie&&L.ie>=8){e.Dom.DOT_ATTRIBUTES.type=true;}})();YAHOO.util.Region=function(d,e,a,c){this.top=d;this.y=d;this[1]=d;this.right=e;this.bottom=a;this.left=c;this.x=c;this[0]=c;this.width=this.right-this.left;this.height=this.bottom-this.top;};YAHOO.util.Region.prototype.contains=function(a){return(a.left>=this.left&&a.right<=this.right&&a.top>=this.top&&a.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(f){var d=Math.max(this.top,f.top),e=Math.min(this.right,f.right),a=Math.min(this.bottom,f.bottom),c=Math.max(this.left,f.left);
+if(a>=d&&e>=c){return new YAHOO.util.Region(d,e,a,c);}else{return null;}};YAHOO.util.Region.prototype.union=function(f){var d=Math.min(this.top,f.top),e=Math.max(this.right,f.right),a=Math.max(this.bottom,f.bottom),c=Math.min(this.left,f.left);return new YAHOO.util.Region(d,e,a,c);};YAHOO.util.Region.prototype.toString=function(){return("Region {"+"top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+", height: "+this.height+", width: "+this.width+"}");};YAHOO.util.Region.getRegion=function(e){var g=YAHOO.util.Dom.getXY(e),d=g[1],f=g[0]+e.offsetWidth,a=g[1]+e.offsetHeight,c=g[0];return new YAHOO.util.Region(d,f,a,c);};YAHOO.util.Point=function(a,b){if(YAHOO.lang.isArray(a)){b=a[1];a=a[0];}YAHOO.util.Point.superclass.constructor.call(this,b,a,b,a);};YAHOO.extend(YAHOO.util.Point,YAHOO.util.Region);(function(){var b=YAHOO.util,a="clientTop",f="clientLeft",j="parentNode",k="right",w="hasLayout",i="px",u="opacity",l="auto",d="borderLeftWidth",g="borderTopWidth",p="borderRightWidth",v="borderBottomWidth",s="visible",q="transparent",n="height",e="width",h="style",t="currentStyle",r=/^width|height$/,o=/^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,m={get:function(x,z){var y="",A=x[t][z];if(z===u){y=b.Dom.getStyle(x,u);}else{if(!A||(A.indexOf&&A.indexOf(i)>-1)){y=A;}else{if(b.Dom.IE_COMPUTED[z]){y=b.Dom.IE_COMPUTED[z](x,z);}else{if(o.test(A)){y=b.Dom.IE.ComputedStyle.getPixel(x,z);}else{y=A;}}}}return y;},getOffset:function(z,E){var B=z[t][E],x=E.charAt(0).toUpperCase()+E.substr(1),C="offset"+x,y="pixel"+x,A="",D;if(B==l){D=z[C];if(D===undefined){A=0;}A=D;if(r.test(E)){z[h][E]=D;if(z[C]>D){A=D-(z[C]-D);}z[h][E]=l;}}else{if(!z[h][y]&&!z[h][E]){z[h][E]=B;}A=z[h][y];}return A+i;},getBorderWidth:function(x,z){var y=null;if(!x[t][w]){x[h].zoom=1;}switch(z){case g:y=x[a];break;case v:y=x.offsetHeight-x.clientHeight-x[a];break;case d:y=x[f];break;case p:y=x.offsetWidth-x.clientWidth-x[f];break;}return y+i;},getPixel:function(y,x){var A=null,B=y[t][k],z=y[t][x];y[h][k]=z;A=y[h].pixelRight;y[h][k]=B;return A+i;},getMargin:function(y,x){var z;if(y[t][x]==l){z=0+i;}else{z=b.Dom.IE.ComputedStyle.getPixel(y,x);}return z;},getVisibility:function(y,x){var z;while((z=y[t])&&z[x]=="inherit"){y=y[j];}return(z)?z[x]:s;},getColor:function(y,x){return b.Dom.Color.toRGB(y[t][x])||q;},getBorderColor:function(y,x){var z=y[t],A=z[x]||z.color;return b.Dom.Color.toRGB(b.Dom.Color.toHex(A));}},c={};c.top=c.right=c.bottom=c.left=c[e]=c[n]=m.getOffset;c.color=m.getColor;c[g]=c[p]=c[v]=c[d]=m.getBorderWidth;c.marginTop=c.marginRight=c.marginBottom=c.marginLeft=m.getMargin;c.visibility=m.getVisibility;c.borderColor=c.borderTopColor=c.borderRightColor=c.borderBottomColor=c.borderLeftColor=m.getBorderColor;b.Dom.IE_COMPUTED=c;b.Dom.IE_ComputedStyle=m;})();(function(){var c="toString",a=parseInt,b=RegExp,d=YAHOO.util;d.Dom.Color={KEYWORDS:{black:"000",silver:"c0c0c0",gray:"808080",white:"fff",maroon:"800000",red:"f00",purple:"800080",fuchsia:"f0f",green:"008000",lime:"0f0",olive:"808000",yellow:"ff0",navy:"000080",blue:"00f",teal:"008080",aqua:"0ff"},re_RGB:/^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,re_hex:/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,re_hex3:/([0-9A-F])/gi,toRGB:function(e){if(!d.Dom.Color.re_RGB.test(e)){e=d.Dom.Color.toHex(e);}if(d.Dom.Color.re_hex.exec(e)){e="rgb("+[a(b.$1,16),a(b.$2,16),a(b.$3,16)].join(", ")+")";}return e;},toHex:function(f){f=d.Dom.Color.KEYWORDS[f]||f;if(d.Dom.Color.re_RGB.exec(f)){f=[Number(b.$1).toString(16),Number(b.$2).toString(16),Number(b.$3).toString(16)];for(var e=0;e<f.length;e++){if(f[e].length<2){f[e]="0"+f[e];}}f=f.join("");}if(f.length<6){f=f.replace(d.Dom.Color.re_hex3,"$1$1");}if(f!=="transparent"&&f.indexOf("#")<0){f="#"+f;}return f.toUpperCase();}};}());YAHOO.register("dom",YAHOO.util.Dom,{version:"2.9.0",build:"2800"});YAHOO.util.CustomEvent=function(d,c,b,a,e){this.type=d;this.scope=c||window;this.silent=b;this.fireOnce=e;this.fired=false;this.firedWith=null;this.signature=a||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent){}var f="_YUICEOnSubscribe";if(d!==f){this.subscribeEvent=new YAHOO.util.CustomEvent(f,this,true);}this.lastError=null;};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;YAHOO.util.CustomEvent.prototype={subscribe:function(b,c,d){if(!b){throw new Error("Invalid callback for subscriber to '"+this.type+"'");}if(this.subscribeEvent){this.subscribeEvent.fire(b,c,d);}var a=new YAHOO.util.Subscriber(b,c,d);if(this.fireOnce&&this.fired){this.notify(a,this.firedWith);}else{this.subscribers.push(a);}},unsubscribe:function(d,f){if(!d){return this.unsubscribeAll();}var e=false;for(var b=0,a=this.subscribers.length;b<a;++b){var c=this.subscribers[b];if(c&&c.contains(d,f)){this._delete(b);e=true;}}return e;},fire:function(){this.lastError=null;var h=[],a=this.subscribers.length;var d=[].slice.call(arguments,0),c=true,f,b=false;if(this.fireOnce){if(this.fired){return true;}else{this.firedWith=d;}}this.fired=true;if(!a&&this.silent){return true;}if(!this.silent){}var e=this.subscribers.slice();for(f=0;f<a;++f){var g=e[f];if(!g||!g.fn){b=true;}else{c=this.notify(g,d);if(false===c){if(!this.silent){}break;}}}return(c!==false);},notify:function(g,c){var b,i=null,f=g.getScope(this.scope),a=YAHOO.util.Event.throwErrors;if(!this.silent){}if(this.signature==YAHOO.util.CustomEvent.FLAT){if(c.length>0){i=c[0];}try{b=g.fn.call(f,i,g.obj);}catch(h){this.lastError=h;if(a){throw h;}}}else{try{b=g.fn.call(f,this.type,c,g.obj);}catch(d){this.lastError=d;if(a){throw d;}}}return b;},unsubscribeAll:function(){var a=this.subscribers.length,b;for(b=a-1;b>-1;b--){this._delete(b);}this.subscribers=[];return a;},_delete:function(a){var b=this.subscribers[a];if(b){delete b.fn;delete b.obj;}this.subscribers.splice(a,1);},toString:function(){return"CustomEvent: "+"'"+this.type+"', "+"context: "+this.scope;}};YAHOO.util.Subscriber=function(a,b,c){this.fn=a;this.obj=YAHOO.lang.isUndefined(b)?null:b;this.overrideContext=c;};YAHOO.util.Subscriber.prototype.getScope=function(a){if(this.overrideContext){if(this.overrideContext===true){return this.obj;}else{return this.overrideContext;}}return a;};YAHOO.util.Subscriber.prototype.contains=function(a,b){if(b){return(this.fn==a&&this.obj==b);}else{return(this.fn==a);}};YAHOO.util.Subscriber.prototype.toString=function(){return"Subscriber { obj: "+this.obj+", overrideContext: "+(this.overrideContext||"no")+" }";};if(!YAHOO.util.Event){YAHOO.util.Event=function(){var g=false,h=[],j=[],a=0,e=[],b=0,c={63232:38,63233:40,63234:37,63235:39,63276:33,63277:34,25:9},d=YAHOO.env.ua.ie,f="focusin",i="focusout";return{POLL_RETRYS:500,POLL_INTERVAL:40,EL:0,TYPE:1,FN:2,WFN:3,UNLOAD_OBJ:3,ADJ_SCOPE:4,OBJ:5,OVERRIDE:6,CAPTURE:7,lastError:null,isSafari:YAHOO.env.ua.webkit,webkit:YAHOO.env.ua.webkit,isIE:d,_interval:null,_dri:null,_specialTypes:{focusin:(d?"focusin":"focus"),focusout:(d?"focusout":"blur")},DOMReady:false,throwErrors:false,startInterval:function(){if(!this._interval){this._interval=YAHOO.lang.later(this.POLL_INTERVAL,this,this._tryPreloadAttach,null,true);}},onAvailable:function(q,m,o,p,n){var k=(YAHOO.lang.isString(q))?[q]:q;for(var l=0;l<k.length;l=l+1){e.push({id:k[l],fn:m,obj:o,overrideContext:p,checkReady:n});}a=this.POLL_RETRYS;this.startInterval();},onContentReady:function(n,k,l,m){this.onAvailable(n,k,l,m,true);},onDOMReady:function(){this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent,arguments);},_addListener:function(m,k,v,p,t,y){if(!v||!v.call){return false;}if(this._isValidCollection(m)){var w=true;for(var q=0,s=m.length;q<s;++q){w=this.on(m[q],k,v,p,t)&&w;}return w;}else{if(YAHOO.lang.isString(m)){var o=this.getEl(m);if(o){m=o;}else{this.onAvailable(m,function(){YAHOO.util.Event._addListener(m,k,v,p,t,y);});return true;}}}if(!m){return false;}if("unload"==k&&p!==this){j[j.length]=[m,k,v,p,t];return true;}var l=m;if(t){if(t===true){l=p;}else{l=t;}}var n=function(z){return v.call(l,YAHOO.util.Event.getEvent(z,m),p);};var x=[m,k,v,n,l,p,t,y];var r=h.length;h[r]=x;try{this._simpleAdd(m,k,n,y);}catch(u){this.lastError=u;this.removeListener(m,k,v);return false;}return true;},_getType:function(k){return this._specialTypes[k]||k;},addListener:function(m,p,l,n,o){var k=((p==f||p==i)&&!YAHOO.env.ua.ie)?true:false;return this._addListener(m,this._getType(p),l,n,o,k);},addFocusListener:function(l,k,m,n){return this.on(l,f,k,m,n);},removeFocusListener:function(l,k){return this.removeListener(l,f,k);},addBlurListener:function(l,k,m,n){return this.on(l,i,k,m,n);},removeBlurListener:function(l,k){return this.removeListener(l,i,k);},removeListener:function(l,k,r){var m,p,u;k=this._getType(k);if(typeof l=="string"){l=this.getEl(l);}else{if(this._isValidCollection(l)){var s=true;for(m=l.length-1;m>-1;m--){s=(this.removeListener(l[m],k,r)&&s);}return s;}}if(!r||!r.call){return this.purgeElement(l,false,k);}if("unload"==k){for(m=j.length-1;m>-1;m--){u=j[m];if(u&&u[0]==l&&u[1]==k&&u[2]==r){j.splice(m,1);return true;}}return false;}var n=null;var o=arguments[3];if("undefined"===typeof o){o=this._getCacheIndex(h,l,k,r);}if(o>=0){n=h[o];}if(!l||!n){return false;}var t=n[this.CAPTURE]===true?true:false;try{this._simpleRemove(l,k,n[this.WFN],t);}catch(q){this.lastError=q;return false;}delete h[o][this.WFN];delete h[o][this.FN];h.splice(o,1);return true;},getTarget:function(m,l){var k=m.target||m.srcElement;return this.resolveTextNode(k);},resolveTextNode:function(l){try{if(l&&3==l.nodeType){return l.parentNode;}}catch(k){return null;}return l;},getPageX:function(l){var k=l.pageX;if(!k&&0!==k){k=l.clientX||0;if(this.isIE){k+=this._getScrollLeft();}}return k;},getPageY:function(k){var l=k.pageY;if(!l&&0!==l){l=k.clientY||0;if(this.isIE){l+=this._getScrollTop();}}return l;},getXY:function(k){return[this.getPageX(k),this.getPageY(k)];},getRelatedTarget:function(l){var k=l.relatedTarget;
+if(!k){if(l.type=="mouseout"){k=l.toElement;}else{if(l.type=="mouseover"){k=l.fromElement;}}}return this.resolveTextNode(k);},getTime:function(m){if(!m.time){var l=new Date().getTime();try{m.time=l;}catch(k){this.lastError=k;return l;}}return m.time;},stopEvent:function(k){this.stopPropagation(k);this.preventDefault(k);},stopPropagation:function(k){if(k.stopPropagation){k.stopPropagation();}else{k.cancelBubble=true;}},preventDefault:function(k){if(k.preventDefault){k.preventDefault();}else{k.returnValue=false;}},getEvent:function(m,k){var l=m||window.event;if(!l){var n=this.getEvent.caller;while(n){l=n.arguments[0];if(l&&Event==l.constructor){break;}n=n.caller;}}return l;},getCharCode:function(l){var k=l.keyCode||l.charCode||0;if(YAHOO.env.ua.webkit&&(k in c)){k=c[k];}return k;},_getCacheIndex:function(n,q,r,p){for(var o=0,m=n.length;o<m;o=o+1){var k=n[o];if(k&&k[this.FN]==p&&k[this.EL]==q&&k[this.TYPE]==r){return o;}}return -1;},generateId:function(k){var l=k.id;if(!l){l="yuievtautoid-"+b;++b;k.id=l;}return l;},_isValidCollection:function(l){try{return(l&&typeof l!=="string"&&l.length&&!l.tagName&&!l.alert&&typeof l[0]!=="undefined");}catch(k){return false;}},elCache:{},getEl:function(k){return(typeof k==="string")?document.getElementById(k):k;},clearCache:function(){},DOMReadyEvent:new YAHOO.util.CustomEvent("DOMReady",YAHOO,0,0,1),_load:function(l){if(!g){g=true;var k=YAHOO.util.Event;k._ready();k._tryPreloadAttach();}},_ready:function(l){var k=YAHOO.util.Event;if(!k.DOMReady){k.DOMReady=true;k.DOMReadyEvent.fire();k._simpleRemove(document,"DOMContentLoaded",k._ready);}},_tryPreloadAttach:function(){if(e.length===0){a=0;if(this._interval){this._interval.cancel();this._interval=null;}return;}if(this.locked){return;}if(this.isIE){if(!this.DOMReady){this.startInterval();return;}}this.locked=true;var q=!g;if(!q){q=(a>0&&e.length>0);}var p=[];var r=function(t,u){var s=t;if(u.overrideContext){if(u.overrideContext===true){s=u.obj;}else{s=u.overrideContext;}}u.fn.call(s,u.obj);};var l,k,o,n,m=[];for(l=0,k=e.length;l<k;l=l+1){o=e[l];if(o){n=this.getEl(o.id);if(n){if(o.checkReady){if(g||n.nextSibling||!q){m.push(o);e[l]=null;}}else{r(n,o);e[l]=null;}}else{p.push(o);}}}for(l=0,k=m.length;l<k;l=l+1){o=m[l];r(this.getEl(o.id),o);}a--;if(q){for(l=e.length-1;l>-1;l--){o=e[l];if(!o||!o.id){e.splice(l,1);}}this.startInterval();}else{if(this._interval){this._interval.cancel();this._interval=null;}}this.locked=false;},purgeElement:function(p,q,s){var n=(YAHOO.lang.isString(p))?this.getEl(p):p;var r=this.getListeners(n,s),o,k;if(r){for(o=r.length-1;o>-1;o--){var m=r[o];this.removeListener(n,m.type,m.fn);}}if(q&&n&&n.childNodes){for(o=0,k=n.childNodes.length;o<k;++o){this.purgeElement(n.childNodes[o],q,s);}}},getListeners:function(n,k){var q=[],m;if(!k){m=[h,j];}else{if(k==="unload"){m=[j];}else{k=this._getType(k);m=[h];}}var s=(YAHOO.lang.isString(n))?this.getEl(n):n;for(var p=0;p<m.length;p=p+1){var u=m[p];if(u){for(var r=0,t=u.length;r<t;++r){var o=u[r];if(o&&o[this.EL]===s&&(!k||k===o[this.TYPE])){q.push({type:o[this.TYPE],fn:o[this.FN],obj:o[this.OBJ],adjust:o[this.OVERRIDE],scope:o[this.ADJ_SCOPE],index:r});}}}}return(q.length)?q:null;},_unload:function(s){var m=YAHOO.util.Event,p,o,n,r,q,t=j.slice(),k;for(p=0,r=j.length;p<r;++p){n=t[p];if(n){try{k=window;if(n[m.ADJ_SCOPE]){if(n[m.ADJ_SCOPE]===true){k=n[m.UNLOAD_OBJ];}else{k=n[m.ADJ_SCOPE];}}n[m.FN].call(k,m.getEvent(s,n[m.EL]),n[m.UNLOAD_OBJ]);}catch(w){}t[p]=null;}}n=null;k=null;j=null;if(h){for(o=h.length-1;o>-1;o--){n=h[o];if(n){try{m.removeListener(n[m.EL],n[m.TYPE],n[m.FN],o);}catch(v){}}}n=null;}try{m._simpleRemove(window,"unload",m._unload);m._simpleRemove(window,"load",m._load);}catch(u){}},_getScrollLeft:function(){return this._getScroll()[1];},_getScrollTop:function(){return this._getScroll()[0];},_getScroll:function(){var k=document.documentElement,l=document.body;if(k&&(k.scrollTop||k.scrollLeft)){return[k.scrollTop,k.scrollLeft];}else{if(l){return[l.scrollTop,l.scrollLeft];}else{return[0,0];}}},regCE:function(){},_simpleAdd:function(){if(window.addEventListener){return function(m,n,l,k){m.addEventListener(n,l,(k));};}else{if(window.attachEvent){return function(m,n,l,k){m.attachEvent("on"+n,l);};}else{return function(){};}}}(),_simpleRemove:function(){if(window.removeEventListener){return function(m,n,l,k){m.removeEventListener(n,l,(k));};}else{if(window.detachEvent){return function(l,m,k){l.detachEvent("on"+m,k);};}else{return function(){};}}}()};}();(function(){var a=YAHOO.util.Event;a.on=a.addListener;a.
+/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
+if(a.isIE){if(self!==self.top){document. b=document.createElement("p");a._dri=setInterval(function(){try{b.doScroll("left");clearInterval(a._dri);a._dri=null;a._ready();b=null;}catch(c){}},a.POLL_INTERVAL);}}else{if(a.webkit&&a.webkit<525){a._dri=setInterval(function(){var c=document.readyState;if("loaded"==c||"complete"==c){clearInterval(a._dri);a._dri=null;a._ready();}},a.POLL_INTERVAL);}else{a._simpleAdd(document,"DOMContentLoaded",a._ready);}}a._simpleAdd(window,"load",a._load);a._simpleAdd(window,"unload",a._unload);a._tryPreloadAttach();})();}YAHOO.util.EventProvider=function(){};YAHOO.util.EventProvider.prototype={__yui_events:null,__yui_subscribers:null,subscribe:function(a,c,f,e){this.__yui_events=this.__yui_events||{};var d=this.__yui_events[a];if(d){d.subscribe(c,f,e);}else{this.__yui_subscribers=this.__yui_subscribers||{};var b=this.__yui_subscribers;if(!b[a]){b[a]=[];}b[a].push({fn:c,obj:f,overrideContext:e});}},unsubscribe:function(c,e,g){this.__yui_events=this.__yui_events||{};var a=this.__yui_events;if(c){var f=a[c];if(f){return f.unsubscribe(e,g);}}else{var b=true;for(var d in a){if(YAHOO.lang.hasOwnProperty(a,d)){b=b&&a[d].unsubscribe(e,g);
+}}return b;}return false;},unsubscribeAll:function(a){return this.unsubscribe(a);},createEvent:function(b,g){this.__yui_events=this.__yui_events||{};var e=g||{},d=this.__yui_events,f;if(d[b]){}else{f=new YAHOO.util.CustomEvent(b,e.scope||this,e.silent,YAHOO.util.CustomEvent.FLAT,e.fireOnce);d[b]=f;if(e.onSubscribeCallback){f.subscribeEvent.subscribe(e.onSubscribeCallback);}this.__yui_subscribers=this.__yui_subscribers||{};var a=this.__yui_subscribers[b];if(a){for(var c=0;c<a.length;++c){f.subscribe(a[c].fn,a[c].obj,a[c].overrideContext);}}}return d[b];},fireEvent:function(b){this.__yui_events=this.__yui_events||{};var d=this.__yui_events[b];if(!d){return null;}var a=[];for(var c=1;c<arguments.length;++c){a.push(arguments[c]);}return d.fire.apply(d,a);},hasEvent:function(a){if(this.__yui_events){if(this.__yui_events[a]){return true;}}return false;}};(function(){var a=YAHOO.util.Event,c=YAHOO.lang;YAHOO.util.KeyListener=function(d,i,e,f){if(!d){}else{if(!i){}else{if(!e){}}}if(!f){f=YAHOO.util.KeyListener.KEYDOWN;}var g=new YAHOO.util.CustomEvent("keyPressed");this.enabledEvent=new YAHOO.util.CustomEvent("enabled");this.disabledEvent=new YAHOO.util.CustomEvent("disabled");if(c.isString(d)){d=document.getElementById(d);}if(c.isFunction(e)){g.subscribe(e);}else{g.subscribe(e.fn,e.scope,e.correctScope);}function h(o,n){if(!i.shift){i.shift=false;}if(!i.alt){i.alt=false;}if(!i.ctrl){i.ctrl=false;}if(o.shiftKey==i.shift&&o.altKey==i.alt&&o.ctrlKey==i.ctrl){var j,m=i.keys,l;if(YAHOO.lang.isArray(m)){for(var k=0;k<m.length;k++){j=m[k];l=a.getCharCode(o);if(j==l){g.fire(l,o);break;}}}else{l=a.getCharCode(o);if(m==l){g.fire(l,o);}}}}this.enable=function(){if(!this.enabled){a.on(d,f,h);this.enabledEvent.fire(i);}this.enabled=true;};this.disable=function(){if(this.enabled){a.removeListener(d,f,h);this.disabledEvent.fire(i);}this.enabled=false;};this.toString=function(){return"KeyListener ["+i.keys+"] "+d.tagName+(d.id?"["+d.id+"]":"");};};var b=YAHOO.util.KeyListener;b.KEYDOWN="keydown";b.KEYUP="keyup";b.KEY={ALT:18,BACK_SPACE:8,CAPS_LOCK:20,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,META:224,NUM_LOCK:144,PAGE_DOWN:34,PAGE_UP:33,PAUSE:19,PRINTSCREEN:44,RIGHT:39,SCROLL_LOCK:145,SHIFT:16,SPACE:32,TAB:9,UP:38};})();YAHOO.register("event",YAHOO.util.Event,{version:"2.9.0",build:"2800"});YAHOO.register("yahoo-dom-event", YAHOO, {version: "2.9.0", build: "2800"});
\ No newline at end of file

Added: branches/wf4ever/public/stylesheets/folders.css (0 => 3265)


--- branches/wf4ever/public/stylesheets/folders.css	                        (rev 0)
+++ branches/wf4ever/public/stylesheets/folders.css	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,13 @@
+
+span.resource {
+  padding-left: 20px;
+  background: transparent url("resource.png") 0 0px no-repeat;
+}
+
+span.root_folder, span.folder {
+  padding-left: 2px;
+}
+
+span.empty {
+  color: #999;
+}

Added: branches/wf4ever/public/stylesheets/yui/datatable-skin.css (0 => 3265)


--- branches/wf4ever/public/stylesheets/yui/datatable-skin.css	                        (rev 0)
+++ branches/wf4ever/public/stylesheets/yui/datatable-skin.css	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,240 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+/* basic skin styles */
+.yui-skin-sam .yui-dt table {
+    margin:0;padding:0;
+    font-family:arial;font-size:inherit;
+    border-collapse:separate;*border-collapse:collapse;border-spacing:0; /* since ie6 and ie7 behave differently */
+    border:1px solid #7F7F7F;
+}
+.yui-skin-sam .yui-dt thead {border-spacing:0;} /* for safari bug */
+
+.yui-skin-sam .yui-dt caption {
+    color:#000000;
+    font-size:85%;
+    font-weight:normal;
+    font-style:italic;
+    line-height:1;
+    padding:1em 0pt;
+    text-align:center;
+}
+
+.yui-skin-sam .yui-dt th {
+    background:#D8D8DA url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0; /* header gradient */
+}
+.yui-skin-sam .yui-dt th,
+.yui-skin-sam .yui-dt th a {
+    font-weight:normal;text-decoration:none;color:#000; /* header text */
+    vertical-align:bottom;
+}
+.yui-skin-sam .yui-dt th {
+    margin:0;padding:0;
+    border:none;
+    border-right:1px solid #CBCBCB;/*  inner column border */
+}
+.yui-skin-sam .yui-dt tr.yui-dt-first td {
+    border-top:1px solid #7F7F7F;  /* tbody top border  */
+}
+.yui-skin-sam .yui-dt th .yui-dt-liner {
+    white-space:nowrap;
+}
+.yui-skin-sam .yui-dt-liner {
+    margin:0;padding:0;
+    padding:4px 10px 4px 10px; /* cell padding */
+}
+.yui-skin-sam .yui-dt-coltarget {
+    width: 5px;
+    background-color: red;
+}
+.yui-skin-sam .yui-dt td {
+    margin:0;padding:0;
+    border:none;
+    border-right:1px solid #CBCBCB; /* inner column border */
+    text-align:left;
+}
+.yui-skin-sam .yui-dt-list td {
+    border-right:none; /* disable inner column border in list mode */
+}
+.yui-skin-sam .yui-dt-resizer {
+    width:6px;
+}
+
+/* mask */
+.yui-skin-sam .yui-dt-mask {
+    background-color: #000;
+    opacity: .25;
+    filter: alpha(opacity=25);  /* Set opacity in IE */
+}
+
+/* messaging */
+.yui-skin-sam .yui-dt-message  {
+    background-color:#FFF;
+}
+
+/* scrolling */
+.yui-skin-sam .yui-dt-scrollable table {border:none;}
+.yui-skin-sam .yui-dt-scrollable .yui-dt-hd {border-left:1px solid #7F7F7F;border-top:1px solid #7F7F7F;border-right:1px solid #7F7F7F;}
+.yui-skin-sam .yui-dt-scrollable .yui-dt-bd {border-left:1px solid #7F7F7F;border-bottom:1px solid #7F7F7F;border-right:1px solid #7F7F7F;background-color:#FFF;}
+.yui-skin-sam .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td {border-bottom:1px solid #7F7F7F;}
+
+/* sortable columns */
+.yui-skin-sam th.yui-dt-asc,
+.yui-skin-sam th.yui-dt-desc {
+    background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -100px; /* sorted header gradient */
+}
+.yui-skin-sam th.yui-dt-sortable .yui-dt-label {
+    margin-right:10px;
+}
+.yui-skin-sam th.yui-dt-asc .yui-dt-liner {
+    background:url(dt-arrow-up.png) no-repeat right; /* sorted header gradient */
+}
+.yui-skin-sam th.yui-dt-desc .yui-dt-liner {
+    background:url(dt-arrow-dn.png) no-repeat right; /* sorted header gradient */
+}
+
+/* editing */
+tbody .yui-dt-editable {
+    cursor:pointer;
+}
+.yui-dt-editor {
+    text-align:left;
+    background-color:#F2F2F2;
+    border:1px solid #808080;
+    padding:6px;
+}
+.yui-dt-editor label {
+    padding-left:4px;padding-right:6px;
+}
+.yui-dt-editor .yui-dt-button {
+    padding-top:6px;text-align:right;
+}
+.yui-dt-editor .yui-dt-button button {
+    background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0;
+    border:1px solid #999;
+    width:4em;height:1.8em;
+    margin-left:6px;
+}
+.yui-dt-editor .yui-dt-button button.yui-dt-default {
+    background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -1400px;
+    background-color: #5584E0;
+    border:1px solid #304369;
+    color:#FFF
+}
+.yui-dt-editor .yui-dt-button button:hover {
+    background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -1300px;
+    color:#000;
+}
+.yui-dt-editor .yui-dt-button button:active {
+    background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -1700px;
+    color:#000;
+}
+
+/* striping */
+.yui-skin-sam tr.yui-dt-even { background-color:#FFF; } /* white */
+.yui-skin-sam tr.yui-dt-odd { background-color:#EDF5FF; } /* light blue */
+.yui-skin-sam tr.yui-dt-even td.yui-dt-asc,
+.yui-skin-sam tr.yui-dt-even td.yui-dt-desc { background-color:#EDF5FF; } /* light blue sorted */
+.yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,
+.yui-skin-sam tr.yui-dt-odd td.yui-dt-desc { background-color:#DBEAFF; } /* dark blue sorted */
+
+/* disable striping in list mode */
+.yui-skin-sam .yui-dt-list tr.yui-dt-even { background-color:#FFF; } /* white */
+.yui-skin-sam .yui-dt-list tr.yui-dt-odd { background-color:#FFF; } /* white */
+.yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-asc,
+.yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-desc { background-color:#EDF5FF; } /* light blue sorted */
+.yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-asc,
+.yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-desc { background-color:#EDF5FF; } /* light blue sorted */
+
+/* highlighting */
+.yui-skin-sam th.yui-dt-highlighted,
+.yui-skin-sam th.yui-dt-highlighted a {
+    background-color:#B2D2FF; /* med blue hover */
+}
+.yui-skin-sam tr.yui-dt-highlighted,
+.yui-skin-sam tr.yui-dt-highlighted td.yui-dt-asc,
+.yui-skin-sam tr.yui-dt-highlighted td.yui-dt-desc,
+.yui-skin-sam tr.yui-dt-even td.yui-dt-highlighted,
+.yui-skin-sam tr.yui-dt-odd td.yui-dt-highlighted {
+    cursor:pointer;
+    background-color:#B2D2FF; /* med blue hover */
+}
+
+/* enable highlighting in list mode */
+.yui-skin-sam .yui-dt-list th.yui-dt-highlighted,
+.yui-skin-sam .yui-dt-list th.yui-dt-highlighted a {
+    background-color:#B2D2FF; /* med blue hover */
+}
+.yui-skin-sam .yui-dt-list tr.yui-dt-highlighted,
+.yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-asc,
+.yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc,
+.yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted,
+.yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted {
+    cursor:pointer;
+    background-color:#B2D2FF; /* med blue  hover */
+}
+
+/* selection */
+.yui-skin-sam th.yui-dt-selected,
+.yui-skin-sam th.yui-dt-selected a {
+    background-color:#446CD7; /* bright blue selected cell */
+}
+.yui-skin-sam tr.yui-dt-selected td,
+.yui-skin-sam tr.yui-dt-selected td.yui-dt-asc,
+.yui-skin-sam tr.yui-dt-selected td.yui-dt-desc {
+    background-color:#426FD9; /* bright blue selected row */
+    color:#FFF;
+}
+.yui-skin-sam tr.yui-dt-even td.yui-dt-selected,
+.yui-skin-sam tr.yui-dt-odd td.yui-dt-selected {
+    background-color:#446CD7; /* bright blue selected cell */
+    color:#FFF;
+}
+
+/* enable selection in list mode */
+.yui-skin-sam .yui-dt-list th.yui-dt-selected,
+.yui-skin-sam .yui-dt-list th.yui-dt-selected a {
+    background-color:#446CD7; /* bright blue selected cell */
+}
+.yui-skin-sam .yui-dt-list tr.yui-dt-selected td,
+.yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-asc,
+.yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-desc {
+    background-color:#426FD9; /* bright blue selected row */
+    color:#FFF;
+}
+.yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-selected,
+.yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-selected {
+    background-color:#446CD7; /* bright blue selected cell */
+    color:#FFF;
+}
+
+/* pagination */
+.yui-skin-sam .yui-dt-paginator {
+    display:block;margin:6px 0;white-space:nowrap;
+}
+.yui-skin-sam .yui-dt-paginator .yui-dt-first,
+.yui-skin-sam .yui-dt-paginator .yui-dt-last,
+.yui-skin-sam .yui-dt-paginator .yui-dt-selected {
+    padding:2px 6px;
+}
+.yui-skin-sam .yui-dt-paginator a.yui-dt-first,
+.yui-skin-sam .yui-dt-paginator a.yui-dt-last {
+    text-decoration:none;
+}
+.yui-skin-sam .yui-dt-paginator .yui-dt-previous,
+.yui-skin-sam .yui-dt-paginator .yui-dt-next {
+    display:none;
+}
+.yui-skin-sam a.yui-dt-page {
+    border:1px solid #CBCBCB;
+    padding:2px 6px;
+    text-decoration:none;
+    background-color:#fff
+}
+.yui-skin-sam .yui-dt-selected {
+    border:1px solid #fff;
+    background-color:#fff;
+}

Added: branches/wf4ever/public/stylesheets/yui/datatable.css (0 => 3265)


--- branches/wf4ever/public/stylesheets/yui/datatable.css	                        (rev 0)
+++ branches/wf4ever/public/stylesheets/yui/datatable.css	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,8 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+.yui-skin-sam .yui-dt-mask{position:absolute;z-index:9500}.yui-dt-tmp{position:absolute;left:-9000px}.yui-dt-scrollable .yui-dt-bd{overflow:auto}.yui-dt-scrollable .yui-dt-hd{overflow:hidden;position:relative}.yui-dt-scrollable .yui-dt-bd thead tr,.yui-dt-scrollable .yui-dt-bd thead th{position:absolute;left:-1500px}.yui-dt-scrollable tbody{-moz-outline:0}.yui-skin-sam thead .yui-dt-sortable{cursor:pointer}.yui-skin-sam thead .yui-dt-draggable{cursor:move}.yui-dt-coltarget{position:absolute;z-index:999}.yui-dt-hd{zoom:1}th.yui-dt-resizeable .yui-dt-resizerliner{position:relative}.yui-dt-resizer{position:absolute;right:0;bottom:0;height:100%;cursor:e-resize;cursor:col-resize;background-color:#CCC;opacity:0;filter:alpha(opacity=0)}.yui-dt-resizerproxy{visibility:hidden;position:absolute;z-index:9000;background-color:#CCC;opacity:0;filter:alpha(opacity=0)}th.yui-dt-hidden .yui-dt-liner,td.yui-dt-hidden .yui-dt-liner,th.yui-dt-hidden .yui-dt-resizer{display:none}.yui-dt-editor,.yui-dt-editor-shim{position:absolute;z-index:9000}.yui-skin-sam .yui-dt table{margin:0;padding:0;font-family:arial;font-size:inherit;border-collapse:separate;*border-collapse:collapse;border-spacing:0;border:1px solid #7f7f7f}.yui-skin-sam .yui-dt thead{border-spacing:0}.yui-skin-sam .yui-dt caption{color:#000;font-size:85%;font-weight:normal;font-style:italic;line-height:1;padding:1em 0;text-align:center}.yui-skin-sam .yui-dt th{background:#d8d8da url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0}.yui-skin-sam .yui-dt th,.yui-skin-sam .yui-dt th a{font-weight:normal;text-decoration:none;color:#000;vertical-align:bottom}.yui-skin-sam .yui-dt th{margin:0;padding:0;border:0;border-right:1px solid #cbcbcb}.yui-skin-sam .yui-dt tr.yui-dt-first td{border-top:1px solid #7f7f7f}.yui-skin-sam .yui-dt th .yui-dt-liner{white-space:nowrap}.yui-skin-sam .yui-dt-liner{margin:0;padding:0;padding:4px 10px 4px 10px}.yui-skin-sam .yui-dt-coltarget{width:5px;background-color:red}.yui-skin-sam .yui-dt td{margin:0;padding:0;border:0;border-right:1px solid #cbcbcb;text-align:left}.yui-skin-sam .yui-dt-list td{border-right:0}.yui-skin-sam .yui-dt-resizer{width:6px}.yui-skin-sam .yui-dt-mask{background-color:#000;opacity:.25;filter:alpha(opacity=25)}.yui-skin-sam .yui-dt-message{background-color:#FFF}.yui-skin-sam .yui-dt-scrollable table{border:0}.yui-skin-sam .yui-dt-scrollable .yui-dt-hd{border-left:1px solid #7f7f7f;border-top:1px solid #7f7f7f;border-right:1px solid #7f7f7f}.yui-skin-sam .yui-dt-scrollable .yui-dt-bd{border-left:1px solid #7f7f7f;border-bottom:1px solid #7f7f7f;border-right:1px solid #7f7f7f;background-color:#FFF}.yui-skin-sam .yui-dt-scrollable .yui-dt-data tr.yui-dt-last td{border-bottom:1px solid #7f7f7f}.yui-skin-sam th.yui-dt-asc,.yui-skin-sam th.yui-dt-desc{background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -100px}.yui-skin-sam th.yui-dt-sortable .yui-dt-label{margin-right:10px}.yui-skin-sam th.yui-dt-asc .yui-dt-liner{background:url(dt-arrow-up.png) no-repeat right}.yui-skin-sam th.yui-dt-desc .yui-dt-liner{background:url(dt-arrow-dn.png) no-repeat right}tbody .yui-dt-editable{cursor:pointer}.yui-dt-editor{text-align:left;background-color:#f2f2f2;border:1px solid #808080;padding:6px}.yui-dt-editor label{padding-left:4px;padding-right:6px}.yui-dt-editor .yui-dt-button{padding-top:6px;text-align:right}.yui-dt-editor .yui-dt-button button{background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0;border:1px solid #999;width:4em;height:1.8em;margin-left:6px}.yui-dt-editor .yui-dt-button button.yui-dt-default{background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -1400px;background-color:#5584e0;border:1px solid #304369;color:#FFF}.yui-dt-editor .yui-dt-button button:hover{background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -1300px;color:#000}.yui-dt-editor .yui-dt-button button:active{background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -1700px;color:#000}.yui-skin-sam tr.yui-dt-even{background-color:#FFF}.yui-skin-sam tr.yui-dt-odd{background-color:#edf5ff}.yui-skin-sam tr.yui-dt-even td.yui-dt-asc,.yui-skin-sam tr.yui-dt-even td.yui-dt-desc{background-color:#edf5ff}.yui-skin-sam tr.yui-dt-odd td.yui-dt-asc,.yui-skin-sam tr.yui-dt-odd td.yui-dt-desc{background-color:#dbeaff}.yui-skin-sam .yui-dt-list tr.yui-dt-even{background-color:#FFF}.yui-skin-sam .yui-dt-list tr.yui-dt-odd{background-color:#FFF}.yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-asc,.yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-desc{background-color:#edf5ff}.yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-asc,.yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-desc{background-color:#edf5ff}.yui-skin-sam th.yui-dt-highlighted,.yui-skin-sam th.yui-dt-highlighted a{background-color:#b2d2ff}.yui-skin-sam tr.yui-dt-highlighted,.yui-skin-sam tr.yui-dt-highlighted td.yui-dt-asc,.yui-skin-sam tr.yui-dt-highlighted td.yui-dt-desc,.yui-skin-sam tr.yui-dt-even td.yui-dt-highlighted,.yui-skin-sam tr.yui-dt-odd td.yui-dt-highlighted{cursor:pointer;background-color:#b2d2ff}.yui-skin-sam .yui-dt-list th.yui-dt-highlighted,.yui-skin-sam .yui-dt-list th.yui-dt-highlighted a{background-color:#b2d2ff}.yui-skin-sam .yui-dt-list tr.yui-dt-highlighted,.yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-asc,.yui-skin-sam .yui-dt-list tr.yui-dt-highlighted td.yui-dt-desc,.yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-highlighted,.yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-highlighted{cursor:pointer;background-color:#b2d2ff}.yui-skin-sam th.yui-dt-selected,.yui-skin-sam th.yui-dt-selected a{background-color:#446cd7}.yui-skin-sam tr.yui-dt-selected td,.yui-skin-sam tr.yui-dt-selected td.yui-dt-asc,.yui-skin-sam tr.yui-dt-selected td.yui-dt-desc{background-color:#426fd9;color:#FFF}.yui-skin-sam tr.yui-dt-even td.yui-dt-selected,.yui-skin-sam tr.yui-dt-odd td.yui-dt-selected{background-color:#446cd7;color:#FFF}.yui-skin-sam .yui-dt-list th.yui-dt-selected,.yui-skin-sam .yui-dt-list th.yui-dt-selected a{background-color:#446cd7}
+.yui-skin-sam .yui-dt-list tr.yui-dt-selected td,.yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-asc,.yui-skin-sam .yui-dt-list tr.yui-dt-selected td.yui-dt-desc{background-color:#426fd9;color:#FFF}.yui-skin-sam .yui-dt-list tr.yui-dt-even td.yui-dt-selected,.yui-skin-sam .yui-dt-list tr.yui-dt-odd td.yui-dt-selected{background-color:#446cd7;color:#FFF}.yui-skin-sam .yui-dt-paginator{display:block;margin:6px 0;white-space:nowrap}.yui-skin-sam .yui-dt-paginator .yui-dt-first,.yui-skin-sam .yui-dt-paginator .yui-dt-last,.yui-skin-sam .yui-dt-paginator .yui-dt-selected{padding:2px 6px}.yui-skin-sam .yui-dt-paginator a.yui-dt-first,.yui-skin-sam .yui-dt-paginator a.yui-dt-last{text-decoration:none}.yui-skin-sam .yui-dt-paginator .yui-dt-previous,.yui-skin-sam .yui-dt-paginator .yui-dt-next{display:none}.yui-skin-sam a.yui-dt-page{border:1px solid #cbcbcb;padding:2px 6px;text-decoration:none;background-color:#fff}.yui-skin-sam .yui-dt-selected{border:1px solid #fff;background-color:#fff}

Added: branches/wf4ever/public/stylesheets/yui/tabview.css (0 => 3265)


--- branches/wf4ever/public/stylesheets/yui/tabview.css	                        (rev 0)
+++ branches/wf4ever/public/stylesheets/yui/tabview.css	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,8 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+.yui-navset .yui-nav li,.yui-navset .yui-navset-top .yui-nav li,.yui-navset .yui-navset-bottom .yui-nav li{margin:0 .5em 0 0}.yui-navset-left .yui-nav li,.yui-navset-right .yui-nav li{margin:0 0 .5em}.yui-navset .yui-content .yui-hidden{border:0;height:0;width:0;padding:0;position:absolute;left:-999999px;overflow:hidden;visibility:hidden}.yui-navset .yui-navset-left .yui-nav,.yui-navset .yui-navset-right .yui-nav,.yui-navset-left .yui-nav,.yui-navset-right .yui-nav{width:6em}.yui-navset-top .yui-nav,.yui-navset-bottom .yui-nav{width:auto}.yui-navset .yui-navset-left,.yui-navset-left{padding:0 0 0 6em}.yui-navset-right{padding:0 6em 0 0}.yui-navset-top,.yui-navset-bottom{padding:auto}.yui-nav,.yui-nav li{margin:0;padding:0;list-style:none}.yui-navset li em{font-style:normal}.yui-navset{position:relative;zoom:1}.yui-navset .yui-content,.yui-navset .yui-content div{zoom:1}.yui-navset .yui-content:after{content:'';display:block;clear:both}.yui-navset .yui-nav li,.yui-navset .yui-navset-top .yui-nav li,.yui-navset .yui-navset-bottom .yui-nav li{display:inline-block;display:-moz-inline-stack;*display:inline;vertical-align:bottom;cursor:pointer;zoom:1}.yui-navset-left .yui-nav li,.yui-navset-right .yui-nav li{display:block}.yui-navset .yui-nav a{position:relative}.yui-navset .yui-nav li a,.yui-navset-top .yui-nav li a,.yui-navset-bottom .yui-nav li a{display:block;display:inline-block;vertical-align:bottom;zoom:1}.yui-navset-left .yui-nav li a,.yui-navset-right .yui-nav li a{display:block}.yui-navset-bottom .yui-nav li a{vertical-align:text-top}.yui-navset .yui-nav li a em,.yui-navset-top .yui-nav li a em,.yui-navset-bottom .yui-nav li a em{display:block}.yui-navset .yui-navset-left .yui-nav,.yui-navset .yui-navset-right .yui-nav,.yui-navset-left .yui-nav,.yui-navset-right .yui-nav{position:absolute;z-index:1}.yui-navset-top .yui-nav,.yui-navset-bottom .yui-nav{position:static}.yui-navset .yui-navset-left .yui-nav,.yui-navset-left .yui-nav{left:0;right:auto}.yui-navset .yui-navset-right .yui-nav,.yui-navset-right .yui-nav{right:0;left:auto}.yui-skin-sam .yui-navset .yui-nav,.yui-skin-sam .yui-navset .yui-navset-top .yui-nav{border:solid #2647a0;border-width:0 0 5px;zoom:1}.yui-skin-sam .yui-navset .yui-nav li,.yui-skin-sam .yui-navset .yui-navset-top .yui-nav li{margin:0 .16em 0 0;padding:1px 0 0;zoom:1}.yui-skin-sam .yui-navset .yui-nav .selected,.yui-skin-sam .yui-navset .yui-navset-top .yui-nav .selected{margin:0 .16em -1px 0}.yui-skin-sam .yui-navset .yui-nav a,.yui-skin-sam .yui-navset .yui-navset-top .yui-nav a{background:#d8d8d8 url(../../../../assets/skins/sam/sprite.png) repeat-x;border:solid #a3a3a3;border-width:0 1px;color:#000;position:relative;text-decoration:none}.yui-skin-sam .yui-navset .yui-nav a em,.yui-skin-sam .yui-navset .yui-navset-top .yui-nav a em{border:solid #a3a3a3;border-width:1px 0 0;padding:.25em .75em;left:0;right:0;bottom:0;top:-1px;position:relative}.yui-skin-sam .yui-navset .yui-nav .selected a,.yui-skin-sam .yui-navset .yui-nav .selected a:focus,.yui-skin-sam .yui-navset .yui-nav .selected a:hover{background:#2647a0 url(../../../../assets/skins/sam/sprite.png) repeat-x left -1400px;color:#fff}.yui-skin-sam .yui-navset .yui-nav a:hover,.yui-skin-sam .yui-navset .yui-nav a:focus{background:#bfdaff url(../../../../assets/skins/sam/sprite.png) repeat-x left -1300px;outline:0}.yui-skin-sam .yui-navset .yui-nav .selected a em{padding:.35em .75em}.yui-skin-sam .yui-navset .yui-nav .selected a,.yui-skin-sam .yui-navset .yui-nav .selected a em{border-color:#243356}.yui-skin-sam .yui-navset .yui-content{background:#edf5ff}.yui-skin-sam .yui-navset .yui-content,.yui-skin-sam .yui-navset .yui-navset-top .yui-content{border:1px solid #808080;padding:.25em .5em}.yui-skin-sam .yui-navset-left .yui-nav,.yui-skin-sam .yui-navset .yui-navset-left .yui-nav,.yui-skin-sam .yui-navset .yui-navset-right .yui-nav,.yui-skin-sam .yui-navset-right .yui-nav{border-width:0 5px 0 0;Xposition:absolute;top:0;bottom:0}.yui-skin-sam .yui-navset .yui-navset-right .yui-nav,.yui-skin-sam .yui-navset-right .yui-nav{border-width:0 0 0 5px}.yui-skin-sam .yui-navset-left .yui-nav li,.yui-skin-sam .yui-navset .yui-navset-left .yui-nav li,.yui-skin-sam .yui-navset-right .yui-nav li{margin:0 0 .16em;padding:0 0 0 1px}.yui-skin-sam .yui-navset-right .yui-nav li{padding:0 1px 0 0}.yui-skin-sam .yui-navset-left .yui-nav .selected,.yui-skin-sam .yui-navset .yui-navset-left .yui-nav .selected{margin:0 -1px .16em 0}.yui-skin-sam .yui-navset-right .yui-nav .selected{margin:0 0 .16em -1px}.yui-skin-sam .yui-navset-left .yui-nav a,.yui-skin-sam .yui-navset-right .yui-nav a{border-width:1px 0}.yui-skin-sam .yui-navset-left .yui-nav a em,.yui-skin-sam .yui-navset .yui-navset-left .yui-nav a em,.yui-skin-sam .yui-navset-right .yui-nav a em{border-width:0 0 0 1px;padding:.2em .75em;top:auto;left:-1px}.yui-skin-sam .yui-navset-right .yui-nav a em{border-width:0 1px 0 0;left:auto;right:-1px}.yui-skin-sam .yui-navset-left .yui-nav a,.yui-skin-sam .yui-navset-left .yui-nav .selected a,.yui-skin-sam .yui-navset-left .yui-nav a:hover,.yui-skin-sam .yui-navset-right .yui-nav a,.yui-skin-sam .yui-navset-right .yui-nav .selected a,.yui-skin-sam .yui-navset-right .yui-nav a:hover,.yui-skin-sam .yui-navset-bottom .yui-nav a,.yui-skin-sam .yui-navset-bottom .yui-nav .selected a,.yui-skin-sam .yui-navset-bottom .yui-nav a:hover{background-image:none}.yui-skin-sam .yui-navset-left .yui-content{border:1px solid #808080;border-left-color:#243356}.yui-skin-sam .yui-navset-bottom .yui-nav,.yui-skin-sam .yui-navset .yui-navset-bottom .yui-nav{border-width:5px 0 0}.yui-skin-sam .yui-navset .yui-navset-bottom .yui-nav .selected,.yui-skin-sam .yui-navset-bottom .yui-nav .selected{margin:-1px .16em 0 0}.yui-skin-sam .yui-navset .yui-navset-bottom .yui-nav li,.yui-skin-sam .yui-navset-bottom .yui-nav li{padding:0 0 1px 0;vertical-align:top}.yui-skin-sam .yui-navset .yui-navset-bottom .yui-nav a em,.yui-skin-sam .yui-navset-bottom .yui-nav a em{border-width:0 0 1px;top:auto;bottom:-1px}
+.yui-skin-sam .yui-navset-bottom .yui-content,.yui-skin-sam .yui-navset .yui-navset-bottom .yui-content{border:1px solid #808080;border-bottom-color:#243356}

Added: branches/wf4ever/public/stylesheets/yui/treeview-base.css (0 => 3265)


--- branches/wf4ever/public/stylesheets/yui/treeview-base.css	                        (rev 0)
+++ branches/wf4ever/public/stylesheets/yui/treeview-base.css	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,249 @@
+/*
+Copyright (c) 2011, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.9.0
+*/
+/*
+Copyright (c) 2008, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.5.2
+*/
+
+/* the style of the div around each node */
+.ygtvitem { }  
+
+table.ygtvtable {
+    margin-bottom:0; 
+	border:none;
+	border-collapse:collapse;
+}
+
+/*.ygtvitem td {*/
+td.ygtvcell {
+    border: none; 
+	padding: 0;
+}
+a.ygtvspacer {
+	text-decoration:none;
+	outline-style:none;
+	display:block;
+}
+
+
+/* first or middle sibling, no children */
+.ygtvtn {
+    width:18px; height:22px; 
+    background: url(treeview-sprite.gif) 0 -5600px no-repeat; 
+	cursor:pointer ;
+}
+
+/* first or middle sibling, collapsable */
+.ygtvtm {
+    width:18px; height:22px; 
+    cursor:pointer ;
+    background: url(treeview-sprite.gif) 0 -4000px no-repeat; 
+}
+
+/* first or middle sibling, collapsable, hover */
+.ygtvtmh,.ygtvtmhh {
+    width:18px; height:22px; 
+    cursor:pointer ;
+    background: url(treeview-sprite.gif) 0 -4800px no-repeat; 
+}
+
+/* first or middle sibling, expandable */
+.ygtvtp {
+    width:18px; height:22px; 
+    cursor:pointer ;
+    background: url(treeview-sprite.gif) 0 -6400px no-repeat; 
+}
+
+/* first or middle sibling, expandable, hover */
+.ygtvtph ,.ygtvtphh {
+    width:18px; height:22px; 
+    cursor:pointer ;
+    background: url(treeview-sprite.gif) 0 -7200px no-repeat; 
+}
+
+/* last sibling, no children */
+.ygtvln {
+    width:18px; height:22px; 
+    background: url(treeview-sprite.gif) 0 -1600px no-repeat; 
+	cursor:pointer ;
+}
+
+/* Last sibling, collapsable */
+.ygtvlm {
+    width:18px; height:22px; 
+    cursor:pointer ;
+    background: url(treeview-sprite.gif) 0 0px no-repeat; 
+}
+
+/* Last sibling, collapsable, hover */
+.ygtvlmh,.ygtvlmhh {
+    width:18px; height:22px; 
+    cursor:pointer ;
+    background: url(treeview-sprite.gif) 0 -800px no-repeat; 
+}
+
+/* Last sibling, expandable */
+.ygtvlp { 
+    width:18px; height:22px; 
+    cursor:pointer ;
+    background: url(treeview-sprite.gif) 0 -2400px no-repeat; 
+}
+
+/* Last sibling, expandable, hover */
+.ygtvlph,.ygtvlphh { 
+    width:18px; height:22px; cursor:pointer ;
+    background: url(treeview-sprite.gif) 0 -3200px no-repeat; 
+	cursor:pointer ;
+}
+
+/* Loading icon */
+.ygtvloading { 
+    width:18px; height:22px; 
+    background: url(treeview-loading.gif) 0 0 no-repeat; 
+}
+
+/* the style for the empty cells that are used for rendering the depth 
+ * of the node */
+.ygtvdepthcell { 
+    width:18px; height:22px; 
+    background: url(treeview-sprite.gif) 0 -8000px no-repeat; 
+}
+
+.ygtvblankdepthcell { width:18px; height:22px; }
+
+
+/* the style of the div around each node's collection of children */
+.ygtvchildren {  }  
+* html .ygtvchildren { height:2%; }  
+
+/* the style of the text label in ygTextNode */
+.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover { 
+    margin-left:2px;
+    text-decoration: none;
+    background-color: white; /* workaround for IE font smoothing bug */
+	cursor:pointer;
+}
+
+.ygtvcontent {
+	cursor:default;
+}
+
+.ygtvspacer { height: 22px; width: 18px; }
+
+.ygtvfocus {
+	background-color: #c0e0e0;
+	border: none;
+}
+.ygtvfocus .ygtvlabel, .ygtvfocus .ygtvlabel:link, .ygtvfocus .ygtvlabel:visited, .ygtvfocus .ygtvlabel:hover {
+	background-color: #c0e0e0;
+}
+
+.ygtvfocus  a  {
+	outline-style:none;
+}
+
+
+.ygtvok {
+    width:18px; height:22px; 
+    background: url(treeview-sprite.gif) 0 -8800px no-repeat; 
+}
+
+.ygtvok:hover {
+    background: url(treeview-sprite.gif) 0 -8844px no-repeat; 
+}
+	
+.ygtvcancel {
+    width:18px; height:22px; 
+    background: url(treeview-sprite.gif) 0 -8822px no-repeat; 
+}
+
+.ygtvcancel:hover  {
+    background: url(treeview-sprite.gif) 0 -8866px no-repeat; 
+}
+
+.ygtv-label-editor {
+	background-color:#f2f2f2;
+	border: 1px solid silver;
+	position:absolute;
+	display:none;
+	overflow:hidden;
+	margin:auto;
+	z-index:9000;
+}
+
+.ygtv-edit-TextNode  {
+	width: 190px;
+}
+
+.ygtv-edit-TextNode .ygtvcancel, .ygtv-edit-TextNode .ygtvok  {
+	border:none;
+}
+
+.ygtv-edit-TextNode .ygtv-button-container {
+	float: right;
+}
+
+.ygtv-edit-TextNode .ygtv-input  input{
+	width: 140px;
+}
+
+.ygtv-edit-DateNode .ygtvcancel {
+	border:none;
+}
+.ygtv-edit-DateNode .ygtvok  {
+	display:none;
+}
+
+.ygtv-edit-DateNode   .ygtv-button-container {
+	text-align:right;
+	margin:auto;
+}
+
+.ygtv-highlight .ygtv-highlight0 , .ygtv-highlight .ygtv-highlight0 .ygtvlabel{
+}
+
+.ygtv-highlight .ygtv-highlight1  , .ygtv-highlight .ygtv-highlight1 .ygtvlabel{
+	background-color:blue;
+	color:white;
+}
+
+.ygtv-highlight .ygtv-highlight2  , .ygtv-highlight .ygtv-highlight2 .ygtvlabel {
+	background-color:silver;
+}
+
+.ygtv-highlight .ygtv-highlight0 .ygtvfocus .ygtvlabel,
+.ygtv-highlight .ygtv-highlight1 .ygtvfocus .ygtvlabel,
+.ygtv-highlight .ygtv-highlight2 .ygtvfocus .ygtvlabel {
+	background-color: #c0e0e0;
+}
+
+.ygtv-highlight .ygtvcontent {
+	padding-right: 1em;
+}
+
+.ygtv-checkbox .ygtv-highlight0 .ygtvcontent {
+	padding-left:1em;
+	background:url(check0.gif) no-repeat;
+}
+
+.ygtv-checkbox .ygtv-highlight0 .ygtvfocus.ygtvcontent,
+.ygtv-checkbox .ygtv-highlight1 .ygtvfocus.ygtvcontent ,
+.ygtv-checkbox .ygtv-highlight2 .ygtvfocus.ygtvcontent  {
+	background-color:#c0e0e0;
+}
+
+.ygtv-checkbox .ygtv-highlight1 .ygtvcontent {
+	padding-left:1em;
+	background:url(check1.gif) no-repeat;
+}
+
+.ygtv-checkbox .ygtv-highlight2 .ygtvcontent{
+	padding-left:1em;
+	background:url(check2.gif) no-repeat;
+}

Added: branches/wf4ever/public/stylesheets/yui/treeview-folders.css (0 => 3265)


--- branches/wf4ever/public/stylesheets/yui/treeview-folders.css	                        (rev 0)
+++ branches/wf4ever/public/stylesheets/yui/treeview-folders.css	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,64 @@
+/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
+
+/* first or middle sibling, no children */
+.ygtvtn { background: url(yui/folders/tn.gif) 0 0 no-repeat; width:17px; height:22px; }
+
+/* first or middle sibling, collapsable */
+.ygtvtm { background: url(yui/folders/tm.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
+
+/* first or middle sibling, collapsable, hover */
+.ygtvtmh { background: url(yui/folders/tmh.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
+
+/* first or middle sibling, expandable */
+.ygtvtp { background: url(yui/folders/tp.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
+
+/* first or middle sibling, expandable, hover */
+.ygtvtph { background: url(yui/folders/tph.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
+
+/* last sibling, no children */
+.ygtvln { background: url(yui/folders/ln.gif) 0 0 no-repeat; width:17px; height:22px; }
+
+/* Last sibling, collapsable */
+.ygtvlm { background: url(yui/folders/lm.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
+
+/* Last sibling, collapsable, hover */
+.ygtvlmh { background: url(yui/folders/lmh.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
+
+/* Last sibling, expandable */
+.ygtvlp { background: url(yui/folders/lp.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
+
+/* Last sibling, expandable, hover */
+.ygtvlph { background: url(yui/folders/lph.gif) 0 0 no-repeat; width:34px; height:22px; cursor:pointer }
+
+/* Loading icon */
+.ygtvloading { background: url(yui/folders/loading.gif) 0 0 no-repeat; width:16px; height:22px; }
+
+/* the style for the empty cells that are used for rendering the depth 
+ * of the node */
+.ygtvdepthcell { background: url(yui/folders/vline.gif) 0 0 no-repeat; width:17px; height:22px; }
+
+.ygtvblankdepthcell { width:17px; height:22px; }
+
+/* the style of the div around each node */
+.ygtvitem { }  
+
+.ygtvitem  table{
+    margin-bottom:0;
+}
+.ygtvitem  td {
+    border:none;padding:0;
+} 
+
+
+
+/* the style of the div around each node's collection of children */
+.ygtvchildren { }  
+* html .ygtvchildren { height:1%; }  
+
+/* the style of the text label in ygTextNode */
+.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover { 
+	margin-left:2px;
+	text-decoration: none;
+}
+
+

Added: branches/wf4ever/public/stylesheets/yui/yui_override.css (0 => 3265)


--- branches/wf4ever/public/stylesheets/yui/yui_override.css	                        (rev 0)
+++ branches/wf4ever/public/stylesheets/yui/yui_override.css	2012-12-14 16:56:06 UTC (rev 3265)
@@ -0,0 +1,14 @@
+//tree
+.ygtvcontent span {
+  display: block;
+  line-height: 22px;
+}
+
+.ygtvcontent span:hover {
+  text-decoration: underline;
+  cursor: pointer;
+}
+
+table.ygtvtable {
+  width: 100%;
+}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]