[fusion-commits] r2249 - in trunk: . common/php layers layers/Generic layers/MapGuide layers/MapGuide/php layers/MapServer layers/MapServer/php lib templates/mapguide/standard templates/mapguide/standard/themes/crispin templates/mapguide/standard/themes/crispin/images templates/mapguide/standard/themes/delicious templates/mapguide/standard/themes/delicious/images templates/mapserver/standard templates/mapserver/standard/themes/crispin templates/mapserver/standard/themes/crispin/images templates/mapserver/standard/themes/delicious templates/mapserver/standard/themes/delicious/images widgets widgets/AddWMSLayer widgets/CursorPosition widgets/EditableScale widgets/Legend widgets/SelectionInfo widgets/ViewSize

svn_fusion at osgeo.org svn_fusion at osgeo.org
Tue Oct 19 10:31:05 EDT 2010


Author: madair
Date: 2010-10-19 07:31:05 -0700 (Tue, 19 Oct 2010)
New Revision: 2249

Added:
   trunk/layers/MapServer/php/GetAttributes.php
   trunk/layers/MapServer/php/SelectionCSV.php
   trunk/lib/OLpatch.js
   trunk/templates/mapguide/standard/themes/crispin/images/delete.gif
   trunk/templates/mapguide/standard/themes/crispin/images/error.png
   trunk/templates/mapguide/standard/themes/crispin/images/green_tick.png
   trunk/templates/mapguide/standard/themes/crispin/images/icons.png
   trunk/templates/mapguide/standard/themes/crispin/images/listitem.png
   trunk/templates/mapguide/standard/themes/crispin/images/menuitem.png
   trunk/templates/mapguide/standard/themes/crispin/images/notice.png
   trunk/templates/mapguide/standard/themes/crispin/images/notice_error.png
   trunk/templates/mapguide/standard/themes/crispin/images/notice_success.png
   trunk/templates/mapguide/standard/themes/crispin/images/notice_warning.png
   trunk/templates/mapguide/standard/themes/delicious/images/delete.gif
   trunk/templates/mapguide/standard/themes/delicious/images/error.png
   trunk/templates/mapguide/standard/themes/delicious/images/green_tick.png
   trunk/templates/mapguide/standard/themes/delicious/images/icons.png
   trunk/templates/mapguide/standard/themes/delicious/images/listitem.png
   trunk/templates/mapguide/standard/themes/delicious/images/menuitem.png
   trunk/templates/mapguide/standard/themes/delicious/images/notice.png
   trunk/templates/mapguide/standard/themes/delicious/images/notice_error.png
   trunk/templates/mapguide/standard/themes/delicious/images/notice_success.png
   trunk/templates/mapguide/standard/themes/delicious/images/notice_warning.png
   trunk/templates/mapserver/standard/themes/crispin/images/delete.gif
   trunk/templates/mapserver/standard/themes/crispin/images/error.png
   trunk/templates/mapserver/standard/themes/crispin/images/green_tick.png
   trunk/templates/mapserver/standard/themes/crispin/images/icons.png
   trunk/templates/mapserver/standard/themes/crispin/images/listitem.png
   trunk/templates/mapserver/standard/themes/crispin/images/menuitem.png
   trunk/templates/mapserver/standard/themes/crispin/images/notice.png
   trunk/templates/mapserver/standard/themes/crispin/images/notice_error.png
   trunk/templates/mapserver/standard/themes/crispin/images/notice_success.png
   trunk/templates/mapserver/standard/themes/crispin/images/notice_warning.png
   trunk/widgets/AddWMSLayer.js
   trunk/widgets/AddWMSLayer/
   trunk/widgets/AddWMSLayer/AddWMSLayer.css
   trunk/widgets/AddWMSLayer/AddWMSLayer.html
   trunk/widgets/AddWMSLayer/AddWMSLayer.php
   trunk/widgets/AddWMSLayer/wms-parser.php
   trunk/widgets/CursorPosition/
   trunk/widgets/CursorPosition/CursorPosition.css
   trunk/widgets/GetFeatureInfo.js
   trunk/widgets/MapMetadata.js
   trunk/widgets/SaveMapImage.js
   trunk/widgets/SelectAttribute.js
   trunk/widgets/SelectionInfo/
   trunk/widgets/SelectionInfo/SelectionInfo.css
   trunk/widgets/ViewSize/
   trunk/widgets/ViewSize/ViewSize.css
Removed:
   trunk/widgets/AddWMSLayer/AddWMSLayer.css
   trunk/widgets/AddWMSLayer/AddWMSLayer.html
   trunk/widgets/AddWMSLayer/AddWMSLayer.php
   trunk/widgets/AddWMSLayer/wms-parser.php
   trunk/widgets/CursorPosition/CursorPosition.css
   trunk/widgets/SelectionInfo/SelectionInfo.css
   trunk/widgets/ViewSize/ViewSize.css
Modified:
   trunk/
   trunk/common/php/Utilities.php
   trunk/config_dist.json
   trunk/layers/Generic/Generic.js
   trunk/layers/Layers.js
   trunk/layers/MapGuide/
   trunk/layers/MapGuide/MapGuide.js
   trunk/layers/MapGuide/php/LoadMap.php
   trunk/layers/MapServer/
   trunk/layers/MapServer/MapServer.js
   trunk/layers/MapServer/php/Common.php
   trunk/layers/MapServer/php/CreateSession.php
   trunk/layers/MapServer/php/LegendIcon.php
   trunk/layers/MapServer/php/LoadMap.php
   trunk/layers/MapServer/php/LoadScaleRanges.php
   trunk/layers/MapServer/php/Query.php
   trunk/layers/MapServer/php/RestoreState.php
   trunk/layers/MapServer/php/Selection.php
   trunk/layers/MapServer/php/Session.php
   trunk/lib/ApplicationDefinition.js
   trunk/lib/Map.js
   trunk/lib/Widget.js
   trunk/lib/fusion.js
   trunk/lib/jxlib.uncompressed.js
   trunk/templates/mapguide/standard/index.html
   trunk/templates/mapguide/standard/themes/crispin/ie6.css
   trunk/templates/mapguide/standard/themes/crispin/ie7.css
   trunk/templates/mapguide/standard/themes/crispin/images/dialog_chrome.png
   trunk/templates/mapguide/standard/themes/crispin/images/emblems.png
   trunk/templates/mapguide/standard/themes/crispin/images/flyout_chrome.png
   trunk/templates/mapguide/standard/themes/crispin/jxtheme.css
   trunk/templates/mapguide/standard/themes/crispin/jxtheme.uncompressed.css
   trunk/templates/mapguide/standard/themes/delicious/ie6.css
   trunk/templates/mapguide/standard/themes/delicious/ie7.css
   trunk/templates/mapguide/standard/themes/delicious/images/dialog_chrome.png
   trunk/templates/mapguide/standard/themes/delicious/images/emblems.png
   trunk/templates/mapguide/standard/themes/delicious/images/flyout_chrome.png
   trunk/templates/mapguide/standard/themes/delicious/jxtheme.css
   trunk/templates/mapguide/standard/themes/delicious/jxtheme.uncompressed.css
   trunk/templates/mapserver/standard/ApplicationDefinition.xml
   trunk/templates/mapserver/standard/index.html
   trunk/templates/mapserver/standard/themes/crispin/ie6.css
   trunk/templates/mapserver/standard/themes/crispin/ie7.css
   trunk/templates/mapserver/standard/themes/crispin/images/button_combo.png
   trunk/templates/mapserver/standard/themes/crispin/images/dialog_chrome.png
   trunk/templates/mapserver/standard/themes/crispin/images/emblems.png
   trunk/templates/mapserver/standard/themes/crispin/images/flyout_chrome.png
   trunk/templates/mapserver/standard/themes/crispin/images/panel_controls.png
   trunk/templates/mapserver/standard/themes/crispin/jxtheme.css
   trunk/templates/mapserver/standard/themes/crispin/jxtheme.uncompressed.css
   trunk/templates/mapserver/standard/themes/delicious/ie6.css
   trunk/templates/mapserver/standard/themes/delicious/ie7.css
   trunk/templates/mapserver/standard/themes/delicious/images/button_combo.png
   trunk/templates/mapserver/standard/themes/delicious/images/dialog_chrome.png
   trunk/templates/mapserver/standard/themes/delicious/images/emblems.png
   trunk/templates/mapserver/standard/themes/delicious/images/flyout_chrome.png
   trunk/templates/mapserver/standard/themes/delicious/images/panel_controls.png
   trunk/templates/mapserver/standard/themes/delicious/images/tab_bottom.png
   trunk/templates/mapserver/standard/themes/delicious/images/tab_close.png
   trunk/templates/mapserver/standard/themes/delicious/images/tab_left.png
   trunk/templates/mapserver/standard/themes/delicious/images/tab_right.png
   trunk/templates/mapserver/standard/themes/delicious/images/tab_top.png
   trunk/templates/mapserver/standard/themes/delicious/jxtheme.css
   trunk/templates/mapserver/standard/themes/delicious/jxtheme.uncompressed.css
   trunk/widgets/ActivityIndicator.js
   trunk/widgets/CursorPosition.js
   trunk/widgets/EditableScale/EditableScale.css
   trunk/widgets/InvokeURL.js
   trunk/widgets/LayerManager.js
   trunk/widgets/Legend.js
   trunk/widgets/Legend/Legend.css
   trunk/widgets/MapMenu.js
   trunk/widgets/ScalebarDual.js
   trunk/widgets/Select.js
   trunk/widgets/SelectPolygon.js
   trunk/widgets/SelectRadius.js
   trunk/widgets/SelectionInfo.js
   trunk/widgets/SelectionPanel.js
   trunk/widgets/TaskPane.js
   trunk/widgets/ViewSize.js
Log:
merge of the fusion/sandbox/jxlib-3.0 branch back into trunk - testing required report bugs in Trac against version='SVN' and milestone 2.3


Property changes on: trunk
___________________________________________________________________
Added: svn:mergeinfo
   + /sandbox/jxlib-3.0:1957-2248

Modified: trunk/common/php/Utilities.php
===================================================================
--- trunk/common/php/Utilities.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/common/php/Utilities.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -25,7 +25,8 @@
  */
 
 /* recursively convert a variable to its json representation */
-function var2json($var) {
+if (!function_exists('var2json')) {
+  function var2json($var) {
     if (function_exists('json_encode')) {
         $result = json_encode($var);
     } else {
@@ -61,8 +62,10 @@
 
     return utf8_encode($result);
 }
+}
 
-function xml2json($domElement) {
+if (!function_exists('xml2json')) {
+  function xml2json($domElement) {
     $result = '';
     if ($domElement->nodeType == XML_COMMENT_NODE) {
         return '';
@@ -166,6 +169,7 @@
     }
     return $result;
 }
+}
 
 /**
  * this function determines if a path represents an absolute path and returns
@@ -173,7 +177,8 @@
  * @param szPath string the path to test
  * @result boolean true if the path is absolute, false otherwise
  */
-function isAbsolutePath( $szPath ) {
+if (!function_exists('isAbsolutePath')) {
+  function isAbsolutePath( $szPath ) {
     if ($szPath == "") { return false; }
     if ($szPath[0] == "/" || preg_match('/^(\w:)/', $szPath)) {
         return true;
@@ -181,8 +186,8 @@
         return false;
     }
 }
+}
 
-
 /**
  * This function translate $szDestPath (relative or absolute)
  * to a absolute path based on $szOrigPath.
@@ -190,7 +195,8 @@
  * @param $szDestPath string Destination path to translate
  * @param $szOrigPath string Reference path
  */
-function resolvePath2 ($szDestPath, $szOrigPath) {
+if (!function_exists('resolvePath2')) {
+  function resolvePath2 ($szDestPath, $szOrigPath) {
     // If one or other path is missing or absolute, return it.
     if ($szDestPath == "") { return $szOrigPath; }
     if ($szOrigPath == "") { return $szDestPath; }
@@ -229,6 +235,7 @@
     //Rest of the function
     return $szPrefix.$szPath;
 }
+}
 
 /**
  * Recursive ereg_replace
@@ -239,7 +246,8 @@
  *
  * @return string szString with the matching result replaced.
  */
-function iterate_ereg_replace ( $szPattern, $szReplacement, $szString) {
+if (!function_exists('iterate_ereg_replace')) {
+  function iterate_ereg_replace ( $szPattern, $szReplacement, $szString) {
     $szResult = $szString;
     do {
         $szString = $szResult;
@@ -248,8 +256,11 @@
 
     return $szResult;
 }
+}
 
-function arguments($argv) {
+
+if (!function_exists('arguments')) {
+  function arguments($argv) {
     $_ARG = array();
     foreach ($argv as $arg) {
         if (ereg('--[a-zA-Z0-9]*=.*',$arg)) {
@@ -266,8 +277,10 @@
     }
     return $_ARG;
 }
+}
 
-function loadFusionConfig() {
+if (!function_exists('loadFusionConfig')) {
+  function loadFusionConfig() {
     /* json decode only in PHP 5.2 and later */
     if (function_exists('json_decode')) {
         $configFile = realpath(dirname(__FILE__)."/../../config.json");
@@ -291,5 +304,6 @@
         }
     }
 }
+}
 
 ?>


Property changes on: trunk/config_dist.json
___________________________________________________________________
Deleted: svn:mergeinfo
   - 

Modified: trunk/layers/Generic/Generic.js
===================================================================
--- trunk/layers/Generic/Generic.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/Generic/Generic.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -39,6 +39,10 @@
     initialize: function(map, mapTag, isMapWidgetLayer) {
         // console.log('Generic.initialize');
         Fusion.Layers.prototype.initialize.apply(this, arguments);
+        
+        if (mapTag.extension.LayerType) {
+          this.layerType = mapTag.extension.LayerType[0];
+        }
 
         this._sMapname = mapTag.layerOptions['name'] ? mapTag.layerOptions['name'] : 'generic layer';
         
@@ -47,7 +51,7 @@
         var scaleRange = new Fusion.Layers.ScaleRange({
             minScale: this.minScale,
             maxScale: this.maxScale}, 
-            Fusion.Constant.LAYER_RASTER_TYPE);
+            Fusion.Constant.LAYER_RASTER_TYPE,{label:this._sMapname});
         
         rootOpts = {
           layerName: this._sMapname,

Modified: trunk/layers/Layers.js
===================================================================
--- trunk/layers/Layers.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/Layers.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -33,6 +33,8 @@
 Fusion.Event.MAP_LAYER_ORDER_CHANGED = Fusion.Event.lastEventId++;
 Fusion.Event.LAYER_LOADED = Fusion.Event.lastEventId++;
 Fusion.Event.LAYER_LOADING = Fusion.Event.lastEventId++;
+Fusion.Event.LAYER_START_EDIT = Fusion.Event.lastEventId++;
+Fusion.Event.LAYER_STOP_EDIT = Fusion.Event.lastEventId++;
 
 Fusion.Layers = OpenLayers.Class(Fusion.Lib.EventMgr, {
     bSingleTile: null,
@@ -48,6 +50,10 @@
     noCache: false,
     _sMapTitle: null,
     _sMapname: null,
+    supports: {
+      query: false,
+      edit: false
+    },
 
     initialize: function(map, mapTag, isMapWidgetLayer) {
         // console.log('Fusion.Layers.initialize');
@@ -59,6 +65,8 @@
         this.registerEventID(Fusion.Event.LAYER_LOADING);
         this.registerEventID(Fusion.Event.MAP_LAYER_ORDER_CHANGED);
         this.registerEventID(Fusion.Event.LAYER_PROPERTY_CHANGED);
+        this.registerEventID(Fusion.Event.LAYER_START_EDIT);
+        this.registerEventID(Fusion.Event.LAYER_STOP_EDIT);
         
         this.mapWidget = map;
         this.oSelection = null;
@@ -87,7 +95,7 @@
      */
 
     loadScaleRanges: function(userFunc) {
-      userFunc();
+      if (userFunc) userFunc();
     },
 
 
@@ -98,6 +106,14 @@
     getMapTitle: function() {
         return this._sMapTitle;
     },
+    
+    getMetadata: function(key) {
+        if (this.metadata && typeof this.metadata[key] != 'undefined') {
+            return this.metadata[key];
+        } else {
+            return '';
+        }
+    },
 
     /**
      * Function: isMapLoaded
@@ -288,7 +304,11 @@
         return null;
     },
     
-    getMapTip: function(mapTipWidget) {}
+    getMapTip: function(mapTipWidget) {},
+    
+    startEditing: function() { },
+
+    stopEditing: function() { }
 });
 
 /***************************************************************************
@@ -416,6 +436,17 @@
             }
         }
         return null;
+    },
+
+    deleteLayer: function(uniqueId) {
+        for (var i=0; i<this.layers.length; i++) {
+            if (this.layers[i]['uniqueId'] == uniqueId) {
+                this.layers.splice(i,1);
+            }
+        }
+        for (var i=0; i<this.groups.length; i++) {
+            this.groups[i].deleteLayer(uniqueId);
+        }
     }
 
 });
@@ -446,12 +477,13 @@
         this.actuallyVisible = o.actuallyVisible;
         this.statusDefault = o.statusdefault;
         this.editable = o.editable;
+        this.isEditing = false;
         this.visible = o.visible;
         this.initiallyVisible = o.visible;
         this.selectable = o.selectable;
+        this.metadata = o.metadata?o.metadata:{};
         this.isBaseMapLayer = o.isBaseMapLayer;
 
-
         //determine the layer type so that the correct icon can be displayed in the legend
         this.layerType = null;
         if (this.supportsType(Fusion.Constant.LAYER_RASTER_TYPE)) {   //raster layers
@@ -529,6 +561,20 @@
     set: function(property, value) {
         this[property] = value;
         this.oMap.triggerEvent(Fusion.Event.LAYER_PROPERTY_CHANGED, this);
+    },
+    
+    startEditing: function() {
+      if (this.oMap.supports.edit && this.editable && !this.isEditing) {
+        this.isEditing = true;
+        this.oMap.triggerEvent(Fusion.Event.LAYER_START_EDIT, this);
+      }
+    },
+    
+    stopEditing: function() {
+      if (this.oMap.supports.edit && this.editable && this.isEditing) {
+        this.isEditing = false;
+        this.oMap.triggerEvent(Fusion.Event.LAYER_STOP_EDIT, this);
+      }
     }
 
 });
@@ -549,7 +595,7 @@
         }
         this.styles = [];
         if (!o.styles) {
-          var styleItem = new Fusion.Layers.StyleItem({legendLabel:'DWF'}, layerType, iconOpt);
+          var styleItem = new Fusion.Layers.StyleItem({legendLabel:iconOpt.label}, layerType, iconOpt);
           this.styles.push(styleItem);
           return;
         }


Property changes on: trunk/layers/MapGuide
___________________________________________________________________
Deleted: svn:mergeinfo
   - 

Modified: trunk/layers/MapGuide/MapGuide.js
===================================================================
--- trunk/layers/MapGuide/MapGuide.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapGuide/MapGuide.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -45,6 +45,10 @@
     useAsyncOverlay: false,
     defaultFormat: 'PNG',
     oLayerOL2: false,   //a layer object for tiled maps that also contains dynamic layers
+    supports: {
+      query: true,
+      edit: true
+    },
 
     initialize: function(map, mapTag, isMapWidgetLayer) {
         // console.log('MapGuide.initialize');
@@ -404,39 +408,34 @@
      * used by the legend widget.
      */
 
-    loadScaleRanges: function(userFunc) {
+    loadScaleRanges: function() {
         var sl = Fusion.getScriptLanguage();
         var loadmapScript = 'layers/' + this.arch + '/' + sl  + '/LoadScaleRanges.' + sl;
 
         var sessionid = this.getSessionID();
 
         var params = {'mapname': this._sMapname, "session": sessionid};
-        var options = {onSuccess: OpenLayers.Function.bind(this.scaleRangesLoaded,this, userFunc),
+        var options = {onSuccess: OpenLayers.Function.bind(this.scaleRangesLoaded,this),
                        parameters:params};
         Fusion.ajaxRequest(loadmapScript, options);
     },
 
-    scaleRangesLoaded: function(userFunc, r)
+    scaleRangesLoaded: function(r)
     {
-        if (r.status == 200)
-        {
+        if (r.status == 200) {
             var o;
             eval('o='+r.responseText);
-            if (o.layers && o.layers.length > 0)
-            {
+            if (o.layers && o.layers.length > 0) {
                 var iconOpt = {
                     url: o.icons_url || null,
                     width: o.icons_width || 16,
                     height: o.icons_height || 16
                 };
-                for (var i=0; i<o.layers.length; i++)
-                {
+                for (var i=0; i<o.layers.length; i++)  {
                     var oLayer = this.getLayerById(o.layers[i].uniqueId);
-                    if (oLayer)
-                    {
+                    if (oLayer) {
                         oLayer.scaleRanges = [];
-                        for (var j=0; j<o.layers[i].scaleRanges.length; j++)
-                        {
+                        for (var j=0; j<o.layers[i].scaleRanges.length; j++) {
                             var scaleRange = new Fusion.Layers.ScaleRange(o.layers[i].scaleRanges[j],
                                                                                  oLayer.layerType, iconOpt);
                             oLayer.scaleRanges.push(scaleRange);
@@ -444,10 +443,10 @@
                     }
                 }
             }
-
-            userFunc();
+            this.mapWidget.triggerEvent(Fusion.Event.MAP_SCALE_RANGE_LOADED);
         }
     },
+    
 //TBD: this function not yet converted for OL
     mapReloaded: function(oldLayers,r) {
         if (r.status == 200) {

Modified: trunk/layers/MapGuide/php/LoadMap.php
===================================================================
--- trunk/layers/MapGuide/php/LoadMap.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapGuide/php/LoadMap.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -110,6 +110,7 @@
 
     $mapObj->sessionId = $sessionID;
     $mapObj->mapId = $mapid;
+    $mapObj->metadata = NULL;
     $mapObj->metersPerUnit = $metersPerUnit;
     $mapObj->wkt = $srs;
     $mapObj->epsg = $epsgCode;


Property changes on: trunk/layers/MapServer
___________________________________________________________________
Deleted: svn:mergeinfo
   - 

Modified: trunk/layers/MapServer/MapServer.js
===================================================================
--- trunk/layers/MapServer/MapServer.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/MapServer.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -47,6 +47,10 @@
 
     //the map file
     sMapFile: null,
+    supports: {
+      query: true,
+      edit: true
+    },
 
     initialize: function(map, mapTag, isMapWidgetLayer) {
         //console.log('Fusion.Layers.MapServer.initialize');
@@ -174,28 +178,12 @@
 
     mapSessionCreated: function() {
         // restore the mapfile from a saved state.
-        if (this.bRestoreMapState === true && this.oRestoredState.loadmap ) {
+        if(this.bRestoreMapState === true && this.oRestoredState.loadmap ){
             this.loadMap(this.oRestoredState.loadmap);
-        } else if (this.sMapFile != '') {
-          var options = {};
-          if (this.bIsMapWidgetLayer) {
-            var showlayers = Fusion.getQueryParam('showlayers');
-            Fusion.queryParams['showlayers'] = null;
-            var hidelayers = Fusion.getQueryParam('hidelayers');
-            Fusion.queryParams['hidelayers'] = null;
-            var showgroups = Fusion.getQueryParam('showgroups');
-            Fusion.queryParams['showgroups'] = null;
-            var hidegroups = Fusion.getQueryParam('hidegroups');
-            Fusion.queryParams['hidegroups'] = null;
-            var options = {
-              showlayers: showlayers == '' ? [] : showlayers.split(','),
-              hidelayers: hidelayers == '' ? [] : hidelayers.split(','),
-              showgroups: showgroups == '' ? [] : showgroups.split(','),
-              hidegroups: hidegroups == '' ? [] : hidegroups.split(',')
-            };
-          }
-          this.loadMap(this.sMapFile, options);
         }
+        else if (this.sMapFile != '') {
+            this.loadMap(this.sMapFile);
+        }
         window.setInterval(OpenLayers.Function.bind(this.pingServer, this), 
                                                 this.keepAliveInterval * 1000);
     },
@@ -231,11 +219,8 @@
 
         options = options || {};
 
-        this.aShowLayers = options.showlayers || [];
-        this.aHideLayers = options.hidelayers || [];
-        this.aShowGroups = options.showgroups || [];
-        this.aHideGroups = options.showgroups || [];
-        this.aVisibleLayers = [];
+        this.aVisibleLayers = options.showlayers || [];
+        this.aVisibleGroups = options.showgroups || [];
         this.aLayers = [];
 
         this.oSelection = null;
@@ -292,27 +277,8 @@
             var minScale = 1.0e10;
             var maxScale = 0;
             for (var i=0; i<this.aLayers.length; i++) {
-              var testLayer = this.aLayers[i].layerName;
               if (this.aLayers[i].visible) {
-                var layerName = testLayer;
-                for (var j=0; j<this.aHideLayers.length; ++j) {
-                  if (layerName == this.aHideLayers[j]) {
-                    layerName = null;
-                    this.aLayers[i].hide(true);
-                    break;
-                  }
-                }
-                if (layerName) this.aVisibleLayers.push(layerName);
-              } else {
-                var layerName = null;
-                for (var j=0; j<this.aShowLayers.length; ++j) {
-                  if (testLayer == this.aShowLayers[j]) {
-                    layerName = testLayer;
-                    this.aLayers[i].show(true);
-                    break;
-                  }
-                }
-                if (layerName) this.aVisibleLayers.push(layerName);
+                  this.aVisibleLayers.push(this.aLayers[i].layerName);
               }
       				minScale = Math.min(minScale, this.aLayers[i].minScale);
       				maxScale = Math.max(maxScale, this.aLayers[i].maxScale);
@@ -408,19 +374,19 @@
      * used by the legend widget.
      */
         
-    loadScaleRanges: function(userFunc) {
+    loadScaleRanges: function() {
         var sl = Fusion.getScriptLanguage();
         var loadmapScript = 'layers/' + this.arch + '/' + sl  + '/LoadScaleRanges.' + sl;
         
         var sessionid = this.getSessionID();
         
         var params = {'mapname': this._sMapname, "session": this.getSessionID()};
-        var options = {onSuccess: OpenLayers.Function.bind(this.scaleRangesLoaded,this, userFunc), 
+        var options = {onSuccess: OpenLayers.Function.bind(this.scaleRangesLoaded, this), 
                        parameters:params};
         Fusion.ajaxRequest(loadmapScript, options);
     },
 
-    scaleRangesLoaded: function(userFunc, r) {
+    scaleRangesLoaded: function(r) {
         if (r.status == 200) {
             var o;
             eval('o='+r.responseText);
@@ -443,8 +409,7 @@
                     }
                 }
             }
-
-            userFunc();
+            this.mapWidget.triggerEvent(Fusion.Event.MAP_SCALE_RANGE_LOADED);
         }
     },
 
@@ -490,17 +455,18 @@
         //console.log("mapLayersReset");
         var o;
         eval('o='+r.responseText);
-        if (o.success) {
-            var layerCopy = $A(this.aLayers);
-            var nLayers = layerCopy.length -1;
-            //Mapserver has list of layers reversed from MapGuide
-            aLayerIndex.reverse();
-
-            this.aLayers = [];
-            this.aVisibleLayers = [];
-
-            for (var i=0; i<aLayerIndex.length; ++i) {
-                this.aLayers.push( layerCopy[aLayerIndex[i] ] );
+  			if (o.success) {
+  				var layerCopy = $A(this.aLayers);
+                var nLayers = layerCopy.length -1;
+          
+          //Mapserver has list of layers reversed from MapGuide
+          //aLayerIndex.reverse();
+    
+  				this.aLayers = [];
+  				this.aVisibleLayers = [];
+          for (var i=0; i<aLayerIndex.length; ++i) {
+                //this.aLayers.push( layerCopy[ nLayers - aLayerIndex[i] ] );
+                this.aLayers.push( layerCopy[ aLayerIndex[i] ] );
                 if (this.aLayers[i].visible) {
                     this.aVisibleLayers.push(this.aLayers[i].layerName);
                 }
@@ -537,6 +503,20 @@
         }
     },
 
+    parseLayers: function() {
+        //this.layerRoot.clear();
+        for (var i=0; i<this.aLayers.length; i++) {
+            var layer = this.aLayers[i];
+            var parent;
+            if (layer.parentGroup != '') {
+                parent = this.layerRoot.findGroup(layer.parentGroup.name);
+            } else {
+                parent = this.layerRoot;
+            }
+            parent.addLayer(layer, this.bLayersReversed);
+        }
+    },
+
     getScale: function() {
         return this.mapWidget.getScale();
     },
@@ -567,23 +547,19 @@
         this.oLayerOL.mergeNewParams(params);
     },
 
-    showLayer: function( sLayer, noDraw ) {
+    showLayer: function( sLayer ) {
         this.aVisibleLayers.push(sLayer.layerName);
-        if (!noDraw) {
-          this.drawMap();
-        }
+        this.drawMap();
     },
 
-    hideLayer: function( sLayer, noDraw ) {
+    hideLayer: function( sLayer ) {
         for (var i=0; i<this.aLayers.length; i++) {
             if (this.aVisibleLayers[i] == sLayer.layerName) {
                 this.aVisibleLayers.splice(i,1);
                 break;
             }
         }
-        if (!noDraw) {
-          this.drawMap();
-        }
+        this.drawMap();
     },
 
     showGroup: function( group, noDraw ) {
@@ -842,6 +818,7 @@
         };
         if (options.filter) {
             params.filter = options.filter;
+            params.filterItem = options.filterItem;
         }
         if (options.extendSelection) {
             params.extendselection = true;
@@ -863,14 +840,44 @@
         Fusion.ajaxRequest(s, params);
   },
 
+    getMetadata: function(key) {
+        if (this.metadata && typeof this.metadata[key] != 'undefined') {
+            return this.metadata[key];
+        } else {
+            return '';
+        }
+    },
+    
     getGroupInfoUrl: function(groupName) {
       return null;
    },
 
     getLayerInfoUrl: function(layerName) {
-      return null;
+      var layer = this.getLayerByName(layerName);
+      var layerUrl = null;
+      if (layer) {
+        var metadata = layer.metadata;
+        if (metadata && metadata["LAYERINFOURL"]) {
+          layerUrl = metadata["LAYERINFOURL"];
+        }
+      }
+      return layerUrl;
   },
 
+    getLayerByName: function(name)
+    {
+        var oLayer = null;
+        for (var i=0; i<this.aLayers.length; i++)
+        {
+            if (this.aLayers[i].layerName == name)
+            {
+                oLayer = this.aLayers[i];
+                break;
+            }
+        }
+        return oLayer;
+    },           
+
     getLayerById: function(id)
     {
         var oLayer = null;
@@ -885,15 +892,7 @@
         return oLayer;
     },           
 
-    getMetadata: function(key) {
-        if (typeof this.metadata[key] != 'undefined') {
-            return this.metadata[key];
-        } else {
-            return '';
-        }
-    },
-
-    getLegendImageURL: function(fScale, layer, style,defaultIcon) {
+    getLegendImageURL: function(fScale, layer, style) {
         var sl = Fusion.getScriptLanguage();
         var url = Fusion.getFusionURL() + '/layers/' + this.arch + '/' + sl  + '/LegendIcon.' + sl;
         var sessionid = this.getSessionID();
@@ -903,42 +902,18 @@
 
     getLinkParams: function() {
       var queryParams = {};
-      queryParams.theme = this.sMapResourceId;
+      queryParams.theme = this.sMapFile;
 
-      //determine which layers have been toggled
-      var showLayers = [];
-      var hideLayers = [];
-      for (var i=0; i<this.aLayers.length; ++i) {
-        var layer = this.aLayers[i];
-        if (layer.visible && !layer.initiallyVisible) {  //layer was turned on
-          showLayers.push(layer.layerName);
-        }
-        if (!layer.visible && layer.initiallyVisible) {  //layer was turned off
-          hideLayers.push(layer.layerName);
-        }
-      }
-      queryParams.showlayers = showLayers.join(',');
-      queryParams.hidelayers = hideLayers.join(',');
-
-      //determine which groups have been toggled
-      var showGroups = [];
-      var hideGroups = [];
-      for (var i=0; i<this.layerRoot.groups.length; ++i) {
-        var group = this.layerRoot.groups[i];
-        if (group.visible && !group.initiallyVisible) {  //layer was turned on
-          showGroups.push(group.groupName);
-        }
-        if (!group.visible && group.initiallyVisible) {  //layer was turned off
-          hideGroups.push(group.groupName);
-        }
-      }
-      queryParams.showgroups = showGroups.join(',');
-      queryParams.hidegroups = hideGroups.join(',');
-
       return queryParams;
     },
     
     getMapTip: function(oMapTips){
+        if (!this.aLayers || 
+            !this.aLayers.length || 
+            !this.aLayers[0].scaleRanges) {
+            return;
+        }
+        
         if(this.bMapTipFired == false){
             //console.log("MAPSERVER:getMapTip");
             var pos = this.mapWidget.pixToGeo(oMapTips.oCurrentPosition.x, oMapTips.oCurrentPosition.y);

Modified: trunk/layers/MapServer/php/Common.php
===================================================================
--- trunk/layers/MapServer/php/Common.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/php/Common.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -64,7 +64,7 @@
 /* handle restoring a session */
 if (isset($REQUEST_VARS['session'])) {
     $sessionID = $REQUEST_VARS['session'];
-    initializeSession( "sid", "", $sessionID );
+    initializeSession( "session", "", $sessionID );
 }
 
 if (isset($_REQUEST['mapname'])) { 

Modified: trunk/layers/MapServer/php/CreateSession.php
===================================================================
--- trunk/layers/MapServer/php/CreateSession.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/php/CreateSession.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -32,7 +32,8 @@
 include(dirname(__FILE__).'/Common.php');
 include('../../../common/php/Utilities.php');
 
-initializeSession( "sid", "", "" );
+$cookie = isset($_COOKIE['session'])?$_COOKIE['session']:"";
+initializeSession( "session", "", $cookie);
 $sessionId = session_id();
 loadFusionConfig();
 

Copied: trunk/layers/MapServer/php/GetAttributes.php (from rev 2248, sandbox/jxlib-3.0/layers/MapServer/php/GetAttributes.php)
===================================================================
--- trunk/layers/MapServer/php/GetAttributes.php	                        (rev 0)
+++ trunk/layers/MapServer/php/GetAttributes.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,121 @@
+<?php
+/**
+ * GetAttributes
+ *
+ * $Id: GetAttributes.php 1972 2009-11-11 20:47:47Z pagameba $
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*****************************************************************************
+ * Purpose: create a new selection based on one or more attribute filters and
+ *          a spatial filter
+ *****************************************************************************/
+
+/* set up the session */
+include ("Common.php");
+include('../../../common/php/Utilities.php');
+
+//header('Content-type: application/json');
+//header('X-JSON: true');
+
+/* the name of the layer in the map to query */
+if (isset($_REQUEST['layers']) && $_REQUEST['layers'] != '') {
+    $layers = explode(',',$_REQUEST['layers']);
+} else {
+    $layers = array();
+}
+
+//echo "<!--";
+//print_r($_REQUEST);
+//echo "-->";
+
+if (!isset($mapName)) {
+    die('mapname not set');
+}
+if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
+    $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
+}
+
+$bAllLayers = false;
+$nLayers = count($layers);
+if ($nLayers == 0) {
+    $nLayers = $oMap->numlayers;
+    $bAllLayers = true;
+}
+
+/*holds selection array*/
+$properties = NULL;
+$properties->layers = array();
+    echo "/* nLayers: ".$nLayers." */";
+
+for ($i=0; $i<$nLayers; $i++) {
+    if (!$bAllLayers) {
+        $oLayer = $oMap->GetLayerByName($layers[$i]);
+    } else {
+        $oLayer = $oMap->GetLayer($i);
+    }
+    
+    $selectable = strtolower($oLayer->getMetaData('selectable'));
+    if ( $selectable == 'true' ) {
+       
+       
+      $oLayer->open();
+      $layerName = $oLayer->name != "" ? $oLayer->name : "Layer_".$i; 
+      echo "/* layername: ".$layerName." */";
+      
+      array_push($properties->layers, $layerName);
+  
+      $properties->$layerName->propertynames = array();
+      $properties->$layerName->propertyvalues = array();
+      $properties->$layerName->propertytypes = array();
+  
+      /*get first shape to get the attributes*/
+      //$oRes = $oLayer->getResult(0);
+      //$oShape = $oLayer->getShape($oRes->tileindex,$oRes->shapeindex);
+      $oShape = $oLayer->getFeature(0);
+      if ($oShape) {
+        while ( list($key,$val) = each($oShape->values) ) {
+                array_push($properties->$layerName->propertynames, $key);
+                //TODO : we should define away to give alias to field names
+                array_push($properties->$layerName->propertyvalues, $key);
+                //TODO we do not know the types of the attributes in MS. Just output 0
+                //we shouls possibly use OGR to get the attributes
+                array_push($properties->$layerName->propertytypes, 0);
+        }
+      }
+      
+      $oLayer->close();
+    }
+/*
+	if(isset($_SESSION['selection_array']->$layerName)){
+		if(isset($_SESSION['selection_array']->$layerName->values)){
+			$properties->$layerName->values = $_SESSION['selection_array']->$layerName->values;
+		}
+	}
+*/
+}
+
+/*save selection in the session*/
+//$_SESSION['selection_array'] = $properties; 
+
+echo var2json($properties);
+
+?>

Modified: trunk/layers/MapServer/php/LegendIcon.php
===================================================================
--- trunk/layers/MapServer/php/LegendIcon.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/php/LegendIcon.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,130 +1,135 @@
-<?php
-/**
- * LegendIcon
- *
- * $Id$
- *
- * Copyright (c) 2007, DM Solutions Group Inc.
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-/*****************************************************************************
- * Purpose: Draw a legend icon
- *****************************************************************************/
-
+<?php
+/**
+ * LegendIcon
+ *
+ * $Id$
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*****************************************************************************
+ * Purpose: Draw a legend icon
+ *****************************************************************************/
+
 /* set up the session */
 include(dirname(__FILE__).'/../../../common/php/Utilities.php');
-include(dirname(__FILE__).'/Common.php');
+include(dirname(__FILE__).'/Common.php');
 include(dirname(__FILE__).'/Utilities.php');
 
-if (!isset($mapName)) {
-    die('mapname not set');
-}
-
+if (!isset($mapName)) {
+    die('mapname not set');
+}
+
 $mapFile = getSessionSavePath().$mapName.".map";
 $str = file_get_contents($mapFile);
-$legendIconCacheFile = "";
-
+$legendIconCacheFile = "";
+
 if ($str) {
-      $configObj = $_SESSION['fusionConfig'];
-      /* if the legendIconCache dir is set */
-      if (isset($configObj->mapserver->legendIconCacheDir)) {
-        $legendIconCacheDir = $configObj->mapserver->legendIconCacheDir;
-
-        // check for closing '/'
-        $legendIconCacheDir = str_replace( '\\', '/', trim( $legendIconCacheDir ) );
-        if ( substr( $legendIconCacheDir, -1 ) != '/' )
-        {
-            $legendIconCacheDir .= '/';
-        }
-
-        $cacheLegendIcons = true;
+      $configObj = $_SESSION['fusionConfig'];
+      /* if the legendIconCache dir is set */
+      if (isset($configObj->mapserver->legendIconCacheDir)) {
+        $legendIconCacheDir = $configObj->mapserver->legendIconCacheDir;
+
+        // check for closing '/'
+        $legendIconCacheDir = str_replace( '\\', '/', trim( $legendIconCacheDir ) );
+        if ( substr( $legendIconCacheDir, -1 ) != '/' )
+        {
+            $legendIconCacheDir .= '/';
+        }
+
+        $cacheLegendIcons = true;
         //$str = file_get_contents($_SESSION['maps'][$mapName]);
-        /* create a unique location for the map icons based on
-         * the content of the of map file.  If the content changes
-         * then the icons should be rebuilt anyway
-         */
-        $legendIconCacheDir = $legendIconCacheDir.md5($str)."/";
-        if (!is_dir($legendIconCacheDir)) {
-          mkdir($legendIconCacheDir);
-        }
-        /* TODO: can we figure out what the content type is? */
-        $legendIconCacheFile = $legendIconCacheDir."_".$REQUEST_VARS['layername']."_".$REQUEST_VARS['classindex'].".png";
-        /* if the icon exists, return it */
-        if (file_exists($legendIconCacheFile)) {
-            /* TODO: can we figure out what the content type is? */
-            header('Content-type: image/png');
-            $etag = '"' . md5_file($legendIconCacheFile) . '"';
-            header ("ETag: " . $etag );
-            $cache_time = mktime(0, 0, 0, 1, 1, 2004);
-            $expires = 3600 * 256;
-            header("last-modified: " . gmdate("D, d M Y H:i:s",$cache_time) . " GMT");
-            $inm = split(',', getenv("HTTP_IF_NONE_MATCH"));
-            $send_body = true;
-            foreach ($inm as $i) {
-            	  if (trim($i) == $etag || trim($i) == $cache_time) {
-            		    header ("HTTP/1.0 304 Not Modified");
-            		    $send_body = false;
-            		}
-          	}
-            //last modified test
-            if(getenv("HTTP_IF_MODIFIED_SINCE") == gmdate("D, d M Y H:i:s",$cache_time). " GMT") {
-              	header ("HTTP/1.0 304 Not Modified"); 
-              	$send_body = false;
-          	}
-            //more headers
-            header("Expires: " . gmdate("D, d M Y H:i:s",$cache_time+$expires) . " GMT");
-            header("Cache-Control: max-age=$expires, must-revalidate");
-            //header('Content-Length: ' . strlen($body));
-            //if we're not cacheing
-            if ($send_body) {
-                readfile($legendIconCacheFile);
-            }
-            exit;
-        }
-    }
-
+        /* create a unique location for the map icons based on
+         * the content of the of map file.  If the content changes
+         * then the icons should be rebuilt anyway
+         */
+        $legendIconCacheDir = $legendIconCacheDir.md5($str)."/";
+        if (!is_dir($legendIconCacheDir)) {
+          mkdir($legendIconCacheDir);
+        }
+        /* TODO: can we figure out what the content type is? */
+        $legendIconCacheFile = $legendIconCacheDir."_".$REQUEST_VARS['layername']."_".$REQUEST_VARS['classindex'].".png";
+        /* if the icon exists, return it */
+        if (file_exists($legendIconCacheFile)) {
+            /* TODO: can we figure out what the content type is? */
+            header('Content-type: image/png');
+            $etag = '"' . md5_file($legendIconCacheFile) . '"';
+            header ("ETag: " . $etag );
+            $cache_time = mktime(0, 0, 0, 1, 1, 2004);
+            $expires = 3600 * 256;
+            header("last-modified: " . gmdate("D, d M Y H:i:s",$cache_time) . " GMT");
+            $inm = split(',', getenv("HTTP_IF_NONE_MATCH"));
+            $send_body = true;
+            foreach ($inm as $i) {
+            	  if (trim($i) == $etag || trim($i) == $cache_time) {
+            		    header ("HTTP/1.0 304 Not Modified");
+            		    $send_body = false;
+            		}
+          	}
+            //last modified test
+            if(getenv("HTTP_IF_MODIFIED_SINCE") == gmdate("D, d M Y H:i:s",$cache_time). " GMT") {
+              	header ("HTTP/1.0 304 Not Modified"); 
+              	$send_body = false;
+          	}
+            //more headers
+            header("Expires: " . gmdate("D, d M Y H:i:s",$cache_time+$expires) . " GMT");
+            header("Cache-Control: max-age=$expires, must-revalidate");
+            //header('Content-Length: ' . strlen($body));
+            //if we're not cacheing
+            if ($send_body) {
+                readfile($legendIconCacheFile);
+            }
+            exit;
+        }
+    }
+
     $oMap = ms_newMapObj($mapFile);
-    $oLayer = $oMap->getLayerByName($REQUEST_VARS['layername']);
-    $oClass = $oLayer->getClass($REQUEST_VARS['classindex']);
-    $width = $oMap->legend->keysizex;
-    $height = $oMap->legend->keysizey;
-    if ($width <=0) {
-        $width = 16;
-    }
-    if ($height <=0) {
-        $height = 16;
-    }
-    $oImg = $oClass->createLegendIcon($width, $height);
-    /* TODO: can we figure out what the content type is? */
-    header('Content-type: image/png');
-    if ($cacheLegendIcons) {
-        $oImg->saveImage($legendIconCacheFile);
-        $etag = '"' . md5_file($legendIconCacheFile) . '"';
-        header ("ETag: " . $etag );
-        $cache_time = mktime(0, 0, 0, 1, 1, 2004);
-        $expires = 3600 * 256;
-        header("last-modified: " . gmdate("D, d M Y H:i:s",$cache_time) . " GMT");
-        readfile($legendIconCacheFile);
-    } else {
-      $oImg->saveImage("");
-    }
-    $oImg->free();
-}
-?>
+    $oLayer = $oMap->getLayerByName($REQUEST_VARS['layername']);
+    $oClass = $oLayer->getClass($REQUEST_VARS['classindex']);
+    $width = $oMap->legend->keysizex;
+    $height = $oMap->legend->keysizey;
+    if ($width <=0) {
+        $width = 16;
+    }
+    if ($height <=0) {
+        $height = 16;
+    }
+    if ($oClass) {
+      $oImg = $oClass->createLegendIcon($width, $height);
+    } else {
+      $oMap->setSize($width,$height);
+      $oImg = $oMap->prepareImage();
+    }
+    /* TODO: can we figure out what the content type is? */
+    header('Content-type: image/png');
+    if ($cacheLegendIcons) {
+        $oImg->saveImage($legendIconCacheFile);
+        $etag = '"' . md5_file($legendIconCacheFile) . '"';
+        header ("ETag: " . $etag );
+        $cache_time = mktime(0, 0, 0, 1, 1, 2004);
+        $expires = 3600 * 256;
+        header("last-modified: " . gmdate("D, d M Y H:i:s",$cache_time) . " GMT");
+        readfile($legendIconCacheFile);
+    } else {
+      $oImg->saveImage("");
+    }
+    $oImg->free();
+}
+?>

Modified: trunk/layers/MapServer/php/LoadMap.php
===================================================================
--- trunk/layers/MapServer/php/LoadMap.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/php/LoadMap.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,420 +1,446 @@
-<?php
-/**
- * LoadMap
- *
- * $Id$
- *
- * Copyright (c) 2007, DM Solutions Group Inc.
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-/*****************************************************************************
- * Purpose: Load a mapfile into the session and return information about the
- *          map to the client
- *****************************************************************************/
-
-/* Common starts the session */
-include(dirname(__FILE__).'/../../../common/php/Utilities.php');
-include(dirname(__FILE__).'/Common.php');
-include(dirname(__FILE__).'/Utilities.php');
-
-/* if scales are not set, these become the default values */
-define('MIN_SCALE', 1);
-define('MAX_SCALE', 1000000000);
-
-/* could potentially make this optional */
-$moveToSession = true;
-
-/**
-   TODO make it possible to specify only a relative path
-   in the WebLayout and have this code know where to
-   look for it on the server somehow
- */
-
-/* only do something if a mapfile was requested */
-if (isset($_REQUEST['mapfile'])) {
-
-    /* look for mapFileRoot specified in config.json and test to see if the map path in appdef is realitive to it.*/
-    $configObj = $_SESSION['fusionConfig'];
-    $szFusionRoot = dirname(__FILE__).'/../../../'; // TODO : not a very elegant way of doing this
-    $szDblSeparator = DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR;
-
-    $szMapFromFusionRoot = str_replace($szDblSeparator,DIRECTORY_SEPARATOR,$szFusionRoot.DIRECTORY_SEPARATOR.$_REQUEST['mapfile']);
-    $szMapFileRoot = str_replace($szDblSeparator,DIRECTORY_SEPARATOR,$configObj->mapserver->mapFileRoot.DIRECTORY_SEPARATOR.$_REQUEST['mapfile']);
-
-    if( file_exists($szMapFileRoot) ) {
-        /* use the realitive path specified in config.json */
-        $szMapFile = $szMapFileRoot;
-    }
-    else
-    {
-        if( file_exists($szMapFromFusionRoot) ) {
-            /* use the realitive path from fusion install root */
-            $szMapFile = $szMapFromFusionRoot;
-        }
-        else
-        {
-            /* use absolute path from appdef */
-            $szMapFile = $_REQUEST['mapfile'];
-        }
-    }
-
-    $oMap = ms_newMapObj($szMapFile);
-
-    /* optionally move the mapfile to the session */
-    if ($moveToSession) {
-        //path to map file in the session is used by the client
-        $mapId = getSessionSavePath().($oMap->name).".map";
-        //modify various paths if necessary
-        $pathToMap = dirname($szMapFile);
-        $cwd = getcwd();
-        chdir($pathToMap);
-        $shapePath = $oMap->shapepath;
-        $oMap->set('shapepath', realpath($shapePath));
-        $symbolSet = $oMap->symbolsetfilename;
-        if ($symbolSet != '') {
-            $oMap->setSymbolSet(realpath($symbolSet));
-        }
-        $fontSet = $oMap->fontsetfilename;
-        if ($fontSet != '') {
-            $oMap->setFontSet(realpath($fontSet));
-        }
-        /* need to modify all image symbols reference in the map file
-         eg STYLE
-             SYMBOL "../etc/markers/target-7.gif" : this is relative to the map file
-        */
-
-        for ($i=0; $i<$oMap->numlayers; $i++)
-        {
-            $oLayer = &$oMap->GetLayer($i);
-            /* check layername for invalid URI characters and replace */
-            $oLayer->set("name",replaceInvalidLayerName($oLayer->name));
-
-            for ($j=0; $j<$oLayer->numclasses; $j++)
-            {
-                $oClass = $oLayer->GetClass($j);
-                /* if keyimage is defined, change the path*/
-                if ($oClass->keyimage && strlen($oClass->keyimage) > 0)
-                {
-                     $oClass->set("keyimage", realpath($oClass->keyimage));
-                }
-                for ($k=0; $k<$oClass->numstyles; $k++)
-                {
-                    $oStyle = $oClass->getStyle($k);
-                    if ($oStyle->symbolname != "")
-                    {
-                        if (file_exists(realpath($oStyle->symbolname)))
-                        {
-                            $oStyle->set("symbolname", realpath($oStyle->symbolname));
-                        }
-                    }
-                }
-            }
-        }
-        $oMap->save($mapId);
-        chdir($cwd);
-    } else {
-        $mapId = $_REQUEST['mapfile'];
-    }
-} elseif (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
-    $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
-    $mapId = getSessionSavePath().($oMap->name).".map";
-}
-
-$mapObj = NULL;
-if ($oMap) {
-    header('Content-type: application/json');
-    header('X-JSON: true');
-    $mapObj->sessionId = $sessionID;
-    $mapObj->mapId = $mapId;
-
-    $mapObj->metadata = NULL;
-    if (isset($_REQUEST['map_metadata'])) {
-        $mapMetadataKeys = explode(',',$_REQUEST['map_metadata']);
-        foreach($mapMetadataKeys as $key) {
-            $mapObj->metadata->$key = $oMap->getMetadata($key);
-        }
-    }
-
-    $mapObj->projString = $oMap->getProjection();
-    $mapObj->metersPerUnit = GetMetersPerUnit($oMap->units);
-
-    $mapObj->dpi = $oMap->resolution;
-    $mapObj->imagetype = $oMap->imagetype;
-    $mapObj->mapName = $oMap->name;
-    if (!isset($_SESSION['maps'])) {
-        $_SESSION['maps'] = array();
-    }
-    if (!isset($_SESSION['maps'][$mapObj->mapName])) {
-        $_SESSION['maps'][$mapObj->mapName] = $mapId;
-    }
-    $mapObj->extent = array( $oMap->extent->minx, $oMap->extent->miny,
-                             $oMap->extent->maxx, $oMap->extent->maxy );
-    $minScale = $oMap->web->minscale == -1 ? MIN_SCALE : $oMap->web->minscale;
-    $maxScale = $oMap->web->maxscale == -1 ? MAX_SCALE : $oMap->web->maxscale;
-    $title = $oMap->getmetadata('legend_title');
-    $mapObj->mapTitle = $title == "" ? $mapObj->mapName : $title;
-    //layers
-    $mapObj->layers = array();
-    for ($i=0;$i<$oMap->numlayers;$i++)
-    {
-        $layer=$oMap->getLayer($i);
-        $layerObj = NULL;
-
-        /* Status Default - If a layer is status Default trac it in layerObj */
-        if($layer->status == MS_DEFAULT)
-            $layerObj->statusdefault = true;
-         else
-            $layerObj->statusdefault = false;
-
-        /* rename layes names with invalid characters */
-        $layer->set("name",replaceInvalidLayerName($layer->name));
-
-        $layerObj->metadata = NULL;
-        if (isset($_REQUEST['layer_metadata'])) {
-            $layerMetadataKeys = explode(',',$_REQUEST['layer_metadata']);
-            foreach($layerMetadataKeys as $key) {
-                $layerObj->metadata->$key = $layer->getMetadata($key);
-            }
-        }
-
-        //$extent = $layer->getExtent();
-        $layerObj->extent = NULL;
-        $layerObj->extent->minx = NULL;
-        $layerObj->extent->maxx = NULL;
-        $layerObj->extent->miny = NULL;
-        $layerObj->extent->maxy = NULL;
-
-         // only proceed if extent is valid
-        if( isset($GLOBALS["extent"]) )
-        {
-            $layerObj->extent->minx = $extent->minx;
-            $layerObj->extent->maxx = $extent->maxx;
-            $layerObj->extent->miny = $extent->miny;
-            $layerObj->extent->maxy = $extent->maxy;
-        }
-
-         $layerObj->propertyMappings = '';
-         $layerObj->uniqueId = $i;
-         $layerObj->layerName = $layer->name;
-         switch($layer->type) {
-             case MS_LAYER_POINT:
-                $type = 0;
-                break;
-             case MS_LAYER_LINE:
-                $type = 1;
-                break;
-             case MS_LAYER_POLYGON:
-                $type = 2;
-                break;
-             case MS_LAYER_RASTER:
-                $type = 4;
-                break;
-             case MS_LAYER_ANNOTATION:
-                $type = 8;
-                break;
-             default:
-                $type = 0;
-         }
-         $layerObj->layerTypes = array($type);
-
-         $displayInLegend = strtolower($layer->getMetaData('displayInLegend'));
-         $layerObj->displayInLegend = $displayInLegend == 'false' ? false : true;
-
-         $expandInLegend = strtolower($layer->getMetaData('expandInLegend'));
-         $layerObj->expandInLegend = $expandInLegend == 'false' ? false : true;
-         $layerObj->resourceId = $layer->name;
-         $layerObj->parentGroup = $layer->group;
-
-         $legendLabel = $layer->getMetaData('legendLabel');
-         if ($legendLabel == '') {
-             $legendLabel = $layer->name;
-         }
-         $layerObj->legendLabel = $legendLabel;
-
-         $selectable = strtolower($layer->getMetaData('selectable'));
-         $layerObj->selectable = $selectable == 'true' ? true : false;
-
-         $layerObj->visible = ($layer->status == MS_ON || $layer->status == MS_DEFAULT);
-         $layerObj->actuallyVisible = true;
-
-         $editable = strtolower($layer->getMetaData('editable'));
-         $layerObj->editable = $editable == 'true' ? true : false;
-
-         /* process the classes.  The legend expects things
-          * organized by scale range so we have to first
-          * find all the scale breaks, then create ranges
-          * for each scale break pair, then slot the classes
-          * into the scale ranges that they apply to.
-          */
-
-         $aScaleRanges = array();
-         //create a default scale range for the layer as a whole
-         $layerMin = $layer->minscale == -1 ? $minScale : $layer->minscale;
-         $layerMax = $layer->maxscale == -1 ? $maxScale : $layer->maxscale;
-
-        /* check to see that the layer has a vaild scale range
-         * inside the defined map's main min max scale ranges
-         * set them to the maps scale ranges if they exceede 
-         */
-        if($layer->minscale != -1 && $layerMin < $minScale)
-            $layerMin = $minScale;
-
-        if($layer->maxscale != -1 && $layerMax > $maxScale)
-            $layerMax = $maxScale;
-
-         //find all the unique scale breaks in this layer
-         $aScaleBreaks = array($layerMin, $layerMax);
-         for ($j=0; $j<$layer->numclasses; $j++) {
-             $oClass = $layer->getClass($j);
-             $classMin = $oClass->minscale == -1 ? $layerMin : max($oClass->minscale, $layerMin);
-             $classMax = $oClass->maxscale == -1 ? $layerMax : min($oClass->maxscale, $layerMax);
-             if (!in_array($classMin, $aScaleBreaks)) {
-                 array_push($aScaleBreaks, $classMin);
-             }
-             if (!in_array($classMax, $aScaleBreaks)) {
-                 array_push($aScaleBreaks, $classMax);
-             }
-         }
-         //sort them
-         sort($aScaleBreaks);
-
-         //create scale ranges for each pair of breaks
-         for ($j=0; $j<count($aScaleBreaks)-1; $j++) {
-             $scaleRange = NULL;
-             $scaleRange->minScale = $aScaleBreaks[$j];
-             $scaleRange->maxScale = $aScaleBreaks[$j+1];
-             $scaleRange->styles = array();
-             array_push($aScaleRanges, $scaleRange);
-         }
-
-         //create classes and slot them into the scale breaks
-         for ($j=0; $j<$layer->numclasses; $j++) {
-             $oClass = $layer->getClass($j);
-             $classObj = NULL;
-             // Use formatted legend label as defined by CLASS->TITLE
-             $classObj->legendLabel = $oClass->title != '' ? $oClass->title : $oClass->name;
-             $classObj->filter = $oClass->getExpression();
-             $classMin = $oClass->minscale == -1 ? $layerMin : max($oClass->minscale, $layerMin);
-             $classMax = $oClass->maxscale == -1 ? $layerMax : min($oClass->maxscale, $layerMax);
-             $classObj->minScale = $classMin;
-             $classObj->maxScale = $classMax;
-             $classObj->index = $j;
-             for ($k=0; $k<count($aScaleRanges); $k++) {
-                 if ($classMin < $aScaleRanges[$k]->maxScale &&
-                     $classMax > $aScaleRanges[$k]->minScale) {
-                     array_push($aScaleRanges[$k]->styles, $classObj);
-                 }
-             }
-         }
-         //$layerObj->scaleRanges = $aScaleRanges;
+<?php
+/**
+ * LoadMap
+ *
+ * $Id$
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*****************************************************************************
+ * Purpose: Load a mapfile into the session and return information about the
+ *          map to the client
+ *****************************************************************************/
+/* Common starts the session */
+include(dirname(__FILE__).'/../../../common/php/Utilities.php');
+include(dirname(__FILE__).'/Common.php');
+include(dirname(__FILE__).'/Utilities.php');
+
+/* if scales are not set, these become the default values */
+define('MIN_SCALE', 1);
+define('MAX_SCALE', 1000000000);
+
+/* could potentially make this optional */
+$moveToSession = true;
+$modifyPaths = false;
+
+/**
+   TODO make it possible to specify only a relative path
+   in the WebLayout and have this code know where to
+   look for it on the server somehow
+ */
+
+/* only do something if a mapfile was requested */
+if (isset($_REQUEST['mapfile'])) {
+
+    /* look for mapFileRoot specified in config.json and test to see if the map path in appdef is realitive to it.*/
+    $configObj = $_SESSION['fusionConfig'];
+    $szFusionRoot = dirname(__FILE__).'/../../../'; // TODO : not a very elegant way of doing this
+    $szDblSeparator = DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR;
+
+    $szMapFromFusionRoot = str_replace($szDblSeparator,DIRECTORY_SEPARATOR,$szFusionRoot.DIRECTORY_SEPARATOR.$_REQUEST['mapfile']);
+    $szMapFileRoot = str_replace($szDblSeparator,DIRECTORY_SEPARATOR,$configObj->mapserver->mapFileRoot.DIRECTORY_SEPARATOR.$_REQUEST['mapfile']);
+
+    if( file_exists($szMapFileRoot) ) {
+        /* use the realitive path specified in config.json */
+        $szMapFile = $szMapFileRoot;
+    }
+    else
+    {
+        if( file_exists($szMapFromFusionRoot) ) {
+            /* use the realitive path from fusion install root */
+            $szMapFile = $szMapFromFusionRoot;
+        }
+        else
+        {
+            /* use absolute path from appdef */
+            $szMapFile = $_REQUEST['mapfile'];
+        }
+    }
+
+    $sMapFileContents = '';
+    if (strpos($szMapFile, 'platform://') === 0) {
+        include_once($configObj->mapserver->platformPath . "/include/Common.php");
+        global $platformDefaultConfig;
+        $platformContext = new PlatformContext($platformDefaultConfig);
+        $platformContext->RestoreSession(session_id());
+
+        $mapGen = new Map($platformContext, substr($szMapFile, 10));
+        if ($mapGen->Exists()) {
+            $result = $mapGen->Save();
+            if ($result->IsOk()) {
+                $sMapFileContents = $result->Get('MAPDATA');
+            }
+        }
+        $oMap = ms_newMapObjFromString($sMapFileContents);
+    } else if (file_exists($szMapFile)) {
+        $modifyPaths = true;
+        $oMap = ms_newMapObj($szMapFile);
+    }
+
+    /* optionally move the mapfile to the session */
+    if ($moveToSession) {
+        //path to map file in the session is used by the client
+        $mapId = getSessionSavePath().($oMap->name).".map";
+        //modify various paths if necessary
+        if ($modifyPaths) {
+          $pathToMap = dirname($szMapFile);
+          $cwd = getcwd();
+          chdir($pathToMap);
+          $shapePath = $oMap->shapepath;
+          $oMap->set('shapepath', realpath($shapePath));
+          $symbolSet = $oMap->symbolsetfilename;
+          if ($symbolSet != '') {
+              $oMap->setSymbolSet(realpath($symbolSet));
+          }
+          $fontSet = $oMap->fontsetfilename;
+          if ($fontSet != '') {
+              $oMap->setFontSet(realpath($fontSet));
+          }
+          /* need to modify all image symbols reference in the map file
+           eg STYLE
+               SYMBOL "../etc/markers/target-7.gif" : this is relative to the map file
+          */
+  
+          for ($i=0; $i<$oMap->numlayers; $i++)
+          {
+              $oLayer = &$oMap->GetLayer($i);
+              /* check layername for invalid URI characters and replace */
+              $oLayer->set("name",replaceInvalidLayerName($oLayer->name));
+  
+              for ($j=0; $j<$oLayer->numclasses; $j++)
+              {
+                  $oClass = $oLayer->GetClass($j);
+                  /* if keyimage is defined, change the path*/
+                  if ($oClass->keyimage && strlen($oClass->keyimage) > 0)
+                  {
+                       $oClass->set("keyimage", realpath($oClass->keyimage));
+                  }
+                  for ($k=0; $k<$oClass->numstyles; $k++)
+                  {
+                      $oStyle = $oClass->getStyle($k);
+                      if ($oStyle->symbolname != "")
+                      {
+                          if (file_exists(realpath($oStyle->symbolname)))
+                          {
+                              $oStyle->set("symbolname", realpath($oStyle->symbolname));
+                          }
+                      }
+                  }
+              }
+          }
+          chdir($cwd);
+        }
+        $oMap->save($mapId);
+    } else {
+        $mapId = $_REQUEST['mapfile'];
+    }
+} elseif (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
+    $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
+    $mapId = getSessionSavePath().($oMap->name).".map";
+}
+
+$mapObj = NULL;
+if ($oMap) {
+    $mapName = $oMap->name;
+    
+    header('Content-type: application/json');
+    header('X-JSON: true');
+    $mapObj->sessionId = $sessionID;
+    $mapObj->mapId = $mapId;
+
+    $mapObj->metadata = NULL;
+    if (isset($_REQUEST['map_metadata'])) {
+        $mapMetadataKeys = explode(',',$_REQUEST['map_metadata']);
+        foreach($mapMetadataKeys as $key) {
+            $mapObj->metadata->$key = $oMap->getMetadata($key);
+        }
+    }
+
+    $mapObj->projString = $oMap->getProjection();
+    $mapObj->metersPerUnit = GetMetersPerUnit($oMap->units);
+
+    $mapObj->dpi = $oMap->resolution;
+    $mapObj->imagetype = $oMap->imagetype;
+    $mapObj->mapName = $oMap->name;
+    if (!isset($_SESSION['maps'])) {
+        $_SESSION['maps'] = array();
+    }
+    if (!isset($_SESSION['maps'][$mapObj->mapName])) {
+        $_SESSION['maps'][$mapObj->mapName] = $mapId;
+    }
+    $mapObj->extent = array( $oMap->extent->minx, $oMap->extent->miny,
+                             $oMap->extent->maxx, $oMap->extent->maxy );
+    $minScale = $oMap->web->minscale == -1 ? MIN_SCALE : $oMap->web->minscale;
+    $maxScale = $oMap->web->maxscale == -1 ? MAX_SCALE : $oMap->web->maxscale;
+    $title = $oMap->getmetadata('legend_title');
+    $mapObj->mapTitle = $title == "" ? $mapObj->mapName : $title;
+    //layers
+    $mapObj->layers = array();
+    for ($i=0;$i<$oMap->numlayers;$i++)
+    {
+        $layer=$oMap->getLayer($i);
+        $layerObj = NULL;
+
+        /* Status Default - If a layer is status Default trac it in layerObj */
+        if($layer->status == MS_DEFAULT)
+            $layerObj->statusdefault = true;
+         else
+            $layerObj->statusdefault = false;
+
+        /* rename layes names with invalid characters */
+        $layer->set("name",replaceInvalidLayerName($layer->name));
+
+        $layerObj->metadata = NULL;
+        if (isset($_REQUEST['layer_metadata'])) {
+            $layerMetadataKeys = explode(',',$_REQUEST['layer_metadata']);
+            foreach($layerMetadataKeys as $key) {
+                $layerObj->metadata->$key = $layer->getMetadata($key);
+            }
+        }
+
+        //$extent = $layer->getExtent();
+        $layerObj->extent = NULL;
+        $layerObj->extent->minx = NULL;
+        $layerObj->extent->maxx = NULL;
+        $layerObj->extent->miny = NULL;
+        $layerObj->extent->maxy = NULL;
+
+         // only proceed if extent is valid
+        if( isset($GLOBALS["extent"]) )
+        {
+            $layerObj->extent->minx = $extent->minx;
+            $layerObj->extent->maxx = $extent->maxx;
+            $layerObj->extent->miny = $extent->miny;
+            $layerObj->extent->maxy = $extent->maxy;
+        }
+
+         $layerObj->propertyMappings = '';
+         $layerObj->uniqueId = $i;
+         $layerObj->layerName = $layer->name;
+         switch($layer->type) {
+             case MS_LAYER_POINT:
+                $type = 0;
+                break;
+             case MS_LAYER_LINE:
+                $type = 1;
+                break;
+             case MS_LAYER_POLYGON:
+                $type = 2;
+                break;
+             case MS_LAYER_RASTER:
+                $type = 4;
+                break;
+             case MS_LAYER_ANNOTATION:
+                $type = 8;
+                break;
+             default:
+                $type = 0;
+         }
+         $layerObj->layerTypes = array($type);
+
+         $displayInLegend = strtolower($layer->getMetaData('displayInLegend'));
+         $layerObj->displayInLegend = $displayInLegend == 'false' ? false : true;
+
+         $expandInLegend = strtolower($layer->getMetaData('expandInLegend'));
+         $layerObj->expandInLegend = $expandInLegend == 'false' ? false : true;
+         $layerObj->resourceId = $layer->name;
+         $layerObj->parentGroup = $layer->group;
+
+         $legendLabel = $layer->getMetaData('legendLabel');
+         if ($legendLabel == '') {
+             $legendLabel = $layer->name;
+         }
+         $layerObj->legendLabel = $legendLabel;
+
+         $selectable = strtolower($layer->getMetaData('selectable'));
+         $layerObj->selectable = $selectable == 'true' ? true : false;
+
+         $layerObj->visible = ($layer->status == MS_ON || $layer->status == MS_DEFAULT);
+         $layerObj->actuallyVisible = true;
+
+         $editable = strtolower($layer->getMetaData('editable'));
+         $layerObj->editable = $editable == 'true' ? true : false;
+
+         /* process the classes.  The legend expects things
+          * organized by scale range so we have to first
+          * find all the scale breaks, then create ranges
+          * for each scale break pair, then slot the classes
+          * into the scale ranges that they apply to.
+          */
+
+         $aScaleRanges = array();
+         //create a default scale range for the layer as a whole
+         $layerMin = $layer->minscale == -1 ? $minScale : $layer->minscale;
+         $layerMax = $layer->maxscale == -1 ? $maxScale : $layer->maxscale;
+
+        /* check to see that the layer has a vaild scale range
+         * inside the defined map's main min max scale ranges
+         * set them to the maps scale ranges if they exceede 
+         */
+        if($layer->minscale != -1 && $layerMin < $minScale)
+            $layerMin = $minScale;
+
+        if($layer->maxscale != -1 && $layerMax > $maxScale)
+            $layerMax = $maxScale;
+
+         //find all the unique scale breaks in this layer
+         $aScaleBreaks = array($layerMin, $layerMax);
+         for ($j=0; $j<$layer->numclasses; $j++) {
+             $oClass = $layer->getClass($j);
+             $classMin = $oClass->minscale == -1 ? $layerMin : max($oClass->minscale, $layerMin);
+             $classMax = $oClass->maxscale == -1 ? $layerMax : min($oClass->maxscale, $layerMax);
+             if (!in_array($classMin, $aScaleBreaks)) {
+                 array_push($aScaleBreaks, $classMin);
+             }
+             if (!in_array($classMax, $aScaleBreaks)) {
+                 array_push($aScaleBreaks, $classMax);
+             }
+         }
+         //sort them
+         sort($aScaleBreaks);
+
+         //create scale ranges for each pair of breaks
+         for ($j=0; $j<count($aScaleBreaks)-1; $j++) {
+             $scaleRange = NULL;
+             $scaleRange->minScale = $aScaleBreaks[$j];
+             $scaleRange->maxScale = $aScaleBreaks[$j+1];
+             $scaleRange->styles = array();
+             array_push($aScaleRanges, $scaleRange);
+         }
+
+         //create classes and slot them into the scale breaks
+         for ($j=0; $j<$layer->numclasses; $j++) {
+             $oClass = $layer->getClass($j);
+             $displayInLegend = strtolower($oClass->getMetaData('displayInLegend')) == 'false' ? false : true;
+             if ($displayInLegend) {
+               $title = $oClass->getMetaData('legendlabel');
+               $classObj = NULL;
+               // Use formatted legend label as defined by CLASS->TITLE
+               $classObj->legendLabel = $title != '' ? $title : $oClass->name;
+               $classObj->filter = $oClass->getExpression();
+               $classMin = $oClass->minscale == -1 ? $layerMin : max($oClass->minscale, $layerMin);
+               $classMax = $oClass->maxscale == -1 ? $layerMax : min($oClass->maxscale, $layerMax);
+               $classObj->minScale = $classMin;
+               $classObj->maxScale = $classMax;
+               $classObj->index = $j;
+               for ($k=0; $k<count($aScaleRanges); $k++) {
+                   if ($classMin < $aScaleRanges[$k]->maxScale &&
+                       $classMax > $aScaleRanges[$k]->minScale) {
+                       array_push($aScaleRanges[$k]->styles, $classObj);
+                   }
+               }
+             }
+         }
+         //$layerObj->scaleRanges = $aScaleRanges;
          $_SESSION['scale_ranges'][$mapName][$layer->name] = $aScaleRanges;
-         /*get the min/max scale for the layer*/
-        $nCount = count($aScaleRanges);
-        $layerObj->minScale = $aScaleRanges[0]->minScale;
-        $layerObj->maxScale = $aScaleRanges[0]->maxScale;
-        for ($j=1; $j<$nCount; $j++)
-        {
-            $layerObj->minScale = min($layerObj->minScale, $aScaleRanges[$j]->minScale);
-            $layerObj->maxScale = max($layerObj->maxScale, $aScaleRanges[$j]->maxScale);
-        }
-        array_push($mapObj->layers, $layerObj);
-    }
-    $mapObj->groups = array();
-    $aGroups = $oMap->getAllGroupNames();
-    foreach($aGroups as $groupName) {
-        $aLayerIndexes = $oMap->getLayersIndexByGroup($groupName);
-        if (count($aLayerIndexes) > 0) {
-            array_push($mapObj->groups, getGroupObject($oMap->getLayer($aLayerIndexes[0])));
-        }
-    }
-    echo var2json($mapObj);
-}
-
-function getGroupObject($layer) {
-    $group = NULL;
-    $group->groupName = $layer->group;
-    $ll = $layer->getMetaData('groupLegendLabel');
-    $group->legendLabel = $ll != '' ? $ll : $group->groupName;
-    $group->uniqueId = $group->groupName;
-    $b = $layer->getMetaData('groupDisplayInLegend');
-    $group->displayInLegend = ($b == 'false') ? false : true;
-    $b = $layer->getMetaData('groupExpandInLegend');
-    $group->expandInLegend = ($b == 'false') ? false : true;
-    $group->layerGroupType = '';
-    /* parent is always not set for mapserver since we can't have nested groups */
-    $group->parentUniqueId = '';
-    $group->parent = '';
-    $b = $layer->getMetaData('groupVisible');
-    $group->visible = ($b == 'false') ? false : true;
-    $group->actuallyVisible = $layer->isVisible();
-    $group->groupParent = $layer->getMetaData('groupParent');
-
-    return $group;
-}
-
-function GetMetersPerUnit($unit)
-{
-    if ($unit == MS_INCHES)
-      return 0.0254;
-    else if ($unit == MS_FEET)
-      return 0.3048;
-    else if ($unit == MS_MILES)
-      return 1609.344;
-    else if ($unit == MS_METERS)
-      return 1;
-    else if ($unit == MS_KILOMETERS)
-      return 1000;
-    else if ($unit == MS_DD)
-      return 111118.7516;
-    else if ($unit == MS_PIXELS)
-      return 1;
-
-}
-
-function replaceInvalidLayerName($szLayerName){
-    /*
-    bug http://trac.osgeo.org/fusion/ticket/96 - Invalid characters in layer name (pdeschamps)
-
-    Fusion requests the map imavge via the Mapserver CGI to toggle the layer visibility.
-    The layer paramerter for the cgi uses spaces as a delimiter for the layer names this creates
-    an issue for the mapserver binary to toggle layers that have these reserved URI characters.
-    also removing characters that could pose potential issues with json.
-    */
-    $aInvalidLayerNameCharacters = array();
-    $aInvalidLayerNameCharacters[0] ="&";
-    $aInvalidLayerNameCharacters[1] =" ";
-    $aInvalidLayerNameCharacters[2] ="#";
-    $aInvalidLayerNameCharacters[3] ="\\";
-    $aInvalidLayerNameCharacters[4] ="=";
-    $aInvalidLayerNameCharacters[5] ="/";
-    $aInvalidLayerNameCharacters[6] ="'";
-
-    $aReplace[0] = "_";
-    $aReplace[1] = "_";
-    $aReplace[2] = "_";
-    $aReplace[3] = "_";
-    $aReplace[4] = "_";
-    $aReplace[5] = "_";
-    $aReplace[6] = "_";
-
-    return str_replace($aInvalidLayerNameCharacters,$aReplace,$szLayerName);
-}
-
-?>
+         /*get the min/max scale for the layer*/
+        $nCount = count($aScaleRanges);
+        $layerObj->minScale = $aScaleRanges[0]->minScale;
+        $layerObj->maxScale = $aScaleRanges[0]->maxScale;
+        for ($j=1; $j<$nCount; $j++)
+        {
+            $layerObj->minScale = min($layerObj->minScale, $aScaleRanges[$j]->minScale);
+            $layerObj->maxScale = max($layerObj->maxScale, $aScaleRanges[$j]->maxScale);
+        }
+        array_push($mapObj->layers, $layerObj);
+    }
+    $mapObj->groups = array();
+    $aGroups = $oMap->getAllGroupNames();
+    foreach($aGroups as $groupName) {
+        $aLayerIndexes = $oMap->getLayersIndexByGroup($groupName);
+        if (count($aLayerIndexes) > 0) {
+            array_push($mapObj->groups, getGroupObject($oMap->getLayer($aLayerIndexes[0])));
+        }
+    }
+    echo var2json($mapObj);
+}
+
+function getGroupObject($layer) {
+    $group = NULL;
+    $group->groupName = $layer->group;
+    $ll = $layer->getMetaData('groupLegendLabel');
+    $group->legendLabel = $ll != '' ? $ll : $group->groupName;
+    $group->uniqueId = $group->groupName;
+    $b = $layer->getMetaData('groupDisplayInLegend');
+    $group->displayInLegend = ($b == 'false') ? false : true;
+    $b = $layer->getMetaData('groupExpandInLegend');
+    $group->expandInLegend = ($b == 'false') ? false : true;
+    $group->layerGroupType = '';
+    /* parent is always not set for mapserver since we can't have nested groups */
+    $group->parentUniqueId = '';
+    $group->parent = '';
+    $b = $layer->getMetaData('groupVisible');
+    $group->visible = ($b == 'false') ? false : true;
+    $group->actuallyVisible = $layer->isVisible();
+    $group->groupParent = $layer->getMetaData('groupParent');
+
+    return $group;
+}
+
+function GetMetersPerUnit($unit)
+{
+    if ($unit == MS_INCHES)
+      return 0.0254;
+    else if ($unit == MS_FEET)
+      return 0.3048;
+    else if ($unit == MS_MILES)
+      return 1609.344;
+    else if ($unit == MS_METERS)
+      return 1;
+    else if ($unit == MS_KILOMETERS)
+      return 1000;
+    else if ($unit == MS_DD)
+      return 111118.7516;
+    else if ($unit == MS_PIXELS)
+      return 1;
+
+}
+
+function replaceInvalidLayerName($szLayerName){
+    /*
+    bug http://trac.osgeo.org/fusion/ticket/96 - Invalid characters in layer name (pdeschamps)
+
+    Fusion requests the map imavge via the Mapserver CGI to toggle the layer visibility.
+    The layer paramerter for the cgi uses spaces as a delimiter for the layer names this creates
+    an issue for the mapserver binary to toggle layers that have these reserved URI characters.
+    also removing characters that could pose potential issues with json.
+    */
+    $aInvalidLayerNameCharacters = array();
+    $aInvalidLayerNameCharacters[0] ="&";
+    $aInvalidLayerNameCharacters[1] =" ";
+    $aInvalidLayerNameCharacters[2] ="#";
+    $aInvalidLayerNameCharacters[3] ="\\";
+    $aInvalidLayerNameCharacters[4] ="=";
+    $aInvalidLayerNameCharacters[5] ="/";
+    $aInvalidLayerNameCharacters[6] ="'";
+
+    $aReplace[0] = "_";
+    $aReplace[1] = "_";
+    $aReplace[2] = "_";
+    $aReplace[3] = "_";
+    $aReplace[4] = "_";
+    $aReplace[5] = "_";
+    $aReplace[6] = "_";
+
+    return str_replace($aInvalidLayerNameCharacters,$aReplace,$szLayerName);
+}
+
+?>

Modified: trunk/layers/MapServer/php/LoadScaleRanges.php
===================================================================
--- trunk/layers/MapServer/php/LoadScaleRanges.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/php/LoadScaleRanges.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,41 +1,41 @@
-<?php
-/**
- * SetLayers
- *
- * $Id: $
- *
- * Copyright (c) 2007, DM Solutions Group Inc.
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-/*****************************************************************************
- * Utility function to load scale ranges for the layers. Initially
- * scale ranges were returned as part of in LoadMap.php. This allows
- * to reduce the size of information that is returned by LoadMap, by putting
- * elements that are unnessary to the map draw her.  
- *****************************************************************************/
-
-/* set up the session */
+<?php
+/**
+ * SetLayers
+ *
+ * $Id: $
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*****************************************************************************
+ * Utility function to load scale ranges for the layers. Initially
+ * scale ranges were returned as part of in LoadMap.php. This allows
+ * to reduce the size of information that is returned by LoadMap, by putting
+ * elements that are unnessary to the map draw her.  
+ *****************************************************************************/
+
+/* set up the session */
 include(dirname(__FILE__).'/../../../common/php/Utilities.php');
 include(dirname(__FILE__).'/Common.php');
 include(dirname(__FILE__).'/Utilities.php');
-
+
 /*if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
     $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
 }*/
@@ -107,7 +107,7 @@
 		
 		if($layer->maxscale != -1 && $layerMax > $maxScale)
 			$layerMax = $maxScale;
-
+
 		//find all the unique scale breaks in this layer
 		$aScaleBreaks = array($layerMin, $layerMax);
 		for ($j=0; $j<$layer->numclasses; $j++) {
@@ -136,117 +136,122 @@
 		//create classes and slot them into the scale breaks
 		for ($j=0; $j<$layer->numclasses; $j++) {
 			$oClass = $layer->getClass($j);
-			$classObj = NULL;
-			// Use formatted legend label as defined by CLASS->TITLE
-			$classObj->legendLabel = $oClass->title != '' ? $oClass->title : $oClass->name;
-			//$classObj->legendLabel = $oClass->name;
-			$classObj->filter = $oClass->getExpression();
-			$classMin = $oClass->minscale == -1 ? $layerMin : max($oClass->minscale, $layerMin);
-			$classMax = $oClass->maxscale == -1 ? $layerMax : min($oClass->maxscale, $layerMax);
-			$classObj->minScale = $classMin;
-			$classObj->maxScale = $classMax;
-			$classObj->index = $j;
-			for ($k=0; $k<count($aScaleRanges); $k++) {
-				if ($classMin < $aScaleRanges[$k]->maxScale &&
-					$classMax > $aScaleRanges[$k]->minScale) {
-					array_push($aScaleRanges[$k]->styles, $classObj);
-				}
-			}
+			$displayInLegend = strtolower($oClass->getMetaData('displayInLegend')) == 'false' ? false : true;
+      if ($displayInLegend) {
+        $title = $oClass->getMetaData('legendlabel');
+        $classObj = NULL;
+  			// Use formatted legend label as defined by CLASS->TITLE
+  			$classObj->legendLabel = $title != '' ? $title : $oClass->name;
+  			//$classObj->legendLabel = $oClass->name;
+  			$classObj->filter = $oClass->getExpression();
+  			$classMin = $oClass->minscale == -1 ? $layerMin : max($oClass->minscale, $layerMin);
+  			$classMax = $oClass->maxscale == -1 ? $layerMax : min($oClass->maxscale, $layerMax);
+  			$classObj->minScale = $classMin;
+  			$classObj->maxScale = $classMax;
+  			$classObj->index = $j;
+  			for ($k=0; $k<count($aScaleRanges); $k++) {
+  				if ($classMin < $aScaleRanges[$k]->maxScale &&
+  					$classMax > $aScaleRanges[$k]->minScale) {
+  					array_push($aScaleRanges[$k]->styles, $classObj);
+  				}
+  			}
+      }
 		}
 		//$layerObj->scaleRanges = $aScaleRanges;
 		$_SESSION['scale_ranges'][$mapName][$layer->name] = $aScaleRanges;
 	}
-
+
 	$scaleObj = NULL;
 	$scaleObj->layers = array();
-
+
 	define('ICON_SIZE', 16);
 	$nIconWidth = $oMap->legend->keysizex;
 	$nIconHeight = $oMap->legend->keysizey;
 	if ($nIconWidth <=0)
-  $nIconWidth = ICON_SIZE;
+  $nIconWidth = ICON_SIZE;
 	if ($nIconWidth <=0)
-  $nIconWidth = ICON_SIZE;
+  $nIconWidth = ICON_SIZE;
 	$nTotalClasses=0;
 	$aIcons = array();
-
+
 	/*special case to force the the legend icons to be drawn using a gd driver
-  This was fixed in ticket http://trac.osgeo.org/mapserver/ticket/2682 which
-  will be available for mapserver version 5.2.1 and 5.4
-  Note that we do not check the outputformat of the map (assuming that we are 
-  using GD or AGG renderers)
+  This was fixed in ticket http://trac.osgeo.org/mapserver/ticket/2682 which
+  will be available for mapserver version 5.2.1 and 5.4
+  Note that we do not check the outputformat of the map (assuming that we are 
+  using GD or AGG renderers)
 	*/
 	$nVersion = ms_GetVersionInt();
 	if ($nVersion <= 50200) /*5.2 and before*/
-  $oMap->selectOutputFormat("png24");
-
-
+  $oMap->selectOutputFormat("png24");
+
+
 	for($i=0;$i<$oMap->numlayers;$i++) 
-    {
-        $layer = $oMap->getLayer($i);
+    {
+        $layer = $oMap->getLayer($i);
 		if (isset($_SESSION['scale_ranges']) && isset($_SESSION['scale_ranges'][$mapName]) && isset($_SESSION['scale_ranges'][$mapName][$layer->name]))
 		{
 			$scaleranges = $_SESSION['scale_ranges'][$mapName][$layer->name];
-        $layerObj = NULL;
-        $layerObj->uniqueId = $i;
-
-        /*generate the legend icons here*/
-        $nScaleRanges = count($scaleranges);
-        for ($j=0; $j<$nScaleRanges; $j++)
-        {
-            $nStyles = count($scaleranges[$j]->styles);
-            for ($k=0; $k<$nStyles; $k++)
-            {
-                $nClassIndex = $scaleranges[$j]->styles[$k]->index;
-                $oClass = $layer->getClass($nClassIndex);
-                
-
-                $oImg = $oClass->createLegendIcon($nIconWidth, $nIconHeight);
-                array_push($aIcons, $oImg);
-                $scaleranges[$j]->styles[$k]->icon_x = ($nTotalClasses*$nIconWidth);
-                $scaleranges[$j]->styles[$k]->icon_y=0;
-                $nTotalClasses++;
-            }
-        }
-
-        $layerObj->scaleRanges = $scaleranges;
-        array_push($scaleObj->layers, $layerObj);
-    }
- }  
-
+        $layerObj = NULL;
+        $layerObj->uniqueId = $i;
+
+        /*generate the legend icons here*/
+        $nScaleRanges = count($scaleranges);
+        for ($j=0; $j<$nScaleRanges; $j++)
+        {
+            $nStyles = count($scaleranges[$j]->styles);
+            for ($k=0; $k<$nStyles; $k++)
+            {
+                $nClassIndex = $scaleranges[$j]->styles[$k]->index;
+                $oClass = $layer->getClass($nClassIndex);
+                
+                if ($oClass) {
+                  $oImg = $oClass->createLegendIcon($nIconWidth, $nIconHeight);
+                  array_push($aIcons, $oImg);
+                  $scaleranges[$j]->styles[$k]->icon_x = ($nTotalClasses*$nIconWidth);
+                  $scaleranges[$j]->styles[$k]->icon_y=0;
+                  $nTotalClasses++;
+                }
+            }
+        }
+
+        $layerObj->scaleRanges = $scaleranges;
+        array_push($scaleObj->layers, $layerObj);
+    }
+ }  
+
 	if ($nTotalClasses > 0) {
-     //set the image path and image dir based on what fusion config file
-     $configObj = $_SESSION['fusionConfig'];
-
-     $original_imagepath = $oMap->web->imagepath;
-     $original_imageurl = $oMap->web->imageurl;
-
-     if (isset($configObj->mapserver->imagePath) && $configObj->mapserver->imagePath !="")
-        $oMap->web->set("imagepath", $configObj->mapserver->imagePath);
-     
-     if(isset($configObj->mapserver->imageUrl) && $configObj->mapserver->imageUrl!="")
-         $oMap->web->set("imageurl", $configObj->mapserver->imageUrl);
-
-     //build and image containing all the icons and return url
-     $nTmpWidth = $oMap->width;
-     $nTmpHeight = $oMap->height;
-    
-     $oMap->set("width", $nTotalClasses*$nIconWidth);
-     $oMap->set("height", $nIconHeight);
-     $oImage = $oMap->prepareImage();
-
-     $oMap->set("width", $nTmpWidth);
-     $oMap->set("height", $nTmpHeight);
-    
-     for ($i=0; $i<$nTotalClasses;$i++)
-       $oImage->pasteImage($aIcons[$i], -1, $i*$nIconWidth, 0);
-
-     $scaleObj->icons_url = $oImage->saveWebImage();
-     $scaleObj->icons_width = $nIconWidth;
-     $scaleObj->icons_height = $nIconHeight;
-
-      $oMap->web->set("imagepath", $original_imagepath);
-      $oMap->web->set("imageurl", $original_imageurl);
+     //set the image path and image dir based on what fusion config file
+     $configObj = $_SESSION['fusionConfig'];
+
+     $original_imagepath = $oMap->web->imagepath;
+     $original_imageurl = $oMap->web->imageurl;
+
+     if (isset($configObj->mapserver->imagePath) && $configObj->mapserver->imagePath !="")
+        $oMap->web->set("imagepath", $configObj->mapserver->imagePath);
+     
+     if(isset($configObj->mapserver->imageUrl) && $configObj->mapserver->imageUrl!="")
+         $oMap->web->set("imageurl", $configObj->mapserver->imageUrl);
+
+     //build and image containing all the icons and return url
+     $nTmpWidth = $oMap->width;
+     $nTmpHeight = $oMap->height;
+    
+     $oMap->set("width", $nTotalClasses*$nIconWidth);
+     $oMap->set("height", $nIconHeight);
+     $oImage = $oMap->prepareImage();
+
+     $oMap->set("width", $nTmpWidth);
+     $oMap->set("height", $nTmpHeight);
+    
+     for ($i=0; $i<$nTotalClasses;$i++)
+       $oImage->pasteImage($aIcons[$i], -1, $i*$nIconWidth, 0);
+
+     $scaleObj->icons_url = $oImage->saveWebImage();
+     $scaleObj->icons_width = $nIconWidth;
+     $scaleObj->icons_height = $nIconHeight;
+
+      $oMap->web->set("imagepath", $original_imagepath);
+      $oMap->web->set("imageurl", $original_imageurl);
 	}
 }
 
@@ -275,13 +280,13 @@
     $aReplace[4] = "_";
     $aReplace[5] = "_";
     $aReplace[6] = "_";
-      
+      
     return str_replace($aInvalidLayerNameCharacters,$aReplace,$szLayerName);
 }
-
-header('Content-type: application/json');
-header('X-JSON: true');
-
-echo var2json($scaleObj);
-exit;
+
+header('Content-type: application/json');
+header('X-JSON: true');
+
+echo var2json($scaleObj);
+exit;
 ?>
\ No newline at end of file

Modified: trunk/layers/MapServer/php/Query.php
===================================================================
--- trunk/layers/MapServer/php/Query.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/php/Query.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -34,6 +34,9 @@
 include ("Utilities.php");
 include('../../../common/php/Utilities.php');
 
+header('Content-type: application/json');
+header('X-JSON: true');
+
 /* the name of the layer in the map to query */
 if ($_REQUEST['layers'] != '') {
     $layers = explode(',',$_REQUEST['layers']);
@@ -54,8 +57,9 @@
         $variant = 'inside';
     }
 }
-/* a filter expression to apply, in the form of an FDO SQL statement */
-$filter = isset($_REQUEST['filter']) ? str_replace(array('*', '"'), array('%', "'"),html_entity_decode(urldecode( $_REQUEST['filter']))) : false;
+/* a filter expression to apply, in the form of an MapServer expression statement */
+$filter = isset($_REQUEST['filter']) ? html_entity_decode(urldecode( $_REQUEST['filter'])) : false;
+$filterItem = isset($_REQUEST['filterItem']) ? html_entity_decode(urldecode( $_REQUEST['filterItem'])) : false;
 //echo "<!-- filter: $filter -->\n";
 /* a spatial filter in the form on a WKT geometry */
 $spatialFilter = (isset($_REQUEST['spatialfilter']) && $_REQUEST['spatialfilter'] != '') ? urldecode($_REQUEST['spatialfilter']) : false;
@@ -74,7 +78,7 @@
 $versArray = explode(" ",$msVersion);
 $versNumber = $versArray[2];
 $versParts = explode(".", $versNumber);
-$queryTemplate = "query.qry";
+$queryTemplate = "query.qy";
 //convert to an integer value to make the comparison easier
 $versValue = $versParts[0]*100 + $versParts[1]*10 + $versParts[2];
 if ($versValue >= 522) {
@@ -91,7 +95,7 @@
 $bExtendSelection = isset($_REQUEST['extendselection']) && strcasecmp($_REQUEST['extendselection'], 'true') == 0;
 if ($bExtendSelection) {
     //TODO figure out how to load an existing selection here
-    $oMap->loadquery(getSessionSavePath()."query.qry");
+    $oMap->loadquery(getSessionSavePath()."query.qy");
 }
 
 $bComputedProperties = isset($_REQUEST['computed']) && strcasecmp($_REQUEST['computed'], 'true') == 0; 
@@ -103,7 +107,6 @@
     $nLayers = $oMap->numlayers;
     $bAllLayers = true;
 }
-
 $result = NULL;
 $result->hasSelection = false;
 $result->layers = array();
@@ -118,9 +121,91 @@
         $oLayer->type ==  MS_LAYER_CIRCLE ||  $oLayer->type == MS_LAYER_CHART) {
         continue;            
     }
+    if ($spatialFilter !== false ) {
+      if (@$oLayer->queryByShape($oSpatialFilter) == MS_SUCCESS) {
+          $result->hasSelection = true;
+          $layerName = $oLayer->name;
+          array_push($result->layers, $layerName);
+          $result->$layerName->featureCount = $oLayer->getNumResults();
+          //TODO: dump out the extents of the selection
+      }
+    }
+    if ($filter !== false ) {
+      if ($oLayer->connectiontype == MS_POSTGIS && $filterItem != '') {
+        $f = $filter;
+        $filter = $filterItem . ' IN (' . $filter . ')';
+      }
+      if (@$oLayer->queryByAttributes($filterItem,$filter,MS_MULTIPLE) == MS_SUCCESS) {
+      //if (@$oLayer->queryByAttributes($filterItem,'([REG_CODE] eq 61)',MS_MULTIPLE) == MS_SUCCESS) {
+      //if (@$oLayer->queryByAttributes('NAME_E','/.*Buffalo.*/gi',MS_MULTIPLE) == MS_SUCCESS) {
+          $result->hasSelection = true;
+          $layerName = $oLayer->name;
+          array_push($result->layers, $layerName);
+          $result->$layerName->featureCount = $oLayer->getNumResults();
+          //TODO: dump out the extents of the selection
+      }
+      if ($oLayer->connectiontype == MS_POSTGIS && $filterItem != '') {
+        $filter = $f;
+      }
+    }
 
+    if ($bExtendSelection) {
+    } else {
+    }
+}
+if ($bExtendSelection) {
+}
+
+/************************************************************************/
+/*         Save the query file here before doing any raster queries     */
+/************************************************************************/
+if ($result->hasSelection) {
+    $oMap->savequery(getSessionSavePath()."query.qy");
+    $result->queryFile = getSessionSavePath()."query.qy";
+ }
+
+/*raster query: limit the result to 100 if it is not already set in the map fle*/
+for ($i=0; $i<$nLayers; $i++) {
+    if (!$bAllLayers) {
+        $oLayer = $oMap->GetLayerByName($layers[$i]);
+    } else {
+        $oLayer = $oMap->GetLayer($i);
+    }
+    $oLayer->set('tolerance', 0);
+    if ($oLayer->type != MS_LAYER_RASTER)
+        continue;            
     
-    if (@$oLayer->queryByShape($oSpatialFilter) == MS_SUCCESS) {
+    $aProcessings = $oLayer->getprocessing();
+
+    $nCount = count($aProcessings);
+    $bRasterMaxSet = 0;
+    for ($i=0;$i<$nCount; $i++)
+    {
+        $aKeyVal = explode("=", $aProcessings[$i]);
+        if (count($aKeyVal) == 2 && 
+            strcasecmp(trim($aKeyVal[0]), "RASTER_QUERY_MAX_RESULT") == 0)
+        {
+            $bRasterMaxSet = 1;
+            break;
+        }
+    }
+
+    if (!$bRasterMaxSet)
+      $oLayer->setprocessing("RASTER_QUERY_MAX_RESULT=100");
+
+    /*are we doing a point query? In that case maxfeatures was set to 1*/
+    /*this is not ideal but It is better to use querybypoint when we do point query and
+      a query by shape when we do other type of queries*/
+    if (isset($_REQUEST['maxfeatures']) && $_REQUEST['maxfeatures'] == '1')
+    {
+        $oCenterPoint = ms_newpointobj();
+        $oPoint = $oSpatialFilter->getCentroid();
+        $status = $oLayer->queryByPoint($oPoint, MS_SINGLE, -1);
+    }
+    else
+      $status = @$oLayer->queryByShape($oSpatialFilter);
+
+    if ($status == MS_SUCCESS) {
         $result->hasSelection = true;
         $layerName = $oLayer->name;
         array_push($result->layers, $layerName);
@@ -132,15 +217,8 @@
     } else {
     }
 }
-if ($bExtendSelection) {
-}
 
-header('Content-type: application/json');
-header('X-JSON: true');
 if ($result->hasSelection) {
-    $oMap->savequery(getSessionSavePath()."query.qry");
-    $result->queryFile = getSessionSavePath()."query.qry";
-
     /*holds selection array*/
     $properties = NULL;
     $properties->layers = array();
@@ -168,7 +246,8 @@
         $properties->$layerName->propertyvalues = array();
         $properties->$layerName->propertytypes = array();
         $properties->$layerName->values = array();
-
+        $aQueryItems = array();
+        
         $properties->$layerName->metadatanames= array();
         array_push($properties->$layerName->metadatanames, 'dimension');
         array_push($properties->$layerName->metadatanames, 'bbox');
@@ -199,7 +278,7 @@
                   $token = strtok($tokenSeparator);
               }
             }
-          
+
             // checking if metadata "query_exclude_items" is set
             $metadataItems = $oLayer->getMetaData('query_exclude_items');
             if ($metadataItems != "") {

Modified: trunk/layers/MapServer/php/RestoreState.php
===================================================================
--- trunk/layers/MapServer/php/RestoreState.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/php/RestoreState.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -5,10 +5,10 @@
 include(dirname(__FILE__).'/Common.php');
 include('../../../common/php/Utilities.php');
 
-header('Content-type: application/json');
-header('X-JSON: true');
+//header('Content-type: application/json');
+//header('X-JSON: true');
 
-isset($_REQUEST["session"])?$szSessionID = $_REQUEST["session"]:$szSessionID = NULL ;
+isset($_POST["session"])?$szSessionID = $_POST["session"]:$szSessionID = NULL ;
 isset($_REQUEST["id"])?$loadSessionID = $_REQUEST["id"]:$loadSessionID = NULL ;
 
 $oReturn = NULL;
@@ -39,6 +39,19 @@
 
                 $oMap = ms_newMapObj($oReturn->loadmap);
                 checkForGMLLayers($szDestination);
+                // turn off all the layers
+                for ($i=0;$i<$oMap->numlayers;$i++){
+                    $oLayer=$oMap->GetLayer($i);
+                    $oLayer->set("status", MS_OFF);
+                    }
+                // turn on the vis layers
+                $aLayers = explode(",",$oReturn->vislayers);
+                foreach($aLayers as $szName){
+                    $oLayer = $oMap->getLayerByName($szName);
+                    if($oLayer){
+                        $oLayer->set("status", MS_ON);
+                    }
+                }
                 $oMap->save($oReturn->loadmap);
                 echo var2json($oReturn);
             }
@@ -57,6 +70,7 @@
     die("{'error':'PHP Session path is invalid.'}");
     }
 
+
 function checkForGMLLayers($szDestination){
     global $oMap;
     @$oLayer = $oMap->getLayerByName("LWEGML-Point");

Modified: trunk/layers/MapServer/php/Selection.php
===================================================================
--- trunk/layers/MapServer/php/Selection.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/php/Selection.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -44,9 +44,12 @@
 
 
 
-if (isset($_REQUEST['queryfile']) && $_REQUEST['queryfile'] != "")
+if ((isset($_REQUEST['queryfile']) && $_REQUEST['queryfile'] != "") ||
+    isset($_SESSION['selection_array']))
+  
 {
-    $oMap->loadquery($_REQUEST['queryfile']);
+    if (isset($_REQUEST['queryfile']) && $_REQUEST['queryfile'] != "")
+      $oMap->loadquery($_REQUEST['queryfile']);
 
 
     if (isset($_SESSION['selection_array']))
@@ -56,14 +59,14 @@
         $aLayers = array();
         if (isset($_REQUEST['layers']) && $_REQUEST['layers'] !='')
         {
-            $aLayers = split(",", $_REQUEST['layers']);
+            $aLayers = explode(",", $_REQUEST['layers']);
             $bAllLayers = 0;
         }
 
         $aStartCount = array();
         if (isset($_REQUEST['startcount']) && $_REQUEST['startcount'] !='')
         {
-            $aStartCount =  split(",", $_REQUEST['startcount']);
+            $aStartCount =  explode(",", $_REQUEST['startcount']);
         }
 
         /* if number of layers and number of startcount should be the same */
@@ -106,7 +109,7 @@
                         {
                             if ($aLayers[$j] == $layerName)
                             {
-                                $aIndiceCount = split(':', $aStartCount[$j]);
+                                $aIndiceCount = explode(':', $aStartCount[$j]);
                                 if (count($aIndiceCount) == 2)
                                 {
                                     $start = $aIndiceCount[0];
@@ -184,7 +187,7 @@
     global $properties;
     foreach($properties->$layername->metadatanames as $key=>$value){
         if($value == "bbox"){
-            $aBBox = split(",",$properties->$layername->metadata[0][$key]);
+            $aBBox = explode(",",$properties->$layername->metadata[0][$key]);
             $oBBox->minx = $aBBox[0];
             $oBBox->miny = $aBBox[1];
             $oBBox->maxx = $aBBox[2];

Copied: trunk/layers/MapServer/php/SelectionCSV.php (from rev 2248, sandbox/jxlib-3.0/layers/MapServer/php/SelectionCSV.php)
===================================================================
--- trunk/layers/MapServer/php/SelectionCSV.php	                        (rev 0)
+++ trunk/layers/MapServer/php/SelectionCSV.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,89 @@
+<?php
+/**
+ * Selection
+ *
+ * $Id: Selection.php 1806 2009-03-04 20:38:53Z pdeschamps $
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*****************************************************************************
+ * Purpose: Get all attribute informations for elements in the 
+ * current selection
+ *****************************************************************************/
+
+/* set up the session */
+include ("Common.php");
+include('../../../common/php/Utilities.php');
+include ("Utilities.php");
+
+//next 3 lines required for this to work in IE
+header("Pragma: public");
+header("Expires: 0");
+header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+header('Content-disposition: attachment; filename=selection.csv');
+header('Content-type: text/csv');
+
+if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
+    $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
+}
+
+$result = NULL;
+$result->layers = array();
+
+if (isset($_REQUEST['queryfile']) && $_REQUEST['queryfile'] != "")
+{
+    $oMap->loadquery($_REQUEST['queryfile']);
+
+
+    if (isset($_SESSION['selection_array']))
+    {
+        //print_r($_SESSION['selection_array']);
+        $bAllLayers = 1;
+        $aLayers = array();
+        if (isset($_REQUEST['layers']) && $_REQUEST['layers'] !='')
+        {
+            $aLayers = split(",", $_REQUEST['layers']);
+            $bAllLayers = 0;
+        }
+
+        $properties = $_SESSION['selection_array'];
+        $aSelectedLayers = $properties->layers;
+        if (count($aSelectedLayers) > 0)
+        {
+            for ($i=0; $i<count($aSelectedLayers); $i++)
+            {
+                $layerName =  $aSelectedLayers[$i];
+                echo "Layer: ".$layerName."\n";
+                if (($bAllLayers || in_array($layerName, $aLayers)) &&
+                    $properties->$layerName->numelements > 0)
+                {
+                    echo implode(",",$properties->$layerName->propertyvalues)."\n";
+                    for ($j=0; $j<count($properties->$layerName->values); $j++)
+                    {
+                      echo implode(",",$properties->$layerName->values[$j])."\n";
+                    }
+                }
+            }
+        }
+    }
+}
+
+?>

Modified: trunk/layers/MapServer/php/Session.php
===================================================================
--- trunk/layers/MapServer/php/Session.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/layers/MapServer/php/Session.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -62,489 +62,498 @@
     $GLOBALS['bLockSession'] = false;
 }
 
-/**
- * _open Called by PHP session manager when a session is opened.
- * We just set the save path and session name into global variable.
- *
- * @param szSavePath String containing the absolute path where to
- *        save session info.
- * @param szSessionName String contqaining the session name.
- */
-function _open($szSavePath, $szSessionName)
-{
-    if ($GLOBALS["bDebug"])
-    {
-      debug_msg("Opened() $szSavePath, $szSessionName");
-      debug_msg("session_id() says ".session_id());
-    }
+if (!function_exists("_open")) {
+  /**
+   * _open Called by PHP session manager when a session is opened.
+   * We just set the save path and session name into global variable.
+   *
+   * @param szSavePath String containing the absolute path where to
+   *        save session info.
+   * @param szSessionName String contqaining the session name.
+   */
+  function _open($szSavePath, $szSessionName)
+  {
+      if ($GLOBALS["bDebug"])
+      {
+        debug_msg("Opened() $szSavePath, $szSessionName");
+        debug_msg("session_id() says ".session_id());
+      }
 
-    $GLOBALS['gszSessSavePath'] = $szSavePath;
-    $GLOBALS['gszSessName'] = $szSessionName;
-    $GLOBALS['gszSessId'] = session_id();
+      $GLOBALS['gszSessSavePath'] = $szSavePath;
+      $GLOBALS['gszSessName'] = $szSessionName;
+      $GLOBALS['gszSessId'] = session_id();
 
-    $szSessionDir = $GLOBALS['gszSessSavePath']."/sess_".$GLOBALS['gszSessId'];
-    $szSessionFile = $szSessionDir."/session_file";
-    $szLockFile = $szSessionDir."/lock";
+      $szSessionDir = $GLOBALS['gszSessSavePath']."/sess_".$GLOBALS['gszSessId'];
+      $szSessionFile = $szSessionDir."/session_file";
+      $szLockFile = $szSessionDir."/lock";
 
-    clearstatcache();
-    if (!file_exists($szSessionDir))
-    {
-        mkdir($szSessionDir);
-    }
+      clearstatcache();
+      if (!file_exists($szSessionDir))
+      {
+          mkdir($szSessionDir);
+      }
 
-    if ( PHP_OS == "WINNT" || PHP_OS == "WIN32" || $GLOBALS['bLockSession'])
-    {
-        $i=0;
-        while ($i < ini_get("max_execution_time") - 2 ) //wait at most 2 seconds less than the execution time for the lock
-        {
-            clearstatcache();
-            if (!file_exists($szLockFile))
-            {
-                break;
-            }
-            if ($GLOBALS["bDebug"])
-            {
-                debug_msg("pausing in open()");
-            }
-            $i++;
-            sleep(1);
-        }
-        $fh = fopen( $szLockFile, "w+" );
-        if ($fh !== false)
-        {
-            fwrite( $fh, "1" );
-            fclose($fh);
-            if ($GLOBALS["bDebug"])
-            {
-                debug_msg("created lock file in open()");
-            }
-        }
-    }
+      if ( PHP_OS == "WINNT" || PHP_OS == "WIN32" || $GLOBALS['bLockSession'])
+      {
+          $i=0;
+          while ($i < ini_get("max_execution_time") - 2 ) //wait at most 2 seconds less than the execution time for the lock
+          {
+              clearstatcache();
+              if (!file_exists($szLockFile))
+              {
+                  break;
+              }
+              if ($GLOBALS["bDebug"])
+              {
+                  debug_msg("pausing in open()");
+              }
+              $i++;
+              sleep(1);
+          }
+          $fh = fopen( $szLockFile, "w+" );
+          if ($fh !== false)
+          {
+              fwrite( $fh, "1" );
+              fclose($fh);
+              if ($GLOBALS["bDebug"])
+              {
+                  debug_msg("created lock file in open()");
+              }
+          }
+      }
 
-    return(true);
-}
+      return(true);
+  }
 
-/**
- * _close Called by PHP session manager when a session is closed,
- * not destroyed. In this case we do nothing.
- */
-function _close()
-{
-    if ($GLOBALS["bDebug"])
-    {
-      debug_msg("Closed()");
-    }
-    if ( PHP_OS == "WINNT" || PHP_OS == "WIN32" || $GLOBALS['bLockSession'])
-    {
-        $szLockFile = $GLOBALS['gszSessSavePath']."/sess_".$GLOBALS["gszSessId"]."/lock";
-        if ($GLOBALS["bDebug"])
-        {
-            debug_msg("checking lock file $szLockFile");
-        }
-        clearstatcache();
-        if ( @is_file( $szLockFile )  )
-        {
-            @unlink( $szLockFile );
-            if ($GLOBALS["bDebug"])
-            {
-                debug_msg("removed lock file $szLockFile");
-            }
-        }
-        else
-        {
-            if ($GLOBALS["bDebug"])
-            {
-                debug_msg("lock file $szLockFile is missing.");
-            }
+  /**
+   * _close Called by PHP session manager when a session is closed,
+   * not destroyed. In this case we do nothing.
+   */
+  function _close()
+  {
+      if ($GLOBALS["bDebug"])
+      {
+        debug_msg("Closed()");
+      }
+      if ( PHP_OS == "WINNT" || PHP_OS == "WIN32" || $GLOBALS['bLockSession'])
+      {
+          $szLockFile = $GLOBALS['gszSessSavePath']."/sess_".$GLOBALS["gszSessId"]."/lock";
+          if ($GLOBALS["bDebug"])
+          {
+              debug_msg("checking lock file $szLockFile");
+          }
+          clearstatcache();
+          if ( @is_file( $szLockFile )  )
+          {
+              @unlink( $szLockFile );
+              if ($GLOBALS["bDebug"])
+              {
+                  debug_msg("removed lock file $szLockFile");
+              }
+          }
+          else
+          {
+              if ($GLOBALS["bDebug"])
+              {
+                  debug_msg("lock file $szLockFile is missing.");
+              }
 
-        }
-    }
-    return(true);
-}
+          }
+      }
+      return(true);
+  }
 
-/**
- * _read Called by PHP session manager when the session file
- * is read. In this case we just return the file content of
- * session_file file.
- */
-function _read($szId)
-{
-    $GLOBALS["gszSessId"] = $szId;
+  /**
+   * _read Called by PHP session manager when the session file
+   * is read. In this case we just return the file content of
+   * session_file file.
+   */
+  function _read($szId)
+  {
+      $GLOBALS["gszSessId"] = $szId;
 
-    if ($GLOBALS["bDebug"])
-    {
-      debug_msg("Read() $szId");
-    }
+      if ($GLOBALS["bDebug"])
+      {
+        debug_msg("Read() $szId");
+      }
 
-    $szSessionDir = $GLOBALS['gszSessSavePath']."/sess_".$szId;
-    $szSessionFile = $szSessionDir."/session_file";
+      $szSessionDir = $GLOBALS['gszSessSavePath']."/sess_".$szId;
+      $szSessionFile = $szSessionDir."/session_file";
 
-    clearstatcache();
-    if (!file_exists($szSessionDir))
-    {
-        mkdir($szSessionDir);
-    }
+      clearstatcache();
+      if (!file_exists($szSessionDir))
+      {
+          mkdir($szSessionDir);
+      }
 
-    if ($fp = @fopen($szSessionFile, "r"))
-    {
-        $szSessionData = fread($fp, filesize($szSessionFile));
-        fclose( $fp );
-        return($szSessionData);
-    }
-    else
-    {
-        return(""); // Must return "" here.
-    }
-}
+      if ($fp = @fopen($szSessionFile, "r"))
+      {
+          $szSessionData = fread($fp, filesize($szSessionFile));
+          fclose( $fp );
+          return($szSessionData);
+      }
+      else
+      {
+          return(""); // Must return "" here.
+      }
+  }
 
-/**
- * _write Called by PHP session manager when session should be
- * saved.
- *
- * @param szId String containing the unique identifier of current
- *             session.
- * @param szSessionData String containig the session file content
- *                      to be saved.
- */
-function _write($szId, $szSessionData)
-{
-    $result = false;
-    if ($GLOBALS["bDebug"])
-    {
-      debug_msg("Write() $szId $szSessionData");
-    }
+  /**
+   * _write Called by PHP session manager when session should be
+   * saved.
+   *
+   * @param szId String containing the unique identifier of current
+   *             session.
+   * @param szSessionData String containig the session file content
+   *                      to be saved.
+   */
+  function _write($szId, $szSessionData)
+  {
+      $result = false;
+      if ($GLOBALS["bDebug"])
+      {
+        debug_msg("Write() $szId $szSessionData");
+      }
 
-    $szSessionFile = $GLOBALS['gszSessSavePath']."/sess_".$szId.
-                     "/session_file";
+      $szSessionFile = $GLOBALS['gszSessSavePath']."/sess_".$szId.
+                       "/session_file";
 
-    // touch command don't works under windows for directory.
-    // since it is only needed for unix (for now) I'll test
-    // the platform and bypass this call is windows platform.
-    if ( !(PHP_OS == "WINNT" || PHP_OS == "WIN32") )
-        @touch($GLOBALS['gszSessSavePath']."/sess_".$szId);
+      // touch command don't works under windows for directory.
+      // since it is only needed for unix (for now) I'll test
+      // the platform and bypass this call is windows platform.
+      if ( !(PHP_OS == "WINNT" || PHP_OS == "WIN32") )
+          @touch($GLOBALS['gszSessSavePath']."/sess_".$szId);
 
-    if ($fp = @fopen($szSessionFile, "w"))
-    {
-        $result = fwrite($fp, $szSessionData);
-        fclose($fp);
+      if ($fp = @fopen($szSessionFile, "w"))
+      {
+          $result = fwrite($fp, $szSessionData);
+          fclose($fp);
 
-    }
-    return($result);
-}
+      }
+      return($result);
+  }
 
-/**
- * _destroy Called by PHP session manager when it should be explicitly
- * destroyed now.
- */
-function _destroy($szId)
-{
-    if ($GLOBALS["bDebug"])
-    {
-      debug_msg("Destroy $szId");
-    }
-    if ( (PHP_OS == "WINNT" || PHP_OS == "WIN32"|| $GLOBALS['bLockSession']) )
-    {
-        $szLockFile = $GLOBALS['gszSessSavePath']."/sess_".$GLOBALS["gszSessId"]."/lock";
+  /**
+   * _destroy Called by PHP session manager when it should be explicitly
+   * destroyed now.
+   */
+  function _destroy($szId)
+  {
+      if ($GLOBALS["bDebug"])
+      {
+        debug_msg("Destroy $szId");
+      }
+      if ( (PHP_OS == "WINNT" || PHP_OS == "WIN32"|| $GLOBALS['bLockSession']) )
+      {
+          $szLockFile = $GLOBALS['gszSessSavePath']."/sess_".$GLOBALS["gszSessId"]."/lock";
 
-        if ( @is_file( $szLockFile )  )
-        {
-            @unlink( $szLockFile );
-        }
-    }
-  /*
-    if ($GLOBALS['gszGarbageColectionCallBackFunction'] != "")
-    {
-        if (function_exists($GLOBALS['gszGarbageColectionCallBackFunction']))
-            eval($GLOBALS['gszGarbageColectionCallBackFunction']);
-    }
+          if ( @is_file( $szLockFile )  )
+          {
+              @unlink( $szLockFile );
+          }
+      }
+    /*
+      if ($GLOBALS['gszGarbageColectionCallBackFunction'] != "")
+      {
+          if (function_exists($GLOBALS['gszGarbageColectionCallBackFunction']))
+              eval($GLOBALS['gszGarbageColectionCallBackFunction']);
+      }
 
-    $bReturn = true;
+      $bReturn = true;
 
-    if (!$szDir = @opendir($GLOBALS['gszSessSavePath']))
-    {
-        return false;
-    }
+      if (!$szDir = @opendir($GLOBALS['gszSessSavePath']))
+      {
+          return false;
+      }
 
-    while($szFile = readdir($szDir))
-    {
-        if (!strstr($szFile,'sess_'.$szId))
-            continue;
+      while($szFile = readdir($szDir))
+      {
+          if (!strstr($szFile,'sess_'.$szId))
+              continue;
 
-        $bReturn = (deleteDirectory($GLOBALS['gszSessSavePath']."/".
-                                    $szFile)) ? $bReturn : false;
-    }
-    closedir($szDir);
+          $bReturn = (deleteDirectory($GLOBALS['gszSessSavePath']."/".
+                                      $szFile)) ? $bReturn : false;
+      }
+      closedir($szDir);
 
-    return $bReturn;
-  */
-  return true;
-}
+      return $bReturn;
+    */
+    return true;
+  }
 
-/**
- * _gc Called by PHP session manager when a session is started or
- * register (not all the time) depending og session.gc_probability
- */
-function _gc($nMaxLifeTime)
-{
-   if ($GLOBALS["bDebug"])
-    {
-      debug_msg("_gc called");
-    }
-    if ($GLOBALS['gszGarbageColectionCallBackFunction'] != "")
-    {
-        if (function_exists($GLOBALS['gszGarbageColectionCallBackFunction']))
-            eval($GLOBALS['gszGarbageColectionCallBackFunction']);
-    }
+  /**
+   * _gc Called by PHP session manager when a session is started or
+   * register (not all the time) depending og session.gc_probability
+   */
+  function _gc($nMaxLifeTime)
+  {
+     if ($GLOBALS["bDebug"])
+      {
+        debug_msg("_gc called");
+      }
+      if ($GLOBALS['gszGarbageColectionCallBackFunction'] != "")
+      {
+          if (function_exists($GLOBALS['gszGarbageColectionCallBackFunction']))
+              eval($GLOBALS['gszGarbageColectionCallBackFunction']);
+      }
 
-    if ( !(PHP_OS == "WINNT" || PHP_OS == "WIN32") )
-    {
-      @touch($GLOBALS['gszSessSavePath']."/deleteme", time()-$nMaxLifeTime*60);
-      system("find ".$GLOBALS['gszSessSavePath']." -name sess_* ! -newer ".
-             $GLOBALS['gszSessSavePath']."/deleteme -exec rm -rf {} \; ");
+      if ( !(PHP_OS == "WINNT" || PHP_OS == "WIN32") )
+      {
+        @touch($GLOBALS['gszSessSavePath']."/deleteme", time()-$nMaxLifeTime*60);
+        system("find ".$GLOBALS['gszSessSavePath']." -name sess_* ! -newer ".
+               $GLOBALS['gszSessSavePath']."/deleteme -exec rm -rf {} \; ");
 
-      return true;
-    }
+        return true;
+      }
 
-    $bReturn = true;
+      $bReturn = true;
 
-    if (!$hDir = @opendir($GLOBALS['gszSessSavePath']))
-    {
-        return false;
-    }
+      if (!$hDir = @opendir($GLOBALS['gszSessSavePath']))
+      {
+          return false;
+      }
 
-    while($szFile = readdir($hDir))
-    {
-        if (!strstr($szFile,'sess_'))
-            continue;
+      while($szFile = readdir($hDir))
+      {
+          if (!strstr($szFile,'sess_'))
+              continue;
 
-        if (strpos($szFile,'sess_') != 0)
-            continue;
+          if (strpos($szFile,'sess_') != 0)
+              continue;
 
-        $szSessionDir = $GLOBALS['gszSessSavePath']."/".$szFile;
-        $szSessionFile = $szSessionDir."/session_file";
+          $szSessionDir = $GLOBALS['gszSessSavePath']."/".$szFile;
+          $szSessionFile = $szSessionDir."/session_file";
 
-        if (!($mtime = @filemtime($szSessionFile)))
-        {
-            $bReturn=false;
-            continue;
-        }
+          if (!($mtime = @filemtime($szSessionFile)))
+          {
+              $bReturn=false;
+              continue;
+          }
 
-        if (time() > $mtime + $nMaxLifeTime)
-        {
-            $bReturn = (deleteDirectory($szSessionDir)) ? $bReturn : false;
-        }
-        closedir($hDir);
+          if (time() > $mtime + $nMaxLifeTime)
+          {
+              $bReturn = (deleteDirectory($szSessionDir)) ? $bReturn : false;
+          }
+          closedir($hDir);
 
-        return $bReturn;
-    }
-}
+          return $bReturn;
+      }
+  }
 
-////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////
+  ////////////////////////////////////////////////////////////////
 
-function deleteDirectory($szFile)
-{
-    if (PHP_OS != "WINNT" && PHP_OS != "WIN32")
-        chmod($szFile,0777);
+  function deleteDirectory($szFile)
+  {
+      if (PHP_OS != "WINNT" && PHP_OS != "WIN32")
+          chmod($szFile,0777);
 
-    if (is_dir($szFile))
-    {
-        $handle = opendir($szFile);
-        while($szFileName = readdir($handle))
-        {
-            if ($szFileName != "." && $szFileName != "..")
-            {
-                deleteDirectory($szFile."/".$szFileName);
-            }
-        }
-        closedir($handle);
-        rmdir($szFile);
-    }
-    else
-    {
-        unlink($szFile);
-    }
-}
+      if (is_dir($szFile))
+      {
+          $handle = opendir($szFile);
+          while($szFileName = readdir($handle))
+          {
+              if ($szFileName != "." && $szFileName != "..")
+              {
+                  deleteDirectory($szFile."/".$szFileName);
+              }
+          }
+          closedir($handle);
+          rmdir($szFile);
+      }
+      else
+      {
+          unlink($szFile);
+      }
+  }
 
-function installSessionDirectoryHandler($szGCCallBack="")
-{
-    $GLOBALS['gszGarbageColectionCallBackFunction'] = $szGCCallBack;
+  function installSessionDirectoryHandler($szGCCallBack="")
+  {
+      
+      if (!isset($GLOBALS['gszSessionDirectoryHandlerInstalled'])) {
+          $GLOBALS['gszGarbageColectionCallBackFunction'] = $szGCCallBack;          
+          // Set handler functions
+          session_set_save_handler("_open",
+                                   "_close",
+                                   "_read",
+                                   "_write",
+                                   "_destroy",
+                                   "_gc");
+          $GLOBALS['gszSessionDire ctoryHandlerInstalled'] = true;
+      }
+      
+  }
 
-    // Set handler functions
-    session_set_save_handler("_open",
-                             "_close",
-                             "_read",
-                             "_write",
-                             "_destroy",
-                             "_gc");
-}
-
-function initializeSession( $szSessName="sid", $szSessSavePath="", $szSessionID="" )
-{
+  function initializeSession( $szSessName="sid", $szSessSavePath="", $szSessionID="" )
+  {
     
-    if ($GLOBALS["bDebug"])
-    {
-      debug_msg("initializeSession( $szSessName, $szSessSavePath, $szSessionID )");
-    }
+      if ($GLOBALS["bDebug"])
+      {
+        debug_msg("initializeSession( $szSessName, $szSessSavePath, $szSessionID )");
+      }
 
     
-    //if session was run already don't execute again
-    if (isset($GLOBALS['session_started']))
-    {
-        return true;   
-    }
-    if ($szSessName == "")
-    {
-        echo "<FONT color=#FF0000>FATAL ERROR: Sessionname not specified</FONT>";
-        exit;
-    }
-    else
-        ini_set("session.name", $szSessName);
+      //if session was run already don't execute again
+      if (isset($GLOBALS['session_started']))
+      {
+          return true;   
+      }
+      if ($szSessName == "")
+      {
+          echo "<FONT color=#FF0000>FATAL ERROR: Sessionname not specified</FONT>";
+          exit;
+      }
+      else
+          ini_set("session.name", $szSessName);
 
-    if ($szSessSavePath != "")
-    {
-        ini_set("session.save_path", $szSessSavePath);
-    }
+      if ($szSessSavePath != "")
+      {
+          ini_set("session.save_path", $szSessSavePath);
+      }
 
-    clearstatcache();
-    // Check if save path is writable
-    if (!(file_exists(ini_get("session.save_path")) &&
-          is_writable(ini_get("session.save_path"))))
-    {
-        echo "<FONT COLOR=#DD0000>FATAL ERROR: Session save path (".ini_get("session.save_path").") doesn't exist or is not writable</FONT>";
-        exit;
-    }
+      clearstatcache();
+      // Check if save path is writable
+      if (!(file_exists(ini_get("session.save_path")) &&
+            is_writable(ini_get("session.save_path"))))
+      {
+          echo "<FONT COLOR=#DD0000>FATAL ERROR: Session save path (".ini_get("session.save_path").") doesn't exist or is not writable</FONT>";
+          exit;
+      }
 
-    //turn off cookies for propagating session ids
-    ini_set( "session.use_cookies", "0" );
+      //turn off cookies for propagating session ids
+      ini_set( "session.use_cookies", "0" );
 
-    // turn off tranparent SID (becuase of buffer problem)
-    ini_set( "session.use_trans_sid", "0" );
+      // turn off tranparent SID (becuase of buffer problem)
+      ini_set( "session.use_trans_sid", "0" );
 
-    // intialize tmp id
-    $szTmpID = "";
+      // intialize tmp id
+      $szTmpID = "";
 
-    // check both get and post variables
-    if ( isset($GLOBALS['_GET'][ini_get('session.name')]) )
-        $szTmpID = $GLOBALS['_GET'][ini_get('session.name')];
-    elseif (isset($GLOBALS['_POST'][ini_get('session.name')]))
-        $szTmpID = $GLOBALS['_POST'][ini_get('session.name')];
+      // check both get and post variables
+      if ( isset($GLOBALS['_GET'][ini_get('session.name')]) )
+          $szTmpID = $GLOBALS['_GET'][ini_get('session.name')];
+      elseif (isset($GLOBALS['_POST'][ini_get('session.name')]))
+          $szTmpID = $GLOBALS['_POST'][ini_get('session.name')];
 
-    // create new if necessary
-    if ( strlen( $szTmpID ) <= 0 )
-    {
-        if ($GLOBALS["bDebug"])
-        {
-            debug_msg("creating a new session because .$szTmpID. has zero characters ");
-        }
-        // create new and set IP flag
-        if ( strlen( $szSessionID ) > 0 )
-        {
-            $szTmpID = $szSessionID;
-        }
-        else            
-        {
-            $szTmpID = uniqid("");
-        }
-        $bNewSession = true;
-        if ($GLOBALS["bDebug"])
-        {
-            debug_msg("creating a new session with id ");
-        }
-   }
-    else
-        $bNewSession = false;
+      // create new if necessary
+      if ( strlen( $szTmpID ) <= 0 )
+      {
+          if ($GLOBALS["bDebug"])
+          {
+              debug_msg("creating a new session because .$szTmpID. has zero characters ");
+          }
+          // create new and set IP flag
+          if ( strlen( $szSessionID ) > 0 )
+          {
+              $szTmpID = $szSessionID;
+          }
+          else            
+          {
+              $szTmpID = uniqid("");
+          }
+          $bNewSession = true;
+          if ($GLOBALS["bDebug"])
+          {
+              debug_msg("creating a new session with id ");
+          }
+     }
+      else
+          $bNewSession = false;
 
-    // initialize flag variable
-    $bSessionOK = true;
+      // initialize flag variable
+      $bSessionOK = true;
 
-    // set the session ID
-    session_id( $szTmpID );
+      // set the session ID
+      session_id( $szTmpID );
 
-    // Check if session is expired
-    if (!$bNewSession)
-    {
-        $szSavePath = getSessionSavePath();
-        $szSessionFile = $szSavePath."/session_file";
+      // Check if session is expired
+      if (!$bNewSession)
+      {
+          $szSavePath = getSessionSavePath();
+          $szSessionFile = $szSavePath."/session_file";
 
-        if (file_exists($szSessionFile))
-            if ($atime=@filemtime($szSessionFile))
-                if (time() > $atime + ini_get("session.gc_maxlifetime"))
-                {
-                    $szTmpID = uniqid("");
+          if (file_exists($szSessionFile))
+              if ($atime=@filemtime($szSessionFile))
+                  if (time() > $atime + ini_get("session.gc_maxlifetime"))
+                  {
+                      $szTmpID = uniqid("");
 
-                    // reset the session ID
-                    session_id( $szTmpID );
+                      // reset the session ID
+                      session_id( $szTmpID );
 
-                    $bNewSession = true;
-                    $bSessionOK = false;
-                }
-    }
+                      $bNewSession = true;
+                      $bSessionOK = false;
+                  }
+      }
 
-    //start the session
-    session_start();
-    register_shutdown_function( "session_write_close" );
+      //start the session
+      session_start();
+      register_shutdown_function( "session_write_close" );
 
   
-    // set IP if a new session
-    if ( $bNewSession ) $_SESSION["gszRemoteAdd"] = $_SERVER["REMOTE_ADDR"];
+      // set IP if a new session
+      if ( $bNewSession ) $_SESSION["gszRemoteAdd"] = $_SERVER["REMOTE_ADDR"];
 
-/* ============================================================================
- * Check IP to see if it is the same
- * ========================================================================= */
+  /* ============================================================================
+   * Check IP to see if it is the same
+   * ========================================================================= */
 
-    // check if the IP has been set and validate
-    if ( isset( $_SESSION["gszRemoteAdd"] ) &&
-                                   strlen(trim($_SESSION["gszRemoteAdd"])) > 0 )
-    {
-        // check if IP matches current client
-        if ( trim( $_SESSION["gszRemoteAdd"] ) !=
-                                               trim( $_SERVER["REMOTE_ADDR"] ) )
-        {
-            // possible security breach void session
-            /* if the session address is the loopback interface then it is
-             * likely that the application was configured to use an external
-             * address but someone is trying to test locally using localhost
-             */
-            if ($_SESSION['gszRemoteAdd'] != '127.0.0.1')
-            {
-                $bSessionOK = false;
-            }
-        }
-    }
-    else
-    {
-        // possible security breach void session
-        $bSessionOK = false;
-    }
+      // check if the IP has been set and validate
+      if ( isset( $_SESSION["gszRemoteAdd"] ) &&
+                                     strlen(trim($_SESSION["gszRemoteAdd"])) > 0 )
+      {
+          // check if IP matches current client
+          if ( trim( $_SESSION["gszRemoteAdd"] ) !=
+                                                 trim( $_SERVER["REMOTE_ADDR"] ) )
+          {
+              // possible security breach void session
+              /* if the session address is the loopback interface then it is
+               * likely that the application was configured to use an external
+               * address but someone is trying to test locally using localhost
+               */
+              if ($_SESSION['gszRemoteAdd'] != '127.0.0.1')
+              {
+                  $bSessionOK = false;
+              }
+          }
+      }
+      else
+      {
+          // possible security breach void session
+          $bSessionOK = false;
+      }
     
-    // return success or failure and set global so we
-    // know session has been inited.
-    if ($bSessionOK)
-    {
-        $GLOBALS['session_started'] = true;
-    }
+      // return success or failure and set global so we
+      // know session has been inited.
+      if ($bSessionOK)
+      {
+          $GLOBALS['session_started'] = true;
+      }
     
         
-    return $bSessionOK;
+      return $bSessionOK;
 
-// end intializeSession() function
-}
+  // end intializeSession() function
+  }
 
-function getSessionSavePath()
-{
-    $szReturn  = ini_get("session.save_path")."/sess_".session_id()."/";
-    $szReturn = str_replace( "\\", "/", $szReturn );
-    return $szReturn;
+  function getSessionSavePath()
+  {
+      $szReturn  = ini_get("session.save_path")."/sess_".session_id()."/";
+      $szReturn = str_replace( "\\", "/", $szReturn );
+      return $szReturn;
+  }
+
+  function debug_msg( $szMsg )
+  {
+      list($usec, $sec) = explode(" ",microtime()); 
+      $ts = sprintf( "%s.%4d", date( "H:s", $sec), round( 10000 * $usec )); 
+      $fh = fopen($GLOBALS['szDebugDir']."session.log", "a+");
+      fwrite($fh, "$ts : ".$GLOBALS['szDebugPage']." : $szMsg\n");
+      fclose($fh);
+  }
+
+
 }
 
-function debug_msg( $szMsg )
-{
-    list($usec, $sec) = explode(" ",microtime()); 
-    $ts = sprintf( "%s.%4d", date( "H:s", $sec), round( 10000 * $usec )); 
-    $fh = fopen($GLOBALS['szDebugDir']."session.log", "a+");
-    fwrite($fh, "$ts : ".$GLOBALS['szDebugPage']." : $szMsg\n");
-    fclose($fh);
-}
 ?>

Modified: trunk/lib/ApplicationDefinition.js
===================================================================
--- trunk/lib/ApplicationDefinition.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/lib/ApplicationDefinition.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,1327 +1,1343 @@
-/**
- * Fusion.Lib.ApplicationDefinition
- *
- * $Id$
- *
- * Copyright (c) 2007, DM Solutions Group Inc.
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition
- *
- * Utility class to parse an application definition
- *
- */
-
-Fusion.Lib.ApplicationDefinition = OpenLayers.Class({
-    /**
-     * Property: mapGroups
-     *
-     * array of map groups, parsed from ApplicationDefinition.  A MapGroup
-     * consists of one or more Maps that can be combined into the same
-     * OpenLayers Map object
-     */
-    mapGroups: null,
-
-    /**
-     * Property: widgetSets
-     *
-     * array of widget sets (each one corresponding to a map) parsed
-     * from the ApplicationDefinition.
-     */
-    widgetSets: null,
-
-    /**
-     * Property: {Object} oBroker
-     *
-     * A Broker object that can communicate with a MapGuide instance
-     * in the case we are running against a MapGuide server
-     */
-    oBroker: null,
-
-    /**
-     * Property: {Array} searchDefinitions
-     *
-     * An array of search definitions
-     */
-    searchDefinitions: null,
-
-    /**
-     * Property: {Array} searchCategories
-     *
-     * An array of search categories
-     */
-    searchCategories: null,
-
-    /**
-     * Constructor: ApplicationDefinition
-     *
-     * construct a new instance of the ApplicationDefinition object.  While
-     * not enforced, this is intended to be a singleton.
-     *
-     * Parameter: sessionId
-     *
-     * an optional session id to initialize the application with, passed to
-     * the map widgets when they are created.
-     */
-
-    initialize: function(sessionId) {
-        //console.log('ApplicationDefinition initialize');
-        this.sessionId = sessionId;
-        this.oBroker = Fusion.getBroker();
-        this.applicationDefinition =  Fusion.getApplicationDefinitionURL();
-
-        this.widgetSets = [];
-        this.mapGroups = {};
-        this.searchDefinitions = [];
-        this.searchCategories = [];
-        this.parse();
-    },
-
-    /**
-     * Function: parse
-     *
-     * start parsing the ApplicationDefinition file.  This happens
-     * asynchronously since the ApplicationDefinition has to be
-     * retrieved from the server or the MapGuide repository.  When
-     * parsing is complete, an APPLICATIONDEFINITION_PARSED event
-     * will be emitted.  This function returns true if parsing
-     * will start, false if it will not (due to a missing
-     * application definition for instance).
-     */
-    parse : function() {
-        if (this.applicationDefinition == '') {
-            //TODO: emit an error
-            return null;
-        }
-        /* if the application definition is not in the mapguide server,
-           just load the xml*/
-
-        if ( (this.applicationDefinition.match('Library://') == null) &&
-             (this.applicationDefinition.match('Session:') == null) ) {
-            if (Fusion.appDefJson) {
-                this.parseAppDef(Fusion.appDefJson);
-            } else {
-                Fusion.getXmlAsJson(this.applicationDefinition,
-                              OpenLayers.Function.bind(this.getAppDefCB, this));
-            }
-        } else {
-          //these need to be called via setTimeout so that the execution thread
-          //is broken in IE and so that the ApplicationDefinition constructor
-          //will return a valid object to Fusion in loadConfig before the loadState
-          //gets incremented.
-            if (!this.sessionId) {
-              window.setTimeout(OpenLayers.Function.bind(this.createSessionThenGetAppDef,this),5);
-            } else {
-              window.setTimeout(OpenLayers.Function.bind(this.getAppDef,this),5);
-            }
-        }
-        return true;
-    },
-
-    createSessionThenGetAppDef: function() {
-      var sl = Fusion.getScriptLanguage();
-      var scriptURL = 'layers/' + 'MapGuide' + '/' + sl + '/CreateSession.' + sl;
-      var options = {onSuccess: OpenLayers.Function.bind(this.createSessionThenGetAppDefCB, this)};
-      Fusion.ajaxRequest(scriptURL, options);
-    },
-
-    createSessionThenGetAppDefCB : function(xhr) {
-      if (xhr && typeof(xhr) == "object" && xhr.responseText) {
-        var o;
-        eval("o="+xhr.responseText);
-        this.sessionId = o.sessionId;
-        Fusion.sessionId = this.sessionId;
-      }
-      this.getAppDef();
-    },
-
-    getAppDef: function(){
-      var r = new Fusion.Lib.MGRequest.MGGetResourceContent(this.applicationDefinition);
-      r.parameters.session = this.sessionId;
-      r.parameters.format = 'application/json';
-      this.oBroker.dispatchRequest(r,
-              OpenLayers.Function.bind(this.getAppDefCB, this));
-    },
-
-    getAppDefCB: function(xhr) {
-        var o;
-        eval("o="+xhr.responseText);
-        this.parseAppDef(o);
-        Fusion.setLoadState(Fusion.LOAD_WIDGETS);
-    },
-
-    /**
-     * Function: parseAppDef
-     *
-     * parse the ApplicationDefinition file into the appropriate Fusion objects
-     *
-     * Parameter: {XmlHttpRequest} xhr
-     *
-     * the XmlHttpRequest object
-     */
-    parseAppDef: function(json) {
-        var appDef = json.ApplicationDefinition;
-
-        /* Set the application title */
-        if (appDef.Title) {
-            var title = appDef.Title[0];
-            document.title = title;
-        }
-
-        /* process Map nodes */
-        if (appDef.MapSet) {
-            var mapSet = appDef.MapSet[0];
-            if (mapSet.MapGroup instanceof Array) {
-                for (var i=0; i<mapSet.MapGroup.length; i++) {
-                    var mapGroup = new Fusion.Lib.ApplicationDefinition.MapGroup(mapSet.MapGroup[i]);
-                    this.mapGroups[mapGroup.mapId] = mapGroup;
-                }
-            }
-        } else {
-          Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL,
-                        OpenLayers.i18n('appDefParseError')));
-        }
-
-        /* process WIDGET sets */
-        if (appDef.WidgetSet) {
-            for (var i=0; i<appDef.WidgetSet.length; i++) {
-                var widgetSet = new Fusion.Lib.ApplicationDefinition.WidgetSet(appDef.WidgetSet[i]);
-                this.widgetSets.push(widgetSet);
-            }
-        } else {
-          Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL,
-                      OpenLayers.i18n('widgetSetParseError')));
-        }
-
-        /* process extensions */
-        if (appDef.Extension) {
-            var extension = appDef.Extension[0];
-            /* process search definitions */
-            if (extension.SearchDefinitions instanceof Array) {
-                var categories = extension.SearchDefinitions[0];
-                if (categories.SearchCategory instanceof Array) {
-                    for (var i=0; i<categories.SearchCategory.length; i++) {
-                        var oCategory = {};
-                        var category = categories.SearchCategory[i];
-                        oCategory.id = category['@id'];
-                        oCategory.name = category['@name'];
-                        oCategory.layer = category.Layer ? category.Layer[0] : '';
-                        oCategory.searchDefinitions = [];
-                        this.searchCategories[oCategory.id] = oCategory;
-                        var defns = category.SearchDefinition;
-                        for (var k=0; k<defns.length; k++) {
-                            var defn = new Fusion.Lib.ApplicationDefinition.SearchDefinition(defns[k]);
-                            defn.category = oCategory;
-                            oCategory.searchDefinitions[defn.id] = defn;
-                            this.searchDefinitions[defn.id] = defn;
-                        }
-                    }
-                }
-            }
-
-        }
-    },
-
-    /**
-     * Function: create
-     *
-     * Create the application definition.  This actually triggers initializing
-     * every widget and container.
-     */
-    create: function() {
-        for (var i=0; i<this.widgetSets.length; i++) {
-            this.widgetSets[i].create(this);
-        }
-    },
-
-    /**
-     * Function: getMapByName
-     *
-     * return a map widget with the given name
-     *
-     * Parameter: {String} name
-     *
-     * The map name to return
-     *
-     * Returns: {Object} a map object or null if not found.
-     */
-    getMapByName : function(name) {
-        var map = null;
-        for (var i=0; i<this.widgetSets.length; i++) {
-            map = this.widgetSets[i].getMapByName(name);
-            if (map) {
-                break;
-            }
-        }
-        return map;
-    },
-
-    /**
-     * Function: getMapById
-     *
-     * return a map widget with the given id
-     *
-     * Parameter: {String} id
-     *
-     * The map id to return.  ID is distinct from map.name in that id is the
-     * id of the HTML tag where the map widget is inserted.
-     *
-     * Returns: {Object} a map object or null if not found.
-     */
-    getMapById : function(id) {
-        var map = null;
-        for (var i=0; i<this.widgetSets.length; i++) {
-            map = this.widgetSets[i].mapWidget;
-            if (map.mapId == id) {
-                break;
-            }
-        }
-        return map;
-    },
-
-    /**
-     * Function: getMapByIndice
-     *
-     * return the map widget at the given index
-     *
-     * Parameter: {String} indice
-     *
-     * The map indice to return
-     *
-     * Returns: {Object} a map object or null if not found.
-     */
-     getMapByIndice : function(indice) {
-         var map = null;
-         if (this.widgetSets.length > indice) {
-             map = this.widgetSets[indice].getMapWidget();
-         }
-         return map;
-     },
-
-    /**
-     * Function: getMapGroup
-     *
-     * return the specified map group from the application definition
-     *
-     * Parameter: {String} mapgroup
-     *
-     * The id of the MapGroup to return
-     *
-     * Returns: {Object} a MapGroup appdef or null if not found.
-     */
-     getMapGroup : function(mapGroupId) {
-         return this.mapGroups[mapGroupId];
-     },
-
-     /**
-      * Function getWidgetsByType
-      *
-      * returns an array of widgets by type.
-      *
-      * Parameter: {String} type
-      *
-      * the type of widget to get references to
-      *
-      * Returns: {Array} an array of widgets, which may be empty
-      */
-     getWidgetsByType: function(type) {
-         var widgets = [];
-         for (var i=0; i<this.widgetSets.length; i++) {
-             widgets = widgets.concat(this.widgetSets[i].getWidgetsByType(type));
-         }
-         return widgets;
-     }
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.MapGroup
- *
- * Holds an internal representation of MapGroup objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.MapGroup = OpenLayers.Class({
-    initialView: null,
-    maps: null,
-
-    initialize: function(jsonNode) {
-        this.mapId = jsonNode['@id'][0];
-        this.maps = [];
-        /* parse InitialView */
-        if (jsonNode.InitialView) {
-            var iv = jsonNode.InitialView[0];
-            if (iv.CenterX && iv.CenterY && iv.Scale) {
-                this.setInitialView({x:parseFloat(iv.CenterX[0]),
-                                     y:parseFloat(iv.CenterY[0]),
-                                     scale:parseFloat(iv.Scale[0])});
-            } else if (iv.MinX && iv.MinY && iv.MaxX && iv.MaxY) {
-                this.setInitialView({minX:parseFloat(iv.MinX[0]),
-                                     minY:parseFloat(iv.MinY[0]),
-                                     maxX:parseFloat(iv.MaxX[0]),
-                                     maxY:parseFloat(iv.MaxY[0])});
-            } else {
-                //TODO: emit warning that the initial view was incomplete
-            }
-        }
-        /* parse maps */
-        if (jsonNode.Map instanceof Array) {
-            for (var i=0; i<jsonNode.Map.length; i++) {
-                var map = new Fusion.Lib.ApplicationDefinition.Map(jsonNode.Map[i]);
-                var links = {groups:[], layers:[]};
-                var mapEvents = {layerEvents:{},groupEvents:{}};
-                if (jsonNode.Map[i].Extension) {
-                    var extension = jsonNode.Map[i].Extension[0];
-                    if (extension.Links) {
-                        /* process Groups */
-                        if (extension.Links[0].Group instanceof Array) {
-                            for (var j=0; j<extension.Links[0].Group.length; j++) {
-                                var group = extension.Links[0].Group[j];
-                                links.groups.push({name:group.Name[0],url:group.Url[0]});
-                            }
-                        }
-                        if (extension.Links[0].Layer instanceof Array) {
-                            for (var j=0; j<extension.Links[0].Layer.length; j++) {
-                                var layer = extension.Links[0].Layer[j];
-                                links.layers.push({name:layer.Name[0],url:layer.Url[0]});
-                            }
-                        }
-                    }
-                    /* process layer events */
-                    //TODO: Should this be called MapEvents?
-                    if (extension.MapEvents) {
-                        if (extension.MapEvents[0].Layer instanceof Array) {
-                            for (var j=0; j<extension.MapEvents[0].Layer.length; j++) {
-                                var layer = extension.MapEvents[0].Layer[j];
-                                var layerObj = {};
-                                layerObj.name = layer.Name[0];
-                                layerObj.onEnable = [];
-                                if (layer.OnEnable instanceof Array) {
-                                    layerObj.onEnable = this.parseMapEventSubBlock(layer.OnEnable[0]);
-                                }
-                                layerObj.onDisable = [];
-                                if (layer.OnDisable instanceof Array) {
-                                    layerObj.onDisable = this.parseMapEventSubBlock(layer.OnDisable[0]);
-                                }
-                                mapEvents.layerEvents[layerObj.name] = layerObj;
-                            }
-                        }
-                        if (extension.MapEvents[0].Group instanceof Array) {
-                            for (var j=0; j<extension.MapEvents[0].Group.length; j++) {
-                                var group = extension.MapEvents[0].Group[j];
-                                var groupObj = {};
-                                groupObj.name = group.Name[0];
-                                groupObj.onEnable = [];
-                                if (layer.OnEnable instanceof Array) {
-                                    groupObj.onEnable = this.parseMapEventSubBlock(group.OnEnable[0]);
-                                }
-                                groupObj.onDisable = [];
-                                if (layer.OnDisable instanceof Array) {
-                                    groupObj.onDisable = this.parseMapEventSubBlock(group.OnDisable[0]);
-                                }
-                                mapEvents.groupEvents[groupObj.name] = groupObj;
-                            }
-                        }
-                    }
-                }
-                map.mapInfo = {links: links, mapEvents: mapEvents};
-                this.maps.push(map);
-            }
-        } else {
-            //TODO: do we need a warning that there are no layers in this map?
-        }
-    },
-
-    parseMapEventSubBlock: function(block) {
-        var a = [];
-        if (block.Layer && block.Layer instanceof Array) {
-            for (var i=0; i<block.Layer.length; i++) {
-                var layer = block.Layer[i];
-                a.push({type: 'layer', name:layer.Name[0], enable: layer.Enable[0] == 'true' ? true : false});
-            }
-        }
-        if (block.Group && block.Group instanceof Array) {
-            for (var i=0; i<block.Group.length; i++) {
-                var group = block.Group[i];
-                a.push({type: 'group', name:group.Name[0], enable: group.Enable[0] == 'true' ? true : false});
-            }
-        }
-        return a;
-    },
-
-    getInitialView: function() {
-        return this.initialView;
-    },
-
-    setInitialView: function(view) {
-        this.initialView = view;
-    }
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.Map
- *
- * Holds an internal representation of Map objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.Map = OpenLayers.Class({
-    type: null,
-    singleTile: false,
-    extension: null,
-    
-    initialize: function(jsonNode) {
-        /* TODO: type can be any supported OpenLayers type */
-        this.type = jsonNode.Type[0];
-        if (jsonNode.SingleTile) {
-            var b = jsonNode.SingleTile[0].toLowerCase();
-            this.singleTile = (b == "true") ? true : false;
-        }
-        if (jsonNode.Extension) {
-            this.extension = jsonNode.Extension[0];
-        } else {
-            this.extension = {};
-        }
-        this.resourceId = this.extension.ResourceId ? this.extension.ResourceId[0] : '';
-
-        var tagOptions = this.extension.Options;
-        this.layerOptions = {};
-        if (tagOptions && tagOptions[0]) {
-          for (var key in tagOptions[0]) {
-            this.layerOptions[key] = tagOptions[0][key][0];
-            if (this.layerOptions[key].toLowerCase() == 'true') {
-              this.layerOptions[key] = true;
-            } else if (this.layerOptions[key].toLowerCase() == 'false') {
-              this.layerOptions[key] = false;
-            }
-            if (key == 'maxExtent' || key == 'minExtent') {
-              this.layerOptions[key] = OpenLayers.Bounds.fromString(this.layerOptions[key]);
-            }
-          }
-        }
-        var tagParams = this.extension.Parameters;
-        this.layerParams = {};
-        if (tagParams && tagParams[0]) {
-          for (var key in tagParams[0]) {
-            this.layerParams[key] = tagParams[0][key][0];
-          }
-        }
-        
-        //projection info from the extension
-        //this.layerOptions.projection = "EPSG:4326";  //default to WGS84 lat long
-        if (this.extension.ProjectionCode) {
-          this.layerOptions.projection = this.extension.ProjectionCode[0];
-        }
-        if (this.extension.ProjectionDef) {
-          var projDef = this.extension.ProjectionDef[0];
-          if (!this.layerOptions.projection) {
-            this.layerOptions.projection = "APP-DEF-PROJ";
-          }
-          Proj4js.defs[this.layerOptions.projection] = projDef;
-        }
-        if (!this.layerOptions.projection) {
-          this.layerOptions.projection = "EPSG:4326";
-        }
-
-        switch (this.type) {
-          case 'MapGuide':
-          case 'MapServer':
-            if ( !Fusion.Layers[this.type] ) {
-                  Fusion.require('layers/' + this.type + '/' + this.type + '.js');
-            }
-            break;
-          case "Google":
-          case "Yahoo":
-          case "VirtualEarth":
-              this.layerOptions.isBaseLayer = true;
-              this.layerOptions.sphericalMercator = true;
-              this.layerOptions.displayProjection = "EPSG:4326";
-              //no break here continue below to set EPSG:3785 as projCode
-          default:
-            if (this.layerOptions.sphericalMercator) {
-              this.layerOptions.projection = "EPSG:3785";  //commercial map layer projection spherical mercator
-            } else {
-              this.layerOptions.projection = "EPSG:4326";  //WGS84 lat long
-            }
-            if ( !Fusion.Layers.Generic ) {
-              Fusion.require('layers/Generic/Generic.js');
-            }
-            break;
-        }
-    }
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.WidgetSet
- *
- * Holds an internal representation of WidgetSet objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.WidgetSet = OpenLayers.Class({
-    containers: null,
-    containersByName: null,
-    widgetTags: null,
-    widgetTagsByName: null,
-    widgetInstances: null,
-    mapWidget: null,
-    mapId: null,
-    initialize: function(jsonNode) {
-        this.containers = [];
-        this.widgetTags = [];
-        this.widgetInstances = [];
-        this.widgetTagsByName = {};
-        this.containersByName = {};
-        /* process map widgets */
-        if (jsonNode.MapWidget) {
-            for (var i=0; i<jsonNode.MapWidget.length; i++) {
-                var widget = new Fusion.Lib.ApplicationDefinition.Widget(jsonNode.MapWidget[i]);
-                widget.widgetSet = this;
-                this.mapWidgetTag = widget;
-                this.mapId = jsonNode.MapWidget[i].MapId[0];
-            }
-        }
-
-        /* process widgets */
-        if (jsonNode.Widget) {
-            for (var i=0; i<jsonNode.Widget.length; i++) {
-                var widget = new Fusion.Lib.ApplicationDefinition.Widget(jsonNode.Widget[i]);
-                widget.widgetSet = this;
-                this.widgetTags.push(widget);
-                this.widgetTagsByName[widget.name] = widget;
-            }
-        }
-        /* process containers */
-        if (jsonNode.Container) {
-            for (var i=0; i<jsonNode.Container.length; i++) {
-                var container = new Fusion.Lib.ApplicationDefinition.Container(jsonNode.Container[i]);
-                this.containers.push(container);
-                this.containersByName[container.name] = container;
-            }
-        }
-
-    },
-
-    /**
-     * Function: addWidgetInstance
-     *
-     * keep track of live widgets created in this widgetSet
-     *
-     * Parameter: {<Fusion.Widget>} widget
-     *
-     * the widget to add
-     */
-    addWidgetInstance: function(widget) {
-        this.widgetInstances.push(widget);
-    },
-
-    /**
-     * Function: getMapWidget
-     *
-     * return the map widget for this widget set
-     *
-     * Returns: {<Fusion.Lib.Map>} a map widget or null
-     */
-    getMapWidget: function() {
-        return this.mapWidget;
-    },
-
-    /**
-     * Function: create
-     *
-     * create all the things required by this widgetSet, including
-     * containers and widgets.
-     *
-     * Parameter: {<Fusion.Lib.ApplicationDefinition>}
-     *
-     * the application definition that this widgetSet is part of
-     */
-    create: function(appDef) {
-
-        //allow over-ride of mapId via URL parameter
-        var mapGroup = null;
-        var paramMapId = Fusion.getQueryParam('mapid');
-        if (paramMapId.length>0) {
-          mapGroup = appDef.getMapGroup(paramMapId);
-          if (mapGroup) {
-            this.mapId = paramMapId;
-          } else {
-            Fusion.reportError(new Fusion.Error(Fusion.Error.WARNING,
-              "can't find MapGroup: " + paramMapId + ' - reverting to default map'));
-          }
-        }
-
-        if (!mapGroup) {
-          mapGroup = appDef.getMapGroup(this.mapId);
-        }
-
-        //create the Map widget for this WidgetSet
-        this.mapWidget = new Fusion.Widget.Map(this.mapWidgetTag,mapGroup,this);
-        this.mapWidget.setMenu();
-        $(this.mapWidgetTag.name).widget = this.mapWidget;
-
-        //create the floating map message
-        this.mapWidget.message = new Fusion.MapMessage(this.mapWidget.oMapOL.viewPortDiv);
-        this.mapWidget.registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, (function(){
-                if (this.message != null){
-                    this.message.refreshLayout();
-                }
-            }).bind(this.mapWidget));
-
-        //create all the other widgets for the widget set
-        for (var i=0; i<this.widgetTags.length; i++) {
-            this.widgetTags[i].create(this);
-        }
-
-        //create all the containers for the widget set
-        for (var i=0; i<this.containers.length; i++) {
-            this.containers[i].create(this);
-        }
-    },
-    /**
-     * Function: getMapByName
-     *
-     * return the map widget from this widget set if the map's name
-     * matches the requested name, or null.
-     *
-     * Parameter: {String} name
-     *
-     * The map name to check
-     *
-     * Returns: {Object} a map object or null.
-     */
-    getMapByName : function(name) {
-        var map = null;
-        if (this.mapWidget.getMapName() == name) {
-            map = this.mapWidget;
-        }
-        return map;
-    },
-
-    /**
-     * Function getWidgetsByType
-     *
-     * returns an array of widgets by type.
-     *
-     * Parameter: {String} type
-     *
-     * the type of widget to get references to
-     *
-     * Returns: {Array} an array of widgets, which may be empty
-     */
-    getWidgetsByType: function(type) {
-        var widgets = [];
-        for (var i=0; i<this.widgetInstances.length; i++) {
-            if (this.widgetInstances[i].type == type) {
-                widgets.push(this.widgetInstances[i]);
-            }
-        }
-        return widgets;
-    },
-
-    getWidgetByName: function(name) {
-        return this.widgetTagsByName[name];
-    },
-
-    getContainerByName: function(name) {
-        return this.containersByName[name];
-    }
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.Container
- *
- * Holds an internal representation of Container objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.Container = OpenLayers.Class({
-    name: null,
-    type: null,
-    validPositions: ['top', 'left', 'bottom', 'right'],
-    position: 'top',
-    items: null,
-    initialize: function(jsonNode) {
-        this.type = jsonNode.Type[0];
-        this.name = jsonNode.Name[0];
-        var position = jsonNode.Position ? jsonNode.Position[0].toLowerCase() : this.position;
-        for (var i=0; i<this.validPositions.length; i++) {
-            if (this.validPositions[i] == position) {
-                this.position = position;
-                break;
-            }
-        }
-        this.items = [];
-        if (jsonNode.Item) {
-            for (var i=0; i<jsonNode.Item.length; i++) {
-                var item = new Fusion.Lib.ApplicationDefinition.Item(jsonNode.Item[i]);
-                this.items.push(item);
-            }
-        } else {
-            //TODO: is this a problem if there are no items?
-        }
-    },
-
-    create: function(widgetSet) {
-        var container;
-        if (this.type == 'Toolbar' || this.type == 'Statusbar') {
-            if ($(this.name)) {
-                container = new Jx.Toolbar({parent: this.name, position: this.position});
-                $(this.name).container = container;
-                this.createWidgets(widgetSet, container);
-            }
-        } else if (this.type == 'Splitterbar') {
-            if ($(this.name)) {
-                container = new Jx.Splitter(this.name, {splitInto: this.items.length});
-                for (var i=0; i<this.items.length; i++) {
-                    container.elements[i].id = this.name + '_' + i;
-                }
-                $(this.name).container = container;
-                this.createWidgets(widgetSet, container);
-            }
-        }
-        if (container && container.domObj.jxLayout) {
-            container.domObj.jxLayout.resize({forceResize: true});
-        }
-    },
-
-    createWidgets: function(widgetSet, container) {
-        for (var i=0; i<this.items.length; i++) {
-            this.items[i].create(widgetSet, container, this.name + '_' + i);
-        }
-    }
-
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.Widget
- *
- * Holds an internal representation of Widget objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.Widget = OpenLayers.Class({
-    name: null,
-    type: null,
-    statusText: null,
-    location: null,
-    imageUrl: null,
-    imageClass: null,
-    tooltip: null,
-    label: null,
-    disabled: null,
-    extension: null,
-    initialize: function(jsonNode) {
-        if (jsonNode) {
-            this.type = jsonNode.Type[0];
-            this.name = jsonNode.Name ? jsonNode.Name[0] : '';
-            this.statusText = jsonNode.StatusText ? jsonNode.StatusText[0] : '';
-            this.statusText = OpenLayers.i18n(this.statusText);
-            //TODO: this may be an extension
-            this.location = jsonNode.Location ? jsonNode.Location[0] : 'widgets/';
-            if (this.location.slice(-1) != '/') {
-                this.location += '/';
-            }
-            this.imageUrl = jsonNode.ImageUrl ? jsonNode.ImageUrl[0] : '';
-            this.imageClass = jsonNode.ImageClass ? jsonNode.ImageClass[0] : '';
-            this.tooltip = jsonNode.Tooltip ? jsonNode.Tooltip[0] : '';
-            this.tooltip = OpenLayers.i18n(this.tooltip);
-            this.label = jsonNode.Label ? jsonNode.Label[0] : '';
-            this.label = OpenLayers.i18n(this.label);
-            this.disabled = jsonNode.Disabled ? (jsonNode.Disabled[0].toLowerCase() == 'true' ? true : false) : false;
-
-            //console.log('Widget: ' + this.type + ', ' + this.name + ', ' + this.description);
-
-            if (jsonNode.Extension) {
-                this.extension = jsonNode.Extension[0];
-            } else {
-                this.extension = {};
-            }
-            //require the widget code
-            if ( !Fusion.Widget[this.type] ) {
-              Fusion.require(this.location + this.type + '.js');
-            }
-        }
-    },
-
-    getMapWidget: function() {
-        if (this.widgetSet) {
-            return this.widgetSet.getMapWidget();
-        } else {
-            return null;
-        }
-    },
-
-    /**
-     * Function: create
-     *
-     * creates a new instance of the widget, optionally using a
-     * different name during instantiation to accomodate
-     * containers
-     *
-     * Parameter: name
-     *
-     * An optional name to use for the widget, overrides the
-     * original name temporarily if passed.
-     *
-     * Returns: an instance of the widget represented by this
-     * object.
-     */
-    create: function(widgetSet, widgetName) {
-        var widget = null;
-        this.widgetSet = widgetSet;
-        var oldName = this.name;
-        if (typeof widgetName == 'undefined') {
-            widgetName = this.name;
-        }
-        /* create the widget if the name is not null and the name
-         * is either an empty string (in the case of buttons in menus)
-         * or something exists in the dom with the right id
-         */
-        if (widgetName != null && (widgetName == '' || $(widgetName) != null)) {
-            this.name = widgetName;
-            widget = eval("new Fusion.Widget."+this.type+"(this)");
-            widgetSet.addWidgetInstance(widget);
-            if ($(this.name)) {
-                widget.id = this.name;
-                $(this.name).widget = widget;
-                if (widget.uiClass) {
-                    widget.setUiObject(new widget.uiClass({
-                        label: this.label,
-                        image: this.imageUrl,
-                        imageClass: this.imageClass,
-                        toggle: this.isExclusive
-                    }));
-                    if (widget.uiObj.addTo) {
-                        widget.uiObj.addTo($(this.name));
-                    }
-                    if (widget.uiObj.setEnabled) {
-                        widget.uiObj.setEnabled(widget.isEnabled());
-                        widget.registerForEvent(Fusion.Event.WIDGET_STATE_CHANGED, function() {
-                            widget.uiObj.setEnabled(widget.isEnabled());
-                        });
-                    }
-                }
-            }
-            this.name = oldName;
-        }
-        return widget;
-    }
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.Item
- *
- * Holds an internal representation of Menu Item objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.Item = OpenLayers.Class({
-    uniqueId: [0],
-    type: null,
-    initialize: function(jsonNode) {
-        this.type = jsonNode.Function[0];
-        switch(this.type) {
-            case 'Widget':
-                this.widgetName = jsonNode.Widget[0];
-                break;
-            case 'Flyout':
-                this.flyout = new Fusion.Lib.ApplicationDefinition.Flyout(jsonNode);
-                break;
-            case 'Multi':
-                this.multi = new Fusion.Lib.ApplicationDefinition.Multi(jsonNode);
-                break;
-            case 'Separator':
-                break;
-        }
-    },
-
-    create: function(widgetSet, container, idx) {
-        switch(this.type) {
-            case 'Widget':
-                var widgetTag = widgetSet.getWidgetByName(this.widgetName);
-                if (widgetTag) {
-                    var name = 'FusionItem'+this.uniqueId[0];
-                    this.uniqueId[0]++;
-                    if (container instanceof Jx.Toolbar) {
-                        var widget = widgetTag.create(widgetSet, '');
-                        if (!widget.uiClass) {
-                            widget.uiClass = Jx.Toolbar.Item;
-                            widget.setUiObject(new widget.uiClass());
-                        } else if (widget.uiClass == Jx.Button || widget.uiClass == Jx.Button.Color || widget.uiClass == Jx.Menu || widget.uiClass == Jx.Button.Flyout) {
-                            widget.setUiObject(new widget.uiClass({
-                                label: widgetTag.label,
-                                image: widgetTag.imageUrl,
-                                imageClass: widgetTag.imageClass,
-                                toggle: widget.isExclusive
-                            }));
-                        }
-                        container.add(widget.uiObj);
-                        if (widget.uiObj.setEnabled) {
-                            widget.uiObj.setEnabled(widget.isEnabled());
-                            widget.registerForEvent(Fusion.Event.WIDGET_STATE_CHANGED, function() {
-                                widget.uiObj.setEnabled(widget.isEnabled());
-                            });
-                        }
-                    } else if (container instanceof Jx.Splitter) {
-                        var widget = widgetTag.create(widgetSet, idx);
-                    } else if (container instanceof Jx.Menu ||
-                               container instanceof Jx.Menu.Context ||
-                               container instanceof Jx.Menu.SubMenu) {
-                        var widget = widgetTag.create(widgetSet, '');
-                        if (!widget.uiClass || widget.uiClass == Jx.Button) {
-                            widget.uiClass = Jx.Menu.Item;
-                        } else if (widget.uiClass == Jx.Menu) {
-                            widget.uiClass = Jx.Menu.SubMenu;
-                        }
-                        widget.setUiObject(new widget.uiClass({
-                            label: widgetTag.label,
-                            image: widgetTag.imageUrl,
-                            imageClass: widgetTag.imageClass,
-                            toggle: widget.isExclusive
-                        }));
-                        container.add(widget.uiObj);
-                        if (widget.uiObj.setEnabled) {
-                            widget.uiObj.setEnabled(widget.isEnabled());
-                            widget.registerForEvent(Fusion.Event.WIDGET_STATE_CHANGED, function() {
-                                widget.uiObj.setEnabled(widget.isEnabled());
-                            });
-                        }
-                    } else if (container instanceof Jx.Button.Multi) {
-                        var widget = widgetTag.create(widgetSet, '');
-                        widget.uiClass = Jx.Button;
-                        widget.setUiObject(new widget.uiClass({
-                            label: widgetTag.label,
-                            image: widgetTag.imageUrl,
-                            imageClass: widgetTag.imageClass,
-                            toggle: widget.isExclusive
-                        }));
-                        container.add(widget.uiObj);
-                        if (widget.uiObj.setEnabled) {
-                            widget.uiObj.setEnabled(widget.isEnabled());
-                            widget.registerForEvent(Fusion.Event.WIDGET_STATE_CHANGED, function() {
-                                widget.uiObj.setEnabled(widget.isEnabled());
-                            });
-                        }
-                    }
-                } else {
-                  Fusion.reportError(new Fusion.Error(Fusion.Error.WARNING,
-                    "can't find widget: " + this.widgetName));
-                 }
-                break;
-            case 'Flyout':
-                /* create a menu */
-                var menu;
-                var opt = {
-                    label: this.flyout.label,
-                    tooltip: this.flyout.tooltip,
-                    image: this.flyout.imageUrl,
-                    imageClass: this.flyout.imageClass
-                };
-                if (container instanceof Jx.Toolbar) {
-                    menu = new Jx.Menu(opt);
-                } else if (container instanceof Jx.Menu ||
-                           container instanceof Jx.Menu.Context ||
-                           container instanceof Jx.Menu.SubMenu) {
-                    menu = new Jx.Menu.SubMenu(opt);
-                }
-                container.add(menu);
-                this.flyout.create(widgetSet, menu);
-
-                break;
-            case 'Multi':
-                var multi;
-                var opt = {
-                    label: this.multi.label,
-                    tooltip: this.multi.tooltip,
-                    image: this.multi.imageUrl,
-                    imageClass: this.multi.imageClass
-                }
-                multi = new Jx.Button.Multi(opt);
-                container.add(multi);
-                this.multi.create(widgetSet, multi);
-                break;
-
-            case 'Separator':
-                if (container instanceof Jx.Toolbar) {
-                    container.add(new Jx.Toolbar.Separator());
-                } else if (container instanceof( Jx.Menu) ||
-                           container instanceof(Jx.Menu.SubMenu) ||
-                           container instanceof(Jx.Menu.Context)) {
-                    container.add(new Jx.Menu.Separator());
-                }
-                break;
-        }
-    }
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.Flyout
- *
- * Holds an internal representation of Flyout objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.Flyout = OpenLayers.Class({
-    label: null,
-    tooltip: null,
-    description: null,
-    imageUrl: null,
-    items: null,
-
-    initialize: function(jsonNode) {
-        this.label = jsonNode.Label ? jsonNode.Label[0] : '';
-        this.tooltip = jsonNode.Tooltip ? jsonNode.Tooltip[0] : '';
-        this.description = jsonNode.Description ? jsonNode.Description[0] : '';
-        this.imageUrl = jsonNode.ImageUrl ? jsonNode.ImageUrl[0] : '';
-        this.items = [];
-        if (jsonNode.Item instanceof Array) {
-            for (var i=0; i<jsonNode.Item.length; i++) {
-                this.items.push(new Fusion.Lib.ApplicationDefinition.Item(jsonNode.Item[i]));
-            }
-        }
-    },
-
-    create: function(widgetSet, menu) {
-        for (var i=0; i<this.items.length; i++) {
-            this.items[i].create(widgetSet, menu);
-        }
-    }
-
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.Multi
- *
- * Holds an internal representation of Multi objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.Multi = OpenLayers.Class({
-    label: null,
-    tooltip: null,
-    description: null,
-    imageUrl: null,
-    items: null,
-
-    initialize: function(jsonNode) {
-        this.label = jsonNode.Label ? jsonNode.Label[0] : '';
-        this.tooltip = jsonNode.Tooltip ? jsonNode.Tooltip[0] : '';
-        this.description = jsonNode.Description ? jsonNode.Description[0] : '';
-        this.imageUrl = jsonNode.ImageUrl ? jsonNode.ImageUrl[0] : '';
-        this.items = [];
-        if (jsonNode.Item instanceof Array) {
-            for (var i=0; i<jsonNode.Item.length; i++) {
-                this.items.push(new Fusion.Lib.ApplicationDefinition.Item(jsonNode.Item[i]));
-            }
-        }
-    },
-
-    create: function(widgetSet, multi) {
-        for (var i=0; i<this.items.length; i++) {
-            this.items[i].create(widgetSet, multi);
-        }
-    }
-
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.SearchDefinition
- *
- * Holds an internal representation of SearchDefinition objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.SearchDefinition = OpenLayers.Class({
-    id: null,
-    name: null,
-    category: null,
-    parameters: null,
-    join: null,
-    rule: null,
-
-    initialize: function(json) {
-        this.id = json['@id'];
-        this.name = json['@name'];
-        if (json.Join instanceof Array) {
-            this.join = new Fusion.Lib.ApplicationDefinition.SearchJoin(json.Join[0]);
-        }
-        this.parameters = [];
-        if (json.Parameter instanceof Array) {
-            for (var i=0; i<json.Parameter.length; i++) {
-                this.parameters.push(json.Parameter[i]['@name']);
-            }
-        }
-        this.reports = [];
-        if (json.Report instanceof Array) {
-            for (var i=0; i<json.Report.length; i++) {
-              var label = json.Report[i].Label[0]
-              var link = json.Report[i].Link[0]
-              this.reports.push({'label':label, 'link':link});
-            }
-        }
-        var rule;
-        if (json.SearchAnd instanceof Array) {
-            this.rule = new Fusion.Lib.ApplicationDefinition.SearchRule('AND');
-            rule = json.SearchAnd[0];
-        } else if (json.SearchOr instanceof Array) {
-            this.rule = new Fusion.Lib.ApplicationDefinition.SearchRule('OR');
-            rule = json.SearchOr[0];
-        }
-        if (rule && rule.SearchCondition instanceof Array) {
-            for (var i=0; i<rule.SearchCondition.length; i++) {
-                this.rule.add(new Fusion.Lib.ApplicationDefinition.SearchCondition(rule.SearchCondition[i]));
-            }
-        }
-    },
-
-    getJoinUrl: function(params) {
-        if (this.join) {
-            return '&joinlayer='+this.join.layer+'&joinpk='+this.join.primaryKey+'&joinfk='+this.join.foreignKey;
-        } else {
-            return '';
-        }
-    },
-
-    getFilterUrl: function(params) {
-        return '&filter='+encodeURIComponent(this.rule.toString(params));
-    }
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.SearchJoin
- *
- * Holds an internal representation of SearchJoin objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.SearchJoin = OpenLayers.Class({
-    layer: null,
-    primaryKey: null,
-    foreignKey: null,
-    initialize: function(json) {
-        this.layer = json.Layer ? json.Layer[0] : '';
-        this.primaryKey = json.PrimaryKey ? json.PrimaryKey[0] : '';
-        this.foreignKey = json.ForeignKey ? json.ForeignKey[0] : '';
-    }
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.SearchRule
- *
- * Holds an internal representation of SearchRule objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.SearchRule = OpenLayers.Class({
-    type: null,
-    conditions: null,
-    initialize: function(type) {
-        this.type = type;
-        this.conditions = [];
-    },
-
-    add: function(condition) {
-        this.conditions.push(condition);
-    },
-
-    remove: function(condition) {
-        for (var i=0; i<this.conditions.length; i++) {
-            if (this.conditions[i] == condition) {
-                this.conditions.splice(i, 1);
-                break;
-            }
-        }
-    },
-
-    toString: function(params) {
-        var conditions = [];
-        for (var i=0; i<this.conditions.length; i++) {
-            this.conditions[i].setParams(params);
-            var c = this.conditions[i].toString();
-            if (c != '') {
-                conditions.push(c);
-            }
-        }
-        return '(' + conditions.join(') ' + this.type + ' (') + ')';
-    }
-});
-
-/****************************************************************************
- * Class: Fusion.Lib.ApplicationDefinition.SearchCondition
- *
- * Holds an internal representation of SearchCondition objects as defined in the AppDef
- *
- */
-
-Fusion.Lib.ApplicationDefinition.SearchCondition = OpenLayers.Class({
-    column: null,
-    operator: null,
-    parameter: null,
-    quote: null,
-    value: null,
-    operators: {eq:'=', like:'like', lt:'<', lte:'<=', gt:'>', gte:'>=', neq:'<>'},
-    includeIfEmpty: false,
-
-    initialize: function(json) {
-        this.column = json.Column[0];
-        this.operator = this.operators[json.Operator[0].toLowerCase()];
-        this.parameter = json.Parameter[0];
-        this.quote = json['@quote'] ? json['@quote'] : '';
-        this.wildcard = json['@wildcard'] ? json['@wildcard'] : 'both';
-        this.caseSensitive = true;
-        if (json['@caseSensitive'] && json['@caseSensitive'] == 'false') {
-            this.caseSensitive = false;
-        }
-    },
-
-    setParams: function(p) {
-        if (p[this.parameter]) {
-            this.value = p[this.parameter];
-        } else {
-            this.value = '';
-        }
-    },
-
-    toString: function() {
-        var value = this.value ? this.value : '';
-        if (value == '' && !this.includeIfEmpty) {
-            return '';
-        }
-        var upper = '';
-        if (!this.caseSensitive) {
-            value = value.toUpperCase();
-            upper = 'Upper';
-        }
-        var prewildcard = '';
-        var prewildcard = '';
-        var postwildcard = '';
-        if (this.operator == 'like') {
-            if (this.wildcard == 'before' || this.wildcard == 'both') {
-                prewildcard = '*';
-            }
-            if (this.wildcard == 'after' || this.wildcard == 'both') {
-                postwildcard = '*';
-            }
-        }
-        var wildcard = this.operator == 'like' ? '*' : '';
-        return upper + '('+this.column + ') ' + this.operator + ' ' + this.quote + prewildcard + value + postwildcard + this.quote;
-    }
-});
+/**
+ * Fusion.Lib.ApplicationDefinition
+ *
+ * $Id$
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition
+ *
+ * Utility class to parse an application definition
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition = OpenLayers.Class({
+    /**
+     * Property: mapGroups
+     *
+     * array of map groups, parsed from ApplicationDefinition.  A MapGroup
+     * consists of one or more Maps that can be combined into the same
+     * OpenLayers Map object
+     */
+    mapGroups: null,
+
+    /**
+     * Property: widgetSets
+     *
+     * array of widget sets (each one corresponding to a map) parsed
+     * from the ApplicationDefinition.
+     */
+    widgetSets: null,
+
+    /**
+     * Property: {Object} oBroker
+     *
+     * A Broker object that can communicate with a MapGuide instance
+     * in the case we are running against a MapGuide server
+     */
+    oBroker: null,
+
+    /**
+     * Property: {Array} searchDefinitions
+     *
+     * An array of search definitions
+     */
+    searchDefinitions: null,
+
+    /**
+     * Property: {Array} searchCategories
+     *
+     * An array of search categories
+     */
+    searchCategories: null,
+
+    /**
+     * Constructor: ApplicationDefinition
+     *
+     * construct a new instance of the ApplicationDefinition object.  While
+     * not enforced, this is intended to be a singleton.
+     *
+     * Parameter: sessionId
+     *
+     * an optional session id to initialize the application with, passed to
+     * the map widgets when they are created.
+     */
+
+    initialize: function(sessionId) {
+        //console.log('ApplicationDefinition initialize');
+        this.sessionId = sessionId;
+        this.oBroker = Fusion.getBroker();
+        this.applicationDefinition =  Fusion.getApplicationDefinitionURL();
+
+        this.widgetSets = [];
+        this.mapGroups = {};
+        this.searchDefinitions = [];
+        this.searchCategories = [];
+        this.parse();
+    },
+
+    /**
+     * Function: parse
+     *
+     * start parsing the ApplicationDefinition file.  This happens
+     * asynchronously since the ApplicationDefinition has to be
+     * retrieved from the server or the MapGuide repository.  When
+     * parsing is complete, an APPLICATIONDEFINITION_PARSED event
+     * will be emitted.  This function returns true if parsing
+     * will start, false if it will not (due to a missing
+     * application definition for instance).
+     */
+    parse : function() {
+        if (this.applicationDefinition == '') {
+            //TODO: emit an error
+            return null;
+        }
+        /* if the application definition is not in the mapguide server,
+           just load the xml*/
+
+        if ( (this.applicationDefinition.match('Library://') == null) &&
+             (this.applicationDefinition.match('Session:') == null) ) {
+            if (Fusion.appDefJson) {
+                this.parseAppDef(Fusion.appDefJson);
+            } else {
+                Fusion.getXmlAsJson(this.applicationDefinition,
+                              OpenLayers.Function.bind(this.getAppDefCB, this));
+            }
+        } else {
+          //these need to be called via setTimeout so that the execution thread
+          //is broken in IE and so that the ApplicationDefinition constructor
+          //will return a valid object to Fusion in loadConfig before the loadState
+          //gets incremented.
+            if (!this.sessionId) {
+              window.setTimeout(OpenLayers.Function.bind(this.createSessionThenGetAppDef,this),5);
+            } else {
+              window.setTimeout(OpenLayers.Function.bind(this.getAppDef,this),5);
+            }
+        }
+        return true;
+    },
+
+    createSessionThenGetAppDef: function() {
+      var sl = Fusion.getScriptLanguage();
+      var scriptURL = 'layers/' + 'MapGuide' + '/' + sl + '/CreateSession.' + sl;
+      var options = {onSuccess: OpenLayers.Function.bind(this.createSessionThenGetAppDefCB, this)};
+      Fusion.ajaxRequest(scriptURL, options);
+    },
+
+    createSessionThenGetAppDefCB : function(xhr) {
+      if (xhr && typeof(xhr) == "object" && xhr.responseText) {
+        var o;
+        eval("o="+xhr.responseText);
+        this.sessionId = o.sessionId;
+        Fusion.sessionId = this.sessionId;
+      }
+      this.getAppDef();
+    },
+
+    getAppDef: function(){
+      var r = new Fusion.Lib.MGRequest.MGGetResourceContent(this.applicationDefinition);
+      r.parameters.session = this.sessionId;
+      r.parameters.format = 'application/json';
+      this.oBroker.dispatchRequest(r,
+              OpenLayers.Function.bind(this.getAppDefCB, this));
+    },
+
+    getAppDefCB: function(xhr) {
+        var o;
+        eval("o="+xhr.responseText);
+        this.parseAppDef(o);
+        Fusion.setLoadState(Fusion.LOAD_WIDGETS);
+    },
+
+    /**
+     * Function: parseAppDef
+     *
+     * parse the ApplicationDefinition file into the appropriate Fusion objects
+     *
+     * Parameter: {XmlHttpRequest} xhr
+     *
+     * the XmlHttpRequest object
+     */
+    parseAppDef: function(json) {
+        var appDef = json.ApplicationDefinition;
+
+        /* Set the application title */
+        if (appDef.Title) {
+            var title = appDef.Title[0];
+            document.title = title;
+        }
+
+        /* process Map nodes */
+        if (appDef.MapSet) {
+            var mapSet = appDef.MapSet[0];
+            if (mapSet.MapGroup instanceof Array) {
+                for (var i=0; i<mapSet.MapGroup.length; i++) {
+                    var mapGroup = new Fusion.Lib.ApplicationDefinition.MapGroup(mapSet.MapGroup[i]);
+                    this.mapGroups[mapGroup.mapId] = mapGroup;
+                }
+            }
+        } else {
+          Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL,
+                        OpenLayers.i18n('appDefParseError')));
+        }
+
+        /* process WIDGET sets */
+        if (appDef.WidgetSet) {
+            for (var i=0; i<appDef.WidgetSet.length; i++) {
+                var widgetSet = new Fusion.Lib.ApplicationDefinition.WidgetSet(appDef.WidgetSet[i]);
+                this.widgetSets.push(widgetSet);
+            }
+        } else {
+          Fusion.reportError(new Fusion.Error(Fusion.Error.FATAL,
+                      OpenLayers.i18n('widgetSetParseError')));
+        }
+
+        /* process extensions */
+        if (appDef.Extension) {
+            var extension = appDef.Extension[0];
+            /* process search definitions */
+            if (extension.SearchDefinitions instanceof Array) {
+                var categories = extension.SearchDefinitions[0];
+                if (categories.SearchCategory instanceof Array) {
+                    for (var i=0; i<categories.SearchCategory.length; i++) {
+                        var oCategory = {};
+                        var category = categories.SearchCategory[i];
+                        oCategory.id = category['@id'];
+                        oCategory.name = category['@name'];
+                        oCategory.layer = category.Layer ? category.Layer[0] : '';
+                        oCategory.searchDefinitions = [];
+                        this.searchCategories[oCategory.id] = oCategory;
+                        var defns = category.SearchDefinition;
+                        for (var k=0; k<defns.length; k++) {
+                            var defn = new Fusion.Lib.ApplicationDefinition.SearchDefinition(defns[k]);
+                            defn.category = oCategory;
+                            oCategory.searchDefinitions[defn.id] = defn;
+                            this.searchDefinitions[defn.id] = defn;
+                        }
+                    }
+                }
+            }
+
+        }
+    },
+
+    /**
+     * Function: create
+     *
+     * Create the application definition.  This actually triggers initializing
+     * every widget and container.
+     */
+    create: function() {
+        for (var i=0; i<this.widgetSets.length; i++) {
+            this.widgetSets[i].create(this);
+        }
+    },
+
+    /**
+     * Function: getMapByName
+     *
+     * return a map widget with the given name
+     *
+     * Parameter: {String} name
+     *
+     * The map name to return
+     *
+     * Returns: {Object} a map object or null if not found.
+     */
+    getMapByName : function(name) {
+        var map = null;
+        for (var i=0; i<this.widgetSets.length; i++) {
+            map = this.widgetSets[i].getMapByName(name);
+            if (map) {
+                break;
+            }
+        }
+        return map;
+    },
+
+    /**
+     * Function: getMapById
+     *
+     * return a map widget with the given id
+     *
+     * Parameter: {String} id
+     *
+     * The map id to return.  ID is distinct from map.name in that id is the
+     * id of the HTML tag where the map widget is inserted.
+     *
+     * Returns: {Object} a map object or null if not found.
+     */
+    getMapById : function(id) {
+        var map = null;
+        for (var i=0; i<this.widgetSets.length; i++) {
+            map = this.widgetSets[i].mapWidget;
+            if (map.mapId == id) {
+                break;
+            }
+        }
+        return map;
+    },
+
+    /**
+     * Function: getMapByIndice
+     *
+     * return the map widget at the given index
+     *
+     * Parameter: {String} indice
+     *
+     * The map indice to return
+     *
+     * Returns: {Object} a map object or null if not found.
+     */
+     getMapByIndice : function(indice) {
+         var map = null;
+         if (this.widgetSets.length > indice) {
+             map = this.widgetSets[indice].getMapWidget();
+         }
+         return map;
+     },
+
+    /**
+     * Function: getMapGroup
+     *
+     * return the specified map group from the application definition
+     *
+     * Parameter: {String} mapgroup
+     *
+     * The id of the MapGroup to return
+     *
+     * Returns: {Object} a MapGroup appdef or null if not found.
+     */
+     getMapGroup : function(mapGroupId) {
+         return this.mapGroups[mapGroupId];
+     },
+
+     /**
+      * Function getWidgetsByType
+      *
+      * returns an array of widgets by type.
+      *
+      * Parameter: {String} type
+      *
+      * the type of widget to get references to
+      *
+      * Returns: {Array} an array of widgets, which may be empty
+      */
+     getWidgetsByType: function(type) {
+         var widgets = [];
+         for (var i=0; i<this.widgetSets.length; i++) {
+             widgets = widgets.concat(this.widgetSets[i].getWidgetsByType(type));
+         }
+         return widgets;
+     }
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.MapGroup
+ *
+ * Holds an internal representation of MapGroup objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.MapGroup = OpenLayers.Class({
+    initialView: null,
+    maps: null,
+
+    initialize: function(jsonNode) {
+        this.mapId = jsonNode['@id'][0];
+        this.maps = [];
+        /* parse InitialView */
+        if (jsonNode.InitialView) {
+            var iv = jsonNode.InitialView[0];
+            if (iv.CenterX && iv.CenterY && iv.Scale) {
+                this.setInitialView({x:parseFloat(iv.CenterX[0]),
+                                     y:parseFloat(iv.CenterY[0]),
+                                     scale:parseFloat(iv.Scale[0])});
+            } else if (iv.MinX && iv.MinY && iv.MaxX && iv.MaxY) {
+                this.setInitialView({minX:parseFloat(iv.MinX[0]),
+                                     minY:parseFloat(iv.MinY[0]),
+                                     maxX:parseFloat(iv.MaxX[0]),
+                                     maxY:parseFloat(iv.MaxY[0])});
+            } else {
+                //TODO: emit warning that the initial view was incomplete
+            }
+        }
+        /* parse maps */
+        if (jsonNode.Map instanceof Array) {
+            for (var i=0; i<jsonNode.Map.length; i++) {
+                var map = new Fusion.Lib.ApplicationDefinition.Map(jsonNode.Map[i]);
+                var links = {groups:[], layers:[]};
+                var mapEvents = {layerEvents:{},groupEvents:{}};
+                if (jsonNode.Map[i].Extension) {
+                    var extension = jsonNode.Map[i].Extension[0];
+                    if (extension.Links) {
+                        /* process Groups */
+                        if (extension.Links[0].Group instanceof Array) {
+                            for (var j=0; j<extension.Links[0].Group.length; j++) {
+                                var group = extension.Links[0].Group[j];
+                                links.groups.push({name:group.Name[0],url:group.Url[0]});
+                            }
+                        }
+                        if (extension.Links[0].Layer instanceof Array) {
+                            for (var j=0; j<extension.Links[0].Layer.length; j++) {
+                                var layer = extension.Links[0].Layer[j];
+                                links.layers.push({name:layer.Name[0],url:layer.Url[0]});
+                            }
+                        }
+                    }
+                    /* process layer events */
+                    //TODO: Should this be called MapEvents?
+                    if (extension.MapEvents) {
+                        if (extension.MapEvents[0].Layer instanceof Array) {
+                            for (var j=0; j<extension.MapEvents[0].Layer.length; j++) {
+                                var layer = extension.MapEvents[0].Layer[j];
+                                var layerObj = {};
+                                layerObj.name = layer.Name[0];
+                                layerObj.onEnable = [];
+                                if (layer.OnEnable instanceof Array) {
+                                    layerObj.onEnable = this.parseMapEventSubBlock(layer.OnEnable[0]);
+                                }
+                                layerObj.onDisable = [];
+                                if (layer.OnDisable instanceof Array) {
+                                    layerObj.onDisable = this.parseMapEventSubBlock(layer.OnDisable[0]);
+                                }
+                                mapEvents.layerEvents[layerObj.name] = layerObj;
+                            }
+                        }
+                        if (extension.MapEvents[0].Group instanceof Array) {
+                            for (var j=0; j<extension.MapEvents[0].Group.length; j++) {
+                                var group = extension.MapEvents[0].Group[j];
+                                var groupObj = {};
+                                groupObj.name = group.Name[0];
+                                groupObj.onEnable = [];
+                                if (layer.OnEnable instanceof Array) {
+                                    groupObj.onEnable = this.parseMapEventSubBlock(group.OnEnable[0]);
+                                }
+                                groupObj.onDisable = [];
+                                if (layer.OnDisable instanceof Array) {
+                                    groupObj.onDisable = this.parseMapEventSubBlock(group.OnDisable[0]);
+                                }
+                                mapEvents.groupEvents[groupObj.name] = groupObj;
+                            }
+                        }
+                    }
+                }
+                map.mapInfo = {links: links, mapEvents: mapEvents};
+                this.maps.push(map);
+            }
+        } else {
+            //TODO: do we need a warning that there are no layers in this map?
+        }
+    },
+
+    parseMapEventSubBlock: function(block) {
+        var a = [];
+        if (block.Layer && block.Layer instanceof Array) {
+            for (var i=0; i<block.Layer.length; i++) {
+                var layer = block.Layer[i];
+                a.push({type: 'layer', name:layer.Name[0], enable: layer.Enable[0] == 'true' ? true : false});
+            }
+        }
+        if (block.Group && block.Group instanceof Array) {
+            for (var i=0; i<block.Group.length; i++) {
+                var group = block.Group[i];
+                a.push({type: 'group', name:group.Name[0], enable: group.Enable[0] == 'true' ? true : false});
+            }
+        }
+        return a;
+    },
+
+    getInitialView: function() {
+        return this.initialView;
+    },
+
+    setInitialView: function(view) {
+        this.initialView = view;
+    }
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.Map
+ *
+ * Holds an internal representation of Map objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.Map = OpenLayers.Class({
+    type: null,
+    singleTile: false,
+    extension: null,
+    
+    initialize: function(jsonNode) {
+        /* TODO: type can be any supported OpenLayers type */
+        this.type = jsonNode.Type[0];
+        if (jsonNode.SingleTile) {
+            var b = jsonNode.SingleTile[0].toLowerCase();
+            this.singleTile = (b == "true") ? true : false;
+        }
+        if (jsonNode.Extension) {
+            this.extension = jsonNode.Extension[0];
+        } else {
+            this.extension = {};
+        }
+        this.resourceId = this.extension.ResourceId ? this.extension.ResourceId[0] : '';
+
+        var tagOptions = this.extension.Options;
+        this.layerOptions = {};
+        if (tagOptions && tagOptions[0]) {
+          for (var key in tagOptions[0]) {
+            var val = tagOptions[0][key][0];
+            if (val.toFloat().toString() == val) {
+              this.layerOptions[key] = val.toFloat();
+            } else {
+              if (val.toLowerCase() == 'true') {
+                this.layerOptions[key] = true;
+              } else if (val.toLowerCase() == 'false') {
+                this.layerOptions[key] = false;
+              } else {
+                this.layerOptions[key] = val;
+              }
+            }
+            if (key == 'maxExtent' || key == 'minExtent') {
+              this.layerOptions[key] = OpenLayers.Bounds.fromString(this.layerOptions[key]);
+            }
+            if (key == 'resolutions' || key == 'scales') {
+              this.layerOptions[key] = this.layerOptions[key].split(',');
+              for (var i=0; i<this.layerOptions[key].length; i++) {
+                this.layerOptions[key][i] = this.layerOptions[key][i].toFloat();
+              }
+            }
+          }
+        }
+        var tagParams = this.extension.Parameters;
+        this.layerParams = {};
+        if (tagParams && tagParams[0]) {
+          for (var key in tagParams[0]) {
+            var val = tagParams[0][key][0];
+            if (val.toFloat().toString() == val) {
+              val = val.toFloat();
+            }
+            this.layerParams[key] = val;
+          }
+        }
+        
+        //projection info from the extension
+        //this.layerOptions.projection = "EPSG:4326";  //default to WGS84 lat long
+        if (this.extension.ProjectionCode) {
+          this.layerOptions.projection = this.extension.ProjectionCode[0];
+        }
+        if (this.extension.ProjectionDef) {
+          var projDef = this.extension.ProjectionDef[0];
+          if (!this.layerOptions.projection) {
+            this.layerOptions.projection = "APP-DEF-PROJ";
+          }
+          Proj4js.defs[this.layerOptions.projection] = projDef;
+        }
+        if (!this.layerOptions.projection) {
+          this.layerOptions.projection = "EPSG:4326";
+        }
+
+        switch (this.type) {
+          case 'MapGuide':
+          case 'MapServer':
+            if ( !Fusion.Layers[this.type] ) {
+                  Fusion.require('layers/' + this.type + '/' + this.type + '.js');
+            }
+            break;
+          case "Google":
+          case "Yahoo":
+          case "VirtualEarth":
+              this.layerOptions.isBaseLayer = true;
+              this.layerOptions.sphericalMercator = true;
+              this.layerOptions.displayProjection = "EPSG:4326";
+              //no break here continue below to set EPSG:3785 as projCode
+          default:
+            if (this.layerOptions.sphericalMercator) {
+              this.layerOptions.projection = "EPSG:900913";  //commercial map layer projection spherical mercator
+            } else {
+              this.layerOptions.projection = "EPSG:4326";  //WGS84 lat long
+            }
+            if ( !Fusion.Layers.Generic ) {
+              Fusion.require('layers/Generic/Generic.js');
+            }
+            break;
+        }
+    }
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.WidgetSet
+ *
+ * Holds an internal representation of WidgetSet objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.WidgetSet = OpenLayers.Class({
+    containers: null,
+    containersByName: null,
+    widgetTags: null,
+    widgetTagsByName: null,
+    widgetInstances: null,
+    mapWidget: null,
+    mapId: null,
+    initialize: function(jsonNode) {
+        this.containers = [];
+        this.widgetTags = [];
+        this.widgetInstances = [];
+        this.widgetTagsByName = {};
+        this.containersByName = {};
+        /* process map widgets */
+        if (jsonNode.MapWidget) {
+            for (var i=0; i<jsonNode.MapWidget.length; i++) {
+                var widget = new Fusion.Lib.ApplicationDefinition.Widget(jsonNode.MapWidget[i]);
+                widget.widgetSet = this;
+                this.mapWidgetTag = widget;
+                this.mapId = jsonNode.MapWidget[i].MapId[0];
+            }
+        }
+
+        /* process widgets */
+        if (jsonNode.Widget) {
+            for (var i=0; i<jsonNode.Widget.length; i++) {
+                var widget = new Fusion.Lib.ApplicationDefinition.Widget(jsonNode.Widget[i]);
+                widget.widgetSet = this;
+                this.widgetTags.push(widget);
+                this.widgetTagsByName[widget.name] = widget;
+            }
+        }
+        /* process containers */
+        if (jsonNode.Container) {
+            for (var i=0; i<jsonNode.Container.length; i++) {
+                var container = new Fusion.Lib.ApplicationDefinition.Container(jsonNode.Container[i]);
+                this.containers.push(container);
+                this.containersByName[container.name] = container;
+            }
+        }
+
+    },
+
+    /**
+     * Function: addWidgetInstance
+     *
+     * keep track of live widgets created in this widgetSet
+     *
+     * Parameter: {<Fusion.Widget>} widget
+     *
+     * the widget to add
+     */
+    addWidgetInstance: function(widget) {
+        this.widgetInstances.push(widget);
+    },
+
+    /**
+     * Function: getMapWidget
+     *
+     * return the map widget for this widget set
+     *
+     * Returns: {<Fusion.Lib.Map>} a map widget or null
+     */
+    getMapWidget: function() {
+        return this.mapWidget;
+    },
+
+    /**
+     * Function: create
+     *
+     * create all the things required by this widgetSet, including
+     * containers and widgets.
+     *
+     * Parameter: {<Fusion.Lib.ApplicationDefinition>}
+     *
+     * the application definition that this widgetSet is part of
+     */
+    create: function(appDef) {
+
+        //allow over-ride of mapId via URL parameter
+        var mapGroup = null;
+        var paramMapId = Fusion.getQueryParam('mapid');
+        if (paramMapId.length>0) {
+          mapGroup = appDef.getMapGroup(paramMapId);
+          if (mapGroup) {
+            this.mapId = paramMapId;
+          } else {
+            Fusion.reportError(new Fusion.Error(Fusion.Error.WARNING,
+              "can't find MapGroup: " + paramMapId + ' - reverting to default map'));
+          }
+        }
+
+        if (!mapGroup) {
+          mapGroup = appDef.getMapGroup(this.mapId);
+        }
+
+        //create the Map widget for this WidgetSet
+        this.mapWidget = new Fusion.Widget.Map(this.mapWidgetTag,mapGroup,this);
+        this.mapWidget.setMenu();
+        $(this.mapWidgetTag.name).widget = this.mapWidget;
+
+        //create all the other widgets for the widget set
+        for (var i=0; i<this.widgetTags.length; i++) {
+            this.widgetTags[i].create(this);
+        }
+
+        //create all the containers for the widget set
+        for (var i=0; i<this.containers.length; i++) {
+            this.containers[i].create(this);
+        }
+    },
+    /**
+     * Function: getMapByName
+     *
+     * return the map widget from this widget set if the map's name
+     * matches the requested name, or null.
+     *
+     * Parameter: {String} name
+     *
+     * The map name to check
+     *
+     * Returns: {Object} a map object or null.
+     */
+    getMapByName : function(name) {
+        var map = null;
+        if (this.mapWidget.getMapName() == name) {
+            map = this.mapWidget;
+        }
+        return map;
+    },
+
+    /**
+     * Function getWidgetsByType
+     *
+     * returns an array of widgets by type.
+     *
+     * Parameter: {String} type
+     *
+     * the type of widget to get references to
+     *
+     * Returns: {Array} an array of widgets, which may be empty
+     */
+    getWidgetsByType: function(type) {
+        var widgets = [];
+        for (var i=0; i<this.widgetInstances.length; i++) {
+            if (this.widgetInstances[i].type == type) {
+                widgets.push(this.widgetInstances[i]);
+            }
+        }
+        return widgets;
+    },
+
+    getWidgetByName: function(name) {
+        return this.widgetTagsByName[name];
+    },
+
+    getContainerByName: function(name) {
+        return this.containersByName[name];
+    }
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.Container
+ *
+ * Holds an internal representation of Container objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.Container = OpenLayers.Class({
+    name: null,
+    type: null,
+    validPositions: ['top', 'left', 'bottom', 'right'],
+    position: 'top',
+    items: null,
+    initialize: function(jsonNode) {
+        this.type = jsonNode.Type[0];
+        this.name = jsonNode.Name[0];
+        var position = jsonNode.Position ? jsonNode.Position[0].toLowerCase() : this.position;
+        for (var i=0; i<this.validPositions.length; i++) {
+            if (this.validPositions[i] == position) {
+                this.position = position;
+                break;
+            }
+        }
+        this.items = [];
+        if (jsonNode.Item) {
+            for (var i=0; i<jsonNode.Item.length; i++) {
+                var item = new Fusion.Lib.ApplicationDefinition.Item(jsonNode.Item[i]);
+                this.items.push(item);
+            }
+        } else {
+            //TODO: is this a problem if there are no items?
+        }
+    },
+
+    create: function(widgetSet) {
+        var container;
+        if (this.type == 'Toolbar' || this.type == 'Statusbar') {
+            if ($(this.name)) {
+                if ($(this.name).retrieve('jxToolbar')) {
+                    container = $(this.name).retrieve('jxToolbar');
+                } else {
+                  container = new Jx.Toolbar({parent: this.name, position: this.position, scroll:false});
+                }
+                $(this.name).container = container;
+                this.createWidgets(widgetSet, container);
+            }
+        } else if (this.type == 'Splitterbar') {
+            if ($(this.name)) {
+                container = new Jx.Splitter(this.name, {splitInto: this.items.length});
+                for (var i=0; i<this.items.length; i++) {
+                    container.elements[i].id = this.name + '_' + i;
+                }
+                $(this.name).container = container;
+                this.createWidgets(widgetSet, container);
+            }
+        }
+        if (container && container.domObj.jxLayout) {
+            container.domObj.jxLayout.resize({forceResize: true});
+        }
+    },
+
+    createWidgets: function(widgetSet, container) {
+        for (var i=0; i<this.items.length; i++) {
+            this.items[i].create(widgetSet, container, this.name + '_' + i);
+        }
+    }
+
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.Widget
+ *
+ * Holds an internal representation of Widget objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.Widget = OpenLayers.Class({
+    name: null,
+    type: null,
+    statusText: null,
+    location: null,
+    imageUrl: null,
+    imageClass: null,
+    tooltip: null,
+    label: null,
+    disabled: null,
+    extension: null,
+    initialize: function(jsonNode) {
+        if (jsonNode) {
+            this.type = jsonNode.Type[0];
+            this.name = jsonNode.Name ? jsonNode.Name[0] : '';
+            this.statusText = jsonNode.StatusText ? jsonNode.StatusText[0] : '';
+            this.statusText = OpenLayers.i18n(this.statusText);
+            //TODO: this may be an extension
+            this.location = jsonNode.Location ? jsonNode.Location[0] : 'widgets/';
+            if (this.location.slice(-1) != '/') {
+                this.location += '/';
+            }
+            this.imageUrl = jsonNode.ImageUrl ? jsonNode.ImageUrl[0] : '';
+            this.imageClass = jsonNode.ImageClass ? jsonNode.ImageClass[0] : '';
+            this.tooltip = jsonNode.Tooltip ? jsonNode.Tooltip[0] : '';
+            this.tooltip = OpenLayers.i18n(this.tooltip);
+            this.label = jsonNode.Label ? jsonNode.Label[0] : '';
+            this.label = OpenLayers.i18n(this.label);
+            this.disabled = jsonNode.Disabled ? (jsonNode.Disabled[0].toLowerCase() == 'true' ? true : false) : false;
+
+            //console.log('Widget: ' + this.type + ', ' + this.name + ', ' + this.description);
+
+            if (jsonNode.Extension) {
+                this.extension = jsonNode.Extension[0];
+            } else {
+                this.extension = {};
+            }
+            //require the widget code
+            if ( !Fusion.Widget[this.type] ) {
+              Fusion.require(this.location + this.type + '.js');
+            }
+        }
+    },
+
+    getMapWidget: function() {
+        if (this.widgetSet) {
+            return this.widgetSet.getMapWidget();
+        } else {
+            return null;
+        }
+    },
+
+    /**
+     * Function: create
+     *
+     * creates a new instance of the widget, optionally using a
+     * different name during instantiation to accomodate
+     * containers
+     *
+     * Parameter: name
+     *
+     * An optional name to use for the widget, overrides the
+     * original name temporarily if passed.
+     *
+     * Returns: an instance of the widget represented by this
+     * object.
+     */
+    create: function(widgetSet, widgetName) {
+        var widget = null;
+        this.widgetSet = widgetSet;
+        var oldName = this.name;
+        if (typeof widgetName == 'undefined') {
+            widgetName = this.name;
+        }
+        /* create the widget if the name is not null and the name
+         * is either an empty string (in the case of buttons in menus)
+         * or something exists in the dom with the right id
+         */
+        if (widgetName != null && (widgetName == '' || $(widgetName) != null)) {
+            this.name = widgetName;
+            widget = eval("new Fusion.Widget."+this.type+"(this)");
+            widgetSet.addWidgetInstance(widget);
+            if ($(this.name)) {
+                widget.id = this.name;
+                $(this.name).widget = widget;
+                if (widget.uiClass) {
+                  widget.setUiObject(new widget.uiClass({
+                      label: this.label,
+                      image: this.imageUrl,
+                      imageClass: this.imageClass,
+                      toggle: this.isExclusive
+                  }));
+                    if (widget.uiObj.addTo) {
+                        widget.uiObj.addTo($(this.name));
+                    }
+                    if (widget.uiObj.setEnabled) {
+                        widget.uiObj.setEnabled(widget.isEnabled());
+                        widget.registerForEvent(Fusion.Event.WIDGET_STATE_CHANGED, function() {
+                            widget.uiObj.setEnabled(widget.isEnabled());
+                        });
+                    }
+                }
+            }
+            this.name = oldName;
+        }
+        return widget;
+    }
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.Item
+ *
+ * Holds an internal representation of Menu Item objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.Item = OpenLayers.Class({
+    uniqueId: [0],
+    type: null,
+    initialize: function(jsonNode) {
+        this.type = jsonNode.Function[0];
+        switch(this.type) {
+            case 'Widget':
+                this.widgetName = jsonNode.Widget[0];
+                break;
+            case 'Flyout':
+                this.flyout = new Fusion.Lib.ApplicationDefinition.Flyout(jsonNode);
+                break;
+            case 'Multi':
+                this.multi = new Fusion.Lib.ApplicationDefinition.Multi(jsonNode);
+                break;
+            case 'Separator':
+                break;
+        }
+    },
+
+    create: function(widgetSet, container, idx) {
+        switch(this.type) {
+            case 'Widget':
+                var widgetTag = widgetSet.getWidgetByName(this.widgetName);
+                if (widgetTag) {
+                    var name = 'FusionItem'+this.uniqueId[0];
+                    this.uniqueId[0]++;
+                    if (container instanceof Jx.Toolbar) {
+                        var widget = widgetTag.create(widgetSet, '');
+                        if (!widget.uiClass) {
+                            widget.uiClass = Jx.Toolbar.Item;
+                            widget.setUiObject(new widget.uiClass());
+                        } else if (widget.uiClass == Jx.Button || widget.uiClass == Jx.Button.Color || widget.uiClass == Jx.Menu || widget.uiClass == Jx.Button.Flyout) {
+                            widget.setUiObject(new widget.uiClass({
+                                label: widgetTag.label,
+                                image: widgetTag.imageUrl,
+                                imageClass: widgetTag.imageClass,
+                                toggle: widget.isExclusive
+                            }));
+                        }
+                        container.add(widget.uiObj);
+                        if (widget.uiObj.setEnabled) {
+                            widget.uiObj.setEnabled(widget.isEnabled());
+                            widget.registerForEvent(Fusion.Event.WIDGET_STATE_CHANGED, function() {
+                                widget.uiObj.setEnabled(widget.isEnabled());
+                            });
+                        }
+                    } else if (container instanceof Jx.Splitter) {
+                        var widget = widgetTag.create(widgetSet, idx);
+                    } else if (container instanceof Jx.Menu ||
+                               container instanceof Jx.Menu.Context ||
+                               container instanceof Jx.Menu.SubMenu) {
+                        var widget = widgetTag.create(widgetSet, '');
+                        if (!widget.uiClass || widget.uiClass == Jx.Button) {
+                            widget.uiClass = Jx.Menu.Item;
+                        } else if (widget.uiClass == Jx.Menu) {
+                            widget.uiClass = Jx.Menu.SubMenu;
+                        }
+                        widget.setUiObject(new widget.uiClass({
+                            label: widgetTag.label,
+                            image: widgetTag.imageUrl,
+                            imageClass: widgetTag.imageClass,
+                            toggle: widget.isExclusive
+                        }));
+                        container.add(widget.uiObj);
+                        if (widget.uiObj.setEnabled) {
+                            widget.uiObj.setEnabled(widget.isEnabled());
+                            widget.registerForEvent(Fusion.Event.WIDGET_STATE_CHANGED, function() {
+                                widget.uiObj.setEnabled(widget.isEnabled());
+                            });
+                        }
+                    } else if (container instanceof Jx.Button.Multi) {
+                        var widget = widgetTag.create(widgetSet, '');
+                        widget.uiClass = Jx.Button;
+                        widget.setUiObject(new widget.uiClass({
+                            label: widgetTag.label,
+                            image: widgetTag.imageUrl,
+                            imageClass: widgetTag.imageClass,
+                            toggle: widget.isExclusive
+                        }));
+                        container.add(widget.uiObj);
+                        if (widget.uiObj.setEnabled) {
+                            widget.uiObj.setEnabled(widget.isEnabled());
+                            widget.registerForEvent(Fusion.Event.WIDGET_STATE_CHANGED, function() {
+                                widget.uiObj.setEnabled(widget.isEnabled());
+                                var b = !container.buttons.some(function(w) {
+                                  return !w.isEnabled();
+                                });
+                                container.setEnabled(b);
+                            });
+                        }
+                    }
+                } else {
+                  Fusion.reportError(new Fusion.Error(Fusion.Error.WARNING,
+                    "can't find widget: " + this.widgetName));
+                 }
+                break;
+            case 'Flyout':
+                /* create a menu */
+                var menu;
+                var opt = {
+                    label: this.flyout.label,
+                    tooltip: this.flyout.tooltip,
+                    image: this.flyout.imageUrl,
+                    imageClass: this.flyout.imageClass
+                };
+                if (container instanceof Jx.Toolbar) {
+                    menu = new Jx.Menu(opt);
+                } else if (container instanceof Jx.Menu ||
+                           container instanceof Jx.Menu.Context ||
+                           container instanceof Jx.Menu.SubMenu) {
+                    menu = new Jx.Menu.SubMenu(opt);
+                }
+                container.add(menu);
+                this.flyout.create(widgetSet, menu);
+
+                break;
+            case 'Multi':
+                var multi;
+                var opt = {
+                    label: this.multi.label,
+                    tooltip: this.multi.tooltip,
+                    image: this.multi.imageUrl,
+                    imageClass: this.multi.imageClass
+                }
+                multi = new Jx.Button.Multi(opt);
+                container.add(multi);
+                this.multi.create(widgetSet, multi);
+                break;
+
+            case 'Separator':
+                if (container instanceof Jx.Toolbar) {
+                    container.add(new Jx.Toolbar.Separator());
+                } else if (container instanceof( Jx.Menu) ||
+                           container instanceof(Jx.Menu.SubMenu) ||
+                           container instanceof(Jx.Menu.Context)) {
+                    container.add(new Jx.Menu.Separator());
+                }
+                break;
+        }
+    }
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.Flyout
+ *
+ * Holds an internal representation of Flyout objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.Flyout = OpenLayers.Class({
+    label: null,
+    tooltip: null,
+    description: null,
+    imageUrl: null,
+    items: null,
+
+    initialize: function(jsonNode) {
+        this.label = jsonNode.Label ? jsonNode.Label[0] : '';
+        this.tooltip = jsonNode.Tooltip ? jsonNode.Tooltip[0] : '';
+        this.description = jsonNode.Description ? jsonNode.Description[0] : '';
+        this.imageUrl = jsonNode.ImageUrl ? jsonNode.ImageUrl[0] : '';
+        this.items = [];
+        if (jsonNode.Item instanceof Array) {
+            for (var i=0; i<jsonNode.Item.length; i++) {
+                this.items.push(new Fusion.Lib.ApplicationDefinition.Item(jsonNode.Item[i]));
+            }
+        }
+    },
+
+    create: function(widgetSet, menu) {
+        for (var i=0; i<this.items.length; i++) {
+            this.items[i].create(widgetSet, menu);
+        }
+    }
+
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.Multi
+ *
+ * Holds an internal representation of Multi objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.Multi = OpenLayers.Class({
+    label: null,
+    tooltip: null,
+    description: null,
+    imageUrl: null,
+    items: null,
+
+    initialize: function(jsonNode) {
+        this.label = jsonNode.Label ? jsonNode.Label[0] : '';
+        this.tooltip = jsonNode.Tooltip ? jsonNode.Tooltip[0] : '';
+        this.description = jsonNode.Description ? jsonNode.Description[0] : '';
+        this.imageUrl = jsonNode.ImageUrl ? jsonNode.ImageUrl[0] : '';
+        this.items = [];
+        if (jsonNode.Item instanceof Array) {
+            for (var i=0; i<jsonNode.Item.length; i++) {
+                this.items.push(new Fusion.Lib.ApplicationDefinition.Item(jsonNode.Item[i]));
+            }
+        }
+    },
+
+    create: function(widgetSet, multi) {
+        for (var i=0; i<this.items.length; i++) {
+            this.items[i].create(widgetSet, multi);
+        }
+    }
+
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.SearchDefinition
+ *
+ * Holds an internal representation of SearchDefinition objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.SearchDefinition = OpenLayers.Class({
+    id: null,
+    name: null,
+    category: null,
+    parameters: null,
+    join: null,
+    rule: null,
+
+    initialize: function(json) {
+        this.id = json['@id'];
+        this.name = json['@name'];
+        if (json.Join instanceof Array) {
+            this.join = new Fusion.Lib.ApplicationDefinition.SearchJoin(json.Join[0]);
+        }
+        this.parameters = [];
+        if (json.Parameter instanceof Array) {
+            for (var i=0; i<json.Parameter.length; i++) {
+                this.parameters.push(json.Parameter[i]['@name']);
+            }
+        }
+        this.reports = [];
+        if (json.Report instanceof Array) {
+            for (var i=0; i<json.Report.length; i++) {
+              var label = json.Report[i].Label[0]
+              var link = json.Report[i].Link[0]
+              this.reports.push({'label':label, 'link':link});
+            }
+        }
+        var rule;
+        if (json.SearchAnd instanceof Array) {
+            this.rule = new Fusion.Lib.ApplicationDefinition.SearchRule('AND');
+            rule = json.SearchAnd[0];
+        } else if (json.SearchOr instanceof Array) {
+            this.rule = new Fusion.Lib.ApplicationDefinition.SearchRule('OR');
+            rule = json.SearchOr[0];
+        }
+        if (rule && rule.SearchCondition instanceof Array) {
+            for (var i=0; i<rule.SearchCondition.length; i++) {
+                this.rule.add(new Fusion.Lib.ApplicationDefinition.SearchCondition(rule.SearchCondition[i]));
+            }
+        }
+    },
+
+    getJoinUrl: function(params) {
+        if (this.join) {
+            return '&joinlayer='+this.join.layer+'&joinpk='+this.join.primaryKey+'&joinfk='+this.join.foreignKey;
+        } else {
+            return '';
+        }
+    },
+
+    getFilterUrl: function(params) {
+        return '&filter='+encodeURIComponent(this.rule.toString(params));
+    }
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.SearchJoin
+ *
+ * Holds an internal representation of SearchJoin objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.SearchJoin = OpenLayers.Class({
+    layer: null,
+    primaryKey: null,
+    foreignKey: null,
+    initialize: function(json) {
+        this.layer = json.Layer ? json.Layer[0] : '';
+        this.primaryKey = json.PrimaryKey ? json.PrimaryKey[0] : '';
+        this.foreignKey = json.ForeignKey ? json.ForeignKey[0] : '';
+    }
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.SearchRule
+ *
+ * Holds an internal representation of SearchRule objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.SearchRule = OpenLayers.Class({
+    type: null,
+    conditions: null,
+    initialize: function(type) {
+        this.type = type;
+        this.conditions = [];
+    },
+
+    add: function(condition) {
+        this.conditions.push(condition);
+    },
+
+    remove: function(condition) {
+        for (var i=0; i<this.conditions.length; i++) {
+            if (this.conditions[i] == condition) {
+                this.conditions.splice(i, 1);
+                break;
+            }
+        }
+    },
+
+    toString: function(params) {
+        var conditions = [];
+        for (var i=0; i<this.conditions.length; i++) {
+            this.conditions[i].setParams(params);
+            var c = this.conditions[i].toString();
+            if (c != '') {
+                conditions.push(c);
+            }
+        }
+        return '(' + conditions.join(') ' + this.type + ' (') + ')';
+    }
+});
+
+/****************************************************************************
+ * Class: Fusion.Lib.ApplicationDefinition.SearchCondition
+ *
+ * Holds an internal representation of SearchCondition objects as defined in the AppDef
+ *
+ */
+
+Fusion.Lib.ApplicationDefinition.SearchCondition = OpenLayers.Class({
+    column: null,
+    operator: null,
+    parameter: null,
+    quote: null,
+    value: null,
+    operators: {eq:'=', like:'like', lt:'<', lte:'<=', gt:'>', gte:'>=', neq:'<>'},
+    includeIfEmpty: false,
+
+    initialize: function(json) {
+        this.column = json.Column[0];
+        this.operator = this.operators[json.Operator[0].toLowerCase()];
+        this.parameter = json.Parameter[0];
+        this.quote = json['@quote'] ? json['@quote'] : '';
+        this.wildcard = json['@wildcard'] ? json['@wildcard'] : 'both';
+        this.caseSensitive = true;
+        if (json['@caseSensitive'] && json['@caseSensitive'] == 'false') {
+            this.caseSensitive = false;
+        }
+    },
+
+    setParams: function(p) {
+        if (p[this.parameter]) {
+            this.value = p[this.parameter];
+        } else {
+            this.value = '';
+        }
+    },
+
+    toString: function() {
+        var value = this.value ? this.value : '';
+        if (value == '' && !this.includeIfEmpty) {
+            return '';
+        }
+        var upper = '';
+        if (!this.caseSensitive) {
+            value = value.toUpperCase();
+            upper = 'Upper';
+        }
+        var prewildcard = '';
+        var prewildcard = '';
+        var postwildcard = '';
+        if (this.operator == 'like') {
+            if (this.wildcard == 'before' || this.wildcard == 'both') {
+                prewildcard = '*';
+            }
+            if (this.wildcard == 'after' || this.wildcard == 'both') {
+                postwildcard = '*';
+            }
+        }
+        var wildcard = this.operator == 'like' ? '*' : '';
+        return upper + '('+this.column + ') ' + this.operator + ' ' + this.quote + prewildcard + value + postwildcard + this.quote;
+    }
+});

Modified: trunk/lib/Map.js
===================================================================
--- trunk/lib/Map.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/lib/Map.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -42,6 +42,8 @@
 Fusion.Event.MAP_RELOADED = Fusion.Event.lastEventId++;
 Fusion.Event.MAP_SESSION_CREATED = Fusion.Event.lastEventId++;
 Fusion.Event.MAP_MAPTIP_REQ_FINISHED = Fusion.Event.lastEventId++;
+Fusion.Event.WMS_LAYER_ADDED = Fusion.Event.lastEventId++;
+Fusion.Event.MAP_SCALE_RANGE_LOADED = Fusion.Event.lastEventId++;
 Fusion.Event.MAP_MAP_GROUP_LOADED = Fusion.Event.lastEventId++;
 
 
@@ -53,57 +55,57 @@
 Fusion.Constant.LAYER_DWF_TYPE = 5;
 
 Fusion.Widget.Map = OpenLayers.Class(Fusion.Lib.EventMgr, {
-
+    
     /** The DOM object that holds the map */
     _oDomObj: null,
-
+    
     /** The id of DOM object */
     _sDomObj: '',
-
+    
     /** The internal name used for the map, usually assigned by the server */
     _sMapname: '',
-
+    
     /** conversion factor to convert from map units to meters */
     _fMetersperunit: -1,
-
+    
     /** scale denominator value */
     _fScale: -1,
-
+    
     /** Dots per inch setting for the server */
     _nDpi: 96,
-
+    
     /** The current bounds of the map, this is updated on every map draw */
     _oCurrentExtents: null,
-
+    
     /** The maximum extent of the map */
     maxExtent: new OpenLayers.Bounds(),
-
+    
     /** counter of worker threads for the ActivityIndicator widget  */
     _nWorkers: 0,
-
+    
     /** The context menu object for right click */
     oContextMenu: null,
-
+    
     /** flag to disable the map context menu, set this in the Map extension  */
     bSupressContextMenu: false,
-
+    
     /** an array to hold all the individual map layer objects */
     aMaps: null,
-
+    
     /** flag to indicate if all the map layers are loaded */
     mapsLoaded: false,
-
+    
     /** the root of the layer structure for the Legend */
     layerRoot: null,
-
+    
     /** flag to indicate if the map is tiled or not, set in the Map extension */
     singleTile: true,
-
+    
     /** flag to indicate if fractional zoom is supported, ie. allow any scale
     *   value rather than a fixed set of scales
     */
     fractionalZoom: true,
-
+    
     /** The DOM object that holds the map */
     maxScale: null, //set this to a large number in AppDef to zoom out beyond maxExtent, e.g. 1 billion
 
@@ -206,9 +208,12 @@
         this.registerEventID(Fusion.Event.MAP_SELECTION_ON);
         this.registerEventID(Fusion.Event.MAP_SELECTION_OFF);
         this.registerEventID(Fusion.Event.MAP_MAPTIP_REQ_FINISHED);
+        this.registerEventID(Fusion.Event.WMS_LAYER_ADDED);
+        this.registerEventID(Fusion.Event.MAP_SCALE_RANGE_LOADED);
         this.registerEventID(Fusion.Event.MAP_MAP_GROUP_LOADED);
 
         this.registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.mapLoaded,this));
+        this.registerForEvent(Fusion.Event.MAP_RELOADED, OpenLayers.Function.bind(this.mapLoaded,this));
 
         //register for OL map extent change events
         this.oMapOL.events.register('moveend', this, this.mapExtentsChanged);
@@ -236,6 +241,7 @@
      */
     mapLoaded: function() {
       this.setViewOptions(this.getUnits());
+      this.loadScaleRanges(OpenLayers.Function.bind(this.scaleRangesLoaded, this));
     },
 
     /**
@@ -279,7 +285,7 @@
             this.aMaps[i].oLayerOL.destroy();
           }
         }
-
+ 
         this._fMetersperunit = -1;
         this.oMapOL.center = null;
         this.maxExtent = new OpenLayers.Bounds();
@@ -305,7 +311,7 @@
     /**
      * Function: layerLoaded
      *
-     * Called after each layer is loaded in the OL viewport.  Nothing happens
+     * Called after each layer is loaded in the OL viewport.  Nothing happens 
      * here until the last layer is laoded, then it sets the OL baselayer and
      * zooms to the initial extent.
      *
@@ -348,7 +354,7 @@
      *
      * handles mouse wheel events by accummulating the events and setting a timer
      * to do the actual zoom in/out
-     *
+     * 
      * Parameters:
      * evt - {Event} the mouse wheel event object
      * deltaZ - {Integer} the number of ticks wheeled
@@ -360,7 +366,7 @@
             this.cumulativeDelta = 0;
         }
         this.cumulativeDelta += (deltaZ < 0)?-1:1;
-
+        
         this.wheelTimer = setTimeout(OpenLayers.Function.bind(function(){this.doWheelChange(evt, deltaZ);}, this), 200);
     },
 
@@ -464,6 +470,12 @@
         return this.aMaps[0]._sMapTitle;
     },
 
+    getMetadata: function(key) {
+        //TODO: what is the map title in the case of multiple map layer objects?
+        //just return baselayer mapTitle for now
+        return this.aMaps[0].getMetadata(key);
+    },
+
     /**
      * Function: getSessionID
      *
@@ -533,14 +545,14 @@
         }
         this.oMapOL.addLayer(map.oLayerOL);
         if (map.oLayerOL.isBaseLayer) {
-          this.projection = this.oMapOL.baseLayer.projection;
-          this.units = this.oMapOL.baseLayer.units;
+          this.projection = map.projection;
+          this.units = map.units;
           this.oMapOL.setOptions({
-                  units: this.units,
+                  units: map.units,
                   projection: this.projection
           });
         }
-
+        
         map.registerForEvent(Fusion.Event.MAP_SELECTION_OFF,
                 OpenLayers.Function.bind(this.selectionHandler, this));
         map.registerForEvent(Fusion.Event.MAP_SELECTION_ON,
@@ -577,18 +589,6 @@
       }
     },
 
-    //this method allows a specified map to be reloaded, rather than reloading all of them
-    reloadNamedMap: function(mapName) {
-        if(mapName != null && mapName != "") {
-            for (var i=0; i<this.aMaps.length; ++i) {
-                var map = this.aMaps[i];
-                if(map.getMapName() == mapName) {
-                    window.setTimeout(OpenLayers.Function.bind(map.reloadMap, map),1);
-                }
-            }
-        }
-    },
-
     /**
      * Function: loadScaleRanges
      *
@@ -600,10 +600,10 @@
      *
      * Return: none
      */
-    loadScaleRanges: function(userFunc) {
+    loadScaleRanges: function() {
       for (var i=0; i<this.aMaps.length; ++i) {
         var map = this.aMaps[i];
-        OpenLayers.Function.bind(map.loadScaleRanges(userFunc), map);
+        map.loadScaleRanges();
       }
     },
 
@@ -688,8 +688,9 @@
          this.oSelection = {};
          this.nSelectionMaps = 0;
          for (var i=0; i<this.aMaps.length; i++ ) {
-             if(this.aMaps[i].layerType == "Google" || this.aMaps[i].layerType == "Yahoo" ||this.aMaps[i].layerType == "VirtualEarth" )
-                 continue;
+           if (!this.aMaps[i].supports.query) 
+         // if(this.aMaps[i].layerType == "Google" || this.aMaps[i].layerType == "Yahoo" ||this.aMaps[i].layerType == "VirtualEarth" )
+			     continue;
              this.nSelectionMaps++;
              this.aMaps[i].getSelection(
                     OpenLayers.Function.bind(this.accumulateSelection, this, this.aMaps[i]),
@@ -702,8 +703,8 @@
       *
       * sets a Selection XML back to the server for each of the maps
       *
-      * Parameters:
-      * {String} selText - the selection string
+      * Parameters: 
+      * {String} selText - the selection string 
       * {Boolean} zoomTo - if set, will zoom to the selection on completion
       */
       setSelection: function(selText, zoomTo) {
@@ -764,7 +765,7 @@
 
     /**
      * Function: _addWorker
-     *
+     * 
      * indicate that a new asynchronous process has started and make sure the
      * visual indicator is visible for the user.  This is intended to be used
      * internally by gMap but could be used by external tools if appropriate.
@@ -777,7 +778,7 @@
 
     /**
      * Function: _removeWorker
-     *
+     * 
      * indicate that an asynchronous process has completed and hide the
      * visual indicator if no remaining processes are active.  This is
      * intended to be used internally by gMap but could be used by
@@ -796,7 +797,7 @@
      * Function: mapExtentsChanged
      *
      * OpenLayers event handler for whenever the map changes either zoom level
-     * of the center has changed.  This in turn triggers the Fusion event.
+     * of the center has changed.  This in turn triggers the Fusion event. 
      *
      * Parameters: none
      *
@@ -823,7 +824,7 @@
     /**
      * Function: sizeChanged
      *
-     * callback for when the browser window size changes, simply calls resize
+     * callback for when the browser window size changes, simply calls resize 
      * on the map viewport layout object
      *
      * Parameters: none
@@ -896,7 +897,7 @@
      * handle selection events from maps and republish for
      * widgets as appropriate
      *
-     * Parameters: {Object} oExtents - an OpenLayers.Bounds object or an array
+     * Parameters: {Object} oExtents - an OpenLayers.Bounds object or an array 
      *                            of 4 values that will be converted to Bounds
      *
      * Return: none
@@ -922,7 +923,7 @@
 
     /**
      * Function: setInitialExtents
-     *
+     * 
      * determine the initialExtents of the map from (in order of precedence):
      * 1. a URL query parameter called 'extent'
      * 2. an <InitialView> specified in the MapGroup in AppDef
@@ -942,6 +943,10 @@
           } else if (iv.minX) {
               initialExtents = new OpenLayers.Bounds(iv.minX, iv.minY, iv.maxX, iv.maxY);
           }
+          var mapProj = this.oMapOL.baseLayer.projection;
+          if (iv.projection && !iv.projection.equals(mapProj)) {
+            initialExtents.transform(iv.projection, mapProj);
+          }
           if (!initialExtents.intersectsBounds(this.maxExtent)) {
             Fusion.reportError("AppDef initial view is outside map maxExtent, resetting initialView to maxExtent");
             initialExtents = this.maxExtent;
@@ -961,7 +966,7 @@
      *
      * gets the intitial extents from all maps with or without including the
      * extent of any Generic/commercial layers
-     *
+     * 
      * Return: {Object} an OpenLayers.Bounds object which is the initial extent
      */
     getMapGroupExtent: function(includeGenericLayers) {
@@ -977,7 +982,7 @@
 
     /**
      * Function: fullExtents
-     *
+     * 
      * sets the extent of the map to the max as returned by loadMap
      */
     fullExtents: function() {
@@ -987,8 +992,8 @@
 
     /**
      * Function: isMapLoaded
-     *
-     * returns if the map has finished loading (based on setting the
+     * 
+     * returns if the map has finished loading (based on setting the 
      * _oCurrentExtent value)
      *
      * Returns {Boolean} true if the map is loaded and extent has been set
@@ -999,7 +1004,7 @@
 
     /**
      * Function: zoom
-     *
+     * 
      * sets the map zoom and extent.
      *
      * Parameters:
@@ -1051,7 +1056,7 @@
 
     /**
      * Function: zoom
-     *
+     * 
      * sets the map zoom and extent.
      *
      * Parameters:
@@ -1073,7 +1078,7 @@
 
     /**
      * Function: zoom
-     *
+     * 
      * sets the map zoom and extent.
      *
      * Parameters:
@@ -1091,7 +1096,7 @@
 
     /**
      * Function: zoom
-     *
+     * 
      * sets the map zoom and extent.
      *
      * Parameters:
@@ -1418,12 +1423,12 @@
         }
         return OpenLayers.Util.getParameterString(queryParams);
      },
-
+     
      getMapTip: function(oMapTips) {
         //TODO send this to the correct aMap object
         this.aMaps[0].getMapTip(oMapTips);
      },
-
+     
      supressContextMenu: function( bSupress ) {
          this.bSupressContextMenu = bSupress;
      },

Copied: trunk/lib/OLpatch.js (from rev 2248, sandbox/jxlib-3.0/lib/OLpatch.js)
===================================================================
--- trunk/lib/OLpatch.js	                        (rev 0)
+++ trunk/lib/OLpatch.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,2 @@
+/** remove String.prototype.contains **/
+String.prototype.contains = null;
\ No newline at end of file

Modified: trunk/lib/Widget.js
===================================================================
--- trunk/lib/Widget.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/lib/Widget.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -94,7 +94,17 @@
           }
         }
         if (this.isExclusive) {
+        	if (Fusion.Widget.uiInstances[this.type].length==1) {
             this.getMap().buttonSet.add(uiObj);
+          } else {
+						var instances = Fusion.Widget.uiInstances[this.type];
+						for (var i=0; i<instances.length; i++) {
+							if (!this.shouldActivateWith(instances[i])) {
+								this.getMap().buttonSet.add(uiObj);
+								break;
+							}
+						}
+					}
         }
         if (uiObj.options.active) {
             this.activate();
@@ -102,11 +112,9 @@
         if (uiObj.addEvents) {
             if (Fusion.Widget.uiInstances[this.type][0].uiObj &&
                 Fusion.Widget.uiInstances[this.type][0].uiObj.options.active &&
-                Fusion.Widget.uiInstances[this.type][0].shouldActivateWith(this)) {
-                uiObj.options.active = true;
-                if (uiObj.domA) {
-                    uiObj.domA.addClass('jx' + uiObj.options.type + 'Active');
-                }
+                Fusion.Widget.uiInstances[this.type][0].shouldActivateWith(this) &&
+                uiObj.setActive) {
+                uiObj.setActive(true);
             }
             
             uiObj.addEvents({
@@ -117,11 +125,9 @@
                     var instances = Fusion.Widget.uiInstances[this.type];
                     for (var i=0; i<instances.length; i++) {
                         var instance = instances[i];
-                        if (instance.shouldActivateWith(this) && instance.uiObj) {
-                            instance.uiObj.options.active = false;
-                            if (instance.uiObj.domA) {
-                                instance.uiObj.domA.removeClass('jx' + instance.uiObj.options.type + 'Active');
-                            }                            
+                        if (instance.shouldActivateWith(this) &&
+                            instance.uiObj && instance.uiObj.setActive) {
+                            instance.uiObj.setActive(false);
                         }
                     }
                     this.deactivate();
@@ -130,11 +136,9 @@
                     var instances = Fusion.Widget.uiInstances[this.type];
                     for (var i=0; i<instances.length; i++) {
                         var instance = instances[i];
-                        if (instance.shouldActivateWith(this) && instance.uiObj) {
-                            instance.uiObj.options.active = true;
-                            if (instance.uiObj.domA) {
-                                instance.uiObj.domA.addClass('jx' + instance.uiObj.options.type + 'Active');
-                            }                            
+                        if (instance.shouldActivateWith(this) &&
+                            instance.uiObj && instance.uiObj.setActive) {
+                            instance.uiObj.setActive(true);
                         }
                     }
                     this.activate();

Modified: trunk/lib/fusion.js
===================================================================
--- trunk/lib/fusion.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/lib/fusion.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1357,6 +1357,7 @@
     
     if (!Fusion._singleFile) {
         var coreScripts = ['lib/OpenLayers/OpenLayers.js',
+                            'lib/OLpatch.js',
                             'lib/jxlib.uncompressed.js',
                             'lib/proj4js-compressed.js',
                             'lib/EventMgr.js',

Modified: trunk/lib/jxlib.uncompressed.js
===================================================================
--- trunk/lib/jxlib.uncompressed.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/lib/jxlib.uncompressed.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,12 +1,12 @@
 /******************************************************************************
- * MooTools 1.2.1
+ * MooTools 1.2.2
  * Copyright (c) 2006-2007 [Valerio Proietti](http://mad4milk.net/).
  * MooTools is distributed under an MIT-style license.
  ******************************************************************************
  * reset.css - Copyright (c) 2006, Yahoo! Inc. All rights reserved.
  * Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt
  ******************************************************************************
- * Jx UI Library, 2.0.2
+ * Jx UI Library, 3.0alpha
  * Copyright (c) 2006-2008, DM Solutions Group Inc. All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -27,10379 +27,21599 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  *****************************************************************************/
+/*
+---
+
+script: Core.js
+
+description: The core of MooTools, contains all the base functions and the Native and Hash implementations. Required by all the other scripts.
+
+license: MIT-style license.
+
+copyright: Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
+
+authors: The MooTools production team (http://mootools.net/developers/)
+
+inspiration:
+- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
+- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+
+provides: [MooTools, Native, Hash.base, Array.each, $util]
+
+...
+*/
+
+var MooTools = {
+	'version': '1.2.5dev',
+	'build': '%build%'
+};
+
+var Native = function(options){
+	options = options || {};
+	var name = options.name;
+	var legacy = options.legacy;
+	var protect = options.protect;
+	var methods = options.implement;
+	var generics = options.generics;
+	var initialize = options.initialize;
+	var afterImplement = options.afterImplement || function(){};
+	var object = initialize || legacy;
+	generics = generics !== false;
+
+	object.constructor = Native;
+	object.$family = {name: 'native'};
+	if (legacy && initialize) object.prototype = legacy.prototype;
+	object.prototype.constructor = object;
+
+	if (name){
+		var family = name.toLowerCase();
+		object.prototype.$family = {name: family};
+		Native.typize(object, family);
+	}
+
+	var add = function(obj, name, method, force){
+		if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
+		if (generics) Native.genericize(obj, name, protect);
+		afterImplement.call(obj, name, method);
+		return obj;
+	};
+
+	object.alias = function(a1, a2, a3){
+		if (typeof a1 == 'string'){
+			var pa1 = this.prototype[a1];
+			if ((a1 = pa1)) return add(this, a2, a1, a3);
+		}
+		for (var a in a1) this.alias(a, a1[a], a2);
+		return this;
+	};
+
+	object.implement = function(a1, a2, a3){
+		if (typeof a1 == 'string') return add(this, a1, a2, a3);
+		for (var p in a1) add(this, p, a1[p], a2);
+		return this;
+	};
+
+	if (methods) object.implement(methods);
+
+	return object;
+};
+
+Native.genericize = function(object, property, check){
+	if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
+		var args = Array.prototype.slice.call(arguments);
+		return object.prototype[property].apply(args.shift(), args);
+	};
+};
+
+Native.implement = function(objects, properties){
+	for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
+};
+
+Native.typize = function(object, family){
+	if (!object.type) object.type = function(item){
+		return ($type(item) === family);
+	};
+};
+
+(function(){
+	var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
+	for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});
+
+	var types = {'boolean': Boolean, 'native': Native, 'object': Object};
+	for (var t in types) Native.typize(types[t], t);
+
+	var generics = {
+		'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
+		'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
+	};
+	for (var g in generics){
+		for (var i = generics[g].length; i--;) Native.genericize(natives[g], generics[g][i], true);
+	}
+})();
+
+var Hash = new Native({
+
+	name: 'Hash',
+
+	initialize: function(object){
+		if ($type(object) == 'hash') object = $unlink(object.getClean());
+		for (var key in object) this[key] = object[key];
+		return this;
+	}
+
+});
+
+Hash.implement({
+
+	forEach: function(fn, bind){
+		for (var key in this){
+			if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
+		}
+	},
+
+	getClean: function(){
+		var clean = {};
+		for (var key in this){
+			if (this.hasOwnProperty(key)) clean[key] = this[key];
+		}
+		return clean;
+	},
+
+	getLength: function(){
+		var length = 0;
+		for (var key in this){
+			if (this.hasOwnProperty(key)) length++;
+		}
+		return length;
+	}
+
+});
+
+Hash.alias('forEach', 'each');
+
+Array.implement({
+
+	forEach: function(fn, bind){
+		for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
+	}
+
+});
+
+Array.alias('forEach', 'each');
+
+function $A(iterable){
+	if (iterable.item){
+		var l = iterable.length, array = new Array(l);
+		while (l--) array[l] = iterable[l];
+		return array;
+	}
+	return Array.prototype.slice.call(iterable);
+};
+
+function $arguments(i){
+	return function(){
+		return arguments[i];
+	};
+};
+
+function $chk(obj){
+	return !!(obj || obj === 0);
+};
+
+function $clear(timer){
+	clearTimeout(timer);
+	clearInterval(timer);
+	return null;
+};
+
+function $defined(obj){
+	return (obj != undefined);
+};
+
+function $each(iterable, fn, bind){
+	var type = $type(iterable);
+	((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
+};
+
+function $empty(){};
+
+function $extend(original, extended){
+	for (var key in (extended || {})) original[key] = extended[key];
+	return original;
+};
+
+function $H(object){
+	return new Hash(object);
+};
+
+function $lambda(value){
+	return ($type(value) == 'function') ? value : function(){
+		return value;
+	};
+};
+
+function $merge(){
+	var args = Array.slice(arguments);
+	args.unshift({});
+	return $mixin.apply(null, args);
+};
+
+function $mixin(mix){
+	for (var i = 1, l = arguments.length; i < l; i++){
+		var object = arguments[i];
+		if ($type(object) != 'object') continue;
+		for (var key in object){
+			var op = object[key], mp = mix[key];
+			mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op);
+		}
+	}
+	return mix;
+};
+
+function $pick(){
+	for (var i = 0, l = arguments.length; i < l; i++){
+		if (arguments[i] != undefined) return arguments[i];
+	}
+	return null;
+};
+
+function $random(min, max){
+	return Math.floor(Math.random() * (max - min + 1) + min);
+};
+
+function $splat(obj){
+	var type = $type(obj);
+	return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
+};
+
+var $time = Date.now || function(){
+	return +new Date;
+};
+
+function $try(){
+	for (var i = 0, l = arguments.length; i < l; i++){
+		try {
+			return arguments[i]();
+		} catch(e){}
+	}
+	return null;
+};
+
+function $type(obj){
+	if (obj == undefined) return false;
+	if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
+	if (obj.nodeName){
+		switch (obj.nodeType){
+			case 1: return 'element';
+			case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
+		}
+	} else if (typeof obj.length == 'number'){
+		if (obj.callee) return 'arguments';
+		else if (obj.item) return 'collection';
+	}
+	return typeof obj;
+};
+
+function $unlink(object){
+	var unlinked;
+	switch ($type(object)){
+		case 'object':
+			unlinked = {};
+			for (var p in object) unlinked[p] = $unlink(object[p]);
+		break;
+		case 'hash':
+			unlinked = new Hash(object);
+		break;
+		case 'array':
+			unlinked = [];
+			for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
+		break;
+		default: return object;
+	}
+	return unlinked;
+};
+/*
+---
+
+script: Browser.js
+
+description: The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
+
+license: MIT-style license.
+
+requires: 
+- /Native
+- /$util
+
+provides: [Browser, Window, Document, $exec]
+
+...
+*/
+
+var Browser = $merge({
+
+	Engine: {name: 'unknown', version: 0},
+
+	Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
+
+	Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
+
+	Plugins: {},
+
+	Engines: {
+
+		presto: function(){
+			return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
+		},
+
+		trident: function(){
+			return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
+		},
+
+		webkit: function(){
+			return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
+		},
+
+		gecko: function(){
+			return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18);
+		}
+
+	}
+
+}, Browser || {});
+
+Browser.Platform[Browser.Platform.name] = true;
+
+Browser.detect = function(){
+
+	for (var engine in this.Engines){
+		var version = this.Engines[engine]();
+		if (version){
+			this.Engine = {name: engine, version: version};
+			this.Engine[engine] = this.Engine[engine + version] = true;
+			break;
+		}
+	}
+
+	return {name: engine, version: version};
+
+};
+
+Browser.detect();
+
+Browser.Request = function(){
+	return $try(function(){
+		return new XMLHttpRequest();
+	}, function(){
+		return new ActiveXObject('MSXML2.XMLHTTP');
+	}, function(){
+		return new ActiveXObject('Microsoft.XMLHTTP');
+	});
+};
+
+Browser.Features.xhr = !!(Browser.Request());
+
+Browser.Plugins.Flash = (function(){
+	var version = ($try(function(){
+		return navigator.plugins['Shockwave Flash'].description;
+	}, function(){
+		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
+	}) || '0 r0').match(/\d+/g);
+	return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
+})();
+
+function $exec(text){
+	if (!text) return text;
+	if (window.execScript){
+		window.execScript(text);
+	} else {
+		var script = document.createElement('script');
+		script.setAttribute('type', 'text/javascript');
+		script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
+		document.head.appendChild(script);
+		document.head.removeChild(script);
+	}
+	return text;
+};
+
+Native.UID = 1;
+
+var $uid = (Browser.Engine.trident) ? function(item){
+	return (item.uid || (item.uid = [Native.UID++]))[0];
+} : function(item){
+	return item.uid || (item.uid = Native.UID++);
+};
+
+var Window = new Native({
+
+	name: 'Window',
+
+	legacy: (Browser.Engine.trident) ? null: window.Window,
+
+	initialize: function(win){
+		$uid(win);
+		if (!win.Element){
+			win.Element = $empty;
+			if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
+			win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
+		}
+		win.document.window = win;
+		return $extend(win, Window.Prototype);
+	},
+
+	afterImplement: function(property, value){
+		window[property] = Window.Prototype[property] = value;
+	}
+
+});
+
+Window.Prototype = {$family: {name: 'window'}};
+
+new Window(window);
+
+var Document = new Native({
+
+	name: 'Document',
+
+	legacy: (Browser.Engine.trident) ? null: window.Document,
+
+	initialize: function(doc){
+		$uid(doc);
+		doc.head = doc.getElementsByTagName('head')[0];
+		doc.html = doc.getElementsByTagName('html')[0];
+		if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
+			doc.execCommand("BackgroundImageCache", false, true);
+		});
+		if (Browser.Engine.trident) doc.window.attachEvent('onunload', function(){
+			doc.window.detachEvent('onunload', arguments.callee);
+			doc.head = doc.html = doc.window = null;
+		});
+		return $extend(doc, Document.Prototype);
+	},
+
+	afterImplement: function(property, value){
+		document[property] = Document.Prototype[property] = value;
+	}
+
+});
+
+Document.Prototype = {$family: {name: 'document'}};
+
+new Document(document);
+/*
+---
+
+script: Array.js
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires:
+- /$util
+- /Array.each
+
+provides: [Array]
+
+...
+*/
+
+Array.implement({
+
+	every: function(fn, bind){
+		for (var i = 0, l = this.length; i < l; i++){
+			if (!fn.call(bind, this[i], i, this)) return false;
+		}
+		return true;
+	},
+
+	filter: function(fn, bind){
+		var results = [];
+		for (var i = 0, l = this.length; i < l; i++){
+			if (fn.call(bind, this[i], i, this)) results.push(this[i]);
+		}
+		return results;
+	},
+
+	clean: function(){
+		return this.filter($defined);
+	},
+
+	indexOf: function(item, from){
+		var len = this.length;
+		for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
+			if (this[i] === item) return i;
+		}
+		return -1;
+	},
+
+	map: function(fn, bind){
+		var results = [];
+		for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
+		return results;
+	},
+
+	some: function(fn, bind){
+		for (var i = 0, l = this.length; i < l; i++){
+			if (fn.call(bind, this[i], i, this)) return true;
+		}
+		return false;
+	},
+
+	associate: function(keys){
+		var obj = {}, length = Math.min(this.length, keys.length);
+		for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
+		return obj;
+	},
+
+	link: function(object){
+		var result = {};
+		for (var i = 0, l = this.length; i < l; i++){
+			for (var key in object){
+				if (object[key](this[i])){
+					result[key] = this[i];
+					delete object[key];
+					break;
+				}
+			}
+		}
+		return result;
+	},
+
+	contains: function(item, from){
+		return this.indexOf(item, from) != -1;
+	},
+
+	extend: function(array){
+		for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
+		return this;
+	},
+	
+	getLast: function(){
+		return (this.length) ? this[this.length - 1] : null;
+	},
+
+	getRandom: function(){
+		return (this.length) ? this[$random(0, this.length - 1)] : null;
+	},
+
+	include: function(item){
+		if (!this.contains(item)) this.push(item);
+		return this;
+	},
+
+	combine: function(array){
+		for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
+		return this;
+	},
+
+	erase: function(item){
+		for (var i = this.length; i--; i){
+			if (this[i] === item) this.splice(i, 1);
+		}
+		return this;
+	},
+
+	empty: function(){
+		this.length = 0;
+		return this;
+	},
+
+	flatten: function(){
+		var array = [];
+		for (var i = 0, l = this.length; i < l; i++){
+			var type = $type(this[i]);
+			if (!type) continue;
+			array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
+		}
+		return array;
+	},
+
+	hexToRgb: function(array){
+		if (this.length != 3) return null;
+		var rgb = this.map(function(value){
+			if (value.length == 1) value += value;
+			return value.toInt(16);
+		});
+		return (array) ? rgb : 'rgb(' + rgb + ')';
+	},
+
+	rgbToHex: function(array){
+		if (this.length < 3) return null;
+		if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
+		var hex = [];
+		for (var i = 0; i < 3; i++){
+			var bit = (this[i] - 0).toString(16);
+			hex.push((bit.length == 1) ? '0' + bit : bit);
+		}
+		return (array) ? hex : '#' + hex.join('');
+	}
+
+});
+/*
+---
+
+script: Function.js
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires:
+- /Native
+- /$util
+
+provides: [Function]
+
+...
+*/
+
+Function.implement({
+
+	extend: function(properties){
+		for (var property in properties) this[property] = properties[property];
+		return this;
+	},
+
+	create: function(options){
+		var self = this;
+		options = options || {};
+		return function(event){
+			var args = options.arguments;
+			args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
+			if (options.event) args = [event || window.event].extend(args);
+			var returns = function(){
+				return self.apply(options.bind || null, args);
+			};
+			if (options.delay) return setTimeout(returns, options.delay);
+			if (options.periodical) return setInterval(returns, options.periodical);
+			if (options.attempt) return $try(returns);
+			return returns();
+		};
+	},
+
+	run: function(args, bind){
+		return this.apply(bind, $splat(args));
+	},
+
+	pass: function(args, bind){
+		return this.create({bind: bind, arguments: args});
+	},
+
+	bind: function(bind, args){
+		return this.create({bind: bind, arguments: args});
+	},
+
+	bindWithEvent: function(bind, args){
+		return this.create({bind: bind, arguments: args, event: true});
+	},
+
+	attempt: function(args, bind){
+		return this.create({bind: bind, arguments: args, attempt: true})();
+	},
+
+	delay: function(delay, bind, args){
+		return this.create({bind: bind, arguments: args, delay: delay})();
+	},
+
+	periodical: function(periodical, bind, args){
+		return this.create({bind: bind, arguments: args, periodical: periodical})();
+	}
+
+});
+/*
+---
+
+script: Number.js
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires:
+- /Native
+- /$util
+
+provides: [Number]
+
+...
+*/
+
+Number.implement({
+
+	limit: function(min, max){
+		return Math.min(max, Math.max(min, this));
+	},
+
+	round: function(precision){
+		precision = Math.pow(10, precision || 0);
+		return Math.round(this * precision) / precision;
+	},
+
+	times: function(fn, bind){
+		for (var i = 0; i < this; i++) fn.call(bind, i, this);
+	},
+
+	toFloat: function(){
+		return parseFloat(this);
+	},
+
+	toInt: function(base){
+		return parseInt(this, base || 10);
+	}
+
+});
+
+Number.alias('times', 'each');
+
+(function(math){
+	var methods = {};
+	math.each(function(name){
+		if (!Number[name]) methods[name] = function(){
+			return Math[name].apply(null, [this].concat($A(arguments)));
+		};
+	});
+	Number.implement(methods);
+})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
+/*
+---
+
+script: String.js
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires:
+- /Native
+
+provides: [String]
+
+...
+*/
+
+String.implement({
+
+	test: function(regex, params){
+		return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
+	},
+
+	contains: function(string, separator){
+		return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
+	},
+
+	trim: function(){
+		return this.replace(/^\s+|\s+$/g, '');
+	},
+
+	clean: function(){
+		return this.replace(/\s+/g, ' ').trim();
+	},
+
+	camelCase: function(){
+		return this.replace(/-\D/g, function(match){
+			return match.charAt(1).toUpperCase();
+		});
+	},
+
+	hyphenate: function(){
+		return this.replace(/[A-Z]/g, function(match){
+			return ('-' + match.charAt(0).toLowerCase());
+		});
+	},
+
+	capitalize: function(){
+		return this.replace(/\b[a-z]/g, function(match){
+			return match.toUpperCase();
+		});
+	},
+
+	escapeRegExp: function(){
+		return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+	},
+
+	toInt: function(base){
+		return parseInt(this, base || 10);
+	},
+
+	toFloat: function(){
+		return parseFloat(this);
+	},
+
+	hexToRgb: function(array){
+		var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+		return (hex) ? hex.slice(1).hexToRgb(array) : null;
+	},
+
+	rgbToHex: function(array){
+		var rgb = this.match(/\d{1,3}/g);
+		return (rgb) ? rgb.rgbToHex(array) : null;
+	},
+
+	stripScripts: function(option){
+		var scripts = '';
+		var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
+			scripts += arguments[1] + '\n';
+			return '';
+		});
+		if (option === true) $exec(scripts);
+		else if ($type(option) == 'function') option(scripts, text);
+		return text;
+	},
+
+	substitute: function(object, regexp){
+		return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+			if (match.charAt(0) == '\\') return match.slice(1);
+			return (object[name] != undefined) ? object[name] : '';
+		});
+	}
+
+});
+/*
+---
+
+script: Hash.js
+
+description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
+
+license: MIT-style license.
+
+requires:
+- /Hash.base
+
+provides: [Hash]
+
+...
+*/
+
+Hash.implement({
+
+	has: Object.prototype.hasOwnProperty,
+
+	keyOf: function(value){
+		for (var key in this){
+			if (this.hasOwnProperty(key) && this[key] === value) return key;
+		}
+		return null;
+	},
+
+	hasValue: function(value){
+		return (Hash.keyOf(this, value) !== null);
+	},
+
+	extend: function(properties){
+		Hash.each(properties || {}, function(value, key){
+			Hash.set(this, key, value);
+		}, this);
+		return this;
+	},
+
+	combine: function(properties){
+		Hash.each(properties || {}, function(value, key){
+			Hash.include(this, key, value);
+		}, this);
+		return this;
+	},
+
+	erase: function(key){
+		if (this.hasOwnProperty(key)) delete this[key];
+		return this;
+	},
+
+	get: function(key){
+		return (this.hasOwnProperty(key)) ? this[key] : null;
+	},
+
+	set: function(key, value){
+		if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
+		return this;
+	},
+
+	empty: function(){
+		Hash.each(this, function(value, key){
+			delete this[key];
+		}, this);
+		return this;
+	},
+
+	include: function(key, value){
+		if (this[key] == undefined) this[key] = value;
+		return this;
+	},
+
+	map: function(fn, bind){
+		var results = new Hash;
+		Hash.each(this, function(value, key){
+			results.set(key, fn.call(bind, value, key, this));
+		}, this);
+		return results;
+	},
+
+	filter: function(fn, bind){
+		var results = new Hash;
+		Hash.each(this, function(value, key){
+			if (fn.call(bind, value, key, this)) results.set(key, value);
+		}, this);
+		return results;
+	},
+
+	every: function(fn, bind){
+		for (var key in this){
+			if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
+		}
+		return true;
+	},
+
+	some: function(fn, bind){
+		for (var key in this){
+			if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
+		}
+		return false;
+	},
+
+	getKeys: function(){
+		var keys = [];
+		Hash.each(this, function(value, key){
+			keys.push(key);
+		});
+		return keys;
+	},
+
+	getValues: function(){
+		var values = [];
+		Hash.each(this, function(value){
+			values.push(value);
+		});
+		return values;
+	},
+
+	toQueryString: function(base){
+		var queryString = [];
+		Hash.each(this, function(value, key){
+			if (base) key = base + '[' + key + ']';
+			var result;
+			switch ($type(value)){
+				case 'object': result = Hash.toQueryString(value, key); break;
+				case 'array':
+					var qs = {};
+					value.each(function(val, i){
+						qs[i] = val;
+					});
+					result = Hash.toQueryString(qs, key);
+				break;
+				default: result = key + '=' + encodeURIComponent(value);
+			}
+			if (value != undefined) queryString.push(result);
+		});
+
+		return queryString.join('&');
+	}
+
+});
+
+Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
+/*
+---
+
+script: Event.js
+
+description: Contains the Event Class, to make the event object cross-browser.
+
+license: MIT-style license.
+
+requires:
+- /Window
+- /Document
+- /Hash
+- /Array
+- /Function
+- /String
+
+provides: [Event]
+
+...
+*/
+
+var Event = new Native({
+
+	name: 'Event',
+
+	initialize: function(event, win){
+		win = win || window;
+		var doc = win.document;
+		event = event || win.event;
+		if (event.$extended) return event;
+		this.$extended = true;
+		var type = event.type;
+		var target = event.target || event.srcElement;
+		while (target && target.nodeType == 3) target = target.parentNode;
+
+		if (type.test(/key/)){
+			var code = event.which || event.keyCode;
+			var key = Event.Keys.keyOf(code);
+			if (type == 'keydown'){
+				var fKey = code - 111;
+				if (fKey > 0 && fKey < 13) key = 'f' + fKey;
+			}
+			key = key || String.fromCharCode(code).toLowerCase();
+		} else if (type.match(/(click|mouse|menu)/i)){
+			doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+			var page = {
+				x: event.pageX || event.clientX + doc.scrollLeft,
+				y: event.pageY || event.clientY + doc.scrollTop
+			};
+			var client = {
+				x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
+				y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
+			};
+			if (type.match(/DOMMouseScroll|mousewheel/)){
+				var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
+			}
+			var rightClick = (event.which == 3) || (event.button == 2);
+			var related = null;
+			if (type.match(/over|out/)){
+				switch (type){
+					case 'mouseover': related = event.relatedTarget || event.fromElement; break;
+					case 'mouseout': related = event.relatedTarget || event.toElement;
+				}
+				if (!(function(){
+					while (related && related.nodeType == 3) related = related.parentNode;
+					return true;
+				}).create({attempt: Browser.Engine.gecko})()) related = false;
+			}
+		}
+
+		return $extend(this, {
+			event: event,
+			type: type,
+
+			page: page,
+			client: client,
+			rightClick: rightClick,
+
+			wheel: wheel,
+
+			relatedTarget: related,
+			target: target,
+
+			code: code,
+			key: key,
+
+			shift: event.shiftKey,
+			control: event.ctrlKey,
+			alt: event.altKey,
+			meta: event.metaKey
+		});
+	}
+
+});
+
+Event.Keys = new Hash({
+	'enter': 13,
+	'up': 38,
+	'down': 40,
+	'left': 37,
+	'right': 39,
+	'esc': 27,
+	'space': 32,
+	'backspace': 8,
+	'tab': 9,
+	'delete': 46
+});
+
+Event.implement({
+
+	stop: function(){
+		return this.stopPropagation().preventDefault();
+	},
+
+	stopPropagation: function(){
+		if (this.event.stopPropagation) this.event.stopPropagation();
+		else this.event.cancelBubble = true;
+		return this;
+	},
+
+	preventDefault: function(){
+		if (this.event.preventDefault) this.event.preventDefault();
+		else this.event.returnValue = false;
+		return this;
+	}
+
+});
+/*
+---
+
+script: Class.js
+
+description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+
+license: MIT-style license.
+
+requires:
+- /$util
+- /Native
+- /Array
+- /String
+- /Function
+- /Number
+- /Hash
+
+provides: [Class]
+
+...
+*/
+
+function Class(params){
+	
+	if (params instanceof Function) params = {initialize: params};
+	
+	var newClass = function(){
+		Object.reset(this);
+		if (newClass._prototyping) return this;
+		this._current = $empty;
+		var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
+		delete this._current; delete this.caller;
+		return value;
+	}.extend(this);
+	
+	newClass.implement(params);
+	
+	newClass.constructor = Class;
+	newClass.prototype.constructor = newClass;
+
+	return newClass;
+
+};
+
+Function.prototype.protect = function(){
+	this._protected = true;
+	return this;
+};
+
+Object.reset = function(object, key){
+		
+	if (key == null){
+		for (var p in object) Object.reset(object, p);
+		return object;
+	}
+	
+	delete object[key];
+	
+	switch ($type(object[key])){
+		case 'object':
+			var F = function(){};
+			F.prototype = object[key];
+			var i = new F;
+			object[key] = Object.reset(i);
+		break;
+		case 'array': object[key] = $unlink(object[key]); break;
+	}
+	
+	return object;
+	
+};
+
+new Native({name: 'Class', initialize: Class}).extend({
+
+	instantiate: function(F){
+		F._prototyping = true;
+		var proto = new F;
+		delete F._prototyping;
+		return proto;
+	},
+	
+	wrap: function(self, key, method){
+		if (method._origin) method = method._origin;
+		
+		return function(){
+			if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.');
+			var caller = this.caller, current = this._current;
+			this.caller = current; this._current = arguments.callee;
+			var result = method.apply(this, arguments);
+			this._current = current; this.caller = caller;
+			return result;
+		}.extend({_owner: self, _origin: method, _name: key});
+
+	}
+	
+});
+
+Class.implement({
+	
+	implement: function(key, value){
+		
+		if ($type(key) == 'object'){
+			for (var p in key) this.implement(p, key[p]);
+			return this;
+		}
+		
+		var mutator = Class.Mutators[key];
+		
+		if (mutator){
+			value = mutator.call(this, value);
+			if (value == null) return this;
+		}
+		
+		var proto = this.prototype;
+
+		switch ($type(value)){
+			
+			case 'function':
+				if (value._hidden) return this;
+				proto[key] = Class.wrap(this, key, value);
+			break;
+			
+			case 'object':
+				var previous = proto[key];
+				if ($type(previous) == 'object') $mixin(previous, value);
+				else proto[key] = $unlink(value);
+			break;
+			
+			case 'array':
+				proto[key] = $unlink(value);
+			break;
+			
+			default: proto[key] = value;
+
+		}
+		
+		return this;
+
+	}
+	
+});
+
+Class.Mutators = {
+	
+	Extends: function(parent){
+
+		this.parent = parent;
+		this.prototype = Class.instantiate(parent);
+
+		this.implement('parent', function(){
+			var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
+			if (!previous) throw new Error('The method "' + name + '" has no parent.');
+			return previous.apply(this, arguments);
+		}.protect());
+
+	},
+
+	Implements: function(items){
+		$splat(items).each(function(item){
+			if (item instanceof Function) item = Class.instantiate(item);
+			this.implement(item);
+		}, this);
+
+	}
+	
+};
+/*
+---
+
+script: Class.Extras.js
+
+description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+
+license: MIT-style license.
+
+requires:
+- /Class
+
+provides: [Chain, Events, Options]
+
+...
+*/
+
+var Chain = new Class({
+
+	$chain: [],
+
+	chain: function(){
+		this.$chain.extend(Array.flatten(arguments));
+		return this;
+	},
+
+	callChain: function(){
+		return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
+	},
+
+	clearChain: function(){
+		this.$chain.empty();
+		return this;
+	}
+
+});
+
+var Events = new Class({
+
+	$events: {},
+
+	addEvent: function(type, fn, internal){
+		type = Events.removeOn(type);
+		if (fn != $empty){
+			this.$events[type] = this.$events[type] || [];
+			this.$events[type].include(fn);
+			if (internal) fn.internal = true;
+		}
+		return this;
+	},
+
+	addEvents: function(events){
+		for (var type in events) this.addEvent(type, events[type]);
+		return this;
+	},
+
+	fireEvent: function(type, args, delay){
+		type = Events.removeOn(type);
+		if (!this.$events || !this.$events[type]) return this;
+		this.$events[type].each(function(fn){
+			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
+		}, this);
+		return this;
+	},
+
+	removeEvent: function(type, fn){
+		type = Events.removeOn(type);
+		if (!this.$events[type]) return this;
+		if (!fn.internal) this.$events[type].erase(fn);
+		return this;
+	},
+
+	removeEvents: function(events){
+		var type;
+		if ($type(events) == 'object'){
+			for (type in events) this.removeEvent(type, events[type]);
+			return this;
+		}
+		if (events) events = Events.removeOn(events);
+		for (type in this.$events){
+			if (events && events != type) continue;
+			var fns = this.$events[type];
+			for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
+		}
+		return this;
+	}
+
+});
+
+Events.removeOn = function(string){
+	return string.replace(/^on([A-Z])/, function(full, first){
+		return first.toLowerCase();
+	});
+};
+
+var Options = new Class({
+
+	setOptions: function(){
+		this.options = $merge.run([this.options].extend(arguments));
+		if (!this.addEvent) return this;
+		for (var option in this.options){
+			if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
+			this.addEvent(option, this.options[option]);
+			delete this.options[option];
+		}
+		return this;
+	}
+
+});
+/*
+---
+
+script: Element.js
+
+description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements.
+
+license: MIT-style license.
+
+requires:
+- /Window
+- /Document
+- /Array
+- /String
+- /Function
+- /Number
+- /Hash
+
+provides: [Element, Elements, $, $$, Iframe]
+
+...
+*/
+
+var Element = new Native({
+
+	name: 'Element',
+
+	legacy: window.Element,
+
+	initialize: function(tag, props){
+		var konstructor = Element.Constructors.get(tag);
+		if (konstructor) return konstructor(props);
+		if (typeof tag == 'string') return document.newElement(tag, props);
+		return document.id(tag).set(props);
+	},
+
+	afterImplement: function(key, value){
+		Element.Prototype[key] = value;
+		if (Array[key]) return;
+		Elements.implement(key, function(){
+			var items = [], elements = true;
+			for (var i = 0, j = this.length; i < j; i++){
+				var returns = this[i][key].apply(this[i], arguments);
+				items.push(returns);
+				if (elements) elements = ($type(returns) == 'element');
+			}
+			return (elements) ? new Elements(items) : items;
+		});
+	}
+
+});
+
+Element.Prototype = {$family: {name: 'element'}};
+
+Element.Constructors = new Hash;
+
+var IFrame = new Native({
+
+	name: 'IFrame',
+
+	generics: false,
+
+	initialize: function(){
+		var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
+		var props = params.properties || {};
+		var iframe = document.id(params.iframe);
+		var onload = props.onload || $empty;
+		delete props.onload;
+		props.id = props.name = $pick(props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + $time());
+		iframe = new Element(iframe || 'iframe', props);
+		var onFrameLoad = function(){
+			var host = $try(function(){
+				return iframe.contentWindow.location.host;
+			});
+			if (!host || host == window.location.host){
+				var win = new Window(iframe.contentWindow);
+				new Document(iframe.contentWindow.document);
+				$extend(win.Element.prototype, Element.Prototype);
+			}
+			onload.call(iframe.contentWindow, iframe.contentWindow.document);
+		};
+		var contentWindow = $try(function(){
+			return iframe.contentWindow;
+		});
+		((contentWindow && contentWindow.document.body) || window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
+		return iframe;
+	}
+
+});
+
+var Elements = new Native({
+
+	initialize: function(elements, options){
+		options = $extend({ddup: true, cash: true}, options);
+		elements = elements || [];
+		if (options.ddup || options.cash){
+			var uniques = {}, returned = [];
+			for (var i = 0, l = elements.length; i < l; i++){
+				var el = document.id(elements[i], !options.cash);
+				if (options.ddup){
+					if (uniques[el.uid]) continue;
+					uniques[el.uid] = true;
+				}
+				if (el) returned.push(el);
+			}
+			elements = returned;
+		}
+		return (options.cash) ? $extend(elements, this) : elements;
+	}
+
+});
+
+Elements.implement({
+
+	filter: function(filter, bind){
+		if (!filter) return this;
+		return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
+			return item.match(filter);
+		} : filter, bind));
+	}
+
+});
+
+Document.implement({
+
+	newElement: function(tag, props){
+		if (Browser.Engine.trident && props){
+			['name', 'type', 'checked'].each(function(attribute){
+				if (!props[attribute]) return;
+				tag += ' ' + attribute + '="' + props[attribute] + '"';
+				if (attribute != 'checked') delete props[attribute];
+			});
+			tag = '<' + tag + '>';
+		}
+		return document.id(this.createElement(tag)).set(props);
+	},
+
+	newTextNode: function(text){
+		return this.createTextNode(text);
+	},
+
+	getDocument: function(){
+		return this;
+	},
+
+	getWindow: function(){
+		return this.window;
+	},
+	
+	id: (function(){
+		
+		var types = {
+
+			string: function(id, nocash, doc){
+				id = doc.getElementById(id);
+				return (id) ? types.element(id, nocash) : null;
+			},
+			
+			element: function(el, nocash){
+				$uid(el);
+				if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
+					var proto = Element.Prototype;
+					for (var p in proto) el[p] = proto[p];
+				};
+				return el;
+			},
+			
+			object: function(obj, nocash, doc){
+				if (obj.toElement) return types.element(obj.toElement(doc), nocash);
+				return null;
+			}
+			
+		};
+
+		types.textnode = types.whitespace = types.window = types.document = $arguments(0);
+		
+		return function(el, nocash, doc){
+			if (el && el.$family && el.uid) return el;
+			var type = $type(el);
+			return (types[type]) ? types[type](el, nocash, doc || document) : null;
+		};
+
+	})()
+
+});
+
+if (window.$ == null) Window.implement({
+	$: function(el, nc){
+		return document.id(el, nc, this.document);
+	}
+});
+
+Window.implement({
+
+	$$: function(selector){
+		if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
+		var elements = [];
+		var args = Array.flatten(arguments);
+		for (var i = 0, l = args.length; i < l; i++){
+			var item = args[i];
+			switch ($type(item)){
+				case 'element': elements.push(item); break;
+				case 'string': elements.extend(this.document.getElements(item, true));
+			}
+		}
+		return new Elements(elements);
+	},
+
+	getDocument: function(){
+		return this.document;
+	},
+
+	getWindow: function(){
+		return this;
+	}
+
+});
+
+Native.implement([Element, Document], {
+
+	getElement: function(selector, nocash){
+		return document.id(this.getElements(selector, true)[0] || null, nocash);
+	},
+
+	getElements: function(tags, nocash){
+		tags = tags.split(',');
+		var elements = [];
+		var ddup = (tags.length > 1);
+		tags.each(function(tag){
+			var partial = this.getElementsByTagName(tag.trim());
+			(ddup) ? elements.extend(partial) : elements = partial;
+		}, this);
+		return new Elements(elements, {ddup: ddup, cash: !nocash});
+	}
+
+});
+
+(function(){
+
+var collected = {}, storage = {};
+var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};
+
+var get = function(uid){
+	return (storage[uid] || (storage[uid] = {}));
+};
+
+var clean = function(item, retain){
+	if (!item) return;
+	var uid = item.uid;
+	if (retain !== true) retain = false;
+	if (Browser.Engine.trident){
+		if (item.clearAttributes){
+			var clone = retain && item.cloneNode(false);
+			item.clearAttributes();
+			if (clone) item.mergeAttributes(clone);
+		} else if (item.removeEvents){
+			item.removeEvents();
+		}
+		if ((/object/i).test(item.tagName)){
+			for (var p in item){
+				if (typeof item[p] == 'function') item[p] = $empty;
+			}
+			Element.dispose(item);
+		}
+	}	
+	if (!uid) return;
+	collected[uid] = storage[uid] = null;
+};
+
+var purge = function(){
+	Hash.each(collected, clean);
+	if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
+	if (window.CollectGarbage) CollectGarbage();
+	collected = storage = null;
+};
+
+var walk = function(element, walk, start, match, all, nocash){
+	var el = element[start || walk];
+	var elements = [];
+	while (el){
+		if (el.nodeType == 1 && (!match || Element.match(el, match))){
+			if (!all) return document.id(el, nocash);
+			elements.push(el);
+		}
+		el = el[walk];
+	}
+	return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
+};
+
+var attributes = {
+	'html': 'innerHTML',
+	'class': 'className',
+	'for': 'htmlFor',
+	'defaultValue': 'defaultValue',
+	'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
+};
+var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
+var camels = ['value', 'type', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
+
+bools = bools.associate(bools);
+
+Hash.extend(attributes, bools);
+Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));
+
+var inserters = {
+
+	before: function(context, element){
+		if (element.parentNode) element.parentNode.insertBefore(context, element);
+	},
+
+	after: function(context, element){
+		if (!element.parentNode) return;
+		var next = element.nextSibling;
+		(next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
+	},
+
+	bottom: function(context, element){
+		element.appendChild(context);
+	},
+
+	top: function(context, element){
+		var first = element.firstChild;
+		(first) ? element.insertBefore(context, first) : element.appendChild(context);
+	}
+
+};
+
+inserters.inside = inserters.bottom;
+
+Hash.each(inserters, function(inserter, where){
+
+	where = where.capitalize();
+
+	Element.implement('inject' + where, function(el){
+		inserter(this, document.id(el, true));
+		return this;
+	});
+
+	Element.implement('grab' + where, function(el){
+		inserter(document.id(el, true), this);
+		return this;
+	});
+
+});
+
+Element.implement({
+
+	set: function(prop, value){
+		switch ($type(prop)){
+			case 'object':
+				for (var p in prop) this.set(p, prop[p]);
+				break;
+			case 'string':
+				var property = Element.Properties.get(prop);
+				(property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
+		}
+		return this;
+	},
+
+	get: function(prop){
+		var property = Element.Properties.get(prop);
+		return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
+	},
+
+	erase: function(prop){
+		var property = Element.Properties.get(prop);
+		(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
+		return this;
+	},
+
+	setProperty: function(attribute, value){
+		var key = attributes[attribute];
+		if (value == undefined) return this.removeProperty(attribute);
+		if (key && bools[attribute]) value = !!value;
+		(key) ? this[key] = value : this.setAttribute(attribute, '' + value);
+		return this;
+	},
+
+	setProperties: function(attributes){
+		for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
+		return this;
+	},
+
+	getProperty: function(attribute){
+		var key = attributes[attribute];
+		var value = (key) ? this[key] : this.getAttribute(attribute, 2);
+		return (bools[attribute]) ? !!value : (key) ? value : value || null;
+	},
+
+	getProperties: function(){
+		var args = $A(arguments);
+		return args.map(this.getProperty, this).associate(args);
+	},
+
+	removeProperty: function(attribute){
+		var key = attributes[attribute];
+		(key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
+		return this;
+	},
+
+	removeProperties: function(){
+		Array.each(arguments, this.removeProperty, this);
+		return this;
+	},
+
+	hasClass: function(className){
+		return this.className.contains(className, ' ');
+	},
+
+	addClass: function(className){
+		if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
+		return this;
+	},
+
+	removeClass: function(className){
+		this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
+		return this;
+	},
+
+	toggleClass: function(className){
+		return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
+	},
+
+	adopt: function(){
+		Array.flatten(arguments).each(function(element){
+			element = document.id(element, true);
+			if (element) this.appendChild(element);
+		}, this);
+		return this;
+	},
+
+	appendText: function(text, where){
+		return this.grab(this.getDocument().newTextNode(text), where);
+	},
+
+	grab: function(el, where){
+		inserters[where || 'bottom'](document.id(el, true), this);
+		return this;
+	},
+
+	inject: function(el, where){
+		inserters[where || 'bottom'](this, document.id(el, true));
+		return this;
+	},
+
+	replaces: function(el){
+		el = document.id(el, true);
+		el.parentNode.replaceChild(this, el);
+		return this;
+	},
+
+	wraps: function(el, where){
+		el = document.id(el, true);
+		return this.replaces(el).grab(el, where);
+	},
+
+	getPrevious: function(match, nocash){
+		return walk(this, 'previousSibling', null, match, false, nocash);
+	},
+
+	getAllPrevious: function(match, nocash){
+		return walk(this, 'previousSibling', null, match, true, nocash);
+	},
+
+	getNext: function(match, nocash){
+		return walk(this, 'nextSibling', null, match, false, nocash);
+	},
+
+	getAllNext: function(match, nocash){
+		return walk(this, 'nextSibling', null, match, true, nocash);
+	},
+
+	getFirst: function(match, nocash){
+		return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
+	},
+
+	getLast: function(match, nocash){
+		return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
+	},
+
+	getParent: function(match, nocash){
+		return walk(this, 'parentNode', null, match, false, nocash);
+	},
+
+	getParents: function(match, nocash){
+		return walk(this, 'parentNode', null, match, true, nocash);
+	},
+	
+	getSiblings: function(match, nocash){
+		return this.getParent().getChildren(match, nocash).erase(this);
+	},
+
+	getChildren: function(match, nocash){
+		return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
+	},
+
+	getWindow: function(){
+		return this.ownerDocument.window;
+	},
+
+	getDocument: function(){
+		return this.ownerDocument;
+	},
+
+	getElementById: function(id, nocash){
+		var el = this.ownerDocument.getElementById(id);
+		if (!el) return null;
+		for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
+			if (!parent) return null;
+		}
+		return document.id(el, nocash);
+	},
+
+	getSelected: function(){
+		return new Elements($A(this.options).filter(function(option){
+			return option.selected;
+		}));
+	},
+
+	getComputedStyle: function(property){
+		if (this.currentStyle) return this.currentStyle[property.camelCase()];
+		var computed = this.getDocument().defaultView.getComputedStyle(this, null);
+		return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
+	},
+
+	toQueryString: function(){
+		var queryString = [];
+		this.getElements('input, select, textarea', true).each(function(el){
+			if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
+			var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
+				return opt.value;
+			}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
+			$splat(value).each(function(val){
+				if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
+			});
+		});
+		return queryString.join('&');
+	},
+
+	clone: function(contents, keepid){
+		contents = contents !== false;
+		var clone = this.cloneNode(contents);
+		var clean = function(node, element){
+			if (!keepid) node.removeAttribute('id');
+			if (Browser.Engine.trident){
+				node.clearAttributes();
+				node.mergeAttributes(element);
+				node.removeAttribute('uid');
+				if (node.options){
+					var no = node.options, eo = element.options;
+					for (var j = no.length; j--;) no[j].selected = eo[j].selected;
+				}
+			}
+			var prop = props[element.tagName.toLowerCase()];
+			if (prop && element[prop]) node[prop] = element[prop];
+		};
+
+		if (contents){
+			var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
+			for (var i = ce.length; i--;) clean(ce[i], te[i]);
+		}
+
+		clean(clone, this);
+		return document.id(clone);
+	},
+
+	destroy: function(){
+		Element.empty(this);
+		Element.dispose(this);
+		clean(this, true);
+		return null;
+	},
+
+	empty: function(){
+		$A(this.childNodes).each(function(node){
+			Element.destroy(node);
+		});
+		return this;
+	},
+
+	dispose: function(){
+		return (this.parentNode) ? this.parentNode.removeChild(this) : this;
+	},
+
+	hasChild: function(el){
+		el = document.id(el, true);
+		if (!el) return false;
+		if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
+		return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
+	},
+
+	match: function(tag){
+		return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
+	}
+
+});
+
+Native.implement([Element, Window, Document], {
+
+	addListener: function(type, fn){
+		if (type == 'unload'){
+			var old = fn, self = this;
+			fn = function(){
+				self.removeListener('unload', fn);
+				old();
+			};
+		} else {
+			collected[this.uid] = this;
+		}
+		if (this.addEventListener) this.addEventListener(type, fn, false);
+		else this.attachEvent('on' + type, fn);
+		return this;
+	},
+
+	removeListener: function(type, fn){
+		if (this.removeEventListener) this.removeEventListener(type, fn, false);
+		else this.detachEvent('on' + type, fn);
+		return this;
+	},
+
+	retrieve: function(property, dflt){
+		var storage = get(this.uid), prop = storage[property];
+		if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
+		return $pick(prop);
+	},
+
+	store: function(property, value){
+		var storage = get(this.uid);
+		storage[property] = value;
+		return this;
+	},
+
+	eliminate: function(property){
+		var storage = get(this.uid);
+		delete storage[property];
+		return this;
+	}
+
+});
+
+window.addListener('unload', purge);
+
+})();
+
+Element.Properties = new Hash;
+
+Element.Properties.style = {
+
+	set: function(style){
+		this.style.cssText = style;
+	},
+
+	get: function(){
+		return this.style.cssText;
+	},
+
+	erase: function(){
+		this.style.cssText = '';
+	}
+
+};
+
+Element.Properties.tag = {
+
+	get: function(){
+		return this.tagName.toLowerCase();
+	}
+
+};
+
+Element.Properties.html = (function(){
+	var wrapper = document.createElement('div');
+
+	var translations = {
+		table: [1, '<table>', '</table>'],
+		select: [1, '<select>', '</select>'],
+		tbody: [2, '<table><tbody>', '</tbody></table>'],
+		tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
+	};
+	translations.thead = translations.tfoot = translations.tbody;
+
+	var html = {
+		set: function(){
+			var html = Array.flatten(arguments).join('');
+			var wrap = Browser.Engine.trident && translations[this.get('tag')];
+			if (wrap){
+				var first = wrapper;
+				first.innerHTML = wrap[1] + html + wrap[2];
+				for (var i = wrap[0]; i--;) first = first.firstChild;
+				this.empty().adopt(first.childNodes);
+			} else {
+				this.innerHTML = html;
+			}
+		}
+	};
+
+	html.erase = html.set;
+
+	return html;
+})();
+
+if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
+	get: function(){
+		if (this.innerText) return this.innerText;
+		var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
+		var text = temp.innerText;
+		temp.destroy();
+		return text;
+	}
+};
+/*
+---
+
+script: Element.Event.js
+
+description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events.
+
+license: MIT-style license.
+
+requires: 
+- /Element
+- /Event
+
+provides: [Element.Event]
+
+...
+*/
+
+Element.Properties.events = {set: function(events){
+	this.addEvents(events);
+}};
+
+Native.implement([Element, Window, Document], {
+
+	addEvent: function(type, fn){
+		var events = this.retrieve('events', {});
+		events[type] = events[type] || {'keys': [], 'values': []};
+		if (events[type].keys.contains(fn)) return this;
+		events[type].keys.push(fn);
+		var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
+		if (custom){
+			if (custom.onAdd) custom.onAdd.call(this, fn);
+			if (custom.condition){
+				condition = function(event){
+					if (custom.condition.call(this, event)) return fn.call(this, event);
+					return true;
+				};
+			}
+			realType = custom.base || realType;
+		}
+		var defn = function(){
+			return fn.call(self);
+		};
+		var nativeEvent = Element.NativeEvents[realType];
+		if (nativeEvent){
+			if (nativeEvent == 2){
+				defn = function(event){
+					event = new Event(event, self.getWindow());
+					if (condition.call(self, event) === false) event.stop();
+				};
+			}
+			this.addListener(realType, defn);
+		}
+		events[type].values.push(defn);
+		return this;
+	},
+
+	removeEvent: function(type, fn){
+		var events = this.retrieve('events');
+		if (!events || !events[type]) return this;
+		var pos = events[type].keys.indexOf(fn);
+		if (pos == -1) return this;
+		events[type].keys.splice(pos, 1);
+		var value = events[type].values.splice(pos, 1)[0];
+		var custom = Element.Events.get(type);
+		if (custom){
+			if (custom.onRemove) custom.onRemove.call(this, fn);
+			type = custom.base || type;
+		}
+		return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
+	},
+
+	addEvents: function(events){
+		for (var event in events) this.addEvent(event, events[event]);
+		return this;
+	},
+
+	removeEvents: function(events){
+		var type;
+		if ($type(events) == 'object'){
+			for (type in events) this.removeEvent(type, events[type]);
+			return this;
+		}
+		var attached = this.retrieve('events');
+		if (!attached) return this;
+		if (!events){
+			for (type in attached) this.removeEvents(type);
+			this.eliminate('events');
+		} else if (attached[events]){
+			while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
+			attached[events] = null;
+		}
+		return this;
+	},
+
+	fireEvent: function(type, args, delay){
+		var events = this.retrieve('events');
+		if (!events || !events[type]) return this;
+		events[type].keys.each(function(fn){
+			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
+		}, this);
+		return this;
+	},
+
+	cloneEvents: function(from, type){
+		from = document.id(from);
+		var fevents = from.retrieve('events');
+		if (!fevents) return this;
+		if (!type){
+			for (var evType in fevents) this.cloneEvents(from, evType);
+		} else if (fevents[type]){
+			fevents[type].keys.each(function(fn){
+				this.addEvent(type, fn);
+			}, this);
+		}
+		return this;
+	}
+
+});
+
+Element.NativeEvents = {
+	click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
+	mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
+	mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
+	keydown: 2, keypress: 2, keyup: 2, //keyboard
+	focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
+	load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
+	error: 1, abort: 1, scroll: 1 //misc
+};
+
+(function(){
+
+var $check = function(event){
+	var related = event.relatedTarget;
+	if (related == undefined) return true;
+	if (related === false) return false;
+	return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
+};
+
+Element.Events = new Hash({
+
+	mouseenter: {
+		base: 'mouseover',
+		condition: $check
+	},
+
+	mouseleave: {
+		base: 'mouseout',
+		condition: $check
+	},
+
+	mousewheel: {
+		base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
+	}
+
+});
+
+})();
+/*
+---
+
+script: Element.Style.js
+
+description: Contains methods for interacting with the styles of Elements in a fashionable way.
+
+license: MIT-style license.
+
+requires:
+- /Element
+
+provides: [Element.Style]
+
+...
+*/
+
+Element.Properties.styles = {set: function(styles){
+	this.setStyles(styles);
+}};
+
+Element.Properties.opacity = {
+
+	set: function(opacity, novisibility){
+		if (!novisibility){
+			if (opacity == 0){
+				if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
+			} else {
+				if (this.style.visibility != 'visible') this.style.visibility = 'visible';
+			}
+		}
+		if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
+		if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
+		this.style.opacity = opacity;
+		this.store('opacity', opacity);
+	},
+
+	get: function(){
+		return this.retrieve('opacity', 1);
+	}
+
+};
+
+Element.implement({
+
+	setOpacity: function(value){
+		return this.set('opacity', value, true);
+	},
+
+	getOpacity: function(){
+		return this.get('opacity');
+	},
+
+	setStyle: function(property, value){
+		switch (property){
+			case 'opacity': return this.set('opacity', parseFloat(value));
+			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
+		}
+		property = property.camelCase();
+		if ($type(value) != 'string'){
+			var map = (Element.Styles.get(property) || '@').split(' ');
+			value = $splat(value).map(function(val, i){
+				if (!map[i]) return '';
+				return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
+			}).join(' ');
+		} else if (value == String(Number(value))){
+			value = Math.round(value);
+		}
+		this.style[property] = value;
+		return this;
+	},
+
+	getStyle: function(property){
+		switch (property){
+			case 'opacity': return this.get('opacity');
+			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
+		}
+		property = property.camelCase();
+		var result = this.style[property];
+		if (!$chk(result)){
+			result = [];
+			for (var style in Element.ShortStyles){
+				if (property != style) continue;
+				for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
+				return result.join(' ');
+			}
+			result = this.getComputedStyle(property);
+		}
+		if (result){
+			result = String(result);
+			var color = result.match(/rgba?\([\d\s,]+\)/);
+			if (color) result = result.replace(color[0], color[0].rgbToHex());
+		}
+		if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))){
+			if (property.test(/^(height|width)$/)){
+				var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
+				values.each(function(value){
+					size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
+				}, this);
+				return this['offset' + property.capitalize()] - size + 'px';
+			}
+			if ((Browser.Engine.presto) && String(result).test('px')) return result;
+			if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
+		}
+		return result;
+	},
+
+	setStyles: function(styles){
+		for (var style in styles) this.setStyle(style, styles[style]);
+		return this;
+	},
+
+	getStyles: function(){
+		var result = {};
+		Array.flatten(arguments).each(function(key){
+			result[key] = this.getStyle(key);
+		}, this);
+		return result;
+	}
+
+});
+
+Element.Styles = new Hash({
+	left: '@px', top: '@px', bottom: '@px', right: '@px',
+	width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
+	backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
+	fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
+	margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
+	borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
+	zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
+});
+
+Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
+
+['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
+	var Short = Element.ShortStyles;
+	var All = Element.Styles;
+	['margin', 'padding'].each(function(style){
+		var sd = style + direction;
+		Short[style][sd] = All[sd] = '@px';
+	});
+	var bd = 'border' + direction;
+	Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
+	var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
+	Short[bd] = {};
+	Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
+	Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
+	Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
+});
+/*
+---
+
+script: Element.Dimensions.js
+
+description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
+
+license: MIT-style license.
+
+credits:
+- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
+- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
+
+requires:
+- /Element
+
+provides: [Element.Dimensions]
+
+...
+*/
+
+(function(){
+
+Element.implement({
+
+	scrollTo: function(x, y){
+		if (isBody(this)){
+			this.getWindow().scrollTo(x, y);
+		} else {
+			this.scrollLeft = x;
+			this.scrollTop = y;
+		}
+		return this;
+	},
+
+	getSize: function(){
+		if (isBody(this)) return this.getWindow().getSize();
+		return {x: this.offsetWidth, y: this.offsetHeight};
+	},
+
+	getScrollSize: function(){
+		if (isBody(this)) return this.getWindow().getScrollSize();
+		return {x: this.scrollWidth, y: this.scrollHeight};
+	},
+
+	getScroll: function(){
+		if (isBody(this)) return this.getWindow().getScroll();
+		return {x: this.scrollLeft, y: this.scrollTop};
+	},
+
+	getScrolls: function(){
+		var element = this, position = {x: 0, y: 0};
+		while (element && !isBody(element)){
+			position.x += element.scrollLeft;
+			position.y += element.scrollTop;
+			element = element.parentNode;
+		}
+		return position;
+	},
+
+	getOffsetParent: function(){
+		var element = this;
+		if (isBody(element)) return null;
+		if (!Browser.Engine.trident) return element.offsetParent;
+		while ((element = element.parentNode) && !isBody(element)){
+			if (styleString(element, 'position') != 'static') return element;
+		}
+		return null;
+	},
+
+	getOffsets: function(){
+		if (this.getBoundingClientRect){
+			var bound = this.getBoundingClientRect(),
+				html = document.id(this.getDocument().documentElement),
+				htmlScroll = html.getScroll(),
+				elemScrolls = this.getScrolls(),
+				elemScroll = this.getScroll(),
+				isFixed = (styleString(this, 'position') == 'fixed');
+
+			return {
+				x: bound.left.toInt() + elemScrolls.x - elemScroll.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
+				y: bound.top.toInt()  + elemScrolls.y - elemScroll.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
+			};
+		}
+
+		var element = this, position = {x: 0, y: 0};
+		if (isBody(this)) return position;
+
+		while (element && !isBody(element)){
+			position.x += element.offsetLeft;
+			position.y += element.offsetTop;
+
+			if (Browser.Engine.gecko){
+				if (!borderBox(element)){
+					position.x += leftBorder(element);
+					position.y += topBorder(element);
+				}
+				var parent = element.parentNode;
+				if (parent && styleString(parent, 'overflow') != 'visible'){
+					position.x += leftBorder(parent);
+					position.y += topBorder(parent);
+				}
+			} else if (element != this && Browser.Engine.webkit){
+				position.x += leftBorder(element);
+				position.y += topBorder(element);
+			}
+
+			element = element.offsetParent;
+		}
+		if (Browser.Engine.gecko && !borderBox(this)){
+			position.x -= leftBorder(this);
+			position.y -= topBorder(this);
+		}
+		return position;
+	},
+
+	getPosition: function(relative){
+		if (isBody(this)) return {x: 0, y: 0};
+		var offset = this.getOffsets(),
+				scroll = this.getScrolls();
+		var position = {
+			x: offset.x - scroll.x,
+			y: offset.y - scroll.y
+		};
+		var relativePosition = (relative && (relative = document.id(relative))) ? relative.getPosition() : {x: 0, y: 0};
+		return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
+	},
+
+	getCoordinates: function(element){
+		if (isBody(this)) return this.getWindow().getCoordinates();
+		var position = this.getPosition(element),
+				size = this.getSize();
+		var obj = {
+			left: position.x,
+			top: position.y,
+			width: size.x,
+			height: size.y
+		};
+		obj.right = obj.left + obj.width;
+		obj.bottom = obj.top + obj.height;
+		return obj;
+	},
+
+	computePosition: function(obj){
+		return {
+			left: obj.x - styleNumber(this, 'margin-left'),
+			top: obj.y - styleNumber(this, 'margin-top')
+		};
+	},
+
+	setPosition: function(obj){
+		return this.setStyles(this.computePosition(obj));
+	}
+
+});
+
+
+Native.implement([Document, Window], {
+
+	getSize: function(){
+		if (Browser.Engine.presto || Browser.Engine.webkit){
+			var win = this.getWindow();
+			return {x: win.innerWidth, y: win.innerHeight};
+		}
+		var doc = getCompatElement(this);
+		return {x: doc.clientWidth, y: doc.clientHeight};
+	},
+
+	getScroll: function(){
+		var win = this.getWindow(), doc = getCompatElement(this);
+		return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
+	},
+
+	getScrollSize: function(){
+		var doc = getCompatElement(this), min = this.getSize();
+		return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
+	},
+
+	getPosition: function(){
+		return {x: 0, y: 0};
+	},
+
+	getCoordinates: function(){
+		var size = this.getSize();
+		return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
+	}
+
+});
+
+// private methods
+
+var styleString = Element.getComputedStyle;
+
+function styleNumber(element, style){
+	return styleString(element, style).toInt() || 0;
+};
+
+function borderBox(element){
+	return styleString(element, '-moz-box-sizing') == 'border-box';
+};
+
+function topBorder(element){
+	return styleNumber(element, 'border-top-width');
+};
+
+function leftBorder(element){
+	return styleNumber(element, 'border-left-width');
+};
+
+function isBody(element){
+	return (/^(?:body|html)$/i).test(element.tagName);
+};
+
+function getCompatElement(element){
+	var doc = element.getDocument();
+	return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
+};
+
+})();
+
+//aliases
+Element.alias('setPosition', 'position'); //compatability
+
+Native.implement([Window, Document, Element], {
+
+	getHeight: function(){
+		return this.getSize().y;
+	},
+
+	getWidth: function(){
+		return this.getSize().x;
+	},
+
+	getScrollTop: function(){
+		return this.getScroll().y;
+	},
+
+	getScrollLeft: function(){
+		return this.getScroll().x;
+	},
+
+	getScrollHeight: function(){
+		return this.getScrollSize().y;
+	},
+
+	getScrollWidth: function(){
+		return this.getScrollSize().x;
+	},
+
+	getTop: function(){
+		return this.getPosition().y;
+	},
+
+	getLeft: function(){
+		return this.getPosition().x;
+	}
+
+});
+/*
+---
+
+script: Selectors.js
+
+description: Adds advanced CSS-style querying capabilities for targeting HTML Elements. Includes pseudo selectors.
+
+license: MIT-style license.
+
+requires:
+- /Element
+
+provides: [Selectors]
+
+...
+*/
+
+Native.implement([Document, Element], {
+
+	getElements: function(expression, nocash){
+		expression = expression.split(',');
+		var items, local = {};
+		for (var i = 0, l = expression.length; i < l; i++){
+			var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
+			if (i != 0 && elements.item) elements = $A(elements);
+			items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
+		}
+		return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
+	}
+
+});
+
+Element.implement({
+
+	match: function(selector){
+		if (!selector || (selector == this)) return true;
+		var tagid = Selectors.Utils.parseTagAndID(selector);
+		var tag = tagid[0], id = tagid[1];
+		if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
+		var parsed = Selectors.Utils.parseSelector(selector);
+		return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
+	}
+
+});
+
+var Selectors = {Cache: {nth: {}, parsed: {}}};
+
+Selectors.RegExps = {
+	id: (/#([\w-]+)/),
+	tag: (/^(\w+|\*)/),
+	quick: (/^(\w+|\*)$/),
+	splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
+	combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
+};
+
+Selectors.Utils = {
+
+	chk: function(item, uniques){
+		if (!uniques) return true;
+		var uid = $uid(item);
+		if (!uniques[uid]) return uniques[uid] = true;
+		return false;
+	},
+
+	parseNthArgument: function(argument){
+		if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
+		var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
+		if (!parsed) return false;
+		var inta = parseInt(parsed[1], 10);
+		var a = (inta || inta === 0) ? inta : 1;
+		var special = parsed[2] || false;
+		var b = parseInt(parsed[3], 10) || 0;
+		if (a != 0){
+			b--;
+			while (b < 1) b += a;
+			while (b >= a) b -= a;
+		} else {
+			a = b;
+			special = 'index';
+		}
+		switch (special){
+			case 'n': parsed = {a: a, b: b, special: 'n'}; break;
+			case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
+			case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
+			case 'first': parsed = {a: 0, special: 'index'}; break;
+			case 'last': parsed = {special: 'last-child'}; break;
+			case 'only': parsed = {special: 'only-child'}; break;
+			default: parsed = {a: (a - 1), special: 'index'};
+		}
+
+		return Selectors.Cache.nth[argument] = parsed;
+	},
+
+	parseSelector: function(selector){
+		if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
+		var m, parsed = {classes: [], pseudos: [], attributes: []};
+		while ((m = Selectors.RegExps.combined.exec(selector))){
+			var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
+			if (cn){
+				parsed.classes.push(cn);
+			} else if (pn){
+				var parser = Selectors.Pseudo.get(pn);
+				if (parser) parsed.pseudos.push({parser: parser, argument: pa});
+				else parsed.attributes.push({name: pn, operator: '=', value: pa});
+			} else if (an){
+				parsed.attributes.push({name: an, operator: ao, value: av});
+			}
+		}
+		if (!parsed.classes.length) delete parsed.classes;
+		if (!parsed.attributes.length) delete parsed.attributes;
+		if (!parsed.pseudos.length) delete parsed.pseudos;
+		if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
+		return Selectors.Cache.parsed[selector] = parsed;
+	},
+
+	parseTagAndID: function(selector){
+		var tag = selector.match(Selectors.RegExps.tag);
+		var id = selector.match(Selectors.RegExps.id);
+		return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
+	},
+
+	filter: function(item, parsed, local){
+		var i;
+		if (parsed.classes){
+			for (i = parsed.classes.length; i--; i){
+				var cn = parsed.classes[i];
+				if (!Selectors.Filters.byClass(item, cn)) return false;
+			}
+		}
+		if (parsed.attributes){
+			for (i = parsed.attributes.length; i--; i){
+				var att = parsed.attributes[i];
+				if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
+			}
+		}
+		if (parsed.pseudos){
+			for (i = parsed.pseudos.length; i--; i){
+				var psd = parsed.pseudos[i];
+				if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
+			}
+		}
+		return true;
+	},
+
+	getByTagAndID: function(ctx, tag, id){
+		if (id){
+			var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
+			return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
+		} else {
+			return ctx.getElementsByTagName(tag);
+		}
+	},
+
+	search: function(self, expression, local){
+		var splitters = [];
+
+		var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
+			splitters.push(m1);
+			return ':)' + m2;
+		}).split(':)');
+
+		var items, filtered, item;
+
+		for (var i = 0, l = selectors.length; i < l; i++){
+
+			var selector = selectors[i];
+
+			if (i == 0 && Selectors.RegExps.quick.test(selector)){
+				items = self.getElementsByTagName(selector);
+				continue;
+			}
+
+			var splitter = splitters[i - 1];
+
+			var tagid = Selectors.Utils.parseTagAndID(selector);
+			var tag = tagid[0], id = tagid[1];
+
+			if (i == 0){
+				items = Selectors.Utils.getByTagAndID(self, tag, id);
+			} else {
+				var uniques = {}, found = [];
+				for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
+				items = found;
+			}
+
+			var parsed = Selectors.Utils.parseSelector(selector);
+
+			if (parsed){
+				filtered = [];
+				for (var m = 0, n = items.length; m < n; m++){
+					item = items[m];
+					if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
+				}
+				items = filtered;
+			}
+
+		}
+
+		return items;
+
+	}
+
+};
+
+Selectors.Getters = {
+
+	' ': function(found, self, tag, id, uniques){
+		var items = Selectors.Utils.getByTagAndID(self, tag, id);
+		for (var i = 0, l = items.length; i < l; i++){
+			var item = items[i];
+			if (Selectors.Utils.chk(item, uniques)) found.push(item);
+		}
+		return found;
+	},
+
+	'>': function(found, self, tag, id, uniques){
+		var children = Selectors.Utils.getByTagAndID(self, tag, id);
+		for (var i = 0, l = children.length; i < l; i++){
+			var child = children[i];
+			if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
+		}
+		return found;
+	},
+
+	'+': function(found, self, tag, id, uniques){
+		while ((self = self.nextSibling)){
+			if (self.nodeType == 1){
+				if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
+				break;
+			}
+		}
+		return found;
+	},
+
+	'~': function(found, self, tag, id, uniques){
+		while ((self = self.nextSibling)){
+			if (self.nodeType == 1){
+				if (!Selectors.Utils.chk(self, uniques)) break;
+				if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
+			}
+		}
+		return found;
+	}
+
+};
+
+Selectors.Filters = {
+
+	byTag: function(self, tag){
+		return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
+	},
+
+	byID: function(self, id){
+		return (!id || (self.id && self.id == id));
+	},
+
+	byClass: function(self, klass){
+		return (self.className && self.className.contains && self.className.contains(klass, ' '));
+	},
+
+	byPseudo: function(self, parser, argument, local){
+		return parser.call(self, argument, local);
+	},
+
+	byAttribute: function(self, name, operator, value){
+		var result = Element.prototype.getProperty.call(self, name);
+		if (!result) return (operator == '!=');
+		if (!operator || value == undefined) return true;
+		switch (operator){
+			case '=': return (result == value);
+			case '*=': return (result.contains(value));
+			case '^=': return (result.substr(0, value.length) == value);
+			case '$=': return (result.substr(result.length - value.length) == value);
+			case '!=': return (result != value);
+			case '~=': return result.contains(value, ' ');
+			case '|=': return result.contains(value, '-');
+		}
+		return false;
+	}
+
+};
+
+Selectors.Pseudo = new Hash({
+
+	// w3c pseudo selectors
+
+	checked: function(){
+		return this.checked;
+	},
+	
+	empty: function(){
+		return !(this.innerText || this.textContent || '').length;
+	},
+
+	not: function(selector){
+		return !Element.match(this, selector);
+	},
+
+	contains: function(text){
+		return (this.innerText || this.textContent || '').contains(text);
+	},
+
+	'first-child': function(){
+		return Selectors.Pseudo.index.call(this, 0);
+	},
+
+	'last-child': function(){
+		var element = this;
+		while ((element = element.nextSibling)){
+			if (element.nodeType == 1) return false;
+		}
+		return true;
+	},
+
+	'only-child': function(){
+		var prev = this;
+		while ((prev = prev.previousSibling)){
+			if (prev.nodeType == 1) return false;
+		}
+		var next = this;
+		while ((next = next.nextSibling)){
+			if (next.nodeType == 1) return false;
+		}
+		return true;
+	},
+
+	'nth-child': function(argument, local){
+		argument = (argument == undefined) ? 'n' : argument;
+		var parsed = Selectors.Utils.parseNthArgument(argument);
+		if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
+		var count = 0;
+		local.positions = local.positions || {};
+		var uid = $uid(this);
+		if (!local.positions[uid]){
+			var self = this;
+			while ((self = self.previousSibling)){
+				if (self.nodeType != 1) continue;
+				count ++;
+				var position = local.positions[$uid(self)];
+				if (position != undefined){
+					count = position + count;
+					break;
+				}
+			}
+			local.positions[uid] = count;
+		}
+		return (local.positions[uid] % parsed.a == parsed.b);
+	},
+
+	// custom pseudo selectors
+
+	index: function(index){
+		var element = this, count = 0;
+		while ((element = element.previousSibling)){
+			if (element.nodeType == 1 && ++count > index) return false;
+		}
+		return (count == index);
+	},
+
+	even: function(argument, local){
+		return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
+	},
+
+	odd: function(argument, local){
+		return Selectors.Pseudo['nth-child'].call(this, '2n', local);
+	},
+	
+	selected: function(){
+		return this.selected;
+	},
+	
+	enabled: function(){
+		return (this.disabled === false);
+	}
+
+});
+/*
+---
+
+script: DomReady.js
+
+description: Contains the custom event domready.
+
+license: MIT-style license.
+
+requires:
+- /Element.Event
+
+provides: [DomReady]
+
+...
+*/
+
+Element.Events.domready = {
+
+	onAdd: function(fn){
+		if (Browser.loaded) fn.call(this);
+	}
+
+};
+
+(function(){
+
+	var domready = function(){
+		if (Browser.loaded) return;
+		Browser.loaded = true;
+		window.fireEvent('domready');
+		document.fireEvent('domready');
+	};
+	
+	window.addEvent('load', domready);
+
+	if (Browser.Engine.trident){
+		var temp = document.createElement('div');
+		(function(){
+			($try(function(){
+				temp.doScroll(); // Technique by Diego Perini
+				return document.id(temp).inject(document.body).set('html', 'temp').dispose();
+			})) ? domready() : arguments.callee.delay(50);
+		})();
+	} else if (Browser.Engine.webkit && Browser.Engine.version < 525){
+		(function(){
+			(['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
+		})();
+	} else {
+		document.addEvent('DOMContentLoaded', domready);
+	}
+
+})();
+/*
+---
+
+script: JSON.js
+
+description: JSON encoder and decoder.
+
+license: MIT-style license.
+
+See Also: <http://www.json.org/>
+
+requires:
+- /Array
+- /String
+- /Number
+- /Function
+- /Hash
+
+provides: [JSON]
+
+...
+*/
+
+var JSON = new Hash(this.JSON && {
+	stringify: JSON.stringify,
+	parse: JSON.parse
+}).extend({
+	
+	$specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
+
+	$replaceChars: function(chr){
+		return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
+	},
+
+	encode: function(obj){
+		switch ($type(obj)){
+			case 'string':
+				return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
+			case 'array':
+				return '[' + String(obj.map(JSON.encode).clean()) + ']';
+			case 'object': case 'hash':
+				var string = [];
+				Hash.each(obj, function(value, key){
+					var json = JSON.encode(value);
+					if (json) string.push(JSON.encode(key) + ':' + json);
+				});
+				return '{' + string + '}';
+			case 'number': case 'boolean': return String(obj);
+			case false: return 'null';
+		}
+		return null;
+	},
+
+	decode: function(string, secure){
+		if ($type(string) != 'string' || !string.length) return null;
+		if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
+		return eval('(' + string + ')');
+	}
+
+});
+
+Native.implement([Hash, Array, String, Number], {
+
+	toJSON: function(){
+		return JSON.encode(this);
+	}
+
+});
+/*
+---
+
+script: Cookie.js
+
+description: Class for creating, reading, and deleting browser Cookies.
+
+license: MIT-style license.
+
+credits:
+- Based on the functions by Peter-Paul Koch (http://quirksmode.org).
+
+requires:
+- /Options
+
+provides: [Cookie]
+
+...
+*/
+
+var Cookie = new Class({
+
+	Implements: Options,
+
+	options: {
+		path: false,
+		domain: false,
+		duration: false,
+		secure: false,
+		document: document
+	},
+
+	initialize: function(key, options){
+		this.key = key;
+		this.setOptions(options);
+	},
+
+	write: function(value){
+		value = encodeURIComponent(value);
+		if (this.options.domain) value += '; domain=' + this.options.domain;
+		if (this.options.path) value += '; path=' + this.options.path;
+		if (this.options.duration){
+			var date = new Date();
+			date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
+			value += '; expires=' + date.toGMTString();
+		}
+		if (this.options.secure) value += '; secure';
+		this.options.document.cookie = this.key + '=' + value;
+		return this;
+	},
+
+	read: function(){
+		var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
+		return (value) ? decodeURIComponent(value[1]) : null;
+	},
+
+	dispose: function(){
+		new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
+		return this;
+	}
+
+});
+
+Cookie.write = function(key, value, options){
+	return new Cookie(key, options).write(value);
+};
+
+Cookie.read = function(key){
+	return new Cookie(key).read();
+};
+
+Cookie.dispose = function(key, options){
+	return new Cookie(key, options).dispose();
+};
+/*
+---
+
+script: Swiff.js
+
+description: Wrapper for embedding SWF movies. Supports External Interface Communication.
+
+license: MIT-style license.
+
+credits: 
+- Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
+
+requires:
+- /Options
+- /$util
+
+provides: [Swiff]
+
+...
+*/
+
+var Swiff = new Class({
+
+	Implements: [Options],
+
+	options: {
+		id: null,
+		height: 1,
+		width: 1,
+		container: null,
+		properties: {},
+		params: {
+			quality: 'high',
+			allowScriptAccess: 'always',
+			wMode: 'transparent',
+			swLiveConnect: true
+		},
+		callBacks: {},
+		vars: {}
+	},
+
+	toElement: function(){
+		return this.object;
+	},
+
+	initialize: function(path, options){
+		this.instance = 'Swiff_' + $time();
+
+		this.setOptions(options);
+		options = this.options;
+		var id = this.id = options.id || this.instance;
+		var container = document.id(options.container);
+
+		Swiff.CallBacks[this.instance] = {};
+
+		var params = options.params, vars = options.vars, callBacks = options.callBacks;
+		var properties = $extend({height: options.height, width: options.width}, options.properties);
+
+		var self = this;
+
+		for (var callBack in callBacks){
+			Swiff.CallBacks[this.instance][callBack] = (function(option){
+				return function(){
+					return option.apply(self.object, arguments);
+				};
+			})(callBacks[callBack]);
+			vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
+		}
+
+		params.flashVars = Hash.toQueryString(vars);
+		if (Browser.Engine.trident){
+			properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
+			params.movie = path;
+		} else {
+			properties.type = 'application/x-shockwave-flash';
+			properties.data = path;
+		}
+		var build = '<object id="' + id + '"';
+		for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
+		build += '>';
+		for (var param in params){
+			if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
+		}
+		build += '</object>';
+		this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
+	},
+
+	replaces: function(element){
+		element = document.id(element, true);
+		element.parentNode.replaceChild(this.toElement(), element);
+		return this;
+	},
+
+	inject: function(element){
+		document.id(element, true).appendChild(this.toElement());
+		return this;
+	},
+
+	remote: function(){
+		return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
+	}
+
+});
+
+Swiff.CallBacks = {};
+
+Swiff.remote = function(obj, fn){
+	var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
+	return eval(rs);
+};
+/*
+---
+
+script: Fx.js
+
+description: Contains the basic animation logic to be extended by all other Fx Classes.
+
+license: MIT-style license.
+
+requires:
+- /Chain
+- /Events
+- /Options
+
+provides: [Fx]
+
+...
+*/
+
+var Fx = new Class({
+
+	Implements: [Chain, Events, Options],
+
+	options: {
+		/*
+		onStart: $empty,
+		onCancel: $empty,
+		onComplete: $empty,
+		*/
+		fps: 50,
+		unit: false,
+		duration: 500,
+		link: 'ignore'
+	},
+
+	initialize: function(options){
+		this.subject = this.subject || this;
+		this.setOptions(options);
+		this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
+		var wait = this.options.wait;
+		if (wait === false) this.options.link = 'cancel';
+	},
+
+	getTransition: function(){
+		return function(p){
+			return -(Math.cos(Math.PI * p) - 1) / 2;
+		};
+	},
+
+	step: function(){
+		var time = $time();
+		if (time < this.time + this.options.duration){
+			var delta = this.transition((time - this.time) / this.options.duration);
+			this.set(this.compute(this.from, this.to, delta));
+		} else {
+			this.set(this.compute(this.from, this.to, 1));
+			this.complete();
+		}
+	},
+
+	set: function(now){
+		return now;
+	},
+
+	compute: function(from, to, delta){
+		return Fx.compute(from, to, delta);
+	},
+
+	check: function(){
+		if (!this.timer) return true;
+		switch (this.options.link){
+			case 'cancel': this.cancel(); return true;
+			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
+		}
+		return false;
+	},
+
+	start: function(from, to){
+		if (!this.check(from, to)) return this;
+		this.from = from;
+		this.to = to;
+		this.time = 0;
+		this.transition = this.getTransition();
+		this.startTimer();
+		this.onStart();
+		return this;
+	},
+
+	complete: function(){
+		if (this.stopTimer()) this.onComplete();
+		return this;
+	},
+
+	cancel: function(){
+		if (this.stopTimer()) this.onCancel();
+		return this;
+	},
+
+	onStart: function(){
+		this.fireEvent('start', this.subject);
+	},
+
+	onComplete: function(){
+		this.fireEvent('complete', this.subject);
+		if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
+	},
+
+	onCancel: function(){
+		this.fireEvent('cancel', this.subject).clearChain();
+	},
+
+	pause: function(){
+		this.stopTimer();
+		return this;
+	},
+
+	resume: function(){
+		this.startTimer();
+		return this;
+	},
+
+	stopTimer: function(){
+		if (!this.timer) return false;
+		this.time = $time() - this.time;
+		this.timer = $clear(this.timer);
+		return true;
+	},
+
+	startTimer: function(){
+		if (this.timer) return false;
+		this.time = $time() - this.time;
+		this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
+		return true;
+	}
+
+});
+
+Fx.compute = function(from, to, delta){
+	return (to - from) * delta + from;
+};
+
+Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
+/*
+---
+
+script: Fx.CSS.js
+
+description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+
+license: MIT-style license.
+
+requires:
+- /Fx
+- /Element.Style
+
+provides: [Fx.CSS]
+
+...
+*/
+
+Fx.CSS = new Class({
+
+	Extends: Fx,
+
+	//prepares the base from/to object
+
+	prepare: function(element, property, values){
+		values = $splat(values);
+		var values1 = values[1];
+		if (!$chk(values1)){
+			values[1] = values[0];
+			values[0] = element.getStyle(property);
+		}
+		var parsed = values.map(this.parse);
+		return {from: parsed[0], to: parsed[1]};
+	},
+
+	//parses a value into an array
+
+	parse: function(value){
+		value = $lambda(value)();
+		value = (typeof value == 'string') ? value.split(' ') : $splat(value);
+		return value.map(function(val){
+			val = String(val);
+			var found = false;
+			Fx.CSS.Parsers.each(function(parser, key){
+				if (found) return;
+				var parsed = parser.parse(val);
+				if ($chk(parsed)) found = {value: parsed, parser: parser};
+			});
+			found = found || {value: val, parser: Fx.CSS.Parsers.String};
+			return found;
+		});
+	},
+
+	//computes by a from and to prepared objects, using their parsers.
+
+	compute: function(from, to, delta){
+		var computed = [];
+		(Math.min(from.length, to.length)).times(function(i){
+			computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
+		});
+		computed.$family = {name: 'fx:css:value'};
+		return computed;
+	},
+
+	//serves the value as settable
+
+	serve: function(value, unit){
+		if ($type(value) != 'fx:css:value') value = this.parse(value);
+		var returned = [];
+		value.each(function(bit){
+			returned = returned.concat(bit.parser.serve(bit.value, unit));
+		});
+		return returned;
+	},
+
+	//renders the change to an element
+
+	render: function(element, property, value, unit){
+		element.setStyle(property, this.serve(value, unit));
+	},
+
+	//searches inside the page css to find the values for a selector
+
+	search: function(selector){
+		if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
+		var to = {};
+		Array.each(document.styleSheets, function(sheet, j){
+			var href = sheet.href;
+			if (href && href.contains('://') && !href.contains(document.domain)) return;
+			var rules = sheet.rules || sheet.cssRules;
+			Array.each(rules, function(rule, i){
+				if (!rule.style) return;
+				var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
+					return m.toLowerCase();
+				}) : null;
+				if (!selectorText || !selectorText.test('^' + selector + '$')) return;
+				Element.Styles.each(function(value, style){
+					if (!rule.style[style] || Element.ShortStyles[style]) return;
+					value = String(rule.style[style]);
+					to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
+				});
+			});
+		});
+		return Fx.CSS.Cache[selector] = to;
+	}
+
+});
+
+Fx.CSS.Cache = {};
+
+Fx.CSS.Parsers = new Hash({
+
+	Color: {
+		parse: function(value){
+			if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
+			return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
+		},
+		compute: function(from, to, delta){
+			return from.map(function(value, i){
+				return Math.round(Fx.compute(from[i], to[i], delta));
+			});
+		},
+		serve: function(value){
+			return value.map(Number);
+		}
+	},
+
+	Number: {
+		parse: parseFloat,
+		compute: Fx.compute,
+		serve: function(value, unit){
+			return (unit) ? value + unit : value;
+		}
+	},
+
+	String: {
+		parse: $lambda(false),
+		compute: $arguments(1),
+		serve: $arguments(0)
+	}
+
+});
+/*
+---
+
+script: Fx.Tween.js
+
+description: Formerly Fx.Style, effect to transition any CSS property for an element.
+
+license: MIT-style license.
+
+requires: 
+- /Fx.CSS
+
+provides: [Fx.Tween, Element.fade, Element.highlight]
+
+...
+*/
+
+Fx.Tween = new Class({
+
+	Extends: Fx.CSS,
+
+	initialize: function(element, options){
+		this.element = this.subject = document.id(element);
+		this.parent(options);
+	},
+
+	set: function(property, now){
+		if (arguments.length == 1){
+			now = property;
+			property = this.property || this.options.property;
+		}
+		this.render(this.element, property, now, this.options.unit);
+		return this;
+	},
+
+	start: function(property, from, to){
+		if (!this.check(property, from, to)) return this;
+		var args = Array.flatten(arguments);
+		this.property = this.options.property || args.shift();
+		var parsed = this.prepare(this.element, this.property, args);
+		return this.parent(parsed.from, parsed.to);
+	}
+
+});
+
+Element.Properties.tween = {
+
+	set: function(options){
+		var tween = this.retrieve('tween');
+		if (tween) tween.cancel();
+		return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('tween')){
+			if (options || !this.retrieve('tween:options')) this.set('tween', options);
+			this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
+		}
+		return this.retrieve('tween');
+	}
+
+};
+
+Element.implement({
+
+	tween: function(property, from, to){
+		this.get('tween').start(arguments);
+		return this;
+	},
+
+	fade: function(how){
+		var fade = this.get('tween'), o = 'opacity', toggle;
+		how = $pick(how, 'toggle');
+		switch (how){
+			case 'in': fade.start(o, 1); break;
+			case 'out': fade.start(o, 0); break;
+			case 'show': fade.set(o, 1); break;
+			case 'hide': fade.set(o, 0); break;
+			case 'toggle':
+				var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
+				fade.start(o, (flag) ? 0 : 1);
+				this.store('fade:flag', !flag);
+				toggle = true;
+			break;
+			default: fade.start(o, arguments);
+		}
+		if (!toggle) this.eliminate('fade:flag');
+		return this;
+	},
+
+	highlight: function(start, end){
+		if (!end){
+			end = this.retrieve('highlight:original', this.getStyle('background-color'));
+			end = (end == 'transparent') ? '#fff' : end;
+		}
+		var tween = this.get('tween');
+		tween.start('background-color', start || '#ffff88', end).chain(function(){
+			this.setStyle('background-color', this.retrieve('highlight:original'));
+			tween.callChain();
+		}.bind(this));
+		return this;
+	}
+
+});
+/*
+---
+
+script: Fx.Morph.js
+
+description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
+
+license: MIT-style license.
+
+requires:
+- /Fx.CSS
+
+provides: [Fx.Morph]
+
+...
+*/
+
+Fx.Morph = new Class({
+
+	Extends: Fx.CSS,
+
+	initialize: function(element, options){
+		this.element = this.subject = document.id(element);
+		this.parent(options);
+	},
+
+	set: function(now){
+		if (typeof now == 'string') now = this.search(now);
+		for (var p in now) this.render(this.element, p, now[p], this.options.unit);
+		return this;
+	},
+
+	compute: function(from, to, delta){
+		var now = {};
+		for (var p in from) now[p] = this.parent(from[p], to[p], delta);
+		return now;
+	},
+
+	start: function(properties){
+		if (!this.check(properties)) return this;
+		if (typeof properties == 'string') properties = this.search(properties);
+		var from = {}, to = {};
+		for (var p in properties){
+			var parsed = this.prepare(this.element, p, properties[p]);
+			from[p] = parsed.from;
+			to[p] = parsed.to;
+		}
+		return this.parent(from, to);
+	}
+
+});
+
+Element.Properties.morph = {
+
+	set: function(options){
+		var morph = this.retrieve('morph');
+		if (morph) morph.cancel();
+		return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('morph')){
+			if (options || !this.retrieve('morph:options')) this.set('morph', options);
+			this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
+		}
+		return this.retrieve('morph');
+	}
+
+};
+
+Element.implement({
+
+	morph: function(props){
+		this.get('morph').start(props);
+		return this;
+	}
+
+});
+/*
+---
+
+script: Fx.Transitions.js
+
+description: Contains a set of advanced transitions to be used with any of the Fx Classes.
+
+license: MIT-style license.
+
+credits:
+- Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
+
+requires:
+- /Fx
+
+provides: [Fx.Transitions]
+
+...
+*/
+
+Fx.implement({
+
+	getTransition: function(){
+		var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
+		if (typeof trans == 'string'){
+			var data = trans.split(':');
+			trans = Fx.Transitions;
+			trans = trans[data[0]] || trans[data[0].capitalize()];
+			if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
+		}
+		return trans;
+	}
+
+});
+
+Fx.Transition = function(transition, params){
+	params = $splat(params);
+	return $extend(transition, {
+		easeIn: function(pos){
+			return transition(pos, params);
+		},
+		easeOut: function(pos){
+			return 1 - transition(1 - pos, params);
+		},
+		easeInOut: function(pos){
+			return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
+		}
+	});
+};
+
+Fx.Transitions = new Hash({
+
+	linear: $arguments(0)
+
+});
+
+Fx.Transitions.extend = function(transitions){
+	for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
+};
+
+Fx.Transitions.extend({
+
+	Pow: function(p, x){
+		return Math.pow(p, x[0] || 6);
+	},
+
+	Expo: function(p){
+		return Math.pow(2, 8 * (p - 1));
+	},
+
+	Circ: function(p){
+		return 1 - Math.sin(Math.acos(p));
+	},
+
+	Sine: function(p){
+		return 1 - Math.sin((1 - p) * Math.PI / 2);
+	},
+
+	Back: function(p, x){
+		x = x[0] || 1.618;
+		return Math.pow(p, 2) * ((x + 1) * p - x);
+	},
+
+	Bounce: function(p){
+		var value;
+		for (var a = 0, b = 1; 1; a += b, b /= 2){
+			if (p >= (7 - 4 * a) / 11){
+				value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
+				break;
+			}
+		}
+		return value;
+	},
+
+	Elastic: function(p, x){
+		return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
+	}
+
+});
+
+['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
+	Fx.Transitions[transition] = new Fx.Transition(function(p){
+		return Math.pow(p, [i + 2]);
+	});
+});
+/*
+---
+
+script: Request.js
+
+description: Powerful all purpose Request Class. Uses XMLHTTPRequest.
+
+license: MIT-style license.
+
+requires:
+- /Element
+- /Chain
+- /Events
+- /Options
+- /Browser
+
+provides: [Request]
+
+...
+*/
+
+var Request = new Class({
+
+	Implements: [Chain, Events, Options],
+
+	options: {/*
+		onRequest: $empty,
+		onComplete: $empty,
+		onCancel: $empty,
+		onSuccess: $empty,
+		onFailure: $empty,
+		onException: $empty,*/
+		url: '',
+		data: '',
+		headers: {
+			'X-Requested-With': 'XMLHttpRequest',
+			'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+		},
+		async: true,
+		format: false,
+		method: 'post',
+		link: 'ignore',
+		isSuccess: null,
+		emulation: true,
+		urlEncoded: true,
+		encoding: 'utf-8',
+		evalScripts: false,
+		evalResponse: false,
+		noCache: false
+	},
+
+	initialize: function(options){
+		this.xhr = new Browser.Request();
+		this.setOptions(options);
+		this.options.isSuccess = this.options.isSuccess || this.isSuccess;
+		this.headers = new Hash(this.options.headers);
+	},
+
+	onStateChange: function(){
+		if (this.xhr.readyState != 4 || !this.running) return;
+		this.running = false;
+		this.status = 0;
+		$try(function(){
+			this.status = this.xhr.status;
+		}.bind(this));
+		this.xhr.onreadystatechange = $empty;
+		if (this.options.isSuccess.call(this, this.status)){
+			this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
+			this.success(this.response.text, this.response.xml);
+		} else {
+			this.response = {text: null, xml: null};
+			this.failure();
+		}
+	},
+
+	isSuccess: function(){
+		return ((this.status >= 200) && (this.status < 300));
+	},
+
+	processScripts: function(text){
+		if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
+		return text.stripScripts(this.options.evalScripts);
+	},
+
+	success: function(text, xml){
+		this.onSuccess(this.processScripts(text), xml);
+	},
+
+	onSuccess: function(){
+		this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
+	},
+
+	failure: function(){
+		this.onFailure();
+	},
+
+	onFailure: function(){
+		this.fireEvent('complete').fireEvent('failure', this.xhr);
+	},
+
+	setHeader: function(name, value){
+		this.headers.set(name, value);
+		return this;
+	},
+
+	getHeader: function(name){
+		return $try(function(){
+			return this.xhr.getResponseHeader(name);
+		}.bind(this));
+	},
+
+	check: function(){
+		if (!this.running) return true;
+		switch (this.options.link){
+			case 'cancel': this.cancel(); return true;
+			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
+		}
+		return false;
+	},
+
+	send: function(options){
+		if (!this.check(options)) return this;
+		this.running = true;
+
+		var type = $type(options);
+		if (type == 'string' || type == 'element') options = {data: options};
+
+		var old = this.options;
+		options = $extend({data: old.data, url: old.url, method: old.method}, options);
+		var data = options.data, url = String(options.url), method = options.method.toLowerCase();
+
+		switch ($type(data)){
+			case 'element': data = document.id(data).toQueryString(); break;
+			case 'object': case 'hash': data = Hash.toQueryString(data);
+		}
+
+		if (this.options.format){
+			var format = 'format=' + this.options.format;
+			data = (data) ? format + '&' + data : format;
+		}
+
+		if (this.options.emulation && !['get', 'post'].contains(method)){
+			var _method = '_method=' + method;
+			data = (data) ? _method + '&' + data : _method;
+			method = 'post';
+		}
+
+		if (this.options.urlEncoded && method == 'post'){
+			var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
+			this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
+		}
+
+		if (this.options.noCache){
+			var noCache = 'noCache=' + new Date().getTime();
+			data = (data) ? noCache + '&' + data : noCache;
+		}
+
+		var trimPosition = url.lastIndexOf('/');
+		if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition);
+
+		if (data && method == 'get'){
+			url = url + (url.contains('?') ? '&' : '?') + data;
+			data = null;
+		}
+
+		this.xhr.open(method.toUpperCase(), url, this.options.async);
+
+		this.xhr.onreadystatechange = this.onStateChange.bind(this);
+
+		this.headers.each(function(value, key){
+			try {
+				this.xhr.setRequestHeader(key, value);
+			} catch (e){
+				this.fireEvent('exception', [key, value]);
+			}
+		}, this);
+
+		this.fireEvent('request');
+		this.xhr.send(data);
+		if (!this.options.async) this.onStateChange();
+		return this;
+	},
+
+	cancel: function(){
+		if (!this.running) return this;
+		this.running = false;
+		this.xhr.abort();
+		this.xhr.onreadystatechange = $empty;
+		this.xhr = new Browser.Request();
+		this.fireEvent('cancel');
+		return this;
+	}
+
+});
+
+(function(){
+
+var methods = {};
+['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
+	methods[method] = function(){
+		var params = Array.link(arguments, {url: String.type, data: $defined});
+		return this.send($extend(params, {method: method}));
+	};
+});
+
+Request.implement(methods);
+
+})();
+
+Element.Properties.send = {
+
+	set: function(options){
+		var send = this.retrieve('send');
+		if (send) send.cancel();
+		return this.eliminate('send').store('send:options', $extend({
+			data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
+		}, options));
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('send')){
+			if (options || !this.retrieve('send:options')) this.set('send', options);
+			this.store('send', new Request(this.retrieve('send:options')));
+		}
+		return this.retrieve('send');
+	}
+
+};
+
+Element.implement({
+
+	send: function(url){
+		var sender = this.get('send');
+		sender.send({data: this, url: url || sender.options.url});
+		return this;
+	}
+
+});
+/*
+---
+
+script: Request.HTML.js
+
+description: Extends the basic Request Class with additional methods for interacting with HTML responses.
+
+license: MIT-style license.
+
+requires:
+- /Request
+- /Element
+
+provides: [Request.HTML]
+
+...
+*/
+
+Request.HTML = new Class({
+
+	Extends: Request,
+
+	options: {
+		update: false,
+		append: false,
+		evalScripts: true,
+		filter: false
+	},
+
+	processHTML: function(text){
+		var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
+		text = (match) ? match[1] : text;
+
+		var container = new Element('div');
+
+		return $try(function(){
+			var root = '<root>' + text + '</root>', doc;
+			if (Browser.Engine.trident){
+				doc = new ActiveXObject('Microsoft.XMLDOM');
+				doc.async = false;
+				doc.loadXML(root);
+			} else {
+				doc = new DOMParser().parseFromString(root, 'text/xml');
+			}
+			root = doc.getElementsByTagName('root')[0];
+			if (!root) return null;
+			for (var i = 0, k = root.childNodes.length; i < k; i++){
+				var child = Element.clone(root.childNodes[i], true, true);
+				if (child) container.grab(child);
+			}
+			return container;
+		}) || container.set('html', text);
+	},
+
+	success: function(text){
+		var options = this.options, response = this.response;
+
+		response.html = text.stripScripts(function(script){
+			response.javascript = script;
+		});
+
+		var temp = this.processHTML(response.html);
+
+		response.tree = temp.childNodes;
+		response.elements = temp.getElements('*');
+
+		if (options.filter) response.tree = response.elements.filter(options.filter);
+		if (options.update) document.id(options.update).empty().set('html', response.html);
+		else if (options.append) document.id(options.append).adopt(temp.getChildren());
+		if (options.evalScripts) $exec(response.javascript);
+
+		this.onSuccess(response.tree, response.elements, response.html, response.javascript);
+	}
+
+});
+
+Element.Properties.load = {
+
+	set: function(options){
+		var load = this.retrieve('load');
+		if (load) load.cancel();
+		return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
+	},
+
+	get: function(options){
+		if (options || ! this.retrieve('load')){
+			if (options || !this.retrieve('load:options')) this.set('load', options);
+			this.store('load', new Request.HTML(this.retrieve('load:options')));
+		}
+		return this.retrieve('load');
+	}
+
+};
+
+Element.implement({
+
+	load: function(){
+		this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
+		return this;
+	}
+
+});
+/*
+---
+
+script: Request.JSON.js
+
+description: Extends the basic Request Class with additional methods for sending and receiving JSON data.
+
+license: MIT-style license.
+
+requires:
+- /Request JSON
+
+provides: [Request.HTML]
+
+...
+*/
+
+Request.JSON = new Class({
+
+	Extends: Request,
+
+	options: {
+		secure: true
+	},
+
+	initialize: function(options){
+		this.parent(options);
+		this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
+	},
+
+	success: function(text){
+		this.response.json = JSON.decode(text, this.options.secure);
+		this.onSuccess(this.response.json, text);
+	}
+
+});
+/*
+---
+
+script: More.js
+
+description: MooTools More
+
+license: MIT-style license
+
+authors:
+ - Guillermo Rauch
+ - Thomas Aylott
+ - Scott Kyle
+
+requires:
+ - core:1.2.4/MooTools
+
+provides: [MooTools.More]
+
+...
+*/
+
+MooTools.More = {
+	'version': '1.2.4.4',
+	'build': '6f6057dc645fdb7547689183b2311063bd653ddf'
+};/*
+---
+
+script: MooTools.Lang.js
+
+description: Provides methods for localization.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Events
+ - /MooTools.More
+
+provides: [MooTools.Lang]
+
+...
+*/
+
+(function(){
+
+	var data = {
+		language: 'en-US',
+		languages: {
+			'en-US': {}
+		},
+		cascades: ['en-US']
+	};
+	
+	var cascaded;
+
+	MooTools.lang = new Events();
+
+	$extend(MooTools.lang, {
+
+		setLanguage: function(lang){
+			if (!data.languages[lang]) return this;
+			data.language = lang;
+			this.load();
+			this.fireEvent('langChange', lang);
+			return this;
+		},
+
+		load: function() {
+			var langs = this.cascade(this.getCurrentLanguage());
+			cascaded = {};
+			$each(langs, function(set, setName){
+				cascaded[setName] = this.lambda(set);
+			}, this);
+		},
+
+		getCurrentLanguage: function(){
+			return data.language;
+		},
+
+		addLanguage: function(lang){
+			data.languages[lang] = data.languages[lang] || {};
+			return this;
+		},
+
+		cascade: function(lang){
+			var cascades = (data.languages[lang] || {}).cascades || [];
+			cascades.combine(data.cascades);
+			cascades.erase(lang).push(lang);
+			var langs = cascades.map(function(lng){
+				return data.languages[lng];
+			}, this);
+			return $merge.apply(this, langs);
+		},
+
+		lambda: function(set) {
+			(set || {}).get = function(key, args){
+				return $lambda(set[key]).apply(this, $splat(args));
+			};
+			return set;
+		},
+
+		get: function(set, key, args){
+			if (cascaded && cascaded[set]) return (key ? cascaded[set].get(key, args) : cascaded[set]);
+		},
+
+		set: function(lang, set, members){
+			this.addLanguage(lang);
+			langData = data.languages[lang];
+			if (!langData[set]) langData[set] = {};
+			$extend(langData[set], members);
+			if (lang == this.getCurrentLanguage()){
+				this.load();
+				this.fireEvent('langChange', lang);
+			}
+			return this;
+		},
+
+		list: function(){
+			return Hash.getKeys(data.languages);
+		}
+
+	});
+
+})();/*
+---
+
+script: Log.js
+
+description: Provides basic logging functionality for plugins to implement.
+
+license: MIT-style license
+
+authors:
+ - Guillermo Rauch
+ - Thomas Aylott
+ - Scott Kyle
+
+requires:
+ - core:1.2.4/Class
+ - /MooTools.More
+
+provides: [Log]
+
+...
+*/
+
+(function(){
+
+var global = this;
+
+var log = function(){
+	if (global.console && console.log){
+		try {
+			console.log.apply(console, arguments);
+		} catch(e) {
+			console.log(Array.slice(arguments));
+		}
+	} else {
+		Log.logged.push(arguments);
+	}
+	return this;
+};
+
+var disabled = function(){
+	this.logged.push(arguments);
+	return this;
+};
+
+this.Log = new Class({
+	
+	logged: [],
+	
+	log: disabled,
+	
+	resetLog: function(){
+		this.logged.empty();
+		return this;
+	},
+
+	enableLog: function(){
+		this.log = log;
+		this.logged.each(function(args){
+			this.log.apply(this, args);
+		}, this);
+		return this.resetLog();
+	},
+
+	disableLog: function(){
+		this.log = disabled;
+		return this;
+	}
+	
+});
+
+Log.extend(new Log).enableLog();
+
+// legacy
+Log.logger = function(){
+	return this.log.apply(this, arguments);
+};
+
+})();/*
+---
+
+script: Depender.js
+
+description: A stand alone dependency loader for the MooTools library.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element.Events
+ - core:1.2.4/Request.JSON
+ - /MooTools.More
+ - /Log
+
+provides: Depender
+
+...
+*/
+
+var Depender = {
+
+	options: {
+		/* 
+		onRequire: $empty(options),
+		onRequirementLoaded: $empty([scripts, options]),
+		onScriptLoaded: $empty({
+			script: script, 
+			totalLoaded: percentOfTotalLoaded, 
+			loaded: scriptsState
+		}),
+		serial: false,
+		target: null,
+		noCache: false,
+		log: false,*/
+		loadedSources: [],
+		loadedScripts: ['Core', 'Browser', 'Array', 'String', 'Function', 'Number', 'Hash', 'Element', 'Event', 'Element.Event', 'Class', 'DomReady', 'Class.Extras', 'Request', 'JSON', 'Request.JSON', 'More', 'Depender', 'Log'],
+		useScriptInjection: true
+	},
+
+	loaded: [],
+
+	sources: {},
+
+	libs: {},
+
+	include: function(libs){
+		this.log('include: ', libs);
+		this.mapLoaded = false;
+		var loader = function(data){
+			this.libs = $merge(this.libs, data);
+			$each(this.libs, function(data, lib){
+				if (data.scripts) this.loadSource(lib, data.scripts);
+			}, this);
+		}.bind(this);
+		if ($type(libs) == 'string'){
+			this.log('fetching libs ', libs);
+			this.request(libs, loader);
+		} else {
+			loader(libs);
+		}
+		return this;
+	},
+
+	required: [],
+
+	require: function(options){
+		var loaded = function(){
+			var scripts = this.calculateDependencies(options.scripts);
+			if (options.sources){
+				options.sources.each(function(source){
+					scripts.combine(this.libs[source].files);
+				}, this);
+			}
+			if (options.serial) scripts.combine(this.getLoadedScripts());
+			options.scripts = scripts;
+			this.required.push(options);
+			this.fireEvent('require', options);
+			this.loadScripts(options.scripts);
+		};
+		if (this.mapLoaded) loaded.call(this);
+		else this.addEvent('mapLoaded', loaded.bind(this));
+		return this;
+	},
+
+	cleanDoubleSlash: function(str){
+		if (!str) return str;
+		var prefix = '';
+		if (str.test(/^http:\/\//)){
+			prefix = 'http://';
+			str = str.substring(7, str.length);
+		}
+		str = str.replace(/\/\//g, '/');
+		return prefix + str;
+	},
+
+	request: function(url, callback){
+		new Request.JSON({
+			url: url,
+			secure: false,
+			onSuccess: callback
+		}).send();
+	},
+
+	loadSource: function(lib, source){
+		if (this.libs[lib].files){
+			this.dataLoaded();
+			return;
+		}
+		this.log('loading source: ', source);
+		this.request(this.cleanDoubleSlash(source + '/scripts.json'), function(result){
+			this.log('loaded source: ', source);
+			this.libs[lib].files = result;
+			this.dataLoaded();
+		}.bind(this));
+	},
+
+	dataLoaded: function(){
+		var loaded = true;
+		$each(this.libs, function(v, k){
+			if (!this.libs[k].files) loaded = false;
+		}, this);
+		if (loaded){
+			this.mapTree();
+			this.mapLoaded = true;
+			this.calculateLoaded();
+			this.lastLoaded = this.getLoadedScripts().getLength();
+			this.fireEvent('mapLoaded');
+			this.removeEvents('mapLoaded');
+		}
+	},
+
+	calculateLoaded: function(){
+		var set = function(script){
+			this.scriptsState[script] = true;
+		}.bind(this);
+		if (this.options.loadedScripts) this.options.loadedScripts.each(set);
+		if (this.options.loadedSources){
+			this.options.loadedSources.each(function(lib){
+				$each(this.libs[lib].files, function(dir){
+					$each(dir, function(data, file){
+						set(file);
+					}, this);
+				}, this);
+			}, this);
+		}
+	},
+
+	deps: {},
+
+	pathMap: {},
+
+	mapTree: function(){
+		$each(this.libs, function(data, source){
+			$each(data.files, function(scripts, folder){
+				$each(scripts, function(details, script){
+					var path = source + ':' + folder + ':' + script;
+					if (this.deps[path]) return;
+					this.deps[path] = details.deps;
+					this.pathMap[script] = path;
+				}, this);
+			}, this);
+		}, this);
+	},
+
+	getDepsForScript: function(script){
+		return this.deps[this.pathMap[script]] || [];
+	},
+
+	calculateDependencies: function(scripts){
+		var reqs = [];
+		$splat(scripts).each(function(script){
+			if (script == 'None' || !script) return;
+			var deps = this.getDepsForScript(script);
+			if (!deps){
+				if (window.console && console.warn) console.warn('dependencies not mapped: script: %o, map: %o, :deps: %o', script, this.pathMap, this.deps);
+			} else {
+				deps.each(function(scr){
+					if (scr == script || scr == 'None' || !scr) return;
+					if (!reqs.contains(scr)) reqs.combine(this.calculateDependencies(scr));
+					reqs.include(scr);
+				}, this);
+			}
+			reqs.include(script);
+		}, this);
+		return reqs;
+	},
+
+	getPath: function(script){
+		try {
+			var chunks = this.pathMap[script].split(':');
+			var lib = this.libs[chunks[0]];
+			var dir = (lib.path || lib.scripts) + '/';
+			chunks.shift();
+			return this.cleanDoubleSlash(dir + chunks.join('/') + '.js');
+		} catch(e){
+			return script;
+		}
+	},
+
+	loadScripts: function(scripts){
+		scripts = scripts.filter(function(s){
+			if (!this.scriptsState[s] && s != 'None'){
+				this.scriptsState[s] = false;
+				return true;
+			}
+		}, this);
+		if (scripts.length){
+			scripts.each(function(scr){
+				this.loadScript(scr);
+			}, this);
+		} else {
+			this.check();
+		}
+	},
+
+	toLoad: [],
+
+	loadScript: function(script){
+		if (this.scriptsState[script] && this.toLoad.length){
+			this.loadScript(this.toLoad.shift());
+			return;
+		} else if (this.loading){
+			this.toLoad.push(script);
+			return;
+		}
+		var finish = function(){
+			this.loading = false;
+			this.scriptLoaded(script);
+			if (this.toLoad.length) this.loadScript(this.toLoad.shift());
+		}.bind(this);
+		var error = function(){
+			this.log('could not load: ', scriptPath);
+		}.bind(this);
+		this.loading = true;
+		var scriptPath = this.getPath(script);
+		if (this.options.useScriptInjection){
+			this.log('injecting script: ', scriptPath);
+			var loaded = function(){
+				this.log('loaded script: ', scriptPath);
+				finish();
+			}.bind(this);
+			new Element('script', {
+				src: scriptPath + (this.options.noCache ? '?noCache=' + new Date().getTime() : ''),
+				events: {
+					load: loaded,
+					readystatechange: function(){
+						if (['loaded', 'complete'].contains(this.readyState)) loaded();
+					},
+					error: error
+				}
+			}).inject(this.options.target || document.head);
+		} else {
+			this.log('requesting script: ', scriptPath);
+			new Request({
+				url: scriptPath,
+				noCache: this.options.noCache,
+				onComplete: function(js){
+					this.log('loaded script: ', scriptPath);
+					$exec(js);
+					finish();
+				}.bind(this),
+				onFailure: error,
+				onException: error
+			}).send();
+		}
+	},
+
+	scriptsState: $H(),
+	
+	getLoadedScripts: function(){
+		return this.scriptsState.filter(function(state){
+			return state;
+		});
+	},
+
+	scriptLoaded: function(script){
+		this.log('loaded script: ', script);
+		this.scriptsState[script] = true;
+		this.check();
+		var loaded = this.getLoadedScripts();
+		var loadedLength = loaded.getLength();
+		var toLoad = this.scriptsState.getLength();
+		this.fireEvent('scriptLoaded', {
+			script: script,
+			totalLoaded: (loadedLength / toLoad * 100).round(),
+			currentLoaded: ((loadedLength - this.lastLoaded) / (toLoad - this.lastLoaded) * 100).round(),
+			loaded: loaded
+		});
+		if (loadedLength == toLoad) this.lastLoaded = loadedLength;
+	},
+
+	lastLoaded: 0,
+
+	check: function(){
+		var incomplete = [];
+		this.required.each(function(required){
+			var loaded = [];
+			required.scripts.each(function(script){
+				if (this.scriptsState[script]) loaded.push(script);
+			}, this);
+			if (required.onStep){
+				required.onStep({
+					percent: loaded.length / required.scripts.length * 100,
+					scripts: loaded
+				});
+			};
+			if (required.scripts.length != loaded.length) return;
+			required.callback();
+			this.required.erase(required);
+			this.fireEvent('requirementLoaded', [loaded, required]);
+		}, this);
+	}
+
+};
+
+$extend(Depender, new Events);
+$extend(Depender, new Options);
+$extend(Depender, new Log);
+
+Depender._setOptions = Depender.setOptions;
+Depender.setOptions = function(){
+	Depender._setOptions.apply(Depender, arguments);
+	if (this.options.log) Depender.enableLog();
+	return this;
+};
+/*
+---
+
+script: Class.Refactor.js
+
+description: Extends a class onto itself with new property, preserving any items attached to the class's namespace.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Class
+ - /MooTools.More
+
+provides: [Class.refactor]
+
+...
+*/
+
+Class.refactor = function(original, refactors){
+
+	$each(refactors, function(item, name){
+		var origin = original.prototype[name];
+		if (origin && (origin = origin._origin) && typeof item == 'function') original.implement(name, function(){
+			var old = this.previous;
+			this.previous = origin;
+			var value = item.apply(this, arguments);
+			this.previous = old;
+			return value;
+		}); else original.implement(name, item);
+	});
+
+	return original;
+
+};/*
+---
+
+script: Class.Binds.js
+
+description: Automagically binds specified methods in a class to the instance of the class.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Class
+ - /MooTools.More
+
+provides: [Class.Binds]
+
+...
+*/
+
+Class.Mutators.Binds = function(binds){
+    return binds;
+};
+
+Class.Mutators.initialize = function(initialize){
+	return function(){
+		$splat(this.Binds).each(function(name){
+			var original = this[name];
+			if (original) this[name] = original.bind(this);
+		}, this);
+		return initialize.apply(this, arguments);
+	};
+};
+/*
+---
+
+script: Class.Occlude.js
+
+description: Prevents a class from being applied to a DOM element twice.
+
+license: MIT-style license.
+
+authors:
+ - Aaron Newton
+
+requires: 
+ - core/1.2.4/Class
+ - core:1.2.4/Element
+ - /MooTools.More
+
+provides: [Class.Occlude]
+
+...
+*/
+
+Class.Occlude = new Class({
+
+	occlude: function(property, element){
+		element = document.id(element || this.element);
+		var instance = element.retrieve(property || this.property);
+		if (instance && !$defined(this.occluded))
+			return this.occluded = instance;
+
+		this.occluded = false;
+		element.store(property || this.property, this);
+		return this.occluded;
+	}
+
+});/*
+---
+
+script: Chain.Wait.js
+
+description: value, Adds a method to inject pauses between chained events.
+
+license: MIT-style license.
+
+authors:
+ - Aaron Newton
+
+requires: 
+ - core:1.2.4/Chain
+ - core:1.2.4/Element
+ - core:1.2.4/Fx
+ - /MooTools.More
+
+provides: [Chain.Wait]
+
+...
+*/
+
+(function(){
+
+	var wait = {
+		wait: function(duration){
+			return this.chain(function(){
+				this.callChain.delay($pick(duration, 500), this);
+			}.bind(this));
+		}
+	};
+
+	Chain.implement(wait);
+
+	if (window.Fx){
+		Fx.implement(wait);
+		['Css', 'Tween', 'Elements'].each(function(cls){
+			if (Fx[cls]) Fx[cls].implement(wait);
+		});
+	}
+
+	Element.implement({
+		chains: function(effects){
+			$splat($pick(effects, ['tween', 'morph', 'reveal'])).each(function(effect){
+				effect = this.get(effect);
+				if (!effect) return;
+				effect.setOptions({
+					link:'chain'
+				});
+			}, this);
+			return this;
+		},
+		pauseFx: function(duration, effect){
+			this.chains(effect).get($pick(effect, 'tween')).wait(duration);
+			return this;
+		}
+	});
+
+})();/*
+---
+
+script: Array.Extras.js
+
+description: Extends the Array native object to include useful methods to work with arrays.
+
+license: MIT-style license
+
+authors:
+ - Christoph Pojer
+
+requires:
+ - core:1.2.4/Array
+
+provides: [Array.Extras]
+
+...
+*/
+Array.implement({
+
+	min: function(){
+		return Math.min.apply(null, this);
+	},
+
+	max: function(){
+		return Math.max.apply(null, this);
+	},
+
+	average: function(){
+		return this.length ? this.sum() / this.length : 0;
+	},
+
+	sum: function(){
+		var result = 0, l = this.length;
+		if (l){
+			do {
+				result += this[--l];
+			} while (l);
+		}
+		return result;
+	},
+
+	unique: function(){
+		return [].combine(this);
+	},
+
+	shuffle: function(){
+		for (var i = this.length; i && --i;){
+			var temp = this[i], r = Math.floor(Math.random() * ( i + 1 ));
+			this[i] = this[r];
+			this[r] = temp;
+		}
+		return this;
+	}
+
+});/*
+---
+
+script: Date.English.US.js
+
+description: Date messages for US English.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.English.US]
+
+...
+*/
+
+MooTools.lang.set('en-US', 'Date', {
+
+	months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
+	days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
+	//culture's date order: MM/DD/YYYY
+	dateOrder: ['month', 'date', 'year'],
+	shortDate: '%m/%d/%Y',
+	shortTime: '%I:%M%p',
+	AM: 'AM',
+	PM: 'PM',
+
+	/* Date.Extras */
+	ordinal: function(dayOfMonth){
+		//1st, 2nd, 3rd, etc.
+		return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)];
+	},
+
+	lessThanMinuteAgo: 'less than a minute ago',
+	minuteAgo: 'about a minute ago',
+	minutesAgo: '{delta} minutes ago',
+	hourAgo: 'about an hour ago',
+	hoursAgo: 'about {delta} hours ago',
+	dayAgo: '1 day ago',
+	daysAgo: '{delta} days ago',
+	weekAgo: '1 week ago',
+	weeksAgo: '{delta} weeks ago',
+	monthAgo: '1 month ago',
+	monthsAgo: '{delta} months ago',
+	yearAgo: '1 year ago',
+	yearsAgo: '{delta} years ago',
+	lessThanMinuteUntil: 'less than a minute from now',
+	minuteUntil: 'about a minute from now',
+	minutesUntil: '{delta} minutes from now',
+	hourUntil: 'about an hour from now',
+	hoursUntil: 'about {delta} hours from now',
+	dayUntil: '1 day from now',
+	daysUntil: '{delta} days from now',
+	weekUntil: '1 week from now',
+	weeksUntil: '{delta} weeks from now',
+	monthUntil: '1 month from now',
+	monthsUntil: '{delta} months from now',
+	yearUntil: '1 year from now',
+	yearsUntil: '{delta} years from now'
+
+});
+/*
+---
+
+script: Date.js
+
+description: Extends the Date native object to include methods useful in managing dates.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+ - Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/
+ - Harald Kirshner - mail [at] digitarald.de; http://digitarald.de
+ - Scott Kyle - scott [at] appden.com; http://appden.com
+
+requires:
+ - core:1.2.4/Array
+ - core:1.2.4/String
+ - core:1.2.4/Number
+ - core:1.2.4/Lang
+ - core:1.2.4/Date.English.US
+ - /MooTools.More
+
+provides: [Date]
+
+...
+*/
+
+(function(){
+
+var Date = this.Date;
+
+if (!Date.now) Date.now = $time;
+
+Date.Methods = {
+	ms: 'Milliseconds',
+	year: 'FullYear',
+	min: 'Minutes',
+	mo: 'Month',
+	sec: 'Seconds',
+	hr: 'Hours'
+};
+
+['Date', 'Day', 'FullYear', 'Hours', 'Milliseconds', 'Minutes', 'Month', 'Seconds', 'Time', 'TimezoneOffset',
+	'Week', 'Timezone', 'GMTOffset', 'DayOfYear', 'LastMonth', 'LastDayOfMonth', 'UTCDate', 'UTCDay', 'UTCFullYear',
+	'AMPM', 'Ordinal', 'UTCHours', 'UTCMilliseconds', 'UTCMinutes', 'UTCMonth', 'UTCSeconds'].each(function(method){
+	Date.Methods[method.toLowerCase()] = method;
+});
+
+var pad = function(what, length){
+	return new Array(length - String(what).length + 1).join('0') + what;
+};
+
+Date.implement({
+
+	set: function(prop, value){
+		switch ($type(prop)){
+			case 'object':
+				for (var p in prop) this.set(p, prop[p]);
+				break;
+			case 'string':
+				prop = prop.toLowerCase();
+				var m = Date.Methods;
+				if (m[prop]) this['set' + m[prop]](value);
+		}
+		return this;
+	},
+
+	get: function(prop){
+		prop = prop.toLowerCase();
+		var m = Date.Methods;
+		if (m[prop]) return this['get' + m[prop]]();
+		return null;
+	},
+
+	clone: function(){
+		return new Date(this.get('time'));
+	},
+
+	increment: function(interval, times){
+		interval = interval || 'day';
+		times = $pick(times, 1);
+
+		switch (interval){
+			case 'year':
+				return this.increment('month', times * 12);
+			case 'month':
+				var d = this.get('date');
+				this.set('date', 1).set('mo', this.get('mo') + times);
+				return this.set('date', d.min(this.get('lastdayofmonth')));
+			case 'week':
+				return this.increment('day', times * 7);
+			case 'day':
+				return this.set('date', this.get('date') + times);
+		}
+
+		if (!Date.units[interval]) throw new Error(interval + ' is not a supported interval');
+
+		return this.set('time', this.get('time') + times * Date.units[interval]());
+	},
+
+	decrement: function(interval, times){
+		return this.increment(interval, -1 * $pick(times, 1));
+	},
+
+	isLeapYear: function(){
+		return Date.isLeapYear(this.get('year'));
+	},
+
+	clearTime: function(){
+		return this.set({hr: 0, min: 0, sec: 0, ms: 0});
+	},
+
+	diff: function(date, resolution){
+		if ($type(date) == 'string') date = Date.parse(date);
+		
+		return ((date - this) / Date.units[resolution || 'day'](3, 3)).toInt(); // non-leap year, 30-day month
+	},
+
+	getLastDayOfMonth: function(){
+		return Date.daysInMonth(this.get('mo'), this.get('year'));
+	},
+
+	getDayOfYear: function(){
+		return (Date.UTC(this.get('year'), this.get('mo'), this.get('date') + 1) 
+			- Date.UTC(this.get('year'), 0, 1)) / Date.units.day();
+	},
+
+	getWeek: function(){
+		return (this.get('dayofyear') / 7).ceil();
+	},
+	
+	getOrdinal: function(day){
+		return Date.getMsg('ordinal', day || this.get('date'));
+	},
+
+	getTimezone: function(){
+		return this.toString()
+			.replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/, '$1')
+			.replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, '$1$2$3');
+	},
+
+	getGMTOffset: function(){
+		var off = this.get('timezoneOffset');
+		return ((off > 0) ? '-' : '+') + pad((off.abs() / 60).floor(), 2) + pad(off % 60, 2);
+	},
+
+	setAMPM: function(ampm){
+		ampm = ampm.toUpperCase();
+		var hr = this.get('hr');
+		if (hr > 11 && ampm == 'AM') return this.decrement('hour', 12);
+		else if (hr < 12 && ampm == 'PM') return this.increment('hour', 12);
+		return this;
+	},
+
+	getAMPM: function(){
+		return (this.get('hr') < 12) ? 'AM' : 'PM';
+	},
+
+	parse: function(str){
+		this.set('time', Date.parse(str));
+		return this;
+	},
+
+	isValid: function(date) {
+		return !!(date || this).valueOf();
+	},
+
+	format: function(f){
+		if (!this.isValid()) return 'invalid date';
+		f = f || '%x %X';
+		f = formats[f.toLowerCase()] || f; // replace short-hand with actual format
+		var d = this;
+		return f.replace(/%([a-z%])/gi,
+			function($0, $1){
+				switch ($1){
+					case 'a': return Date.getMsg('days')[d.get('day')].substr(0, 3);
+					case 'A': return Date.getMsg('days')[d.get('day')];
+					case 'b': return Date.getMsg('months')[d.get('month')].substr(0, 3);
+					case 'B': return Date.getMsg('months')[d.get('month')];
+					case 'c': return d.toString();
+					case 'd': return pad(d.get('date'), 2);
+					case 'H': return pad(d.get('hr'), 2);
+					case 'I': return ((d.get('hr') % 12) || 12);
+					case 'j': return pad(d.get('dayofyear'), 3);
+					case 'm': return pad((d.get('mo') + 1), 2);
+					case 'M': return pad(d.get('min'), 2);
+					case 'o': return d.get('ordinal');
+					case 'p': return Date.getMsg(d.get('ampm'));
+					case 'S': return pad(d.get('seconds'), 2);
+					case 'U': return pad(d.get('week'), 2);
+					case 'w': return d.get('day');
+					case 'x': return d.format(Date.getMsg('shortDate'));
+					case 'X': return d.format(Date.getMsg('shortTime'));
+					case 'y': return d.get('year').toString().substr(2);
+					case 'Y': return d.get('year');
+					case 'T': return d.get('GMTOffset');
+					case 'Z': return d.get('Timezone');
+				}
+				return $1;
+			}
+		);
+	},
+
+	toISOString: function(){
+		return this.format('iso8601');
+	}
+
+});
+
+Date.alias('toISOString', 'toJSON');
+Date.alias('diff', 'compare');
+Date.alias('format', 'strftime');
+
+var formats = {
+	db: '%Y-%m-%d %H:%M:%S',
+	compact: '%Y%m%dT%H%M%S',
+	iso8601: '%Y-%m-%dT%H:%M:%S%T',
+	rfc822: '%a, %d %b %Y %H:%M:%S %Z',
+	'short': '%d %b %H:%M',
+	'long': '%B %d, %Y %H:%M'
+};
+
+var parsePatterns = [];
+var nativeParse = Date.parse;
+
+var parseWord = function(type, word, num){
+	var ret = -1;
+	var translated = Date.getMsg(type + 's');
+
+	switch ($type(word)){
+		case 'object':
+			ret = translated[word.get(type)];
+			break;
+		case 'number':
+			ret = translated[month - 1];
+			if (!ret) throw new Error('Invalid ' + type + ' index: ' + index);
+			break;
+		case 'string':
+			var match = translated.filter(function(name){
+				return this.test(name);
+			}, new RegExp('^' + word, 'i'));
+			if (!match.length)    throw new Error('Invalid ' + type + ' string');
+			if (match.length > 1) throw new Error('Ambiguous ' + type);
+			ret = match[0];
+	}
+
+	return (num) ? translated.indexOf(ret) : ret;
+};
+
+Date.extend({
+
+	getMsg: function(key, args) {
+		return MooTools.lang.get('Date', key, args);
+	},
+
+	units: {
+		ms: $lambda(1),
+		second: $lambda(1000),
+		minute: $lambda(60000),
+		hour: $lambda(3600000),
+		day: $lambda(86400000),
+		week: $lambda(608400000),
+		month: function(month, year){
+			var d = new Date;
+			return Date.daysInMonth($pick(month, d.get('mo')), $pick(year, d.get('year'))) * 86400000;
+		},
+		year: function(year){
+			year = year || new Date().get('year');
+			return Date.isLeapYear(year) ? 31622400000 : 31536000000;
+		}
+	},
+
+	daysInMonth: function(month, year){
+		return [31, Date.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
+	},
+
+	isLeapYear: function(year){
+		return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0);
+	},
+
+	parse: function(from){
+		var t = $type(from);
+		if (t == 'number') return new Date(from);
+		if (t != 'string') return from;
+		from = from.clean();
+		if (!from.length) return null;
+
+		var parsed;
+		parsePatterns.some(function(pattern){
+			var bits = pattern.re.exec(from);
+			return (bits) ? (parsed = pattern.handler(bits)) : false;
+		});
+
+		return parsed || new Date(nativeParse(from));
+	},
+
+	parseDay: function(day, num){
+		return parseWord('day', day, num);
+	},
+
+	parseMonth: function(month, num){
+		return parseWord('month', month, num);
+	},
+
+	parseUTC: function(value){
+		var localDate = new Date(value);
+		var utcSeconds = Date.UTC(
+			localDate.get('year'),
+			localDate.get('mo'),
+			localDate.get('date'),
+			localDate.get('hr'),
+			localDate.get('min'),
+			localDate.get('sec')
+		);
+		return new Date(utcSeconds);
+	},
+
+	orderIndex: function(unit){
+		return Date.getMsg('dateOrder').indexOf(unit) + 1;
+	},
+
+	defineFormat: function(name, format){
+		formats[name] = format;
+	},
+
+	defineFormats: function(formats){
+		for (var name in formats) Date.defineFormat(name, formats[name]);
+	},
+
+	parsePatterns: parsePatterns, // this is deprecated
+	
+	defineParser: function(pattern){
+		parsePatterns.push((pattern.re && pattern.handler) ? pattern : build(pattern));
+	},
+	
+	defineParsers: function(){
+		Array.flatten(arguments).each(Date.defineParser);
+	},
+	
+	define2DigitYearStart: function(year){
+		startYear = year % 100;
+		startCentury = year - startYear;
+	}
+
+});
+
+var startCentury = 1900;
+var startYear = 70;
+
+var regexOf = function(type){
+	return new RegExp('(?:' + Date.getMsg(type).map(function(name){
+		return name.substr(0, 3);
+	}).join('|') + ')[a-z]*');
+};
+
+var replacers = function(key){
+	switch(key){
+		case 'x': // iso8601 covers yyyy-mm-dd, so just check if month is first
+			return ((Date.orderIndex('month') == 1) ? '%m[.-/]%d' : '%d[.-/]%m') + '([.-/]%y)?';
+		case 'X':
+			return '%H([.:]%M)?([.:]%S([.:]%s)?)? ?%p? ?%T?';
+	}
+	return null;
+};
+
+var keys = {
+	d: /[0-2]?[0-9]|3[01]/,
+	H: /[01]?[0-9]|2[0-3]/,
+	I: /0?[1-9]|1[0-2]/,
+	M: /[0-5]?\d/,
+	s: /\d+/,
+	o: /[a-z]*/,
+	p: /[ap]\.?m\.?/,
+	y: /\d{2}|\d{4}/,
+	Y: /\d{4}/,
+	T: /Z|[+-]\d{2}(?::?\d{2})?/
+};
+
+keys.m = keys.I;
+keys.S = keys.M;
+
+var currentLanguage;
+
+var recompile = function(language){
+	currentLanguage = language;
+	
+	keys.a = keys.A = regexOf('days');
+	keys.b = keys.B = regexOf('months');
+	
+	parsePatterns.each(function(pattern, i){
+		if (pattern.format) parsePatterns[i] = build(pattern.format);
+	});
+};
+
+var build = function(format){
+	if (!currentLanguage) return {format: format};
+	
+	var parsed = [];
+	var re = (format.source || format) // allow format to be regex
+	 .replace(/%([a-z])/gi,
+		function($0, $1){
+			return replacers($1) || $0;
+		}
+	).replace(/\((?!\?)/g, '(?:') // make all groups non-capturing
+	 .replace(/ (?!\?|\*)/g, ',? ') // be forgiving with spaces and commas
+	 .replace(/%([a-z%])/gi,
+		function($0, $1){
+			var p = keys[$1];
+			if (!p) return $1;
+			parsed.push($1);
+			return '(' + p.source + ')';
+		}
+	).replace(/\[a-z\]/gi, '[a-z\\u00c0-\\uffff]'); // handle unicode words
+
+	return {
+		format: format,
+		re: new RegExp('^' + re + '$', 'i'),
+		handler: function(bits){
+			bits = bits.slice(1).associate(parsed);
+			var date = new Date().clearTime();
+			if ('d' in bits) handle.call(date, 'd', 1);
+			if ('m' in bits || 'b' in bits || 'B' in bits) handle.call(date, 'm', 1);
+			for (var key in bits) handle.call(date, key, bits[key]);
+			return date;
+		}
+	};
+};
+
+var handle = function(key, value){
+	if (!value) return this;
+
+	switch(key){
+		case 'a': case 'A': return this.set('day', Date.parseDay(value, true));
+		case 'b': case 'B': return this.set('mo', Date.parseMonth(value, true));
+		case 'd': return this.set('date', value);
+		case 'H': case 'I': return this.set('hr', value);
+		case 'm': return this.set('mo', value - 1);
+		case 'M': return this.set('min', value);
+		case 'p': return this.set('ampm', value.replace(/\./g, ''));
+		case 'S': return this.set('sec', value);
+		case 's': return this.set('ms', ('0.' + value) * 1000);
+		case 'w': return this.set('day', value);
+		case 'Y': return this.set('year', value);
+		case 'y':
+			value = +value;
+			if (value < 100) value += startCentury + (value < startYear ? 100 : 0);
+			return this.set('year', value);
+		case 'T':
+			if (value == 'Z') value = '+00';
+			var offset = value.match(/([+-])(\d{2}):?(\d{2})?/);
+			offset = (offset[1] + '1') * (offset[2] * 60 + (+offset[3] || 0)) + this.getTimezoneOffset();
+			return this.set('time', this - offset * 60000);
+	}
+
+	return this;
+};
+
+Date.defineParsers(
+	'%Y([-./]%m([-./]%d((T| )%X)?)?)?', // "1999-12-31", "1999-12-31 11:59pm", "1999-12-31 23:59:59", ISO8601
+	'%Y%m%d(T%H(%M%S?)?)?', // "19991231", "19991231T1159", compact
+	'%x( %X)?', // "12/31", "12.31.99", "12-31-1999", "12/31/2008 11:59 PM"
+	'%d%o( %b( %Y)?)?( %X)?', // "31st", "31st December", "31 Dec 1999", "31 Dec 1999 11:59pm"
+	'%b( %d%o)?( %Y)?( %X)?', // Same as above with month and day switched
+	'%Y %b( %d%o( %X)?)?', // Same as above with year coming first
+	'%o %b %d %X %T %Y' // "Thu Oct 22 08:11:23 +0000 2009"
+);
+
+MooTools.lang.addEvent('langChange', function(language){
+	if (MooTools.lang.get('Date')) recompile(language);
+}).fireEvent('langChange', MooTools.lang.getCurrentLanguage());
+
+})();/*
+---
+
+script: Date.Extras.js
+
+description: Extends the Date native object to include extra methods (on top of those in Date.js).
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+ - Scott Kyle
+
+requires:
+ - /Date
+
+provides: [Date.Extras]
+
+...
+*/
+
+Date.implement({
+
+	timeDiffInWords: function(relative_to){
+		return Date.distanceOfTimeInWords(this, relative_to || new Date);
+	},
+
+	timeDiff: function(to, joiner){
+		if (to == null) to = new Date;
+		var delta = ((to - this) / 1000).toInt();
+		if (!delta) return '0s';
+		
+		var durations = {s: 60, m: 60, h: 24, d: 365, y: 0};
+		var duration, vals = [];
+		
+		for (var step in durations){
+			if (!delta) break;
+			if ((duration = durations[step])){
+				vals.unshift((delta % duration) + step);
+				delta = (delta / duration).toInt();
+			} else {
+				vals.unshift(delta + step);
+			}
+		}
+		
+		return vals.join(joiner || ':');
+	}
+
+});
+
+Date.alias('timeDiffInWords', 'timeAgoInWords');
+
+Date.extend({
+
+	distanceOfTimeInWords: function(from, to){
+		return Date.getTimePhrase(((to - from) / 1000).toInt());
+	},
+
+	getTimePhrase: function(delta){
+		var suffix = (delta < 0) ? 'Until' : 'Ago';
+		if (delta < 0) delta *= -1;
+		
+		var units = {
+			minute: 60,
+			hour: 60,
+			day: 24,
+			week: 7,
+			month: 52 / 12,
+			year: 12,
+			eon: Infinity
+		};
+		
+		var msg = 'lessThanMinute';
+		
+		for (var unit in units){
+			var interval = units[unit];
+			if (delta < 1.5 * interval){
+				if (delta > 0.75 * interval) msg = unit;
+				break;
+			}
+			delta /= interval;
+			msg = unit + 's';
+		}
+		
+		return Date.getMsg(msg + suffix).substitute({delta: delta.round()});
+	}
+
+});
+
+
+Date.defineParsers(
+
+	{
+		// "today", "tomorrow", "yesterday"
+		re: /^(?:tod|tom|yes)/i,
+		handler: function(bits){
+			var d = new Date().clearTime();
+			switch(bits[0]){
+				case 'tom': return d.increment();
+				case 'yes': return d.decrement();
+				default: 	return d;
+			}
+		}
+	},
+
+	{
+		// "next Wednesday", "last Thursday"
+		re: /^(next|last) ([a-z]+)$/i,
+		handler: function(bits){
+			var d = new Date().clearTime();
+			var day = d.getDay();
+			var newDay = Date.parseDay(bits[2], true);
+			var addDays = newDay - day;
+			if (newDay <= day) addDays += 7;
+			if (bits[1] == 'last') addDays -= 7;
+			return d.set('date', d.getDate() + addDays);
+		}
+	}
+
+);
+/*
+---
+
+script: Hash.Extras.js
+
+description: Extends the Hash native object to include getFromPath which allows a path notation to child elements.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Hash.base
+ - /MooTools.More
+
+provides: [Hash.Extras]
+
+...
+*/
+
+Hash.implement({
+
+	getFromPath: function(notation){
+		var source = this.getClean();
+		notation.replace(/\[([^\]]+)\]|\.([^.[]+)|[^[.]+/g, function(match){
+			if (!source) return null;
+			var prop = arguments[2] || arguments[1] || arguments[0];
+			source = (prop in source) ? source[prop] : null;
+			return match;
+		});
+		return source;
+	},
+
+	cleanValues: function(method){
+		method = method || $defined;
+		this.each(function(v, k){
+			if (!method(v)) this.erase(k);
+		}, this);
+		return this;
+	},
+
+	run: function(){
+		var args = arguments;
+		this.each(function(v, k){
+			if ($type(v) == 'function') v.run(args);
+		});
+	}
+
+});/*
+---
+
+script: String.Extras.js
+
+description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+ - Guillermo Rauch
+
+requires:
+ - core:1.2.4/String
+ - core:1.2.4/$util
+ - core:1.2.4/Array
+
+provides: [String.Extras]
+
+...
+*/
+
+(function(){
+  
+var special = ['À','à','Ã?','á','Â','â','Ã','ã','Ä','ä','Ã…','Ã¥','Ä‚','ă','Ä„','Ä…','Ć','ć','ÄŒ','Ä?','Ç','ç', 'ÄŽ','Ä?','Ä?','Ä‘', 'È','è','É','é','Ê','ê','Ë','ë','Äš','Ä›','Ę','Ä™', 'Äž','ÄŸ','ÃŒ','ì','Ã?','í','ÃŽ','î','Ã?','ï', 'Ĺ','ĺ','Ľ','ľ','Å?','Å‚', 'Ñ','ñ','Ň','ň','Ń','Å„','Ã’','ò','Ó','ó','Ô','ô','Õ','õ','Ö','ö','Ø','ø','Å‘','Ř','Å™','Å”','Å•','Å ','Å¡','Åž','ÅŸ','Åš','Å›', 'Ť','Å¥','Ť','Å¥','Å¢','Å£','Ù','ù','Ú','ú','Û','û','Ãœ','ü','Å®','ů', 'Ÿ','ÿ','ý','Ã?','Ž','ž','Ź','ź','Å»','ż', 'Þ','þ','Ã?','ð','ß','Å’','Å“','Æ','æ','µ'];
+
+var standard = ['A','a','A','a','A','a','A','a','Ae','ae','A','a','A','a','A','a','C','c','C','c','C','c','D','d','D','d', 'E','e','E','e','E','e','E','e','E','e','E','e','G','g','I','i','I','i','I','i','I','i','L','l','L','l','L','l', 'N','n','N','n','N','n', 'O','o','O','o','O','o','O','o','Oe','oe','O','o','o', 'R','r','R','r', 'S','s','S','s','S','s','T','t','T','t','T','t', 'U','u','U','u','U','u','Ue','ue','U','u','Y','y','Y','y','Z','z','Z','z','Z','z','TH','th','DH','dh','ss','OE','oe','AE','ae','u'];
+
+var tidymap = {
+	"[\xa0\u2002\u2003\u2009]": " ",
+	"\xb7": "*",
+	"[\u2018\u2019]": "'",
+	"[\u201c\u201d]": '"',
+	"\u2026": "...",
+	"\u2013": "-",
+	"\u2014": "--",
+	"\uFFFD": "&raquo;"
+};
+
+var getRegForTag = function(tag, contents) {
+	tag = tag || '';
+	var regstr = contents ? "<" + tag + "[^>]*>([\\s\\S]*?)<\/" + tag + ">" : "<\/?" + tag + "([^>]+)?>";
+	reg = new RegExp(regstr, "gi");
+	return reg;
+};
+
+String.implement({
+
+	standardize: function(){
+		var text = this;
+		special.each(function(ch, i){
+			text = text.replace(new RegExp(ch, 'g'), standard[i]);
+		});
+		return text;
+	},
+
+	repeat: function(times){
+		return new Array(times + 1).join(this);
+	},
+
+	pad: function(length, str, dir){
+		if (this.length >= length) return this;
+		var pad = (str == null ? ' ' : '' + str).repeat(length - this.length).substr(0, length - this.length);
+		if (!dir || dir == 'right') return this + pad;
+		if (dir == 'left') return pad + this;
+		return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil());
+	},
+
+	getTags: function(tag, contents){
+		return this.match(getRegForTag(tag, contents)) || [];
+	},
+
+	stripTags: function(tag, contents){
+		return this.replace(getRegForTag(tag, contents), '');
+	},
+
+	tidy: function(){
+		var txt = this.toString();
+		$each(tidymap, function(value, key){
+			txt = txt.replace(new RegExp(key, 'g'), value);
+		});
+		return txt;
+	}
+
+});
+
+})();/*
+---
+
+script: String.QueryString.js
+
+description: Methods for dealing with URI query strings.
+
+license: MIT-style license
+
+authors:
+ - Sebastian Markbåge, Aaron Newton, Lennart Pilon, Valerio Proietti
+
+requires:
+ - core:1.2.4/Array
+ - core:1.2.4/String
+ - /MooTools.More
+
+provides: [String.QueryString]
+
+...
+*/
+
+String.implement({
+
+	parseQueryString: function(){
+		var vars = this.split(/[&;]/), res = {};
+		if (vars.length) vars.each(function(val){
+			var index = val.indexOf('='),
+				keys = index < 0 ? [''] : val.substr(0, index).match(/[^\]\[]+/g),
+				value = decodeURIComponent(val.substr(index + 1)),
+				obj = res;
+			keys.each(function(key, i){
+				var current = obj[key];
+				if(i < keys.length - 1)
+					obj = obj[key] = current || {};
+				else if($type(current) == 'array')
+					current.push(value);
+				else
+					obj[key] = $defined(current) ? [current, value] : value;
+			});
+		});
+		return res;
+	},
+
+	cleanQueryString: function(method){
+		return this.split('&').filter(function(val){
+			var index = val.indexOf('='),
+			key = index < 0 ? '' : val.substr(0, index),
+			value = val.substr(index + 1);
+			return method ? method.run([key, value]) : $chk(value);
+		}).join('&');
+	}
+
+});/*
+---
+
+script: URI.js
+
+description: Provides methods useful in managing the window location and uris.
+
+license: MIT-style license
+
+authors:
+ - Sebastian Markbåge
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Selectors
+ - /String.QueryString
+
+provides: URI
+
+...
+*/
+
+var URI = new Class({
+
+	Implements: Options,
+
+	options: {
+		/*base: false*/
+	},
+
+	regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/,
+	parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'],
+	schemes: {http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0},
+
+	initialize: function(uri, options){
+		this.setOptions(options);
+		var base = this.options.base || URI.base;
+		if(!uri) uri = base;
+		
+		if (uri && uri.parsed) this.parsed = $unlink(uri.parsed);
+		else this.set('value', uri.href || uri.toString(), base ? new URI(base) : false);
+	},
+
+	parse: function(value, base){
+		var bits = value.match(this.regex);
+		if (!bits) return false;
+		bits.shift();
+		return this.merge(bits.associate(this.parts), base);
+	},
+
+	merge: function(bits, base){
+		if ((!bits || !bits.scheme) && (!base || !base.scheme)) return false;
+		if (base){
+			this.parts.every(function(part){
+				if (bits[part]) return false;
+				bits[part] = base[part] || '';
+				return true;
+			});
+		}
+		bits.port = bits.port || this.schemes[bits.scheme.toLowerCase()];
+		bits.directory = bits.directory ? this.parseDirectory(bits.directory, base ? base.directory : '') : '/';
+		return bits;
+	},
+
+	parseDirectory: function(directory, baseDirectory) {
+		directory = (directory.substr(0, 1) == '/' ? '' : (baseDirectory || '/')) + directory;
+		if (!directory.test(URI.regs.directoryDot)) return directory;
+		var result = [];
+		directory.replace(URI.regs.endSlash, '').split('/').each(function(dir){
+			if (dir == '..' && result.length > 0) result.pop();
+			else if (dir != '.') result.push(dir);
+		});
+		return result.join('/') + '/';
+	},
+
+	combine: function(bits){
+		return bits.value || bits.scheme + '://' +
+			(bits.user ? bits.user + (bits.password ? ':' + bits.password : '') + '@' : '') +
+			(bits.host || '') + (bits.port && bits.port != this.schemes[bits.scheme] ? ':' + bits.port : '') +
+			(bits.directory || '/') + (bits.file || '') +
+			(bits.query ? '?' + bits.query : '') +
+			(bits.fragment ? '#' + bits.fragment : '');
+	},
+
+	set: function(part, value, base){
+		if (part == 'value'){
+			var scheme = value.match(URI.regs.scheme);
+			if (scheme) scheme = scheme[1];
+			if (scheme && !$defined(this.schemes[scheme.toLowerCase()])) this.parsed = { scheme: scheme, value: value };
+			else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value });
+		} else if (part == 'data') {
+			this.setData(value);
+		} else {
+			this.parsed[part] = value;
+		}
+		return this;
+	},
+
+	get: function(part, base){
+		switch(part){
+			case 'value': return this.combine(this.parsed, base ? base.parsed : false);
+			case 'data' : return this.getData();
+		}
+		return this.parsed[part] || '';
+	},
+
+	go: function(){
+		document.location.href = this.toString();
+	},
+
+	toURI: function(){
+		return this;
+	},
+
+	getData: function(key, part){
+		var qs = this.get(part || 'query');
+		if (!$chk(qs)) return key ? null : {};
+		var obj = qs.parseQueryString();
+		return key ? obj[key] : obj;
+	},
+
+	setData: function(values, merge, part){
+		if (typeof values == 'string'){
+			data = this.getData();
+			data[arguments[0]] = arguments[1];
+			values = data;
+		} else if (merge) {
+			values = $merge(this.getData(), values);
+		}
+		return this.set(part || 'query', Hash.toQueryString(values));
+	},
+
+	clearData: function(part){
+		return this.set(part || 'query', '');
+	}
+
+});
+
+URI.prototype.toString = URI.prototype.valueOf = function(){
+	return this.get('value');
+};
+
+URI.regs = {
+	endSlash: /\/$/,
+	scheme: /^(\w+):/,
+	directoryDot: /\.\/|\.$/
+};
+
+URI.base = new URI(document.getElements('base[href]', true).getLast(), {base: document.location});
+
+String.implement({
+
+	toURI: function(options){
+		return new URI(this, options);
+	}
+
+});/*
+---
+
+script: URI.Relative.js
+
+description: Extends the URI class to add methods for computing relative and absolute urls.
+
+license: MIT-style license
+
+authors:
+ - Sebastian Markbåge
+
+
+requires:
+ - /Class.refactor
+ - /URI
+
+provides: [URI.Relative]
+
+...
+*/
+
+URI = Class.refactor(URI, {
+
+	combine: function(bits, base){
+		if (!base || bits.scheme != base.scheme || bits.host != base.host || bits.port != base.port)
+			return this.previous.apply(this, arguments);
+		var end = bits.file + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : '');
+
+		if (!base.directory) return (bits.directory || (bits.file ? '' : './')) + end;
+
+		var baseDir = base.directory.split('/'),
+			relDir = bits.directory.split('/'),
+			path = '',
+			offset;
+
+		var i = 0;
+		for(offset = 0; offset < baseDir.length && offset < relDir.length && baseDir[offset] == relDir[offset]; offset++);
+		for(i = 0; i < baseDir.length - offset - 1; i++) path += '../';
+		for(i = offset; i < relDir.length - 1; i++) path += relDir[i] + '/';
+
+		return (path || (bits.file ? '' : './')) + end;
+	},
+
+	toAbsolute: function(base){
+		base = new URI(base);
+		if (base) base.set('directory', '').set('file', '');
+		return this.toRelative(base);
+	},
+
+	toRelative: function(base){
+		return this.get('value', new URI(base));
+	}
+
+});/*
+---
+
+script: Element.Forms.js
+
+description: Extends the Element native object to include methods useful in managing inputs.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element
+ - /MooTools.More
+
+provides: [Element.Forms]
+
+...
+*/
+
+Element.implement({
+
+	tidy: function(){
+		this.set('value', this.get('value').tidy());
+	},
+
+	getTextInRange: function(start, end){
+		return this.get('value').substring(start, end);
+	},
+
+	getSelectedText: function(){
+		if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
+		return document.selection.createRange().text;
+	},
+
+	getSelectedRange: function() {
+		if ($defined(this.selectionStart)) return {start: this.selectionStart, end: this.selectionEnd};
+		var pos = {start: 0, end: 0};
+		var range = this.getDocument().selection.createRange();
+		if (!range || range.parentElement() != this) return pos;
+		var dup = range.duplicate();
+		if (this.type == 'text') {
+			pos.start = 0 - dup.moveStart('character', -100000);
+			pos.end = pos.start + range.text.length;
+		} else {
+			var value = this.get('value');
+			var offset = value.length;
+			dup.moveToElementText(this);
+			dup.setEndPoint('StartToEnd', range);
+			if(dup.text.length) offset -= value.match(/[\n\r]*$/)[0].length;
+			pos.end = offset - dup.text.length;
+			dup.setEndPoint('StartToStart', range);
+			pos.start = offset - dup.text.length;
+		}
+		return pos;
+	},
+
+	getSelectionStart: function(){
+		return this.getSelectedRange().start;
+	},
+
+	getSelectionEnd: function(){
+		return this.getSelectedRange().end;
+	},
+
+	setCaretPosition: function(pos){
+		if (pos == 'end') pos = this.get('value').length;
+		this.selectRange(pos, pos);
+		return this;
+	},
+
+	getCaretPosition: function(){
+		return this.getSelectedRange().start;
+	},
+
+	selectRange: function(start, end){
+		if (this.setSelectionRange) {
+			this.focus();
+			this.setSelectionRange(start, end);
+		} else {
+			var value = this.get('value');
+			var diff = value.substr(start, end - start).replace(/\r/g, '').length;
+			start = value.substr(0, start).replace(/\r/g, '').length;
+			var range = this.createTextRange();
+			range.collapse(true);
+			range.moveEnd('character', start + diff);
+			range.moveStart('character', start);
+			range.select();
+		}
+		return this;
+	},
+
+	insertAtCursor: function(value, select){
+		var pos = this.getSelectedRange();
+		var text = this.get('value');
+		this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length));
+		if ($pick(select, true)) this.selectRange(pos.start, pos.start + value.length);
+		else this.setCaretPosition(pos.start + value.length);
+		return this;
+	},
+
+	insertAroundCursor: function(options, select){
+		options = $extend({
+			before: '',
+			defaultMiddle: '',
+			after: ''
+		}, options);
+		var value = this.getSelectedText() || options.defaultMiddle;
+		var pos = this.getSelectedRange();
+		var text = this.get('value');
+		if (pos.start == pos.end){
+			this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length));
+			this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length);
+		} else {
+			var current = text.substring(pos.start, pos.end);
+			this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length));
+			var selStart = pos.start + options.before.length;
+			if ($pick(select, true)) this.selectRange(selStart, selStart + current.length);
+			else this.setCaretPosition(selStart + text.length);
+		}
+		return this;
+	}
+
+});/*
+---
+
+script: Elements.From.js
+
+description: Returns a collection of elements from a string of html.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element
+ - /MooTools.More
+
+provides: [Elements.from]
+
+...
+*/
+
+Elements.from = function(text, excludeScripts){
+	if ($pick(excludeScripts, true)) text = text.stripScripts();
+
+	var container, match = text.match(/^\s*<(t[dhr]|tbody|tfoot|thead)/i);
+
+	if (match){
+		container = new Element('table');
+		var tag = match[1].toLowerCase();
+		if (['td', 'th', 'tr'].contains(tag)){
+			container = new Element('tbody').inject(container);
+			if (tag != 'tr') container = new Element('tr').inject(container);
+		}
+	}
+
+	return (container || new Element('div')).set('html', text).getChildren();
+};/*
+---
+
+script: Element.Delegation.js
+
+description: Extends the Element native object to include the delegate method for more efficient event management.
+
+credits:
+ - "Event checking based on the work of Daniel Steigerwald. License: MIT-style license.	Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+ - Daniel Steigerwald
+
+requires:
+ - core:1.2.4/Element.Event
+ - core:1.2.4/Selectors
+ - /MooTools.More
+
+provides: [Element.Delegation]
+
+...
+*/
+
+(function(addEvent, removeEvent){
+	
+	var match = /(.*?):relay\(([^)]+)\)$/,
+		combinators = /[+>~\s]/,
+		splitType = function(type){
+			var bits = type.match(match);
+			return !bits ? {event: type} : {
+				event: bits[1],
+				selector: bits[2]
+			};
+		},
+		check = function(e, selector){
+			var t = e.target;
+			if (combinators.test(selector = selector.trim())){
+				var els = this.getElements(selector);
+				for (var i = els.length; i--; ){
+					var el = els[i];
+					if (t == el || el.hasChild(t)) return el;
+				}
+			} else {
+				for ( ; t && t != this; t = t.parentNode){
+					if (Element.match(t, selector)) return document.id(t);
+				}
+			}
+			return null;
+		};
+
+	Element.implement({
+
+		addEvent: function(type, fn){
+			var splitted = splitType(type);
+			if (splitted.selector){
+				var monitors = this.retrieve('$moo:delegateMonitors', {});
+				if (!monitors[type]){
+					var monitor = function(e){
+						var el = check.call(this, e, splitted.selector);
+						if (el) this.fireEvent(type, [e, el], 0, el);
+					}.bind(this);
+					monitors[type] = monitor;
+					addEvent.call(this, splitted.event, monitor);
+				}
+			}
+			return addEvent.apply(this, arguments);
+		},
+
+		removeEvent: function(type, fn){
+			var splitted = splitType(type);
+			if (splitted.selector){
+				var events = this.retrieve('events');
+				if (!events || !events[type] || (fn && !events[type].keys.contains(fn))) return this;
+
+				if (fn) removeEvent.apply(this, [type, fn]);
+				else removeEvent.apply(this, type);
+
+				events = this.retrieve('events');
+				if (events && events[type] && events[type].keys.length == 0){
+					var monitors = this.retrieve('$moo:delegateMonitors', {});
+					removeEvent.apply(this, [splitted.event, monitors[type]]);
+					delete monitors[type];
+				}
+				return this;
+			}
+			return removeEvent.apply(this, arguments);
+		},
+
+		fireEvent: function(type, args, delay, bind){
+			var events = this.retrieve('events');
+			if (!events || !events[type]) return this;
+			events[type].keys.each(function(fn){
+				fn.create({bind: bind || this, delay: delay, arguments: args})();
+			}, this);
+			return this;
+		}
+
+	});
+
+})(Element.prototype.addEvent, Element.prototype.removeEvent);/*
+---
+
+script: Element.Measure.js
+
+description: Extends the Element native object to include methods useful in measuring dimensions.
+
+credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz"
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element.Style
+ - core:1.2.4/Element.Dimensions
+ - /MooTools.More
+
+provides: [Element.Measure]
+
+...
+*/
+
+Element.implement({
+
+	measure: function(fn){
+		var vis = function(el) {
+			return !!(!el || el.offsetHeight || el.offsetWidth);
+		};
+		if (vis(this)) return fn.apply(this);
+		var parent = this.getParent(),
+			restorers = [],
+			toMeasure = []; 
+		while (!vis(parent) && parent != document.body) {
+			toMeasure.push(parent.expose());
+			parent = parent.getParent();
+		}
+		var restore = this.expose();
+		var result = fn.apply(this);
+		restore();
+		toMeasure.each(function(restore){
+			restore();
+		});
+		return result;
+	},
+
+	expose: function(){
+		if (this.getStyle('display') != 'none') return $empty;
+		var before = this.style.cssText;
+		this.setStyles({
+			display: 'block',
+			position: 'absolute',
+			visibility: 'hidden'
+		});
+		return function(){
+			this.style.cssText = before;
+		}.bind(this);
+	},
+
+	getDimensions: function(options){
+		options = $merge({computeSize: false},options);
+		var dim = {};
+		var getSize = function(el, options){
+			return (options.computeSize)?el.getComputedSize(options):el.getSize();
+		};
+		var parent = this.getParent('body');
+		if (parent && this.getStyle('display') == 'none'){
+			dim = this.measure(function(){
+				return getSize(this, options);
+			});
+		} else if (parent){
+			try { //safari sometimes crashes here, so catch it
+				dim = getSize(this, options);
+			}catch(e){}
+		} else {
+			dim = {x: 0, y: 0};
+		}
+		return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height});
+	},
+
+	getComputedSize: function(options){
+		options = $merge({
+			styles: ['padding','border'],
+			plains: {
+				height: ['top','bottom'],
+				width: ['left','right']
+			},
+			mode: 'both'
+		}, options);
+		var size = {width: 0,height: 0};
+		switch (options.mode){
+			case 'vertical':
+				delete size.width;
+				delete options.plains.width;
+				break;
+			case 'horizontal':
+				delete size.height;
+				delete options.plains.height;
+				break;
+		}
+		var getStyles = [];
+		//this function might be useful in other places; perhaps it should be outside this function?
+		$each(options.plains, function(plain, key){
+			plain.each(function(edge){
+				options.styles.each(function(style){
+					getStyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge);
+				});
+			});
+		});
+		var styles = {};
+		getStyles.each(function(style){ styles[style] = this.getComputedStyle(style); }, this);
+		var subtracted = [];
+		$each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom']
+			var capitalized = key.capitalize();
+			size['total' + capitalized] = size['computed' + capitalized] = 0;
+			plain.each(function(edge){ //top, left, right, bottom
+				size['computed' + edge.capitalize()] = 0;
+				getStyles.each(function(style, i){ //padding, border, etc.
+					//'padding-left'.test('left') size['totalWidth'] = size['width'] + [padding-left]
+					if (style.test(edge)){
+						styles[style] = styles[style].toInt() || 0; //styles['padding-left'] = 5;
+						size['total' + capitalized] = size['total' + capitalized] + styles[style];
+						size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style];
+					}
+					//if width != width (so, padding-left, for instance), then subtract that from the total
+					if (style.test(edge) && key != style &&
+						(style.test('border') || style.test('padding')) && !subtracted.contains(style)){
+						subtracted.push(style);
+						size['computed' + capitalized] = size['computed' + capitalized]-styles[style];
+					}
+				});
+			});
+		});
+
+		['Width', 'Height'].each(function(value){
+			var lower = value.toLowerCase();
+			if(!$chk(size[lower])) return;
+
+			size[lower] = size[lower] + this['offset' + value] + size['computed' + value];
+			size['total' + value] = size[lower] + size['total' + value];
+			delete size['computed' + value];
+		}, this);
+
+		return $extend(styles, size);
+	}
+
+});/*
+---
+
+script: Element.Pin.js
+
+description: Extends the Element native object to include the pin method useful for fixed positioning for elements.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element.Event
+ - core:1.2.4/Element.Dimensions
+ - core:1.2.4/Element.Style
+ - /MooTools.More
+
+provides: [Element.Pin]
+
+...
+*/
+
+(function(){
+	var supportsPositionFixed = false;
+	window.addEvent('domready', function(){
+		var test = new Element('div').setStyles({
+			position: 'fixed',
+			top: 0,
+			right: 0
+		}).inject(document.body);
+		supportsPositionFixed = (test.offsetTop === 0);
+		test.dispose();
+	});
+
+	Element.implement({
+
+		pin: function(enable){
+			if (this.getStyle('display') == 'none') return null;
+			
+			var p,
+					scroll = window.getScroll();
+			if (enable !== false){
+				p = this.getPosition();
+				if (!this.retrieve('pinned')){
+					var pos = {
+						top: p.y - scroll.y,
+						left: p.x - scroll.x
+					};
+					if (supportsPositionFixed){
+						this.setStyle('position', 'fixed').setStyles(pos);
+					} else {
+						this.store('pinnedByJS', true);
+						this.setStyles({
+							position: 'absolute',
+							top: p.y,
+							left: p.x
+						}).addClass('isPinned');
+						this.store('scrollFixer', (function(){
+							if (this.retrieve('pinned'))
+								var scroll = window.getScroll();
+								this.setStyles({
+									top: pos.top.toInt() + scroll.y,
+									left: pos.left.toInt() + scroll.x
+								});
+						}).bind(this));
+						window.addEvent('scroll', this.retrieve('scrollFixer'));
+					}
+					this.store('pinned', true);
+				}
+			} else {
+				var op;
+				if (!Browser.Engine.trident){
+					var parent = this.getParent();
+					op = (parent.getComputedStyle('position') != 'static' ? parent : parent.getOffsetParent());
+				}
+				p = this.getPosition(op);
+				this.store('pinned', false);
+				var reposition;
+				if (supportsPositionFixed && !this.retrieve('pinnedByJS')){
+					reposition = {
+						top: p.y + scroll.y,
+						left: p.x + scroll.x
+					};
+				} else {
+					this.store('pinnedByJS', false);
+					window.removeEvent('scroll', this.retrieve('scrollFixer'));
+					reposition = {
+						top: p.y,
+						left: p.x
+					};
+				}
+				this.setStyles($merge(reposition, {position: 'absolute'})).removeClass('isPinned');
+			}
+			return this;
+		},
+
+		unpin: function(){
+			return this.pin(false);
+		},
+
+		togglepin: function(){
+			this.pin(!this.retrieve('pinned'));
+		}
+
+	});
+
+})();/*
+---
+
+script: Element.Position.js
+
+description: Extends the Element native object to include methods useful positioning elements relative to others.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element.Dimensions
+ - /Element.Measure
+
+provides: [Elements.Position]
+
+...
+*/
+
+(function(){
+
+var original = Element.prototype.position;
+
+Element.implement({
+
+	position: function(options){
+		//call original position if the options are x/y values
+		if (options && ($defined(options.x) || $defined(options.y))) return original ? original.apply(this, arguments) : this;
+		$each(options||{}, function(v, k){ if (!$defined(v)) delete options[k]; });
+		options = $merge({
+			// minimum: { x: 0, y: 0 },
+			// maximum: { x: 0, y: 0},
+			relativeTo: document.body,
+			position: {
+				x: 'center', //left, center, right
+				y: 'center' //top, center, bottom
+			},
+			edge: false,
+			offset: {x: 0, y: 0},
+			returnPos: false,
+			relFixedPosition: false,
+			ignoreMargins: false,
+			ignoreScroll: false,
+			allowNegative: false
+		}, options);
+		//compute the offset of the parent positioned element if this element is in one
+		var parentOffset = {x: 0, y: 0}, 
+				parentPositioned = false;
+		/* dollar around getOffsetParent should not be necessary, but as it does not return
+		 * a mootools extended element in IE, an error occurs on the call to expose. See:
+		 * http://mootools.lighthouseapp.com/projects/2706/tickets/333-element-getoffsetparent-inconsistency-between-ie-and-other-browsers */
+		var offsetParent = this.measure(function(){
+			return document.id(this.getOffsetParent());
+		});
+		if (offsetParent && offsetParent != this.getDocument().body){
+			parentOffset = offsetParent.measure(function(){
+				return this.getPosition();
+			});
+			parentPositioned = offsetParent != document.id(options.relativeTo);
+			options.offset.x = options.offset.x - parentOffset.x;
+			options.offset.y = options.offset.y - parentOffset.y;
+		}
+		//upperRight, bottomRight, centerRight, upperLeft, bottomLeft, centerLeft
+		//topRight, topLeft, centerTop, centerBottom, center
+		var fixValue = function(option){
+			if ($type(option) != 'string') return option;
+			option = option.toLowerCase();
+			var val = {};
+			if (option.test('left')) val.x = 'left';
+			else if (option.test('right')) val.x = 'right';
+			else val.x = 'center';
+			if (option.test('upper') || option.test('top')) val.y = 'top';
+			else if (option.test('bottom')) val.y = 'bottom';
+			else val.y = 'center';
+			return val;
+		};
+		options.edge = fixValue(options.edge);
+		options.position = fixValue(options.position);
+		if (!options.edge){
+			if (options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center', y:'center'};
+			else options.edge = {x:'left', y:'top'};
+		}
+
+		this.setStyle('position', 'absolute');
+		var rel = document.id(options.relativeTo) || document.body,
+				calc = rel == document.body ? window.getScroll() : rel.getPosition(),
+				top = calc.y, left = calc.x;
+
+		var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']});
+		var pos = {},
+				prefY = options.offset.y,
+				prefX = options.offset.x,
+				winSize = window.getSize();
+		switch(options.position.x){
+			case 'left':
+				pos.x = left + prefX;
+				break;
+			case 'right':
+				pos.x = left + prefX + rel.offsetWidth;
+				break;
+			default: //center
+				pos.x = left + ((rel == document.body ? winSize.x : rel.offsetWidth)/2) + prefX;
+				break;
+		}
+		switch(options.position.y){
+			case 'top':
+				pos.y = top + prefY;
+				break;
+			case 'bottom':
+				pos.y = top + prefY + rel.offsetHeight;
+				break;
+			default: //center
+				pos.y = top + ((rel == document.body ? winSize.y : rel.offsetHeight)/2) + prefY;
+				break;
+		}
+		if (options.edge){
+			var edgeOffset = {};
+
+			switch(options.edge.x){
+				case 'left':
+					edgeOffset.x = 0;
+					break;
+				case 'right':
+					edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft;
+					break;
+				default: //center
+					edgeOffset.x = -(dim.totalWidth/2);
+					break;
+			}
+			switch(options.edge.y){
+				case 'top':
+					edgeOffset.y = 0;
+					break;
+				case 'bottom':
+					edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom;
+					break;
+				default: //center
+					edgeOffset.y = -(dim.totalHeight/2);
+					break;
+			}
+			pos.x += edgeOffset.x;
+			pos.y += edgeOffset.y;
+		}
+		pos = {
+			left: ((pos.x >= 0 || parentPositioned || options.allowNegative) ? pos.x : 0).toInt(),
+			top: ((pos.y >= 0 || parentPositioned || options.allowNegative) ? pos.y : 0).toInt()
+		};
+		var xy = {left: 'x', top: 'y'};
+		['minimum', 'maximum'].each(function(minmax) {
+			['left', 'top'].each(function(lr) {
+				var val = options[minmax] ? options[minmax][xy[lr]] : null;
+				if (val != null && pos[lr] < val) pos[lr] = val;
+			});
+		});
+		if (rel.getStyle('position') == 'fixed' || options.relFixedPosition){
+			var winScroll = window.getScroll();
+			pos.top+= winScroll.y;
+			pos.left+= winScroll.x;
+		}
+		if (options.ignoreScroll) {
+			var relScroll = rel.getScroll();
+			pos.top-= relScroll.y;
+			pos.left-= relScroll.x;
+		}
+		if (options.ignoreMargins) {
+			pos.left += (
+				options.edge.x == 'right' ? dim['margin-right'] : 
+				options.edge.x == 'center' ? -dim['margin-left'] + ((dim['margin-right'] + dim['margin-left'])/2) : 
+					- dim['margin-left']
+			);
+			pos.top += (
+				options.edge.y == 'bottom' ? dim['margin-bottom'] : 
+				options.edge.y == 'center' ? -dim['margin-top'] + ((dim['margin-bottom'] + dim['margin-top'])/2) : 
+					- dim['margin-top']
+			);
+		}
+		pos.left = Math.ceil(pos.left);
+		pos.top = Math.ceil(pos.top);
+		if (options.returnPos) return pos;
+		else this.setStyles(pos);
+		return this;
+	}
+
+});
+
+})();/*
+---
+
+script: Element.Shortcuts.js
+
+description: Extends the Element native object to include some shortcut methods.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element.Style
+ - /MooTools.More
+
+provides: [Element.Shortcuts]
+
+...
+*/
+
+Element.implement({
+
+	isDisplayed: function(){
+		return this.getStyle('display') != 'none';
+	},
+
+	isVisible: function(){
+		var w = this.offsetWidth,
+			h = this.offsetHeight;
+		return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.isDisplayed();
+	},
+
+	toggle: function(){
+		return this[this.isDisplayed() ? 'hide' : 'show']();
+	},
+
+	hide: function(){
+		var d;
+		try {
+			//IE fails here if the element is not in the dom
+			d = this.getStyle('display');
+		} catch(e){}
+		return this.store('originalDisplay', d || '').setStyle('display', 'none');
+	},
+
+	show: function(display){
+		display = display || this.retrieve('originalDisplay') || 'block';
+		return this.setStyle('display', (display == 'none') ? 'block' : display);
+	},
+
+	swapClass: function(remove, add){
+		return this.removeClass(remove).addClass(add);
+	}
+
+});
+/*
+---
+
+script: IframeShim.js
+
+description: Defines IframeShim, a class for obscuring select lists and flash objects in IE.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element.Event
+ - core:1.2.4/Element.Style
+ - core:1.2.4/Options Events
+ - /Element.Position
+ - /Class.Occlude
+
+provides: [IframeShim]
+
+...
+*/
+
+var IframeShim = new Class({
+
+	Implements: [Options, Events, Class.Occlude],
+
+	options: {
+		className: 'iframeShim',
+		src: 'javascript:false;document.write("");',
+		display: false,
+		zIndex: null,
+		margin: 0,
+		offset: {x: 0, y: 0},
+		browsers: (Browser.Engine.trident4 || (Browser.Engine.gecko && !Browser.Engine.gecko19 && Browser.Platform.mac))
+	},
+
+	property: 'IframeShim',
+
+	initialize: function(element, options){
+		this.element = document.id(element);
+		if (this.occlude()) return this.occluded;
+		this.setOptions(options);
+		this.makeShim();
+		return this;
+	},
+
+	makeShim: function(){
+		if(this.options.browsers){
+			var zIndex = this.element.getStyle('zIndex').toInt();
+
+			if (!zIndex){
+				zIndex = 1;
+				var pos = this.element.getStyle('position');
+				if (pos == 'static' || !pos) this.element.setStyle('position', 'relative');
+				this.element.setStyle('zIndex', zIndex);
+			}
+			zIndex = ($chk(this.options.zIndex) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1;
+			if (zIndex < 0) zIndex = 1;
+			this.shim = new Element('iframe', {
+				src: this.options.src,
+				scrolling: 'no',
+				frameborder: 0,
+				styles: {
+					zIndex: zIndex,
+					position: 'absolute',
+					border: 'none',
+					filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
+				},
+				'class': this.options.className
+			}).store('IframeShim', this);
+			var inject = (function(){
+				this.shim.inject(this.element, 'after');
+				this[this.options.display ? 'show' : 'hide']();
+				this.fireEvent('inject');
+			}).bind(this);
+			if (!IframeShim.ready) window.addEvent('load', inject);
+			else inject();
+		} else {
+			this.position = this.hide = this.show = this.dispose = $lambda(this);
+		}
+	},
+
+	position: function(){
+		if (!IframeShim.ready || !this.shim) return this;
+		var size = this.element.measure(function(){ 
+			return this.getSize(); 
+		});
+		if (this.options.margin != undefined){
+			size.x = size.x - (this.options.margin * 2);
+			size.y = size.y - (this.options.margin * 2);
+			this.options.offset.x += this.options.margin;
+			this.options.offset.y += this.options.margin;
+		}
+		this.shim.set({width: size.x, height: size.y}).position({
+			relativeTo: this.element,
+			offset: this.options.offset
+		});
+		return this;
+	},
+
+	hide: function(){
+		if (this.shim) this.shim.setStyle('display', 'none');
+		return this;
+	},
+
+	show: function(){
+		if (this.shim) this.shim.setStyle('display', 'block');
+		return this.position();
+	},
+
+	dispose: function(){
+		if (this.shim) this.shim.dispose();
+		return this;
+	},
+
+	destroy: function(){
+		if (this.shim) this.shim.destroy();
+		return this;
+	}
+
+});
+
+window.addEvent('load', function(){
+	IframeShim.ready = true;
+});/*
+---
+
+script: Mask.js
+
+description: Creates a mask element to cover another.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Options
+ - core:1.2.4/Events
+ - core:1.2.4/Element.Event
+ - /Class.Binds
+ - /Element.Position
+ - /IframeShim
+
+provides: [Mask]
+
+...
+*/
+
+var Mask = new Class({
+
+	Implements: [Options, Events],
+
+	Binds: ['position'],
+
+	options: {
+		// onShow: $empty,
+		// onHide: $empty,
+		// onDestroy: $empty,
+		// onClick: $empty,
+		//inject: {
+		//  where: 'after',
+		//  target: null,
+		//},
+		// hideOnClick: false,
+		// id: null,
+		// destroyOnHide: false,
+		style: {},
+		'class': 'mask',
+		maskMargins: false,
+		useIframeShim: true,
+		iframeShimOptions: {}
+	},
+
+	initialize: function(target, options){
+		this.target = document.id(target) || document.id(document.body);
+		this.target.store('Mask', this);
+		this.setOptions(options);
+		this.render();
+		this.inject();
+	},
+	
+	render: function() {
+		this.element = new Element('div', {
+			'class': this.options['class'],
+			id: this.options.id || 'mask-' + $time(),
+			styles: $merge(this.options.style, {
+				display: 'none'
+			}),
+			events: {
+				click: function(){
+					this.fireEvent('click');
+					if (this.options.hideOnClick) this.hide();
+				}.bind(this)
+			}
+		});
+		this.hidden = true;
+	},
+
+	toElement: function(){
+		return this.element;
+	},
+
+	inject: function(target, where){
+		where = where || this.options.inject ? this.options.inject.where : '' || this.target == document.body ? 'inside' : 'after';
+		target = target || this.options.inject ? this.options.inject.target : '' || this.target;
+		this.element.inject(target, where);
+		if (this.options.useIframeShim) {
+			this.shim = new IframeShim(this.element, this.options.iframeShimOptions);
+			this.addEvents({
+				show: this.shim.show.bind(this.shim),
+				hide: this.shim.hide.bind(this.shim),
+				destroy: this.shim.destroy.bind(this.shim)
+			});
+		}
+	},
+
+	position: function(){
+		this.resize(this.options.width, this.options.height);
+		this.element.position({
+			relativeTo: this.target,
+			position: 'topLeft',
+			ignoreMargins: !this.options.maskMargins,
+			ignoreScroll: this.target == document.body
+		});
+		return this;
+	},
+
+	resize: function(x, y){
+		var opt = {
+			styles: ['padding', 'border']
+		};
+		if (this.options.maskMargins) opt.styles.push('margin');
+		var dim = this.target.getComputedSize(opt);
+		if (this.target == document.body) {
+			var win = window.getSize();
+			if (dim.totalHeight < win.y) dim.totalHeight = win.y;
+			if (dim.totalWidth < win.x) dim.totalWidth = win.x;
+		}
+		this.element.setStyles({
+			width: $pick(x, dim.totalWidth, dim.x),
+			height: $pick(y, dim.totalHeight, dim.y)
+		});
+		return this;
+	},
+
+	show: function(){
+		if (!this.hidden) return this;
+		window.addEvent('resize', this.position);
+		this.position();
+		this.showMask.apply(this, arguments);
+		return this;
+	},
+
+	showMask: function(){
+		this.element.setStyle('display', 'block');
+		this.hidden = false;
+		this.fireEvent('show');
+	},
+
+	hide: function(){
+		if (this.hidden) return this;
+		window.removeEvent('resize', this.position);
+		this.hideMask.apply(this, arguments);
+		if (this.options.destroyOnHide) return this.destroy();
+		return this;
+	},
+
+	hideMask: function(){
+		this.element.setStyle('display', 'none');
+		this.hidden = true;
+		this.fireEvent('hide');
+	},
+
+	toggle: function(){
+		this[this.hidden ? 'show' : 'hide']();
+	},
+
+	destroy: function(){
+		this.hide();
+		this.element.destroy();
+		this.fireEvent('destroy');
+		this.target.eliminate('mask');
+	}
+
+});
+
+Element.Properties.mask = {
+
+	set: function(options){
+		var mask = this.retrieve('mask');
+		return this.eliminate('mask').store('mask:options', options);
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('mask')){
+			if (this.retrieve('mask')) this.retrieve('mask').destroy();
+			if (options || !this.retrieve('mask:options')) this.set('mask', options);
+			this.store('mask', new Mask(this, this.retrieve('mask:options')));
+		}
+		return this.retrieve('mask');
+	}
+
+};
+
+Element.implement({
+
+	mask: function(options){
+		this.get('mask', options).show();
+		return this;
+	},
+
+	unmask: function(){
+		this.get('mask').hide();
+		return this;
+	}
+
+});/*
+---
+
+script: Spinner.js
+
+description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Fx.Tween
+ - /Class.refactor
+ - /Mask
+
+provides: [Spinner]
+
+...
+*/
+
+var Spinner = new Class({
+
+	Extends: Mask,
+
+	options: {
+		/*message: false,*/
+		'class':'spinner',
+		containerPosition: {},
+		content: {
+			'class':'spinner-content'
+		},
+		messageContainer: {
+			'class':'spinner-msg'
+		},
+		img: {
+			'class':'spinner-img'
+		},
+		fxOptions: {
+			link: 'chain'
+		}
+	},
+
+	initialize: function(){
+		this.parent.apply(this, arguments);
+		this.target.store('spinner', this);
+
+		//add this to events for when noFx is true; parent methods handle hide/show
+		var deactivate = function(){ this.active = false; }.bind(this);
+		this.addEvents({
+			hide: deactivate,
+			show: deactivate
+		});
+	},
+
+	render: function(){
+		this.parent();
+		this.element.set('id', this.options.id || 'spinner-'+$time());
+		this.content = document.id(this.options.content) || new Element('div', this.options.content);
+		this.content.inject(this.element);
+		if (this.options.message) {
+			this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message);
+			this.msg.inject(this.content);
+		}
+		if (this.options.img) {
+			this.img = document.id(this.options.img) || new Element('div', this.options.img);
+			this.img.inject(this.content);
+		}
+		this.element.set('tween', this.options.fxOptions);
+	},
+
+	show: function(noFx){
+		if (this.active) return this.chain(this.show.bind(this));
+		if (!this.hidden) {
+			this.callChain.delay(20, this);
+			return this;
+		}
+		this.active = true;
+		return this.parent(noFx);
+	},
+
+	showMask: function(noFx){
+		var pos = function(){
+			this.content.position($merge({
+				relativeTo: this.element
+			}, this.options.containerPosition));
+		}.bind(this);
+		if (noFx) {
+			this.parent();
+			pos();
+		} else {
+			this.element.setStyles({
+				display: 'block',
+				opacity: 0
+			}).tween('opacity', this.options.style.opacity || 0.9);
+			pos();
+			this.hidden = false;
+			this.fireEvent('show');
+			this.callChain();
+		}
+	},
+
+	hide: function(noFx){
+		if (this.active) return this.chain(this.hide.bind(this));
+		if (this.hidden) {
+			this.callChain.delay(20, this);
+			return this;
+		}
+		this.active = true;
+		return this.parent(noFx);
+	},
+
+	hideMask: function(noFx){
+		if (noFx) return this.parent();
+		this.element.tween('opacity', 0).get('tween').chain(function(){
+			this.element.setStyle('display', 'none');
+			this.hidden = true;
+			this.fireEvent('hide');
+			this.callChain();
+		}.bind(this));
+	},
+
+	destroy: function(){
+		this.content.destroy();
+		this.parent();
+		this.target.eliminate('spinner');
+	}
+
+});
+
+Spinner.implement(new Chain);
+
+if (window.Request) {
+	Request = Class.refactor(Request, {
+		
+		options: {
+			useSpinner: false,
+			spinnerOptions: {},
+			spinnerTarget: false
+		},
+		
+		initialize: function(options){
+			this._send = this.send;
+			this.send = function(options){
+				if (this.spinner) this.spinner.chain(this._send.bind(this, options)).show();
+				else this._send(options);
+				return this;
+			};
+			this.previous(options);
+			var update = document.id(this.options.spinnerTarget) || document.id(this.options.update);
+			if (this.options.useSpinner && update) {
+				this.spinner = update.get('spinner', this.options.spinnerOptions);
+				['onComplete', 'onException', 'onCancel'].each(function(event){
+					this.addEvent(event, this.spinner.hide.bind(this.spinner));
+				}, this);
+			}
+		},
+		
+		getSpinner: function(){
+			return this.spinner;
+		}
+		
+	});
+}
+
+Element.Properties.spinner = {
+
+	set: function(options){
+		var spinner = this.retrieve('spinner');
+		return this.eliminate('spinner').store('spinner:options', options);
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('spinner')){
+			if (this.retrieve('spinner')) this.retrieve('spinner').destroy();
+			if (options || !this.retrieve('spinner:options')) this.set('spinner', options);
+			new Spinner(this, this.retrieve('spinner:options'));
+		}
+		return this.retrieve('spinner');
+	}
+
+};
+
+Element.implement({
+
+	spin: function(options){
+		this.get('spinner', options).show();
+		return this;
+	},
+
+	unspin: function(){
+		var opt = Array.link(arguments, {options: Object.type, callback: Function.type});
+		this.get('spinner', opt.options).hide(opt.callback);
+		return this;
+	}
+
+});/*
+---
+
+script: Form.Request.js
+
+description: Handles the basic functionality of submitting a form and updating a dom element with the result.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element.Event
+ - core:1.2.4/Request.HTML
+ - /Class.Binds
+ - /Class.Occlude
+ - /Spinner
+ - /String.QueryString
+
+provides: [Form.Request]
+
+...
+*/
+
+if (!window.Form) window.Form = {};
+
+(function(){
+
+	Form.Request = new Class({
+
+		Binds: ['onSubmit', 'onFormValidate'],
+
+		Implements: [Options, Events, Class.Occlude],
+
+		options: {
+			//onFailure: $empty,
+			//onSuccess: #empty, //aliased to onComplete,
+			//onSend: $empty
+			requestOptions: {
+				evalScripts: true,
+				useSpinner: true,
+				emulation: false,
+				link: 'ignore'
+			},
+			extraData: {},
+			resetForm: true
+		},
+
+		property: 'form.request',
+
+		initialize: function(form, update, options) {
+			this.element = document.id(form);
+			if (this.occlude()) return this.occluded;
+			this.update = document.id(update);
+			this.setOptions(options);
+			this.makeRequest();
+			if (this.options.resetForm) {
+				this.request.addEvent('success', function(){
+					$try(function(){ this.element.reset(); }.bind(this));
+					if (window.OverText) OverText.update();
+				}.bind(this));
+			}
+			this.attach();
+		},
+
+		toElement: function() {
+			return this.element;
+		},
+
+		makeRequest: function(){
+			this.request = new Request.HTML($merge({
+					update: this.update,
+					emulation: false,
+					spinnerTarget: this.element,
+					method: this.element.get('method') || 'post'
+			}, this.options.requestOptions)).addEvents({
+				success: function(text, xml){
+					['complete', 'success'].each(function(evt){
+						this.fireEvent(evt, [this.update, text, xml]);
+					}, this);
+				}.bind(this),
+				failure: function(xhr){
+					this.fireEvent('complete').fireEvent('failure', xhr);
+				}.bind(this),
+				exception: function(){
+					this.fireEvent('failure', xhr);
+				}.bind(this)
+			});
+		},
+
+		attach: function(attach){
+			attach = $pick(attach, true);
+			method = attach ? 'addEvent' : 'removeEvent';
+			
+			var fv = this.element.retrieve('validator');
+			if (fv) fv[method]('onFormValidate', this.onFormValidate);
+			if (!fv || !attach) this.element[method]('submit', this.onSubmit);
+		},
+
+		detach: function(){
+			this.attach(false);
+		},
+
+		//public method
+		enable: function(){
+			this.attach();
+		},
+
+		//public method
+		disable: function(){
+			this.detach();
+		},
+
+		onFormValidate: function(valid, form, e) {
+			var fv = this.element.retrieve('validator');
+			if (valid || (fv && !fv.options.stopOnFailure)) {
+				if (e && e.stop) e.stop();
+				this.send();
+			}
+		},
+
+		onSubmit: function(e){
+			if (this.element.retrieve('validator')) {
+				//form validator was created after Form.Request
+				this.detach();
+				return;
+			}
+			e.stop();
+			this.send();
+		},
+
+		send: function(){
+			var str = this.element.toQueryString().trim();
+			var data = $H(this.options.extraData).toQueryString();
+			if (str) str += "&" + data;
+			else str = data;
+			this.fireEvent('send', [this.element, str.parseQueryString()]);
+			this.request.send({data: str, url: this.element.get("action")});
+			return this;
+		}
+
+	});
+
+	Element.Properties.formRequest = {
+
+		set: function(){
+			var opt = Array.link(arguments, {options: Object.type, update: Element.type, updateId: String.type});
+			var update = opt.update || opt.updateId;
+			var updater = this.retrieve('form.request');
+			if (update) {
+				if (updater) updater.update = document.id(update);
+				this.store('form.request:update', update);
+			}
+			if (opt.options) {
+				if (updater) updater.setOptions(opt.options);
+				this.store('form.request:options', opt.options);
+			}
+			return this;
+		},
+
+		get: function(){
+			var opt = Array.link(arguments, {options: Object.type, update: Element.type, updateId: String.type});
+			var update = opt.update || opt.updateId;
+			if (opt.options || update || !this.retrieve('form.request')){
+				if (opt.options || !this.retrieve('form.request:options')) this.set('form.request', opt.options);
+				if (update) this.set('form.request', update);
+				this.store('form.request', new Form.Request(this, this.retrieve('form.request:update'), this.retrieve('form.request:options')));
+			}
+			return this.retrieve('form.request');
+		}
+
+	};
+
+	Element.implement({
+
+		formUpdate: function(update, options){
+			this.get('form.request', update, options).send();
+			return this;
+		}
+
+	});
+
+})();/*
+---
+
+script: Fx.Reveal.js
+
+description: Defines Fx.Reveal, a class that shows and hides elements with a transition.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Fx.Morph
+ - /Element.Shortcuts
+ - /Element.Measure
+
+provides: [Fx.Reveal]
+
+...
+*/
+
+Fx.Reveal = new Class({
+
+	Extends: Fx.Morph,
+
+	options: {/*	  
+		onShow: $empty(thisElement),
+		onHide: $empty(thisElement),
+		onComplete: $empty(thisElement),
+		heightOverride: null,
+		widthOverride: null, */
+		link: 'cancel',
+		styles: ['padding', 'border', 'margin'],
+		transitionOpacity: !Browser.Engine.trident4,
+		mode: 'vertical',
+		display: 'block',
+		hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed' : false
+	},
+
+	dissolve: function(){
+		try {
+			if (!this.hiding && !this.showing){
+				if (this.element.getStyle('display') != 'none'){
+					this.hiding = true;
+					this.showing = false;
+					this.hidden = true;
+					this.cssText = this.element.style.cssText;
+					var startStyles = this.element.getComputedSize({
+						styles: this.options.styles,
+						mode: this.options.mode
+					});
+					this.element.setStyle('display', this.options.display);
+					if (this.options.transitionOpacity) startStyles.opacity = 1;
+					var zero = {};
+					$each(startStyles, function(style, name){
+						zero[name] = [style, 0];
+					}, this);
+					this.element.setStyle('overflow', 'hidden');
+					var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
+					this.$chain.unshift(function(){
+						if (this.hidden){
+							this.hiding = false;
+							$each(startStyles, function(style, name){
+								startStyles[name] = style;
+							}, this);
+							this.element.style.cssText = this.cssText;
+							this.element.setStyle('display', 'none');
+							if (hideThese) hideThese.setStyle('visibility', 'visible');
+						}
+						this.fireEvent('hide', this.element);
+						this.callChain();
+					}.bind(this));
+					if (hideThese) hideThese.setStyle('visibility', 'hidden');
+					this.start(zero);
+				} else {
+					this.callChain.delay(10, this);
+					this.fireEvent('complete', this.element);
+					this.fireEvent('hide', this.element);
+				}
+			} else if (this.options.link == 'chain'){
+				this.chain(this.dissolve.bind(this));
+			} else if (this.options.link == 'cancel' && !this.hiding){
+				this.cancel();
+				this.dissolve();
+			}
+		} catch(e){
+			this.hiding = false;
+			this.element.setStyle('display', 'none');
+			this.callChain.delay(10, this);
+			this.fireEvent('complete', this.element);
+			this.fireEvent('hide', this.element);
+		}
+		return this;
+	},
+
+	reveal: function(){
+		try {
+			if (!this.showing && !this.hiding){
+				if (this.element.getStyle('display') == 'none' ||
+					 this.element.getStyle('visiblity') == 'hidden' ||
+					 this.element.getStyle('opacity') == 0){
+					this.showing = true;
+					this.hiding = this.hidden =  false;
+					var startStyles;
+					this.cssText = this.element.style.cssText;
+					//toggle display, but hide it
+					this.element.measure(function(){
+						//create the styles for the opened/visible state
+						startStyles = this.element.getComputedSize({
+							styles: this.options.styles,
+							mode: this.options.mode
+						});
+					}.bind(this));
+					$each(startStyles, function(style, name){
+						startStyles[name] = style;
+					});
+					//if we're overridding height/width
+					if ($chk(this.options.heightOverride)) startStyles.height = this.options.heightOverride.toInt();
+					if ($chk(this.options.widthOverride)) startStyles.width = this.options.widthOverride.toInt();
+					if (this.options.transitionOpacity) {
+						this.element.setStyle('opacity', 0);
+						startStyles.opacity = 1;
+					}
+					//create the zero state for the beginning of the transition
+					var zero = {
+						height: 0,
+						display: this.options.display
+					};
+					$each(startStyles, function(style, name){ zero[name] = 0; });
+					//set to zero
+					this.element.setStyles($merge(zero, {overflow: 'hidden'}));
+					//hide inputs
+					var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
+					if (hideThese) hideThese.setStyle('visibility', 'hidden');
+					//start the effect
+					this.start(startStyles);
+					this.$chain.unshift(function(){
+						this.element.style.cssText = this.cssText;
+						this.element.setStyle('display', this.options.display);
+						if (!this.hidden) this.showing = false;
+						if (hideThese) hideThese.setStyle('visibility', 'visible');
+						this.callChain();
+						this.fireEvent('show', this.element);
+					}.bind(this));
+				} else {
+					this.callChain();
+					this.fireEvent('complete', this.element);
+					this.fireEvent('show', this.element);
+				}
+			} else if (this.options.link == 'chain'){
+				this.chain(this.reveal.bind(this));
+			} else if (this.options.link == 'cancel' && !this.showing){
+				this.cancel();
+				this.reveal();
+			}
+		} catch(e){
+			this.element.setStyles({
+				display: this.options.display,
+				visiblity: 'visible',
+				opacity: 1
+			});
+			this.showing = false;
+			this.callChain.delay(10, this);
+			this.fireEvent('complete', this.element);
+			this.fireEvent('show', this.element);
+		}
+		return this;
+	},
+
+	toggle: function(){
+		if (this.element.getStyle('display') == 'none' ||
+			 this.element.getStyle('visiblity') == 'hidden' ||
+			 this.element.getStyle('opacity') == 0){
+			this.reveal();
+		} else {
+			this.dissolve();
+		}
+		return this;
+	},
+
+	cancel: function(){
+		this.parent.apply(this, arguments);
+		this.element.style.cssText = this.cssText;
+		this.hidding = false;
+		this.showing = false;
+	}
+
+});
+
+Element.Properties.reveal = {
+
+	set: function(options){
+		var reveal = this.retrieve('reveal');
+		if (reveal) reveal.cancel();
+		return this.eliminate('reveal').store('reveal:options', options);
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('reveal')){
+			if (options || !this.retrieve('reveal:options')) this.set('reveal', options);
+			this.store('reveal', new Fx.Reveal(this, this.retrieve('reveal:options')));
+		}
+		return this.retrieve('reveal');
+	}
+
+};
+
+Element.Properties.dissolve = Element.Properties.reveal;
+
+Element.implement({
+
+	reveal: function(options){
+		this.get('reveal', options).reveal();
+		return this;
+	},
+
+	dissolve: function(options){
+		this.get('reveal', options).dissolve();
+		return this;
+	},
+
+	nix: function(){
+		var params = Array.link(arguments, {destroy: Boolean.type, options: Object.type});
+		this.get('reveal', params.options).dissolve().chain(function(){
+			this[params.destroy ? 'destroy' : 'dispose']();
+		}.bind(this));
+		return this;
+	},
+
+	wink: function(){
+		var params = Array.link(arguments, {duration: Number.type, options: Object.type});
+		var reveal = this.get('reveal', params.options);
+		reveal.reveal().chain(function(){
+			(function(){
+				reveal.dissolve();
+			}).delay(params.duration || 2000);
+		});
+	}
+
+
+});/*
+---
+
+script: Form.Request.Append.js
+
+description: Handles the basic functionality of submitting a form and updating a dom element with the result. The result is appended to the DOM element instead of replacing its contents.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - /Form.Request
+ - /Fx.Reveal
+ - /Elements.from
+
+provides: [Form.Request.Append]
+
+...
+*/
+
+Form.Request.Append = new Class({
+
+	Extends: Form.Request,
+
+	options: {
+		//onBeforeEffect: $empty,
+		useReveal: true,
+		revealOptions: {},
+		inject: 'bottom'
+	},
+
+	makeRequest: function(){
+		this.request = new Request.HTML($merge({
+				url: this.element.get('action'),
+				method: this.element.get('method') || 'post',
+				spinnerTarget: this.element
+			}, this.options.requestOptions, {
+				evalScripts: false
+			})
+		).addEvents({
+			success: function(tree, elements, html, javascript){
+				var container;
+				var kids = Elements.from(html);
+				if (kids.length == 1) {
+					container = kids[0];
+				} else {
+					 container = new Element('div', {
+						styles: {
+							display: 'none'
+						}
+					}).adopt(kids);
+				}
+				container.inject(this.update, this.options.inject);
+				if (this.options.requestOptions.evalScripts) $exec(javascript);
+				this.fireEvent('beforeEffect', container);
+				var finish = function(){
+					this.fireEvent('success', [container, this.update, tree, elements, html, javascript]);
+				}.bind(this);
+				if (this.options.useReveal) {
+					container.get('reveal', this.options.revealOptions).chain(finish);
+					container.reveal();
+				} else {
+					finish();
+				}
+			}.bind(this),
+			failure: function(xhr){
+				this.fireEvent('failure', xhr);
+			}.bind(this)
+		});
+	}
+
+});/*
+---
+
+script: Form.Validator.English.js
+
+description: Form Validator messages for English.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.English]
+
+...
+*/
+
+MooTools.lang.set('en-US', 'Form.Validator', {
+
+	required:'This field is required.',
+	minLength:'Please enter at least {minLength} characters (you entered {length} characters).',
+	maxLength:'Please enter no more than {maxLength} characters (you entered {length} characters).',
+	integer:'Please enter an integer in this field. Numbers with decimals (e.g. 1.25) are not permitted.',
+	numeric:'Please enter only numeric values in this field (i.e. "1" or "1.1" or "-1" or "-1.1").',
+	digits:'Please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).',
+	alpha:'Please use letters only (a-z) with in this field. No spaces or other characters are allowed.',
+	alphanum:'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.',
+	dateSuchAs:'Please enter a valid date such as {date}',
+	dateInFormatMDY:'Please enter a valid date such as MM/DD/YYYY (i.e. "12/31/1999")',
+	email:'Please enter a valid email address. For example "fred at domain.com".',
+	url:'Please enter a valid URL such as http://www.google.com.',
+	currencyDollar:'Please enter a valid $ amount. For example $100.00 .',
+	oneRequired:'Please enter something for at least one of these inputs.',
+	errorPrefix: 'Error: ',
+	warningPrefix: 'Warning: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'There can be no spaces in this input.',
+	reqChkByNode: 'No items are selected.',
+	requiredChk: 'This field is required.',
+	reqChkByName: 'Please select a {label}.',
+	match: 'This field needs to match the {matchName} field',
+	startDate: 'the start date',
+	endDate: 'the end date',
+	currendDate: 'the current date',
+	afterDate: 'The date should be the same or after {label}.',
+	beforeDate: 'The date should be the same or before {label}.',
+	startMonth: 'Please select a start month',
+	sameMonth: 'These two dates must be in the same month - you must change one or the other.',
+	creditcard: 'The credit card number entered is invalid. Please check the number and try again. {length} digits entered.'
+
+});
+/*
+---
+
+script: Form.Validator.js
+
+description: A css-class based form validation system.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Options
+ - core:1.2.4/Events
+ - core:1.2.4/Selectors
+ - core:1.2.4/Element.Event
+ - core:1.2.4/Element.Style
+ - core:1.2.4/JSON
+ - /Lang
+ - /Class.Binds
+ - /Date Element.Forms
+ - /Form.Validator.English
+ - /Element.Shortcuts
+
+provides: [Form.Validator, InputValidator, FormValidator.BaseValidators]
+
+...
+*/
+if (!window.Form) window.Form = {};
+
+var InputValidator = new Class({
+
+	Implements: [Options],
+
+	options: {
+		errorMsg: 'Validation failed.',
+		test: function(field){return true;}
+	},
+
+	initialize: function(className, options){
+		this.setOptions(options);
+		this.className = className;
+	},
+
+	test: function(field, props){
+		if (document.id(field)) return this.options.test(document.id(field), props||this.getProps(field));
+		else return false;
+	},
+
+	getError: function(field, props){
+		var err = this.options.errorMsg;
+		if ($type(err) == 'function') err = err(document.id(field), props||this.getProps(field));
+		return err;
+	},
+
+	getProps: function(field){
+		if (!document.id(field)) return {};
+		return field.get('validatorProps');
+	}
+
+});
+
+Element.Properties.validatorProps = {
+
+	set: function(props){
+		return this.eliminate('validatorProps').store('validatorProps', props);
+	},
+
+	get: function(props){
+		if (props) this.set(props);
+		if (this.retrieve('validatorProps')) return this.retrieve('validatorProps');
+		if (this.getProperty('validatorProps')){
+			try {
+				this.store('validatorProps', JSON.decode(this.getProperty('validatorProps')));
+			}catch(e){
+				return {};
+			}
+		} else {
+			var vals = this.get('class').split(' ').filter(function(cls){
+				return cls.test(':');
+			});
+			if (!vals.length){
+				this.store('validatorProps', {});
+			} else {
+				props = {};
+				vals.each(function(cls){
+					var split = cls.split(':');
+					if (split[1]) {
+						try {
+							props[split[0]] = JSON.decode(split[1]);
+						} catch(e) {}
+					}
+				});
+				this.store('validatorProps', props);
+			}
+		}
+		return this.retrieve('validatorProps');
+	}
+
+};
+
+Form.Validator = new Class({
+
+	Implements:[Options, Events],
+
+	Binds: ['onSubmit'],
+
+	options: {/*
+		onFormValidate: $empty(isValid, form, event),
+		onElementValidate: $empty(isValid, field, className, warn),
+		onElementPass: $empty(field),
+		onElementFail: $empty(field, validatorsFailed) */
+		fieldSelectors: 'input, select, textarea',
+		ignoreHidden: true,
+		ignoreDisabled: true,
+		useTitles: false,
+		evaluateOnSubmit: true,
+		evaluateFieldsOnBlur: true,
+		evaluateFieldsOnChange: true,
+		serial: true,
+		stopOnFailure: true,
+		warningPrefix: function(){
+			return Form.Validator.getMsg('warningPrefix') || 'Warning: ';
+		},
+		errorPrefix: function(){
+			return Form.Validator.getMsg('errorPrefix') || 'Error: ';
+		}
+	},
+
+	initialize: function(form, options){
+		this.setOptions(options);
+		this.element = document.id(form);
+		this.element.store('validator', this);
+		this.warningPrefix = $lambda(this.options.warningPrefix)();
+		this.errorPrefix = $lambda(this.options.errorPrefix)();
+		if (this.options.evaluateOnSubmit) this.element.addEvent('submit', this.onSubmit);
+		if (this.options.evaluateFieldsOnBlur || this.options.evaluateFieldsOnChange) this.watchFields(this.getFields());
+	},
+
+	toElement: function(){
+		return this.element;
+	},
+
+	getFields: function(){
+		return (this.fields = this.element.getElements(this.options.fieldSelectors));
+	},
+
+	watchFields: function(fields){
+		fields.each(function(el){
+			if (this.options.evaluateFieldsOnBlur)
+				el.addEvent('blur', this.validationMonitor.pass([el, false], this));
+			if (this.options.evaluateFieldsOnChange)
+				el.addEvent('change', this.validationMonitor.pass([el, true], this));
+		}, this);
+	},
+
+	validationMonitor: function(){
+		$clear(this.timer);
+		this.timer = this.validateField.delay(50, this, arguments);
+	},
+
+	onSubmit: function(event){
+		if (!this.validate(event) && event) event.preventDefault();
+		else this.reset();
+	},
+
+	reset: function(){
+		this.getFields().each(this.resetField, this);
+		return this;
+	},
+
+	validate: function(event){
+		var result = this.getFields().map(function(field){
+			return this.validateField(field, true);
+		}, this).every(function(v){ return v;});
+		this.fireEvent('formValidate', [result, this.element, event]);
+		if (this.options.stopOnFailure && !result && event) event.preventDefault();
+		return result;
+	},
+
+	validateField: function(field, force){
+		if (this.paused) return true;
+		field = document.id(field);
+		var passed = !field.hasClass('validation-failed');
+		var failed, warned;
+		if (this.options.serial && !force){
+			failed = this.element.getElement('.validation-failed');
+			warned = this.element.getElement('.warning');
+		}
+		if (field && (!failed || force || field.hasClass('validation-failed') || (failed && !this.options.serial))){
+			var validators = field.className.split(' ').some(function(cn){
+				return this.getValidator(cn);
+			}, this);
+			var validatorsFailed = [];
+			field.className.split(' ').each(function(className){
+				if (className && !this.test(className, field)) validatorsFailed.include(className);
+			}, this);
+			passed = validatorsFailed.length === 0;
+			if (validators && !field.hasClass('warnOnly')){
+				if (passed){
+					field.addClass('validation-passed').removeClass('validation-failed');
+					this.fireEvent('elementPass', field);
+				} else {
+					field.addClass('validation-failed').removeClass('validation-passed');
+					this.fireEvent('elementFail', [field, validatorsFailed]);
+				}
+			}
+			if (!warned){
+				var warnings = field.className.split(' ').some(function(cn){
+					if (cn.test('^warn-') || field.hasClass('warnOnly'))
+						return this.getValidator(cn.replace(/^warn-/,''));
+					else return null;
+				}, this);
+				field.removeClass('warning');
+				var warnResult = field.className.split(' ').map(function(cn){
+					if (cn.test('^warn-') || field.hasClass('warnOnly'))
+						return this.test(cn.replace(/^warn-/,''), field, true);
+					else return null;
+				}, this);
+			}
+		}
+		return passed;
+	},
+
+	test: function(className, field, warn){
+		field = document.id(field);
+		if((this.options.ignoreHidden && !field.isVisible()) || (this.options.ignoreDisabled && field.get('disabled'))) return true;
+		var validator = this.getValidator(className);
+		if (field.hasClass('ignoreValidation')) return true;
+		warn = $pick(warn, false);
+		if (field.hasClass('warnOnly')) warn = true;
+		var isValid = validator ? validator.test(field) : true;
+		if (validator && field.isVisible()) this.fireEvent('elementValidate', [isValid, field, className, warn]);
+		if (warn) return true;
+		return isValid;
+	},
+
+	resetField: function(field){
+		field = document.id(field);
+		if (field){
+			field.className.split(' ').each(function(className){
+				if (className.test('^warn-')) className = className.replace(/^warn-/, '');
+				field.removeClass('validation-failed');
+				field.removeClass('warning');
+				field.removeClass('validation-passed');
+			}, this);
+		}
+		return this;
+	},
+
+	stop: function(){
+		this.paused = true;
+		return this;
+	},
+
+	start: function(){
+		this.paused = false;
+		return this;
+	},
+
+	ignoreField: function(field, warn){
+		field = document.id(field);
+		if (field){
+			this.enforceField(field);
+			if (warn) field.addClass('warnOnly');
+			else field.addClass('ignoreValidation');
+		}
+		return this;
+	},
+
+	enforceField: function(field){
+		field = document.id(field);
+		if (field) field.removeClass('warnOnly').removeClass('ignoreValidation');
+		return this;
+	}
+
+});
+
+Form.Validator.getMsg = function(key){
+	return MooTools.lang.get('Form.Validator', key);
+};
+
+Form.Validator.adders = {
+
+	validators:{},
+
+	add : function(className, options){
+		this.validators[className] = new InputValidator(className, options);
+		//if this is a class (this method is used by instances of Form.Validator and the Form.Validator namespace)
+		//extend these validators into it
+		//this allows validators to be global and/or per instance
+		if (!this.initialize){
+			this.implement({
+				validators: this.validators
+			});
+		}
+	},
+
+	addAllThese : function(validators){
+		$A(validators).each(function(validator){
+			this.add(validator[0], validator[1]);
+		}, this);
+	},
+
+	getValidator: function(className){
+		return this.validators[className.split(':')[0]];
+	}
+
+};
+
+$extend(Form.Validator, Form.Validator.adders);
+
+Form.Validator.implement(Form.Validator.adders);
+
+Form.Validator.add('IsEmpty', {
+
+	errorMsg: false,
+	test: function(element){
+		if (element.type == 'select-one' || element.type == 'select')
+			return !(element.selectedIndex >= 0 && element.options[element.selectedIndex].value != '');
+		else
+			return ((element.get('value') == null) || (element.get('value').length == 0));
+	}
+
+});
+
+Form.Validator.addAllThese([
+
+	['required', {
+		errorMsg: function(){
+			return Form.Validator.getMsg('required');
+		},
+		test: function(element){
+			return !Form.Validator.getValidator('IsEmpty').test(element);
+		}
+	}],
+
+	['minLength', {
+		errorMsg: function(element, props){
+			if ($type(props.minLength))
+				return Form.Validator.getMsg('minLength').substitute({minLength:props.minLength,length:element.get('value').length });
+			else return '';
+		},
+		test: function(element, props){
+			if ($type(props.minLength)) return (element.get('value').length >= $pick(props.minLength, 0));
+			else return true;
+		}
+	}],
+
+	['maxLength', {
+		errorMsg: function(element, props){
+			//props is {maxLength:10}
+			if ($type(props.maxLength))
+				return Form.Validator.getMsg('maxLength').substitute({maxLength:props.maxLength,length:element.get('value').length });
+			else return '';
+		},
+		test: function(element, props){
+			//if the value is <= than the maxLength value, element passes test
+			return (element.get('value').length <= $pick(props.maxLength, 10000));
+		}
+	}],
+
+	['validate-integer', {
+		errorMsg: Form.Validator.getMsg.pass('integer'),
+		test: function(element){
+			return Form.Validator.getValidator('IsEmpty').test(element) || (/^(-?[1-9]\d*|0)$/).test(element.get('value'));
+		}
+	}],
+
+	['validate-numeric', {
+		errorMsg: Form.Validator.getMsg.pass('numeric'),
+		test: function(element){
+			return Form.Validator.getValidator('IsEmpty').test(element) ||
+				(/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(element.get('value'));
+		}
+	}],
+
+	['validate-digits', {
+		errorMsg: Form.Validator.getMsg.pass('digits'),
+		test: function(element){
+			return Form.Validator.getValidator('IsEmpty').test(element) || (/^[\d() .:\-\+#]+$/.test(element.get('value')));
+		}
+	}],
+
+	['validate-alpha', {
+		errorMsg: Form.Validator.getMsg.pass('alpha'),
+		test: function(element){
+			return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z]+$/).test(element.get('value'));
+		}
+	}],
+
+	['validate-alphanum', {
+		errorMsg: Form.Validator.getMsg.pass('alphanum'),
+		test: function(element){
+			return Form.Validator.getValidator('IsEmpty').test(element) || !(/\W/).test(element.get('value'));
+		}
+	}],
+
+	['validate-date', {
+		errorMsg: function(element, props){
+			if (Date.parse){
+				var format = props.dateFormat || '%x';
+				return Form.Validator.getMsg('dateSuchAs').substitute({date: new Date().format(format)});
+			} else {
+				return Form.Validator.getMsg('dateInFormatMDY');
+			}
+		},
+		test: function(element, props){
+			if (Form.Validator.getValidator('IsEmpty').test(element)) return true;
+			var d;
+			if (Date.parse){
+				var format = props.dateFormat || '%x';
+				d = Date.parse(element.get('value'));
+				var formatted = d.format(format);
+				if (formatted != 'invalid date') element.set('value', formatted);
+				return !isNaN(d);
+			} else {
+				var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
+				if (!regex.test(element.get('value'))) return false;
+				d = new Date(element.get('value').replace(regex, '$1/$2/$3'));
+				return (parseInt(RegExp.$1, 10) == (1 + d.getMonth())) &&
+					(parseInt(RegExp.$2, 10) == d.getDate()) &&
+					(parseInt(RegExp.$3, 10) == d.getFullYear());
+			}
+		}
+	}],
+
+	['validate-email', {
+		errorMsg: Form.Validator.getMsg.pass('email'),
+		test: function(element){
+			return Form.Validator.getValidator('IsEmpty').test(element) || (/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i).test(element.get('value'));
+		}
+	}],
+
+	['validate-url', {
+		errorMsg: Form.Validator.getMsg.pass('url'),
+		test: function(element){
+			return Form.Validator.getValidator('IsEmpty').test(element) || (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(element.get('value'));
+		}
+	}],
+
+	['validate-currency-dollar', {
+		errorMsg: Form.Validator.getMsg.pass('currencyDollar'),
+		test: function(element){
+			// [$]1[##][,###]+[.##]
+			// [$]1###+[.##]
+			// [$]0.##
+			// [$].##
+			return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value'));
+		}
+	}],
+
+	['validate-one-required', {
+		errorMsg: Form.Validator.getMsg.pass('oneRequired'),
+		test: function(element, props){
+			var p = document.id(props['validate-one-required']) || element.getParent();
+			return p.getElements('input').some(function(el){
+				if (['checkbox', 'radio'].contains(el.get('type'))) return el.get('checked');
+				return el.get('value');
+			});
+		}
+	}]
+
+]);
+
+Element.Properties.validator = {
+
+	set: function(options){
+		var validator = this.retrieve('validator');
+		if (validator) validator.setOptions(options);
+		return this.store('validator:options');
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('validator')){
+			if (options || !this.retrieve('validator:options')) this.set('validator', options);
+			this.store('validator', new Form.Validator(this, this.retrieve('validator:options')));
+		}
+		return this.retrieve('validator');
+	}
+
+};
+
+Element.implement({
+
+	validate: function(options){
+		this.set('validator', options);
+		return this.get('validator', options).validate();
+	}
+
+});
+//legacy
+var FormValidator = Form.Validator;/*
+---
+
+script: Form.Validator.Inline.js
+
+description: Extends Form.Validator to add inline messages.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - /Form.Validator
+
+provides: [Form.Validator.Inline]
+
+...
+*/
+
+Form.Validator.Inline = new Class({
+
+	Extends: Form.Validator,
+
+	options: {
+		scrollToErrorsOnSubmit: true,
+		scrollFxOptions: {
+			transition: 'quad:out',
+			offset: {
+				y: -20
+			}
+		}
+	},
+
+	initialize: function(form, options){
+		this.parent(form, options);
+		this.addEvent('onElementValidate', function(isValid, field, className, warn){
+			var validator = this.getValidator(className);
+			if (!isValid && validator.getError(field)){
+				if (warn) field.addClass('warning');
+				var advice = this.makeAdvice(className, field, validator.getError(field), warn);
+				this.insertAdvice(advice, field);
+				this.showAdvice(className, field);
+			} else {
+				this.hideAdvice(className, field);
+			}
+		});
+	},
+
+	makeAdvice: function(className, field, error, warn){
+		var errorMsg = (warn)?this.warningPrefix:this.errorPrefix;
+			errorMsg += (this.options.useTitles) ? field.title || error:error;
+		var cssClass = (warn) ? 'warning-advice' : 'validation-advice';
+		var advice = this.getAdvice(className, field);
+		if(advice) {
+			advice = advice.set('html', errorMsg);
+		} else {
+			advice = new Element('div', {
+				html: errorMsg,
+				styles: { display: 'none' },
+				id: 'advice-' + className + '-' + this.getFieldId(field)
+			}).addClass(cssClass);
+		}
+		field.store('advice-' + className, advice);
+		return advice;
+	},
+
+	getFieldId : function(field){
+		return field.id ? field.id : field.id = 'input_' + field.name;
+	},
+
+	showAdvice: function(className, field){
+		var advice = this.getAdvice(className, field);
+		if (advice && !field.retrieve(this.getPropName(className))
+				&& (advice.getStyle('display') == 'none'
+				|| advice.getStyle('visiblity') == 'hidden'
+				|| advice.getStyle('opacity') == 0)){
+			field.store(this.getPropName(className), true);
+			if (advice.reveal) advice.reveal();
+			else advice.setStyle('display', 'block');
+		}
+	},
+
+	hideAdvice: function(className, field){
+		var advice = this.getAdvice(className, field);
+		if (advice && field.retrieve(this.getPropName(className))){
+			field.store(this.getPropName(className), false);
+			//if Fx.Reveal.js is present, transition the advice out
+			if (advice.dissolve) advice.dissolve();
+			else advice.setStyle('display', 'none');
+		}
+	},
+
+	getPropName: function(className){
+		return 'advice' + className;
+	},
+
+	resetField: function(field){
+		field = document.id(field);
+		if (!field) return this;
+		this.parent(field);
+		field.className.split(' ').each(function(className){
+			this.hideAdvice(className, field);
+		}, this);
+		return this;
+	},
+
+	getAllAdviceMessages: function(field, force){
+		var advice = [];
+		if (field.hasClass('ignoreValidation') && !force) return advice;
+		var validators = field.className.split(' ').some(function(cn){
+			var warner = cn.test('^warn-') || field.hasClass('warnOnly');
+			if (warner) cn = cn.replace(/^warn-/, '');
+			var validator = this.getValidator(cn);
+			if (!validator) return;
+			advice.push({
+				message: validator.getError(field),
+				warnOnly: warner,
+				passed: validator.test(),
+				validator: validator
+			});
+		}, this);
+		return advice;
+	},
+
+	getAdvice: function(className, field){
+		return field.retrieve('advice-' + className);
+	},
+
+	insertAdvice: function(advice, field){
+		//Check for error position prop
+		var props = field.get('validatorProps');
+		//Build advice
+		if (!props.msgPos || !document.id(props.msgPos)){
+			if(field.type.toLowerCase() == 'radio') field.getParent().adopt(advice);
+			else advice.inject(document.id(field), 'after');
+		} else {
+			document.id(props.msgPos).grab(advice);
+		}
+	},
+
+	validateField: function(field, force){
+		var result = this.parent(field, force);
+		if (this.options.scrollToErrorsOnSubmit && !result){
+			var failed = document.id(this).getElement('.validation-failed');
+			var par = document.id(this).getParent();
+			while (par != document.body && par.getScrollSize().y == par.getSize().y){
+				par = par.getParent();
+			}
+			var fx = par.retrieve('fvScroller');
+			if (!fx && window.Fx && Fx.Scroll){
+				fx = new Fx.Scroll(par, this.options.scrollFxOptions);
+				par.store('fvScroller', fx);
+			}
+			if (failed){
+				if (fx) fx.toElement(failed);
+				else par.scrollTo(par.getScroll().x, failed.getPosition(par).y - 20);
+			}
+		}
+		return result;
+	}
+
+});
+/*
+---
+
+script: Form.Validator.Extras.js
+
+description: Additional validators for the Form.Validator class.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - /Form.Validator
+
+provides: [Form.Validator.Extras]
+
+...
+*/
+Form.Validator.addAllThese([
+
+	['validate-enforce-oncheck', {
+		test: function(element, props){
+			if (element.checked){
+				var fv = element.getParent('form').retrieve('validator');
+				if (!fv) return true;
+				(props.toEnforce || document.id(props.enforceChildrenOf).getElements('input, select, textarea')).map(function(item){
+					fv.enforceField(item);
+				});
+			}
+			return true;
+		}
+	}],
+
+	['validate-ignore-oncheck', {
+		test: function(element, props){
+			if (element.checked){
+				var fv = element.getParent('form').retrieve('validator');
+				if (!fv) return true;
+				(props.toIgnore || document.id(props.ignoreChildrenOf).getElements('input, select, textarea')).each(function(item){
+					fv.ignoreField(item);
+					fv.resetField(item);
+				});
+			}
+			return true;
+		}
+	}],
+
+	['validate-nospace', {
+		errorMsg: function(){
+			return Form.Validator.getMsg('noSpace');
+		},
+		test: function(element, props){
+			return !element.get('value').test(/\s/);
+		}
+	}],
+
+	['validate-toggle-oncheck', {
+		test: function(element, props){
+			var fv = element.getParent('form').retrieve('validator');
+			if (!fv) return true;
+			var eleArr = props.toToggle || document.id(props.toToggleChildrenOf).getElements('input, select, textarea');
+			if (!element.checked){
+				eleArr.each(function(item){
+					fv.ignoreField(item);
+					fv.resetField(item);
+				});
+			} else {
+				eleArr.each(function(item){
+					fv.enforceField(item);
+				});
+			}
+			return true;
+		}
+	}],
+
+	['validate-reqchk-bynode', {
+		errorMsg: function(){
+			return Form.Validator.getMsg('reqChkByNode');
+		},
+		test: function(element, props){
+			return (document.id(props.nodeId).getElements(props.selector || 'input[type=checkbox], input[type=radio]')).some(function(item){
+				return item.checked;
+			});
+		}
+	}],
+
+	['validate-required-check', {
+		errorMsg: function(element, props){
+			return props.useTitle ? element.get('title') : Form.Validator.getMsg('requiredChk');
+		},
+		test: function(element, props){
+			return !!element.checked;
+		}
+	}],
+
+	['validate-reqchk-byname', {
+		errorMsg: function(element, props){
+			return Form.Validator.getMsg('reqChkByName').substitute({label: props.label || element.get('type')});
+		},
+		test: function(element, props){
+			var grpName = props.groupName || element.get('name');
+			var oneCheckedItem = $$(document.getElementsByName(grpName)).some(function(item, index){
+				return item.checked;
+			});
+			var fv = element.getParent('form').retrieve('validator');
+			if (oneCheckedItem && fv) fv.resetField(element);
+			return oneCheckedItem;
+		}
+	}],
+
+	['validate-match', {
+		errorMsg: function(element, props){
+			return Form.Validator.getMsg('match').substitute({matchName: props.matchName || document.id(props.matchInput).get('name')});
+		},
+		test: function(element, props){
+			var eleVal = element.get('value');
+			var matchVal = document.id(props.matchInput) && document.id(props.matchInput).get('value');
+			return eleVal && matchVal ? eleVal == matchVal : true;
+		}
+	}],
+
+	['validate-after-date', {
+		errorMsg: function(element, props){
+			return Form.Validator.getMsg('afterDate').substitute({
+				label: props.afterLabel || (props.afterElement ? Form.Validator.getMsg('startDate') : Form.Validator.getMsg('currentDate'))
+			});
+		},
+		test: function(element, props){
+			var start = document.id(props.afterElement) ? Date.parse(document.id(props.afterElement).get('value')) : new Date();
+			var end = Date.parse(element.get('value'));
+			return end && start ? end >= start : true;
+		}
+	}],
+
+	['validate-before-date', {
+		errorMsg: function(element, props){
+			return Form.Validator.getMsg('beforeDate').substitute({
+				label: props.beforeLabel || (props.beforeElement ? Form.Validator.getMsg('endDate') : Form.Validator.getMsg('currentDate'))
+			});
+		},
+		test: function(element, props){
+			var start = Date.parse(element.get('value'));
+			var end = document.id(props.beforeElement) ? Date.parse(document.id(props.beforeElement).get('value')) : new Date();
+			return end && start ? end >= start : true;
+		}
+	}],
+
+	['validate-custom-required', {
+		errorMsg: function(){
+			return Form.Validator.getMsg('required');
+		},
+		test: function(element, props){
+			return element.get('value') != props.emptyValue;
+		}
+	}],
+
+	['validate-same-month', {
+		errorMsg: function(element, props){
+			var startMo = document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value');
+			var eleVal = element.get('value');
+			if (eleVal != '') return Form.Validator.getMsg(startMo ? 'sameMonth' : 'startMonth');
+		},
+		test: function(element, props){
+			var d1 = Date.parse(element.get('value'));
+			var d2 = Date.parse(document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value'));
+			return d1 && d2 ? d1.format('%B') == d2.format('%B') : true;
+		}
+	}],
+
+
+	['validate-cc-num', {
+		errorMsg: function(element){
+			var ccNum = element.get('value').replace(/[^0-9]/g, '');
+			return Form.Validator.getMsg('creditcard').substitute({length: ccNum.length});
+		},
+		test: function(element){
+			// required is a different test
+			if (Form.Validator.getValidator('IsEmpty').test(element)) { return true; }
+
+			// Clean number value
+			var ccNum = element.get('value');
+			ccNum = ccNum.replace(/[^0-9]/g, '');
+
+			var valid_type = false;
+
+			if (ccNum.test(/^4[0-9]{12}([0-9]{3})?$/)) valid_type = 'Visa';
+			else if (ccNum.test(/^5[1-5]([0-9]{14})$/)) valid_type = 'Master Card';
+			else if (ccNum.test(/^3[47][0-9]{13}$/)) valid_type = 'American Express';
+			else if (ccNum.test(/^6011[0-9]{12}$/)) valid_type = 'Discover';
+
+			if (valid_type) {
+				var sum = 0;
+				var cur = 0;
+
+				for(var i=ccNum.length-1; i>=0; --i) {
+					cur = ccNum.charAt(i).toInt();
+					if (cur == 0) { continue; }
+
+					if ((ccNum.length-i) % 2 == 0) { cur += cur; }
+					if (cur > 9) { cur = cur.toString().charAt(0).toInt() + cur.toString().charAt(1).toInt(); }
+
+					sum += cur;
+				}
+				if ((sum % 10) == 0) { return true; }
+			}
+
+			var chunks = '';
+			while (ccNum != '') {
+				chunks += ' ' + ccNum.substr(0,4);
+				ccNum = ccNum.substr(4);
+			}
+
+			element.getParent('form').retrieve('validator').ignoreField(element);
+			element.set('value', chunks.clean());
+			element.getParent('form').retrieve('validator').enforceField(element);
+			return false;
+		}
+	}]
+
+
+]);/*
+---
+
+script: OverText.js
+
+description: Shows text over an input that disappears when the user clicks into it. The text remains hidden if the user adds a value.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Options
+ - core:1.2.4/Events
+ - core:1.2.4/Element.Event
+ - /Class.Binds
+ - /Class.Occlude
+ - /Element.Position
+ - /Element.Shortcuts
+
+provides: [OverText]
+
+...
+*/
+
+var OverText = new Class({
+
+	Implements: [Options, Events, Class.Occlude],
+
+	Binds: ['reposition', 'assert', 'focus', 'hide'],
+
+	options: {/*
+		textOverride: null,
+		onFocus: $empty()
+		onTextHide: $empty(textEl, inputEl),
+		onTextShow: $empty(textEl, inputEl), */
+		element: 'label',
+		positionOptions: {
+			position: 'upperLeft',
+			edge: 'upperLeft',
+			offset: {
+				x: 4,
+				y: 2
+			}
+		},
+		poll: false,
+		pollInterval: 250,
+		wrap: false
+	},
+
+	property: 'OverText',
+
+	initialize: function(element, options){
+		this.element = document.id(element);
+		if (this.occlude()) return this.occluded;
+		this.setOptions(options);
+		this.attach(this.element);
+		OverText.instances.push(this);
+		if (this.options.poll) this.poll();
+		return this;
+	},
+
+	toElement: function(){
+		return this.element;
+	},
+
+	attach: function(){
+		var val = this.options.textOverride || this.element.get('alt') || this.element.get('title');
+		if (!val) return;
+		this.text = new Element(this.options.element, {
+			'class': 'overTxtLabel',
+			styles: {
+				lineHeight: 'normal',
+				position: 'absolute',
+				cursor: 'text'
+			},
+			html: val,
+			events: {
+				click: this.hide.pass(this.options.element == 'label', this)
+			}
+		}).inject(this.element, 'after');
+		if (this.options.element == 'label') {
+			if (!this.element.get('id')) this.element.set('id', 'input_' + new Date().getTime());
+			this.text.set('for', this.element.get('id'));
+		}
+
+		if (this.options.wrap) {
+			this.textHolder = new Element('div', {
+				styles: {
+					lineHeight: 'normal',
+					position: 'relative'
+				},
+				'class':'overTxtWrapper'
+			}).adopt(this.text).inject(this.element, 'before');
+		}
+
+		this.element.addEvents({
+			focus: this.focus,
+			blur: this.assert,
+			change: this.assert
+		}).store('OverTextDiv', this.text);
+		window.addEvent('resize', this.reposition.bind(this));
+		this.assert(true);
+		this.reposition();
+	},
+
+	wrap: function(){
+		if (this.options.element == 'label') {
+			if (!this.element.get('id')) this.element.set('id', 'input_' + new Date().getTime());
+			this.text.set('for', this.element.get('id'));
+		}
+	},
+
+	startPolling: function(){
+		this.pollingPaused = false;
+		return this.poll();
+	},
+
+	poll: function(stop){
+		//start immediately
+		//pause on focus
+		//resumeon blur
+		if (this.poller && !stop) return this;
+		var test = function(){
+			if (!this.pollingPaused) this.assert(true);
+		}.bind(this);
+		if (stop) $clear(this.poller);
+		else this.poller = test.periodical(this.options.pollInterval, this);
+		return this;
+	},
+
+	stopPolling: function(){
+		this.pollingPaused = true;
+		return this.poll(true);
+	},
+
+	focus: function(){
+		if (this.text && (!this.text.isDisplayed() || this.element.get('disabled'))) return;
+		this.hide();
+	},
+
+	hide: function(suppressFocus, force){
+		if (this.text && (this.text.isDisplayed() && (!this.element.get('disabled') || force))){
+			this.text.hide();
+			this.fireEvent('textHide', [this.text, this.element]);
+			this.pollingPaused = true;
+			if (!suppressFocus){
+				try {
+					this.element.fireEvent('focus');
+					this.element.focus();
+				} catch(e){} //IE barfs if you call focus on hidden elements
+			}
+		}
+		return this;
+	},
+
+	show: function(){
+		if (this.text && !this.text.isDisplayed()){
+			this.text.show();
+			this.reposition();
+			this.fireEvent('textShow', [this.text, this.element]);
+			this.pollingPaused = false;
+		}
+		return this;
+	},
+
+	assert: function(suppressFocus){
+		this[this.test() ? 'show' : 'hide'](suppressFocus);
+	},
+
+	test: function(){
+		var v = this.element.get('value');
+		return !v;
+	},
+
+	reposition: function(){
+		this.assert(true);
+		if (!this.element.isVisible()) return this.stopPolling().hide();
+		if (this.text && this.test()) this.text.position($merge(this.options.positionOptions, {relativeTo: this.element}));
+		return this;
+	}
+
+});
+
+OverText.instances = [];
+
+$extend(OverText, {
+
+	each: function(fn) {
+		return OverText.instances.map(function(ot, i){
+			if (ot.element && ot.text) return fn.apply(OverText, [ot, i]);
+			return null; //the input or the text was destroyed
+		});
+	},
+	
+	update: function(){
+
+		return OverText.each(function(ot){
+			return ot.reposition();
+		});
+
+	},
+
+	hideAll: function(){
+
+		return OverText.each(function(ot){
+			return ot.hide(true, true);
+		});
+
+	},
+
+	showAll: function(){
+		return OverText.each(function(ot) {
+			return ot.show();
+		});
+	}
+
+});
+
+if (window.Fx && Fx.Reveal) {
+	Fx.Reveal.implement({
+		hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed, .overTxtLabel' : false
+	});
+}/*
+---
+
+script: Fx.Elements.js
+
+description: Effect to change any number of CSS properties of any number of Elements.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Fx.CSS
+ - /MooTools.More
+
+provides: [Fx.Elements]
+
+...
+*/
+
+Fx.Elements = new Class({
+
+	Extends: Fx.CSS,
+
+	initialize: function(elements, options){
+		this.elements = this.subject = $$(elements);
+		this.parent(options);
+	},
+
+	compute: function(from, to, delta){
+		var now = {};
+		for (var i in from){
+			var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
+			for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
+		}
+		return now;
+	},
+
+	set: function(now){
+		for (var i in now){
+			var iNow = now[i];
+			for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
+		}
+		return this;
+	},
+
+	start: function(obj){
+		if (!this.check(obj)) return this;
+		var from = {}, to = {};
+		for (var i in obj){
+			var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
+			for (var p in iProps){
+				var parsed = this.prepare(this.elements[i], p, iProps[p]);
+				iFrom[p] = parsed.from;
+				iTo[p] = parsed.to;
+			}
+		}
+		return this.parent(from, to);
+	}
+
+});/*
+---
+
+script: Fx.Accordion.js
+
+description: An Fx.Elements extension which allows you to easily create accordion type controls.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Element.Event
+ - /Fx.Elements
+
+provides: [Fx.Accordion]
+
+...
+*/
+
+Fx.Accordion = new Class({
+
+	Extends: Fx.Elements,
+
+	options: {/*
+		onActive: $empty(toggler, section),
+		onBackground: $empty(toggler, section),
+		fixedHeight: false,
+		fixedWidth: false,
+		*/
+		display: 0,
+		show: false,
+		height: true,
+		width: false,
+		opacity: true,
+		alwaysHide: false,
+		trigger: 'click',
+		initialDisplayFx: true,
+		returnHeightToAuto: true
+	},
+
+	initialize: function(){
+		var params = Array.link(arguments, {
+			'container': Element.type, //deprecated
+			'options': Object.type,
+			'togglers': $defined,
+			'elements': $defined
+		});
+		this.parent(params.elements, params.options);
+		this.togglers = $$(params.togglers);
+		this.previous = -1;
+		this.internalChain = new Chain();
+		if (this.options.alwaysHide) this.options.wait = true;
+		if ($chk(this.options.show)){
+			this.options.display = false;
+			this.previous = this.options.show;
+		}
+		if (this.options.start){
+			this.options.display = false;
+			this.options.show = false;
+		}
+		this.effects = {};
+		if (this.options.opacity) this.effects.opacity = 'fullOpacity';
+		if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
+		if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
+		for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
+		this.elements.each(function(el, i){
+			if (this.options.show === i){
+				this.fireEvent('active', [this.togglers[i], el]);
+			} else {
+				for (var fx in this.effects) el.setStyle(fx, 0);
+			}
+		}, this);
+		if ($chk(this.options.display) || this.options.initialDisplayFx === false) this.display(this.options.display, this.options.initialDisplayFx);
+		if (this.options.fixedHeight !== false) this.options.returnHeightToAuto = false;
+		this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain));
+	},
+
+	addSection: function(toggler, element){
+		toggler = document.id(toggler);
+		element = document.id(element);
+		var test = this.togglers.contains(toggler);
+		this.togglers.include(toggler);
+		this.elements.include(element);
+		var idx = this.togglers.indexOf(toggler);
+		var displayer = this.display.bind(this, idx);
+		toggler.store('accordion:display', displayer);
+		toggler.addEvent(this.options.trigger, displayer);
+		if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
+		if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
+		element.fullOpacity = 1;
+		if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
+		if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
+		element.setStyle('overflow', 'hidden');
+		if (!test){
+			for (var fx in this.effects) element.setStyle(fx, 0);
+		}
+		return this;
+	},
+
+	detach: function(){
+		this.togglers.each(function(toggler) {
+			toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display'));
+		}, this);
+	},
+
+	display: function(index, useFx){
+		if (!this.check(index, useFx)) return this;
+		useFx = $pick(useFx, true);
+		if (this.options.returnHeightToAuto){
+			var prev = this.elements[this.previous];
+			if (prev && !this.selfHidden){
+				for (var fx in this.effects){
+					prev.setStyle(fx, prev[this.effects[fx]]);
+				}
+			}
+		}
+		index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
+		if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
+		this.previous = index;
+		var obj = {};
+		this.elements.each(function(el, i){
+			obj[i] = {};
+			var hide;
+			if (i != index){
+				hide = true;
+			} else if (this.options.alwaysHide && ((el.offsetHeight > 0 && this.options.height) || el.offsetWidth > 0 && this.options.width)){
+				hide = true;
+				this.selfHidden = true;
+			}
+			this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
+			for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
+		}, this);
+		this.internalChain.chain(function(){
+			if (this.options.returnHeightToAuto && !this.selfHidden){
+				var el = this.elements[index];
+				if (el) el.setStyle('height', 'auto');
+			};
+		}.bind(this));
+		return useFx ? this.start(obj) : this.set(obj);
+	}
+
+});
+
+/*
+	Compatibility with 1.2.0
+*/
+var Accordion = new Class({
+
+	Extends: Fx.Accordion,
+
+	initialize: function(){
+		this.parent.apply(this, arguments);
+		var params = Array.link(arguments, {'container': Element.type});
+		this.container = params.container;
+	},
+
+	addSection: function(toggler, element, pos){
+		toggler = document.id(toggler);
+		element = document.id(element);
+		var test = this.togglers.contains(toggler);
+		var len = this.togglers.length;
+		if (len && (!test || pos)){
+			pos = $pick(pos, len - 1);
+			toggler.inject(this.togglers[pos], 'before');
+			element.inject(toggler, 'after');
+		} else if (this.container && !test){
+			toggler.inject(this.container);
+			element.inject(this.container);
+		}
+		return this.parent.apply(this, arguments);
+	}
+
+});/*
+---
+
+script: Fx.Move.js
+
+description: Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Fx.Morph
+ - /Element.Position
+
+provides: [Fx.Move]
+
+...
+*/
+
+Fx.Move = new Class({
+
+	Extends: Fx.Morph,
+
+	options: {
+		relativeTo: document.body,
+		position: 'center',
+		edge: false,
+		offset: {x: 0, y: 0}
+	},
+
+	start: function(destination){
+		return this.parent(this.element.position($merge(this.options, destination, {returnPos: true})));
+	}
+
+});
+
+Element.Properties.move = {
+
+	set: function(options){
+		var morph = this.retrieve('move');
+		if (morph) morph.cancel();
+		return this.eliminate('move').store('move:options', $extend({link: 'cancel'}, options));
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('move')){
+			if (options || !this.retrieve('move:options')) this.set('move', options);
+			this.store('move', new Fx.Move(this, this.retrieve('move:options')));
+		}
+		return this.retrieve('move');
+	}
+
+};
+
+Element.implement({
+
+	move: function(options){
+		this.get('move').start(options);
+		return this;
+	}
+
+});
+/*
+---
+
+script: Fx.Scroll.js
+
+description: Effect to smoothly scroll any element, including the window.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Fx
+ - core:1.2.4/Element.Event
+ - core:1.2.4/Element.Dimensions
+ - /MooTools.More
+
+provides: [Fx.Scroll]
+
+...
+*/
+
+Fx.Scroll = new Class({
+
+	Extends: Fx,
+
+	options: {
+		offset: {x: 0, y: 0},
+		wheelStops: true
+	},
+
+	initialize: function(element, options){
+		this.element = this.subject = document.id(element);
+		this.parent(options);
+		var cancel = this.cancel.bind(this, false);
+
+		if ($type(this.element) != 'element') this.element = document.id(this.element.getDocument().body);
+
+		var stopper = this.element;
+
+		if (this.options.wheelStops){
+			this.addEvent('start', function(){
+				stopper.addEvent('mousewheel', cancel);
+			}, true);
+			this.addEvent('complete', function(){
+				stopper.removeEvent('mousewheel', cancel);
+			}, true);
+		}
+	},
+
+	set: function(){
+		var now = Array.flatten(arguments);
+		if (Browser.Engine.gecko) now = [Math.round(now[0]), Math.round(now[1])];
+		this.element.scrollTo(now[0], now[1]);
+	},
+
+	compute: function(from, to, delta){
+		return [0, 1].map(function(i){
+			return Fx.compute(from[i], to[i], delta);
+		});
+	},
+
+	start: function(x, y){
+		if (!this.check(x, y)) return this;
+		var scrollSize = this.element.getScrollSize(),
+			scroll = this.element.getScroll(), 
+			values = {x: x, y: y};
+		for (var z in values){
+			var max = scrollSize[z];
+			if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z] : max;
+			else values[z] = scroll[z];
+			values[z] += this.options.offset[z];
+		}
+		return this.parent([scroll.x, scroll.y], [values.x, values.y]);
+	},
+
+	toTop: function(){
+		return this.start(false, 0);
+	},
+
+	toLeft: function(){
+		return this.start(0, false);
+	},
+
+	toRight: function(){
+		return this.start('right', false);
+	},
+
+	toBottom: function(){
+		return this.start(false, 'bottom');
+	},
+
+	toElement: function(el){
+		var position = document.id(el).getPosition(this.element);
+		return this.start(position.x, position.y);
+	},
+
+	scrollIntoView: function(el, axes, offset){
+		axes = axes ? $splat(axes) : ['x','y'];
+		var to = {};
+		el = document.id(el);
+		var pos = el.getPosition(this.element);
+		var size = el.getSize();
+		var scroll = this.element.getScroll();
+		var containerSize = this.element.getSize();
+		var edge = {
+			x: pos.x + size.x,
+			y: pos.y + size.y
+		};
+		['x','y'].each(function(axis) {
+			if (axes.contains(axis)) {
+				if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis];
+				if (pos[axis] < scroll[axis]) to[axis] = pos[axis];
+			}
+			if (to[axis] == null) to[axis] = scroll[axis];
+			if (offset && offset[axis]) to[axis] = to[axis] + offset[axis];
+		}, this);
+		if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
+		return this;
+	},
+
+	scrollToCenter: function(el, axes, offset){
+		axes = axes ? $splat(axes) : ['x', 'y'];
+		el = $(el);
+		var to = {},
+			pos = el.getPosition(this.element),
+			size = el.getSize(),
+			scroll = this.element.getScroll(),
+			containerSize = this.element.getSize(),
+			edge = {
+				x: pos.x + size.x,
+				y: pos.y + size.y
+			};
+
+		['x','y'].each(function(axis){
+			if(axes.contains(axis)){
+				to[axis] = pos[axis] - (containerSize[axis] - size[axis])/2;
+			}
+			if(to[axis] == null) to[axis] = scroll[axis];
+			if(offset && offset[axis]) to[axis] = to[axis] + offset[axis];
+		}, this);
+		if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y);
+		return this;
+	}
+
+});
+/*
+---
+
+script: Fx.Slide.js
+
+description: Effect to slide an element in and out of view.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Fx Element.Style
+ - /MooTools.More
+
+provides: [Fx.Slide]
+
+...
+*/
+
+Fx.Slide = new Class({
+
+	Extends: Fx,
+
+	options: {
+		mode: 'vertical',
+		wrapper: false,
+		hideOverflow: true
+	},
+
+	initialize: function(element, options){
+		this.addEvent('complete', function(){
+			this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
+			if (this.open) this.wrapper.setStyle('height', '');
+			if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
+		}, true);
+		this.element = this.subject = document.id(element);
+		this.parent(options);
+		var wrapper = this.element.retrieve('wrapper');
+		var styles = this.element.getStyles('margin', 'position', 'overflow');
+		if (this.options.hideOverflow) styles = $extend(styles, {overflow: 'hidden'});
+		if (this.options.wrapper) wrapper = document.id(this.options.wrapper).setStyles(styles);
+		this.wrapper = wrapper || new Element('div', {
+			styles: styles
+		}).wraps(this.element);
+		this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
+		this.now = [];
+		this.open = true;
+	},
+
+	vertical: function(){
+		this.margin = 'margin-top';
+		this.layout = 'height';
+		this.offset = this.element.offsetHeight;
+	},
+
+	horizontal: function(){
+		this.margin = 'margin-left';
+		this.layout = 'width';
+		this.offset = this.element.offsetWidth;
+	},
+
+	set: function(now){
+		this.element.setStyle(this.margin, now[0]);
+		this.wrapper.setStyle(this.layout, now[1]);
+		return this;
+	},
+
+	compute: function(from, to, delta){
+		return [0, 1].map(function(i){
+			return Fx.compute(from[i], to[i], delta);
+		});
+	},
+
+	start: function(how, mode){
+		if (!this.check(how, mode)) return this;
+		this[mode || this.options.mode]();
+		var margin = this.element.getStyle(this.margin).toInt();
+		var layout = this.wrapper.getStyle(this.layout).toInt();
+		var caseIn = [[margin, layout], [0, this.offset]];
+		var caseOut = [[margin, layout], [-this.offset, 0]];
+		var start;
+		switch (how){
+			case 'in': start = caseIn; break;
+			case 'out': start = caseOut; break;
+			case 'toggle': start = (layout == 0) ? caseIn : caseOut;
+		}
+		return this.parent(start[0], start[1]);
+	},
+
+	slideIn: function(mode){
+		return this.start('in', mode);
+	},
+
+	slideOut: function(mode){
+		return this.start('out', mode);
+	},
+
+	hide: function(mode){
+		this[mode || this.options.mode]();
+		this.open = false;
+		return this.set([-this.offset, 0]);
+	},
+
+	show: function(mode){
+		this[mode || this.options.mode]();
+		this.open = true;
+		return this.set([0, this.offset]);
+	},
+
+	toggle: function(mode){
+		return this.start('toggle', mode);
+	}
+
+});
+
+Element.Properties.slide = {
+
+	set: function(options){
+		var slide = this.retrieve('slide');
+		if (slide) slide.cancel();
+		return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));
+	},
+
+	get: function(options){
+		if (options || !this.retrieve('slide')){
+			if (options || !this.retrieve('slide:options')) this.set('slide', options);
+			this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
+		}
+		return this.retrieve('slide');
+	}
+
+};
+
+Element.implement({
+
+	slide: function(how, mode){
+		how = how || 'toggle';
+		var slide = this.get('slide'), toggle;
+		switch (how){
+			case 'hide': slide.hide(mode); break;
+			case 'show': slide.show(mode); break;
+			case 'toggle':
+				var flag = this.retrieve('slide:flag', slide.open);
+				slide[flag ? 'slideOut' : 'slideIn'](mode);
+				this.store('slide:flag', !flag);
+				toggle = true;
+			break;
+			default: slide.start(how, mode);
+		}
+		if (!toggle) this.eliminate('slide:flag');
+		return this;
+	}
+
+});
+/*
+---
+
+script: Fx.SmoothScroll.js
+
+description: Class for creating a smooth scrolling effect to all internal links on the page.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Selectors
+ - /Fx.Scroll
+
+provides: [Fx.SmoothScroll]
+
+...
+*/
+
+var SmoothScroll = Fx.SmoothScroll = new Class({
+
+	Extends: Fx.Scroll,
+
+	initialize: function(options, context){
+		context = context || document;
+		this.doc = context.getDocument();
+		var win = context.getWindow();
+		this.parent(this.doc, options);
+		this.links = $$(this.options.links || this.doc.links);
+		var location = win.location.href.match(/^[^#]*/)[0] + '#';
+		this.links.each(function(link){
+			if (link.href.indexOf(location) != 0) {return;}
+			var anchor = link.href.substr(location.length);
+			if (anchor) this.useLink(link, anchor);
+		}, this);
+		if (!Browser.Engine.webkit419) {
+			this.addEvent('complete', function(){
+				win.location.hash = this.anchor;
+			}, true);
+		}
+	},
+
+	useLink: function(link, anchor){
+		var el;
+		link.addEvent('click', function(event){
+			if (el !== false && !el) el = document.id(anchor) || this.doc.getElement('a[name=' + anchor + ']');
+			if (el) {
+				event.preventDefault();
+				this.anchor = anchor;
+				this.toElement(el).chain(function(){
+					this.fireEvent('scrolledTo', [link, el]);
+				}.bind(this));
+				link.blur();
+			}
+		}.bind(this));
+	}
+});/*
+---
+
+script: Fx.Sort.js
+
+description: Defines Fx.Sort, a class that reorders lists with a transition.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element.Dimensions
+ - /Fx.Elements
+ - /Element.Measure
+
+provides: [Fx.Sort]
+
+...
+*/
+
+Fx.Sort = new Class({
+
+	Extends: Fx.Elements,
+
+	options: {
+		mode: 'vertical'
+	},
+
+	initialize: function(elements, options){
+		this.parent(elements, options);
+		this.elements.each(function(el){
+			if (el.getStyle('position') == 'static') el.setStyle('position', 'relative');
+		});
+		this.setDefaultOrder();
+	},
+
+	setDefaultOrder: function(){
+		this.currentOrder = this.elements.map(function(el, index){
+			return index;
+		});
+	},
+
+	sort: function(newOrder){
+		if ($type(newOrder) != 'array') return false;
+		var top = 0,
+			left = 0,
+			next = {},
+			zero = {},
+			vert = this.options.mode == 'vertical';
+		var current = this.elements.map(function(el, index){
+			var size = el.getComputedSize({styles: ['border', 'padding', 'margin']});
+			var val;
+			if (vert){
+				val = {
+					top: top,
+					margin: size['margin-top'],
+					height: size.totalHeight
+				};
+				top += val.height - size['margin-top'];
+			} else {
+				val = {
+					left: left,
+					margin: size['margin-left'],
+					width: size.totalWidth
+				};
+				left += val.width;
+			}
+			var plain = vert ? 'top' : 'left';
+			zero[index] = {};
+			var start = el.getStyle(plain).toInt();
+			zero[index][plain] = start || 0;
+			return val;
+		}, this);
+		this.set(zero);
+		newOrder = newOrder.map(function(i){ return i.toInt(); });
+		if (newOrder.length != this.elements.length){
+			this.currentOrder.each(function(index){
+				if (!newOrder.contains(index)) newOrder.push(index);
+			});
+			if (newOrder.length > this.elements.length)
+				newOrder.splice(this.elements.length-1, newOrder.length - this.elements.length);
+		}
+		var margin = top = left = 0;
+		newOrder.each(function(item, index){
+			var newPos = {};
+			if (vert){
+				newPos.top = top - current[item].top - margin;
+				top += current[item].height;
+			} else {
+				newPos.left = left - current[item].left;
+				left += current[item].width;
+			}
+			margin = margin + current[item].margin;
+			next[item]=newPos;
+		}, this);
+		var mapped = {};
+		$A(newOrder).sort().each(function(index){
+			mapped[index] = next[index];
+		});
+		this.start(mapped);
+		this.currentOrder = newOrder;
+		return this;
+	},
+
+	rearrangeDOM: function(newOrder){
+		newOrder = newOrder || this.currentOrder;
+		var parent = this.elements[0].getParent();
+		var rearranged = [];
+		this.elements.setStyle('opacity', 0);
+		//move each element and store the new default order
+		newOrder.each(function(index){
+			rearranged.push(this.elements[index].inject(parent).setStyles({
+				top: 0,
+				left: 0
+			}));
+		}, this);
+		this.elements.setStyle('opacity', 1);
+		this.elements = $$(rearranged);
+		this.setDefaultOrder();
+		return this;
+	},
+
+	getDefaultOrder: function(){
+		return this.elements.map(function(el, index){
+			return index;
+		});
+	},
+
+	forward: function(){
+		return this.sort(this.getDefaultOrder());
+	},
+
+	backward: function(){
+		return this.sort(this.getDefaultOrder().reverse());
+	},
+
+	reverse: function(){
+		return this.sort(this.currentOrder.reverse());
+	},
+
+	sortByElements: function(elements){
+		return this.sort(elements.map(function(el){
+			return this.elements.indexOf(el);
+		}, this));
+	},
+
+	swap: function(one, two){
+		if ($type(one) == 'element') one = this.elements.indexOf(one);
+		if ($type(two) == 'element') two = this.elements.indexOf(two);
+		
+		var newOrder = $A(this.currentOrder);
+		newOrder[this.currentOrder.indexOf(one)] = two;
+		newOrder[this.currentOrder.indexOf(two)] = one;
+		return this.sort(newOrder);
+	}
+
+});/*
+---
+
+script: Drag.js
+
+description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Tom Occhinno
+ - Jan Kassens
+
+requires:
+ - core:1.2.4/Events
+ - core:1.2.4/Options
+ - core:1.2.4/Element.Event
+ - core:1.2.4/Element.Style
+ - /MooTools.More
+
+provides: [Drag]
+
+...
+*/
+
+var Drag = new Class({
+
+	Implements: [Events, Options],
+
+	options: {/*
+		onBeforeStart: $empty(thisElement),
+		onStart: $empty(thisElement, event),
+		onSnap: $empty(thisElement)
+		onDrag: $empty(thisElement, event),
+		onCancel: $empty(thisElement),
+		onComplete: $empty(thisElement, event),*/
+		snap: 6,
+		unit: 'px',
+		grid: false,
+		style: true,
+		limit: false,
+		handle: false,
+		invert: false,
+		preventDefault: false,
+		stopPropagation: false,
+		modifiers: {x: 'left', y: 'top'}
+	},
+
+	initialize: function(){
+		var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
+		this.element = document.id(params.element);
+		this.document = this.element.getDocument();
+		this.setOptions(params.options || {});
+		var htype = $type(this.options.handle);
+		this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
+		this.mouse = {'now': {}, 'pos': {}};
+		this.value = {'start': {}, 'now': {}};
+
+		this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
+
+		this.bound = {
+			start: this.start.bind(this),
+			check: this.check.bind(this),
+			drag: this.drag.bind(this),
+			stop: this.stop.bind(this),
+			cancel: this.cancel.bind(this),
+			eventStop: $lambda(false)
+		};
+		this.attach();
+	},
+
+	attach: function(){
+		this.handles.addEvent('mousedown', this.bound.start);
+		return this;
+	},
+
+	detach: function(){
+		this.handles.removeEvent('mousedown', this.bound.start);
+		return this;
+	},
+
+	start: function(event){
+		if (event.rightClick) return;
+		if (this.options.preventDefault) event.preventDefault();
+		if (this.options.stopPropagation) event.stopPropagation();
+		this.mouse.start = event.page;
+		this.fireEvent('beforeStart', this.element);
+		var limit = this.options.limit;
+		this.limit = {x: [], y: []};
+		for (var z in this.options.modifiers){
+			if (!this.options.modifiers[z]) continue;
+			if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
+			else this.value.now[z] = this.element[this.options.modifiers[z]];
+			if (this.options.invert) this.value.now[z] *= -1;
+			this.mouse.pos[z] = event.page[z] - this.value.now[z];
+			if (limit && limit[z]){
+				for (var i = 2; i--; i){
+					if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
+				}
+			}
+		}
+		if ($type(this.options.grid) == 'number') this.options.grid = {x: this.options.grid, y: this.options.grid};
+		this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
+		this.document.addEvent(this.selection, this.bound.eventStop);
+	},
+
+	check: function(event){
+		if (this.options.preventDefault) event.preventDefault();
+		var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
+		if (distance > this.options.snap){
+			this.cancel();
+			this.document.addEvents({
+				mousemove: this.bound.drag,
+				mouseup: this.bound.stop
+			});
+			this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
+		}
+	},
+
+	drag: function(event){
+		if (this.options.preventDefault) event.preventDefault();
+		this.mouse.now = event.page;
+		for (var z in this.options.modifiers){
+			if (!this.options.modifiers[z]) continue;
+			this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
+			if (this.options.invert) this.value.now[z] *= -1;
+			if (this.options.limit && this.limit[z]){
+				if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
+					this.value.now[z] = this.limit[z][1];
+				} else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
+					this.value.now[z] = this.limit[z][0];
+				}
+			}
+			if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % this.options.grid[z]);
+			if (this.options.style) {
+				this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
+			} else {
+				this.element[this.options.modifiers[z]] = this.value.now[z];
+			}
+		}
+		this.fireEvent('drag', [this.element, event]);
+	},
+
+	cancel: function(event){
+		this.document.removeEvent('mousemove', this.bound.check);
+		this.document.removeEvent('mouseup', this.bound.cancel);
+		if (event){
+			this.document.removeEvent(this.selection, this.bound.eventStop);
+			this.fireEvent('cancel', this.element);
+		}
+	},
+
+	stop: function(event){
+		this.document.removeEvent(this.selection, this.bound.eventStop);
+		this.document.removeEvent('mousemove', this.bound.drag);
+		this.document.removeEvent('mouseup', this.bound.stop);
+		if (event) this.fireEvent('complete', [this.element, event]);
+	}
+
+});
+
+Element.implement({
+
+	makeResizable: function(options){
+		var drag = new Drag(this, $merge({modifiers: {x: 'width', y: 'height'}}, options));
+		this.store('resizer', drag);
+		return drag.addEvent('drag', function(){
+			this.fireEvent('resize', drag);
+		}.bind(this));
+	}
+
+});
+/*
+---
+
+script: Drag.Move.js
+
+description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Tom Occhinno
+ - Jan Kassens
+ - Aaron Newton
+ - Scott Kyle
+
+requires:
+ - core:1.2.4/Element.Dimensions
+ - /Drag
+
+provides: [Drag.Move]
+
+...
+*/
+
+Drag.Move = new Class({
+
+	Extends: Drag,
+
+	options: {/*
+		onEnter: $empty(thisElement, overed),
+		onLeave: $empty(thisElement, overed),
+		onDrop: $empty(thisElement, overed, event),*/
+		droppables: [],
+		container: false,
+		precalculate: false,
+		includeMargins: true,
+		checkDroppables: true
+	},
+
+	initialize: function(element, options){
+		this.parent(element, options);
+		element = this.element;
+		
+		this.droppables = $$(this.options.droppables);
+		this.container = document.id(this.options.container);
+		
+		if (this.container && $type(this.container) != 'element')
+			this.container = document.id(this.container.getDocument().body);
+		
+		var styles = element.getStyles('left', 'top', 'position');
+		if (styles.left == 'auto' || styles.top == 'auto')
+			element.setPosition(element.getPosition(element.getOffsetParent()));
+		
+		if (styles.position == 'static')
+			element.setStyle('position', 'absolute');
+
+		this.addEvent('start', this.checkDroppables, true);
+
+		this.overed = null;
+	},
+
+	start: function(event){
+		if (this.container) this.options.limit = this.calculateLimit();
+		
+		if (this.options.precalculate){
+			this.positions = this.droppables.map(function(el){
+				return el.getCoordinates();
+			});
+		}
+		
+		this.parent(event);
+	},
+	
+	calculateLimit: function(){
+		var offsetParent = this.element.getOffsetParent(),
+			containerCoordinates = this.container.getCoordinates(offsetParent),
+			containerBorder = {},
+			elementMargin = {},
+			elementBorder = {},
+			containerMargin = {},
+			offsetParentPadding = {};
+
+		['top', 'right', 'bottom', 'left'].each(function(pad){
+			containerBorder[pad] = this.container.getStyle('border-' + pad).toInt();
+			elementBorder[pad] = this.element.getStyle('border-' + pad).toInt();
+			elementMargin[pad] = this.element.getStyle('margin-' + pad).toInt();
+			containerMargin[pad] = this.container.getStyle('margin-' + pad).toInt();
+			offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
+		}, this);
+
+		var width = this.element.offsetWidth + elementMargin.left + elementMargin.right,
+			height = this.element.offsetHeight + elementMargin.top + elementMargin.bottom,
+			left = 0,
+			top = 0,
+			right = containerCoordinates.right - containerBorder.right - width,
+			bottom = containerCoordinates.bottom - containerBorder.bottom - height;
+
+		if (this.options.includeMargins){
+			left += elementMargin.left;
+			top += elementMargin.top;
+		} else {
+			right += elementMargin.right;
+			bottom += elementMargin.bottom;
+		}
+		
+		if (this.element.getStyle('position') == 'relative'){
+			var coords = this.element.getCoordinates(offsetParent);
+			coords.left -= this.element.getStyle('left').toInt();
+			coords.top -= this.element.getStyle('top').toInt();
+			
+			left += containerBorder.left - coords.left;
+			top += containerBorder.top - coords.top;
+			right += elementMargin.left - coords.left;
+			bottom += elementMargin.top - coords.top;
+			
+			if (this.container != offsetParent){
+				left += containerMargin.left + offsetParentPadding.left;
+				top += (Browser.Engine.trident4 ? 0 : containerMargin.top) + offsetParentPadding.top;
+			}
+		} else {
+			left -= elementMargin.left;
+			top -= elementMargin.top;
+			
+			if (this.container == offsetParent){
+				right -= containerBorder.left;
+				bottom -= containerBorder.top;
+			} else {
+				left += containerCoordinates.left + containerBorder.left;
+				top += containerCoordinates.top + containerBorder.top;
+			}
+		}
+		
+		return {
+			x: [left, right],
+			y: [top, bottom]
+		};
+	},
+
+	checkAgainst: function(el, i){
+		el = (this.positions) ? this.positions[i] : el.getCoordinates();
+		var now = this.mouse.now;
+		return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
+	},
+
+	checkDroppables: function(){
+		var overed = this.droppables.filter(this.checkAgainst, this).getLast();
+		if (this.overed != overed){
+			if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
+			if (overed) this.fireEvent('enter', [this.element, overed]);
+			this.overed = overed;
+		}
+	},
+
+	drag: function(event){
+		this.parent(event);
+		if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
+	},
+
+	stop: function(event){
+		this.checkDroppables();
+		this.fireEvent('drop', [this.element, this.overed, event]);
+		this.overed = null;
+		return this.parent(event);
+	}
+
+});
+
+Element.implement({
+
+	makeDraggable: function(options){
+		var drag = new Drag.Move(this, options);
+		this.store('dragger', drag);
+		return drag;
+	}
+
+});
+/*
+---
+
+script: Slider.js
+
+description: Class for creating horizontal and vertical slider controls.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Element.Dimensions
+ - /Class.Binds
+ - /Drag
+ - /Element.Dimensions
+ - /Element.Measure
+
+provides: [Slider]
+
+...
+*/
+
+var Slider = new Class({
+
+	Implements: [Events, Options],
+
+	Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'],
+
+	options: {/*
+		onTick: $empty(intPosition),
+		onChange: $empty(intStep),
+		onComplete: $empty(strStep),*/
+		onTick: function(position){
+			if (this.options.snap) position = this.toPosition(this.step);
+			this.knob.setStyle(this.property, position);
+		},
+		initialStep: 0,
+		snap: false,
+		offset: 0,
+		range: false,
+		wheel: false,
+		steps: 100,
+		mode: 'horizontal'
+	},
+
+	initialize: function(element, knob, options){
+		this.setOptions(options);
+		this.element = document.id(element);
+		this.knob = document.id(knob);
+		this.previousChange = this.previousEnd = this.step = -1;
+		var offset, limit = {}, modifiers = {'x': false, 'y': false};
+		switch (this.options.mode){
+			case 'vertical':
+				this.axis = 'y';
+				this.property = 'top';
+				offset = 'offsetHeight';
+				break;
+			case 'horizontal':
+				this.axis = 'x';
+				this.property = 'left';
+				offset = 'offsetWidth';
+		}
+		
+		this.full = this.element.measure(function(){ 
+			this.half = this.knob[offset] / 2; 
+			return this.element[offset] - this.knob[offset] + (this.options.offset * 2); 
+		}.bind(this));
+		
+		this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
+		this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
+		this.range = this.max - this.min;
+		this.steps = this.options.steps || this.full;
+		this.stepSize = Math.abs(this.range) / this.steps;
+		this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
+
+		this.knob.setStyle('position', 'relative').setStyle(this.property, this.options.initialStep ? this.toPosition(this.options.initialStep) : - this.options.offset);
+		modifiers[this.axis] = this.property;
+		limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
+
+		var dragOptions = {
+			snap: 0,
+			limit: limit,
+			modifiers: modifiers,
+			onDrag: this.draggedKnob,
+			onStart: this.draggedKnob,
+			onBeforeStart: (function(){
+				this.isDragging = true;
+			}).bind(this),
+			onCancel: function() {
+				this.isDragging = false;
+			}.bind(this),
+			onComplete: function(){
+				this.isDragging = false;
+				this.draggedKnob();
+				this.end();
+			}.bind(this)
+		};
+		if (this.options.snap){
+			dragOptions.grid = Math.ceil(this.stepWidth);
+			dragOptions.limit[this.axis][1] = this.full;
+		}
+
+		this.drag = new Drag(this.knob, dragOptions);
+		this.attach();
+	},
+
+	attach: function(){
+		this.element.addEvent('mousedown', this.clickedElement);
+		if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement);
+		this.drag.attach();
+		return this;
+	},
+
+	detach: function(){
+		this.element.removeEvent('mousedown', this.clickedElement);
+		this.element.removeEvent('mousewheel', this.scrolledElement);
+		this.drag.detach();
+		return this;
+	},
+
+	set: function(step){
+		if (!((this.range > 0) ^ (step < this.min))) step = this.min;
+		if (!((this.range > 0) ^ (step > this.max))) step = this.max;
+
+		this.step = Math.round(step);
+		this.checkStep();
+		this.fireEvent('tick', this.toPosition(this.step));
+		this.end();
+		return this;
+	},
+
+	clickedElement: function(event){
+		if (this.isDragging || event.target == this.knob) return;
+
+		var dir = this.range < 0 ? -1 : 1;
+		var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
+		position = position.limit(-this.options.offset, this.full -this.options.offset);
+
+		this.step = Math.round(this.min + dir * this.toStep(position));
+		this.checkStep();
+		this.fireEvent('tick', position);
+		this.end();
+	},
+
+	scrolledElement: function(event){
+		var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
+		this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
+		event.stop();
+	},
+
+	draggedKnob: function(){
+		var dir = this.range < 0 ? -1 : 1;
+		var position = this.drag.value.now[this.axis];
+		position = position.limit(-this.options.offset, this.full -this.options.offset);
+		this.step = Math.round(this.min + dir * this.toStep(position));
+		this.checkStep();
+	},
+
+	checkStep: function(){
+		if (this.previousChange != this.step){
+			this.previousChange = this.step;
+			this.fireEvent('change', this.step);
+		}
+	},
+
+	end: function(){
+		if (this.previousEnd !== this.step){
+			this.previousEnd = this.step;
+			this.fireEvent('complete', this.step + '');
+		}
+	},
+
+	toStep: function(position){
+		var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
+		return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
+	},
+
+	toPosition: function(step){
+		return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
+	}
+
+});/*
+---
+
+script: Sortables.js
+
+description: Class for creating a drag and drop sorting interface for lists of items.
+
+license: MIT-style license
+
+authors:
+ - Tom Occhino
+
+requires:
+ - /Drag.Move
+
+provides: [Slider]
+
+...
+*/
+
+var Sortables = new Class({
+
+	Implements: [Events, Options],
+
+	options: {/*
+		onSort: $empty(element, clone),
+		onStart: $empty(element, clone),
+		onComplete: $empty(element),*/
+		snap: 4,
+		opacity: 1,
+		clone: false,
+		revert: false,
+		handle: false,
+		constrain: false
+	},
+
+	initialize: function(lists, options){
+		this.setOptions(options);
+		this.elements = [];
+		this.lists = [];
+		this.idle = true;
+
+		this.addLists($$(document.id(lists) || lists));
+		if (!this.options.clone) this.options.revert = false;
+		if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
+	},
+
+	attach: function(){
+		this.addLists(this.lists);
+		return this;
+	},
+
+	detach: function(){
+		this.lists = this.removeLists(this.lists);
+		return this;
+	},
+
+	addItems: function(){
+		Array.flatten(arguments).each(function(element){
+			this.elements.push(element);
+			var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element));
+			(this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
+		}, this);
+		return this;
+	},
+
+	addLists: function(){
+		Array.flatten(arguments).each(function(list){
+			this.lists.push(list);
+			this.addItems(list.getChildren());
+		}, this);
+		return this;
+	},
+
+	removeItems: function(){
+		return $$(Array.flatten(arguments).map(function(element){
+			this.elements.erase(element);
+			var start = element.retrieve('sortables:start');
+			(this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
+			
+			return element;
+		}, this));
+	},
+
+	removeLists: function(){
+		return $$(Array.flatten(arguments).map(function(list){
+			this.lists.erase(list);
+			this.removeItems(list.getChildren());
+			
+			return list;
+		}, this));
+	},
+
+	getClone: function(event, element){
+		if (!this.options.clone) return new Element('div').inject(document.body);
+		if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
+		var clone = element.clone(true).setStyles({
+			margin: '0px',
+			position: 'absolute',
+			visibility: 'hidden',
+			'width': element.getStyle('width')
+		});
+		//prevent the duplicated radio inputs from unchecking the real one
+		if (clone.get('html').test('radio')) {
+			clone.getElements('input[type=radio]').each(function(input, i) {
+				input.set('name', 'clone_' + i);
+			});
+		}
+		
+		return clone.inject(this.list).setPosition(element.getPosition(element.getOffsetParent()));
+	},
+
+	getDroppables: function(){
+		var droppables = this.list.getChildren();
+		if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list);
+		return droppables.erase(this.clone).erase(this.element);
+	},
+
+	insert: function(dragging, element){
+		var where = 'inside';
+		if (this.lists.contains(element)){
+			this.list = element;
+			this.drag.droppables = this.getDroppables();
+		} else {
+			where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
+		}
+		this.element.inject(element, where);
+		this.fireEvent('sort', [this.element, this.clone]);
+	},
+
+	start: function(event, element){
+		if (!this.idle) return;
+		this.idle = false;
+		this.element = element;
+		this.opacity = element.get('opacity');
+		this.list = element.getParent();
+		this.clone = this.getClone(event, element);
+
+		this.drag = new Drag.Move(this.clone, {
+			snap: this.options.snap,
+			container: this.options.constrain && this.element.getParent(),
+			droppables: this.getDroppables(),
+			onSnap: function(){
+				event.stop();
+				this.clone.setStyle('visibility', 'visible');
+				this.element.set('opacity', this.options.opacity || 0);
+				this.fireEvent('start', [this.element, this.clone]);
+			}.bind(this),
+			onEnter: this.insert.bind(this),
+			onCancel: this.reset.bind(this),
+			onComplete: this.end.bind(this)
+		});
+
+		this.clone.inject(this.element, 'before');
+		this.drag.start(event);
+	},
+
+	end: function(){
+		this.drag.detach();
+		this.element.set('opacity', this.opacity);
+		if (this.effect){
+			var dim = this.element.getStyles('width', 'height');
+			var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
+			this.effect.element = this.clone;
+			this.effect.start({
+				top: pos.top,
+				left: pos.left,
+				width: dim.width,
+				height: dim.height,
+				opacity: 0.25
+			}).chain(this.reset.bind(this));
+		} else {
+			this.reset();
+		}
+	},
+
+	reset: function(){
+		this.idle = true;
+		this.clone.destroy();
+		this.fireEvent('complete', this.element);
+	},
+
+	serialize: function(){
+		var params = Array.link(arguments, {modifier: Function.type, index: $defined});
+		var serial = this.lists.map(function(list){
+			return list.getChildren().map(params.modifier || function(element){
+				return element.get('id');
+			}, this);
+		}, this);
+
+		var index = params.index;
+		if (this.lists.length == 1) index = 0;
+		return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
+	}
+
+});
+/*
+---
+
+script: Request.JSONP.js
+
+description: Defines Request.JSONP, a class for cross domain javascript via script injection.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+ - Guillermo Rauch
+
+requires:
+ - core:1.2.4/Element
+ - core:1.2.4/Request
+ - /Log
+
+provides: [Request.JSONP]
+
+...
+*/
+
+Request.JSONP = new Class({
+
+	Implements: [Chain, Events, Options, Log],
+
+	options: {/*
+		onRetry: $empty(intRetries),
+		onRequest: $empty(scriptElement),
+		onComplete: $empty(data),
+		onSuccess: $empty(data),
+		onCancel: $empty(),
+		log: false,
+		*/
+		url: '',
+		data: {},
+		retries: 0,
+		timeout: 0,
+		link: 'ignore',
+		callbackKey: 'callback',
+		injectScript: document.head
+	},
+
+	initialize: function(options){
+		this.setOptions(options);
+		if (this.options.log) this.enableLog();
+		this.running = false;
+		this.requests = 0;
+		this.triesRemaining = [];
+	},
+
+	check: function(){
+		if (!this.running) return true;
+		switch (this.options.link){
+			case 'cancel': this.cancel(); return true;
+			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
+		}
+		return false;
+	},
+
+	send: function(options){
+		if (!$chk(arguments[1]) && !this.check(options)) return this;
+
+		var type = $type(options), 
+				old = this.options, 
+				index = $chk(arguments[1]) ? arguments[1] : this.requests++;
+		if (type == 'string' || type == 'element') options = {data: options};
+
+		options = $extend({data: old.data, url: old.url}, options);
+
+		if (!$chk(this.triesRemaining[index])) this.triesRemaining[index] = this.options.retries;
+		var remaining = this.triesRemaining[index];
+
+		(function(){
+			var script = this.getScript(options);
+			this.log('JSONP retrieving script with url: ' + script.get('src'));
+			this.fireEvent('request', script);
+			this.running = true;
+
+			(function(){
+				if (remaining){
+					this.triesRemaining[index] = remaining - 1;
+					if (script){
+						script.destroy();
+						this.send(options, index).fireEvent('retry', this.triesRemaining[index]);
+					}
+				} else if(script && this.options.timeout){
+					script.destroy();
+					this.cancel().fireEvent('failure');
+				}
+			}).delay(this.options.timeout, this);
+		}).delay(Browser.Engine.trident ? 50 : 0, this);
+		return this;
+	},
+
+	cancel: function(){
+		if (!this.running) return this;
+		this.running = false;
+		this.fireEvent('cancel');
+		return this;
+	},
+
+	getScript: function(options){
+		var index = Request.JSONP.counter,
+				data;
+		Request.JSONP.counter++;
+
+		switch ($type(options.data)){
+			case 'element': data = document.id(options.data).toQueryString(); break;
+			case 'object': case 'hash': data = Hash.toQueryString(options.data);
+		}
+
+		var src = options.url + 
+			 (options.url.test('\\?') ? '&' :'?') + 
+			 (options.callbackKey || this.options.callbackKey) + 
+			 '=Request.JSONP.request_map.request_'+ index + 
+			 (data ? '&' + data : '');
+		if (src.length > 2083) this.log('JSONP '+ src +' will fail in Internet Explorer, which enforces a 2083 bytes length limit on URIs');
+
+		var script = new Element('script', {type: 'text/javascript', src: src});
+		Request.JSONP.request_map['request_' + index] = function(){ this.success(arguments, script); }.bind(this);
+		return script.inject(this.options.injectScript);
+	},
+
+	success: function(args, script){
+		if (script) script.destroy();
+		this.running = false;
+		this.log('JSONP successfully retrieved: ', args);
+		this.fireEvent('complete', args).fireEvent('success', args).callChain();
+	}
+
+});
+
+Request.JSONP.counter = 0;
+Request.JSONP.request_map = {};/*
+---
+
+script: Request.Queue.js
+
+description: Controls several instances of Request and its variants to run only one request at a time.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Element
+ - core:1.2.4/Request
+ - /Log
+
+provides: [Request.Queue]
+
+...
+*/
+
+Request.Queue = new Class({
+
+	Implements: [Options, Events],
+
+	Binds: ['attach', 'request', 'complete', 'cancel', 'success', 'failure', 'exception'],
+
+	options: {/*
+		onRequest: $empty(argsPassedToOnRequest),
+		onSuccess: $empty(argsPassedToOnSuccess),
+		onComplete: $empty(argsPassedToOnComplete),
+		onCancel: $empty(argsPassedToOnCancel),
+		onException: $empty(argsPassedToOnException),
+		onFailure: $empty(argsPassedToOnFailure),
+		onEnd: $empty,
+		*/
+		stopOnFailure: true,
+		autoAdvance: true,
+		concurrent: 1,
+		requests: {}
+	},
+
+	initialize: function(options){
+		if(options){
+			var requests = options.requests;
+			delete options.requests;	
+		}
+		this.setOptions(options);
+		this.requests = new Hash;
+		this.queue = [];
+		this.reqBinders = {};
+		
+		if(requests) this.addRequests(requests);
+	},
+
+	addRequest: function(name, request){
+		this.requests.set(name, request);
+		this.attach(name, request);
+		return this;
+	},
+
+	addRequests: function(obj){
+		$each(obj, function(req, name){
+			this.addRequest(name, req);
+		}, this);
+		return this;
+	},
+
+	getName: function(req){
+		return this.requests.keyOf(req);
+	},
+
+	attach: function(name, req){
+		if (req._groupSend) return this;
+		['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
+			if(!this.reqBinders[name]) this.reqBinders[name] = {};
+			this.reqBinders[name][evt] = function(){
+				this['on' + evt.capitalize()].apply(this, [name, req].extend(arguments));
+			}.bind(this);
+			req.addEvent(evt, this.reqBinders[name][evt]);
+		}, this);
+		req._groupSend = req.send;
+		req.send = function(options){
+			this.send(name, options);
+			return req;
+		}.bind(this);
+		return this;
+	},
+
+	removeRequest: function(req){
+		var name = $type(req) == 'object' ? this.getName(req) : req;
+		if (!name && $type(name) != 'string') return this;
+		req = this.requests.get(name);
+		if (!req) return this;
+		['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
+			req.removeEvent(evt, this.reqBinders[name][evt]);
+		}, this);
+		req.send = req._groupSend;
+		delete req._groupSend;
+		return this;
+	},
+
+	getRunning: function(){
+		return this.requests.filter(function(r){
+			return r.running;
+		});
+	},
+
+	isRunning: function(){
+		return !!(this.getRunning().getKeys().length);
+	},
+
+	send: function(name, options){
+		var q = function(){
+			this.requests.get(name)._groupSend(options);
+			this.queue.erase(q);
+		}.bind(this);
+		q.name = name;
+		if (this.getRunning().getKeys().length >= this.options.concurrent || (this.error && this.options.stopOnFailure)) this.queue.push(q);
+		else q();
+		return this;
+	},
+
+	hasNext: function(name){
+		return (!name) ? !!this.queue.length : !!this.queue.filter(function(q){ return q.name == name; }).length;
+	},
+
+	resume: function(){
+		this.error = false;
+		(this.options.concurrent - this.getRunning().getKeys().length).times(this.runNext, this);
+		return this;
+	},
+
+	runNext: function(name){
+		if (!this.queue.length) return this;
+		if (!name){
+			this.queue[0]();
+		} else {
+			var found;
+			this.queue.each(function(q){
+				if (!found && q.name == name){
+					found = true;
+					q();
+				}
+			});
+		}
+		return this;
+	},
+
+	runAll: function() {
+		this.queue.each(function(q) {
+			q();
+		});
+		return this;
+	},
+
+	clear: function(name){
+		if (!name){
+			this.queue.empty();
+		} else {
+			this.queue = this.queue.map(function(q){
+				if (q.name != name) return q;
+				else return false;
+			}).filter(function(q){ return q; });
+		}
+		return this;
+	},
+
+	cancel: function(name){
+		this.requests.get(name).cancel();
+		return this;
+	},
+
+	onRequest: function(){
+		this.fireEvent('request', arguments);
+	},
+
+	onComplete: function(){
+		this.fireEvent('complete', arguments);
+		if (!this.queue.length) this.fireEvent('end');
+	},
+
+	onCancel: function(){
+		if (this.options.autoAdvance && !this.error) this.runNext();
+		this.fireEvent('cancel', arguments);
+	},
+
+	onSuccess: function(){
+		if (this.options.autoAdvance && !this.error) this.runNext();
+		this.fireEvent('success', arguments);
+	},
+
+	onFailure: function(){
+		this.error = true;
+		if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
+		this.fireEvent('failure', arguments);
+	},
+
+	onException: function(){
+		this.error = true;
+		if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
+		this.fireEvent('exception', arguments);
+	}
+
+});
+/*
+---
+
+script: Request.Periodical.js
+
+description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load
+
+license: MIT-style license
+
+authors:
+ - Christoph Pojer
+
+requires:
+ - core:1.2.4/Request
+ - /MooTools.More
+
+provides: [Request.Periodical]
+
+...
+*/
+
+Request.implement({
+
+	options: {
+		initialDelay: 5000,
+		delay: 5000,
+		limit: 60000
+	},
+
+	startTimer: function(data){
+		var fn = function(){
+			if (!this.running) this.send({data: data});
+		};
+		this.timer = fn.delay(this.options.initialDelay, this);
+		this.lastDelay = this.options.initialDelay;
+		this.completeCheck = function(response){
+			$clear(this.timer);
+			this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit);
+			this.timer = fn.delay(this.lastDelay, this);
+		};
+		return this.addEvent('complete', this.completeCheck);
+	},
+
+	stopTimer: function(){
+		$clear(this.timer);
+		return this.removeEvent('complete', this.completeCheck);
+	}
+
+});/*
+---
+
+script: Assets.js
+
+description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Element.Event
+ - /MooTools.More
+
+provides: [Assets]
+
+...
+*/
+
+var Asset = {
+
+	javascript: function(source, properties){
+		properties = $extend({
+			onload: $empty,
+			document: document,
+			check: $lambda(true)
+		}, properties);
+		
+		if (properties.onLoad) properties.onload = properties.onLoad;
+		
+		var script = new Element('script', {src: source, type: 'text/javascript'});
+
+		var load = properties.onload.bind(script), 
+			check = properties.check, 
+			doc = properties.document;
+		delete properties.onload;
+		delete properties.check;
+		delete properties.document;
+
+		script.addEvents({
+			load: load,
+			readystatechange: function(){
+				if (['loaded', 'complete'].contains(this.readyState)) load();
+			}
+		}).set(properties);
+
+		if (Browser.Engine.webkit419) var checker = (function(){
+			if (!$try(check)) return;
+			$clear(checker);
+			load();
+		}).periodical(50);
+
+		return script.inject(doc.head);
+	},
+
+	css: function(source, properties){
+		return new Element('link', $merge({
+			rel: 'stylesheet',
+			media: 'screen',
+			type: 'text/css',
+			href: source
+		}, properties)).inject(document.head);
+	},
+
+	image: function(source, properties){
+		properties = $merge({
+			onload: $empty,
+			onabort: $empty,
+			onerror: $empty
+		}, properties);
+		var image = new Image();
+		var element = document.id(image) || new Element('img');
+		['load', 'abort', 'error'].each(function(name){
+			var type = 'on' + name;
+			var cap = name.capitalize();
+			if (properties['on' + cap]) properties[type] = properties['on' + cap];
+			var event = properties[type];
+			delete properties[type];
+			image[type] = function(){
+				if (!image) return;
+				if (!element.parentNode){
+					element.width = image.width;
+					element.height = image.height;
+				}
+				image = image.onload = image.onabort = image.onerror = null;
+				event.delay(1, element, element);
+				element.fireEvent(name, element, 1);
+			};
+		});
+		image.src = element.src = source;
+		if (image && image.complete) image.onload.delay(1);
+		return element.set(properties);
+	},
+
+	images: function(sources, options){
+		options = $merge({
+			onComplete: $empty,
+			onProgress: $empty,
+			onError: $empty,
+			properties: {}
+		}, options);
+		sources = $splat(sources);
+		var images = [];
+		var counter = 0;
+		return new Elements(sources.map(function(source){
+			return Asset.image(source, $extend(options.properties, {
+				onload: function(){
+					options.onProgress.call(this, counter, sources.indexOf(source));
+					counter++;
+					if (counter == sources.length) options.onComplete();
+				},
+				onerror: function(){
+					options.onError.call(this, counter, sources.indexOf(source));
+					counter++;
+					if (counter == sources.length) options.onComplete();
+				}
+			}));
+		}));
+	}
+
+};/*
+---
+
+script: Color.js
+
+description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Array
+ - core:1.2.4/String
+ - core:1.2.4/Number
+ - core:1.2.4/Hash
+ - core:1.2.4/Function
+ - core:1.2.4/$util
+
+provides: [Color]
+
+...
+*/
+
+var Color = new Native({
+
+	initialize: function(color, type){
+		if (arguments.length >= 3){
+			type = 'rgb'; color = Array.slice(arguments, 0, 3);
+		} else if (typeof color == 'string'){
+			if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
+			else if (color.match(/hsb/)) color = color.hsbToRgb();
+			else color = color.hexToRgb(true);
+		}
+		type = type || 'rgb';
+		switch (type){
+			case 'hsb':
+				var old = color;
+				color = color.hsbToRgb();
+				color.hsb = old;
+			break;
+			case 'hex': color = color.hexToRgb(true); break;
+		}
+		color.rgb = color.slice(0, 3);
+		color.hsb = color.hsb || color.rgbToHsb();
+		color.hex = color.rgbToHex();
+		return $extend(color, this);
+	}
+
+});
+
+Color.implement({
+
+	mix: function(){
+		var colors = Array.slice(arguments);
+		var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50;
+		var rgb = this.slice();
+		colors.each(function(color){
+			color = new Color(color);
+			for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
+		});
+		return new Color(rgb, 'rgb');
+	},
+
+	invert: function(){
+		return new Color(this.map(function(value){
+			return 255 - value;
+		}));
+	},
+
+	setHue: function(value){
+		return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
+	},
+
+	setSaturation: function(percent){
+		return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
+	},
+
+	setBrightness: function(percent){
+		return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
+	}
+
+});
+
+var $RGB = function(r, g, b){
+	return new Color([r, g, b], 'rgb');
+};
+
+var $HSB = function(h, s, b){
+	return new Color([h, s, b], 'hsb');
+};
+
+var $HEX = function(hex){
+	return new Color(hex, 'hex');
+};
+
+Array.implement({
+
+	rgbToHsb: function(){
+		var red = this[0],
+				green = this[1],
+				blue = this[2],
+				hue = 0;
+		var max = Math.max(red, green, blue),
+				min = Math.min(red, green, blue);
+		var delta = max - min;
+		var brightness = max / 255,
+				saturation = (max != 0) ? delta / max : 0;
+		if(saturation != 0) {
+			var rr = (max - red) / delta;
+			var gr = (max - green) / delta;
+			var br = (max - blue) / delta;
+			if (red == max) hue = br - gr;
+			else if (green == max) hue = 2 + rr - br;
+			else hue = 4 + gr - rr;
+			hue /= 6;
+			if (hue < 0) hue++;
+		}
+		return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
+	},
+
+	hsbToRgb: function(){
+		var br = Math.round(this[2] / 100 * 255);
+		if (this[1] == 0){
+			return [br, br, br];
+		} else {
+			var hue = this[0] % 360;
+			var f = hue % 60;
+			var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
+			var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
+			var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
+			switch (Math.floor(hue / 60)){
+				case 0: return [br, t, p];
+				case 1: return [q, br, p];
+				case 2: return [p, br, t];
+				case 3: return [p, q, br];
+				case 4: return [t, p, br];
+				case 5: return [br, p, q];
+			}
+		}
+		return false;
+	}
+
+});
+
+String.implement({
+
+	rgbToHsb: function(){
+		var rgb = this.match(/\d{1,3}/g);
+		return (rgb) ? rgb.rgbToHsb() : null;
+	},
+
+	hsbToRgb: function(){
+		var hsb = this.match(/\d{1,3}/g);
+		return (hsb) ? hsb.hsbToRgb() : null;
+	}
+
+});
+/*
+---
+
+script: Group.js
+
+description: Class for monitoring collections of events
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Events
+ - /MooTools.More
+
+provides: [Group]
+
+...
+*/
+
+var Group = new Class({
+
+	initialize: function(){
+		this.instances = Array.flatten(arguments);
+		this.events = {};
+		this.checker = {};
+	},
+
+	addEvent: function(type, fn){
+		this.checker[type] = this.checker[type] || {};
+		this.events[type] = this.events[type] || [];
+		if (this.events[type].contains(fn)) return false;
+		else this.events[type].push(fn);
+		this.instances.each(function(instance, i){
+			instance.addEvent(type, this.check.bind(this, [type, instance, i]));
+		}, this);
+		return this;
+	},
+
+	check: function(type, instance, i){
+		this.checker[type][i] = true;
+		var every = this.instances.every(function(current, j){
+			return this.checker[type][j] || false;
+		}, this);
+		if (!every) return;
+		this.checker[type] = {};
+		this.events[type].each(function(event){
+			event.call(this, this.instances, instance);
+		}, this);
+	}
+
+});
+/*
+---
+
+script: Hash.Cookie.js
+
+description: Class for creating, reading, and deleting Cookies in JSON format.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Cookie
+ - core:1.2.4/JSON
+ - /MooTools.More
+
+provides: [Hash.Cookie]
+
+...
+*/
+
+Hash.Cookie = new Class({
+
+	Extends: Cookie,
+
+	options: {
+		autoSave: true
+	},
+
+	initialize: function(name, options){
+		this.parent(name, options);
+		this.load();
+	},
+
+	save: function(){
+		var value = JSON.encode(this.hash);
+		if (!value || value.length > 4096) return false; //cookie would be truncated!
+		if (value == '{}') this.dispose();
+		else this.write(value);
+		return true;
+	},
+
+	load: function(){
+		this.hash = new Hash(JSON.decode(this.read(), true));
+		return this;
+	}
+
+});
+
+Hash.each(Hash.prototype, function(method, name){
+	if (typeof method == 'function') Hash.Cookie.implement(name, function(){
+		var value = method.apply(this.hash, arguments);
+		if (this.options.autoSave) this.save();
+		return value;
+	});
+});/*
+---
+
+script: HtmlTable.js
+
+description: Builds table elements with methods to add rows.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Options
+ - core:1.2.4/Events
+ - /Class.Occlude
+
+provides: [HtmlTable]
+
+...
+*/
+
+var HtmlTable = new Class({
+
+	Implements: [Options, Events, Class.Occlude],
+
+	options: {
+		properties: {
+			cellpadding: 0,
+			cellspacing: 0,
+			border: 0
+		},
+		rows: [],
+		headers: [],
+		footers: []
+	},
+
+	property: 'HtmlTable',
+
+	initialize: function(){
+		var params = Array.link(arguments, {options: Object.type, table: Element.type});
+		this.setOptions(params.options);
+		this.element = params.table || new Element('table', this.options.properties);
+		if (this.occlude()) return this.occluded;
+		this.build();
+	},
+
+	build: function(){
+		this.element.store('HtmlTable', this);
+
+		this.body = document.id(this.element.tBodies[0]) || new Element('tbody').inject(this.element);
+		$$(this.body.rows);
+
+		if (this.options.headers.length) this.setHeaders(this.options.headers);
+		else this.thead = document.id(this.element.tHead);
+		if (this.thead) this.head = document.id(this.thead.rows[0]);
+
+		if (this.options.footers.length) this.setFooters(this.options.footers);
+		this.tfoot = document.id(this.element.tFoot);
+		if (this.tfoot) this.foot = document.id(this.thead.rows[0]);
+
+		this.options.rows.each(function(row){
+			this.push(row);
+		}, this);
+
+		['adopt', 'inject', 'wraps', 'grab', 'replaces', 'dispose'].each(function(method){
+				this[method] = this.element[method].bind(this.element);
+		}, this);
+	},
+
+	toElement: function(){
+		return this.element;
+	},
+
+	empty: function(){
+		this.body.empty();
+		return this;
+	},
+
+	set: function(what, items) {
+		var target = (what == 'headers') ? 'tHead' : 'tFoot';
+		this[target.toLowerCase()] = (document.id(this.element[target]) || new Element(target.toLowerCase()).inject(this.element, 'top')).empty();
+		var data = this.push(items, {}, this[target.toLowerCase()], what == 'headers' ? 'th' : 'td');
+		if (what == 'headers') this.head = document.id(this.thead.rows[0]);
+		else this.foot = document.id(this.thead.rows[0]);
+		return data;
+	},
+
+	setHeaders: function(headers){
+		this.set('headers', headers);
+		return this;
+	},
+
+	setFooters: function(footers){
+		this.set('footers', footers);
+		return this;
+	},
+
+	push: function(row, rowProperties, target, tag){
+		var tds = row.map(function(data){
+			var td = new Element(tag || 'td', data.properties),
+				type = data.content || data || '',
+				element = document.id(type);
+			if($type(type) != 'string' && element) td.adopt(element);
+			else td.set('html', type);
+
+			return td;
+		});
+
+		return {
+			tr: new Element('tr', rowProperties).inject(target || this.body).adopt(tds),
+			tds: tds
+		};
+	}
+
+});
+/*
+---
+
+script: HtmlTable.Zebra.js
+
+description: Builds a stripy table with methods to add rows.
+
+license: MIT-style license
+
+authors:
+ - Harald Kirschner
+ - Aaron Newton
+
+requires:
+ - /HtmlTable
+ - /Class.refactor
+
+provides: [HtmlTable.Zebra]
+
+...
+*/
+
+HtmlTable = Class.refactor(HtmlTable, {
+
+	options: {
+		classZebra: 'table-tr-odd',
+		zebra: true
+	},
+
+	initialize: function(){
+		this.previous.apply(this, arguments);
+		if (this.occluded) return this.occluded;
+		if (this.options.zebra) this.updateZebras();
+	},
+
+	updateZebras: function(){
+		Array.each(this.body.rows, this.zebra, this);
+	},
+
+	zebra: function(row, i){
+		return row[((i % 2) ? 'remove' : 'add')+'Class'](this.options.classZebra);
+	},
+
+	push: function(){
+		var pushed = this.previous.apply(this, arguments);
+		if (this.options.zebra) this.updateZebras();
+		return pushed;
+	}
+
+});/*
+---
+
+script: HtmlTable.Sort.js
+
+description: Builds a stripy, sortable table with methods to add rows.
+
+license: MIT-style license
+
+authors:
+ - Harald Kirschner
+ - Aaron Newton
+
+requires:
+ - core:1.2.4/Hash
+ - /HtmlTable
+ - /Class.refactor
+ - /Element.Delegation
+ - /Date
+
+provides: [HtmlTable.Sort]
+
+...
+*/
+
+HtmlTable = Class.refactor(HtmlTable, {
+
+	options: {/*
+		onSort: $empty, */
+		sortIndex: 0,
+		sortReverse: false,
+		parsers: [],
+		defaultParser: 'string',
+		classSortable: 'table-sortable',
+		classHeadSort: 'table-th-sort',
+		classHeadSortRev: 'table-th-sort-rev',
+		classNoSort: 'table-th-nosort',
+		classGroupHead: 'table-tr-group-head',
+		classGroup: 'table-tr-group',
+		classCellSort: 'table-td-sort',
+		classSortSpan: 'table-th-sort-span',
+		sortable: false
+	},
+
+	initialize: function () {
+		this.previous.apply(this, arguments);
+		if (this.occluded) return this.occluded;
+		this.sorted = {index: null, dir: 1};
+		this.bound = {
+			headClick: this.headClick.bind(this)
+		};
+		this.sortSpans = new Elements();
+		if (this.options.sortable) {
+			this.enableSort();
+			if (this.options.sortIndex != null) this.sort(this.options.sortIndex, this.options.sortReverse);
+		}
+	},
+
+	attachSorts: function(attach){
+		this.element.removeEvents('click:relay(th)');
+		this.element[$pick(attach, true) ? 'addEvent' : 'removeEvent']('click:relay(th)', this.bound.headClick);
+	},
+
+	setHeaders: function(){
+		this.previous.apply(this, arguments);
+		if (this.sortEnabled) this.detectParsers();
+	},
+	
+	detectParsers: function(force){
+		if (!this.head) return;
+		var parsers = this.options.parsers, 
+				rows = this.body.rows;
+
+		// auto-detect
+		this.parsers = $$(this.head.cells).map(function(cell, index) {
+			if (!force && (cell.hasClass(this.options.classNoSort) || cell.retrieve('htmltable-parser'))) return cell.retrieve('htmltable-parser');
+			var thDiv = new Element('div');
+			$each(cell.childNodes, function(node) {
+				thDiv.adopt(node);
+			});
+			thDiv.inject(cell);
+			var sortSpan = new Element('span', {'html': '&#160;', 'class': this.options.classSortSpan}).inject(thDiv, 'top');
+			
+			this.sortSpans.push(sortSpan);
+
+			var parser = parsers[index], 
+					cancel;
+			switch ($type(parser)) {
+				case 'function': parser = {convert: parser}; cancel = true; break;
+				case 'string': parser = parser; cancel = true; break;
+			}
+			if (!cancel) {
+				HtmlTable.Parsers.some(function(current) {
+					var match = current.match;
+					if (!match) return false;
+					for (var i = 0, j = rows.length; i < j; i++) {
+						var text = $(rows[i].cells[index]).get('html').clean();
+						if (text && match.test(text)) {
+							parser = current;
+							return true;
+						}
+					}
+				});
+			}
+
+			if (!parser) parser = this.options.defaultParser;
+			cell.store('htmltable-parser', parser);
+			return parser;
+		}, this);
+	},
+
+	headClick: function(event, el) {
+		console.log(el);
+		if (!this.head || el.hasClass(this.options.classNoSort)) return;
+		var index = Array.indexOf(this.head.cells, el);
+		this.sort(index);
+		return false;
+	},
+
+	sort: function(index, reverse, pre) {
+		if (!this.head) return;
+		pre = !!(pre);
+		var classCellSort = this.options.classCellSort;
+		var classGroup = this.options.classGroup, 
+				classGroupHead = this.options.classGroupHead;
+
+		if (!pre) {
+			if (index != null) {
+				if (this.sorted.index == index) {
+					this.sorted.reverse = !(this.sorted.reverse);
+				} else {
+					if (this.sorted.index != null) {
+						this.sorted.reverse = false;
+						this.head.cells[this.sorted.index].removeClass(this.options.classHeadSort).removeClass(this.options.classHeadSortRev);
+					} else {
+						this.sorted.reverse = true;
+					}
+					this.sorted.index = index;
+				}
+			} else {
+				index = this.sorted.index;
+			}
+
+			if (reverse != null) this.sorted.reverse = reverse;
+
+			var head = document.id(this.head.cells[index]);
+			if (head) {
+				head.addClass(this.options.classHeadSort);
+				if (this.sorted.reverse) head.addClass(this.options.classHeadSortRev);
+				else head.removeClass(this.options.classHeadSortRev);
+			}
+
+			this.body.getElements('td').removeClass(this.options.classCellSort);
+		}
+
+		var parser = this.parsers[index];
+		if ($type(parser) == 'string') parser = HtmlTable.Parsers.get(parser);
+		if (!parser) return;
+
+		if (!Browser.Engine.trident) {
+			var rel = this.body.getParent();
+			this.body.dispose();
+		}
+
+		var data = Array.map(this.body.rows, function(row, i) {
+			var value = parser.convert.call(document.id(row.cells[index]));
+
+			return {
+				position: i,
+				value: value,
+				toString:  function() {
+					return value.toString();
+				}
+			};
+		}, this);
+		data.reverse(true);
+
+		data.sort(function(a, b){
+			if (a.value === b.value) return 0;
+			return a.value > b.value ? 1 : -1;
+		});
+
+		if (!this.sorted.reverse) data.reverse(true);
+
+		var i = data.length, body = this.body;
+		var j, position, entry, group;
+
+		while (i) {
+			var item = data[--i];
+			position = item.position;
+			var row = body.rows[position];
+			if (row.disabled) continue;
+
+			if (!pre) {
+				if (group === item.value) {
+					row.removeClass(classGroupHead).addClass(classGroup);
+				} else {
+					group = item.value;
+					row.removeClass(classGroup).addClass(classGroupHead);
+				}
+				if (this.zebra) this.zebra(row, i);
+
+				row.cells[index].addClass(classCellSort);
+			}
+
+			body.appendChild(row);
+			for (j = 0; j < i; j++) {
+				if (data[j].position > position) data[j].position--;
+			}
+		};
+		data = null;
+		if (rel) rel.grab(body);
+
+		return this.fireEvent('sort', [body, index]);
+	},
+
+	reSort: function(){
+		if (this.sortEnabled) this.sort.call(this, this.sorted.index, this.sorted.reverse);
+		return this;
+	},
+
+	enableSort: function(){
+		this.element.addClass(this.options.classSortable);
+		this.attachSorts(true);
+		this.detectParsers();
+		this.sortEnabled = true;
+		return this;
+	},
+
+	disableSort: function(){
+		this.element.removeClass(this.options.classSortable);
+		this.attachSorts(false);
+		this.sortSpans.each(function(span) { span.destroy(); });
+		this.sortSpans.empty();
+		this.sortEnabled = false;
+		return this;
+	}
+
+});
+
+HtmlTable.Parsers = new Hash({
+
+	'date': {
+		match: /^\d{2}[-\/ ]\d{2}[-\/ ]\d{2,4}$/,
+		convert: function() {
+			return Date.parse(this.get('text')).format('db');
+		},
+		type: 'date'
+	},
+	'input-checked': {
+		match: / type="(radio|checkbox)" /,
+		convert: function() {
+			return this.getElement('input').checked;
+		}
+	},
+	'input-value': {
+		match: /<input/,
+		convert: function() {
+			return this.getElement('input').value;
+		}
+	},
+	'number': {
+		match: /^\d+[^\d.,]*$/,
+		convert: function() {
+			return this.get('text').toInt();
+		},
+		number: true
+	},
+	'numberLax': {
+		match: /^[^\d]+\d+$/,
+		convert: function() {
+			return this.get('text').replace(/[^-?^0-9]/, '').toInt();
+		},
+		number: true
+	},
+	'float': {
+		match: /^[\d]+\.[\d]+/,
+		convert: function() {
+			return this.get('text').replace(/[^-?^\d.]/, '').toFloat();
+		},
+		number: true
+	},
+	'floatLax': {
+		match: /^[^\d]+[\d]+\.[\d]+$/,
+		convert: function() {
+			return this.get('text').replace(/[^-?^\d.]/, '');
+		},
+		number: true
+	},
+	'string': {
+		match: null,
+		convert: function() {
+			return this.get('text');
+		}
+	},
+	'title': {
+		match: null,
+		convert: function() {
+			return this.title;
+		}
+	}
+
+});
+
+/*
+---
+
+script: Keyboard.js
+
+description: KeyboardEvents used to intercept events on a class for keyboard and format modifiers in a specific order so as to make alt+shift+c the same as shift+alt+c.
+
+license: MIT-style license
+
+authors:
+ - Perrin Westrich
+ - Aaron Newton
+ - Scott Kyle
+
+requires:
+ - core:1.2.4/Events
+ - core:1.2.4/Options
+ - core:1.2.4/Element.Event
+ - /Log
+
+provides: [Keyboard]
+
+...
+*/
+
+(function(){
+	
+	var Keyboard = this.Keyboard = new Class({
+
+		Extends: Events,
+
+		Implements: [Options, Log],
+
+		options: {
+			/*
+			onActivate: $empty,
+			onDeactivate: $empty,
+			*/
+			defaultEventType: 'keydown',
+			active: false,
+			events: {},
+			nonParsedEvents: ['activate', 'deactivate', 'onactivate', 'ondeactivate', 'changed', 'onchanged']
+		},
+
+		initialize: function(options){
+			this.setOptions(options);
+			this.setup();
+		}, 
+		setup: function(){
+			this.addEvents(this.options.events);
+			//if this is the root manager, nothing manages it
+			if (Keyboard.manager && !this.manager) Keyboard.manager.manage(this);
+			if (this.options.active) this.activate();
+		},
+
+		handle: function(event, type){
+			//Keyboard.stop(event) prevents key propagation
+			if (event.preventKeyboardPropagation) return;
+			
+			var bubbles = !!this.manager;
+			if (bubbles && this.activeKB){
+				this.activeKB.handle(event, type);
+				if (event.preventKeyboardPropagation) return;
+			}
+			this.fireEvent(type, event);
+			
+			if (!bubbles && this.activeKB) this.activeKB.handle(event, type);
+		},
+
+		addEvent: function(type, fn, internal){
+			return this.parent(Keyboard.parse(type, this.options.defaultEventType, this.options.nonParsedEvents), fn, internal);
+		},
+
+		removeEvent: function(type, fn){
+			return this.parent(Keyboard.parse(type, this.options.defaultEventType, this.options.nonParsedEvents), fn);
+		},
+
+		toggleActive: function(){
+			return this[this.active ? 'deactivate' : 'activate']();
+		},
+
+		activate: function(instance){
+			if (instance) {
+				//if we're stealing focus, store the last keyboard to have it so the relenquish command works
+				if (instance != this.activeKB) this.previous = this.activeKB;
+				//if we're enabling a child, assign it so that events are now passed to it
+				this.activeKB = instance.fireEvent('activate');
+				Keyboard.manager.fireEvent('changed');
+			} else if (this.manager) {
+				//else we're enabling ourselves, we must ask our parent to do it for us
+				this.manager.activate(this);
+			}
+			return this;
+		},
+
+		deactivate: function(instance){
+			if (instance) {
+				if(instance === this.activeKB) {
+					this.activeKB = null;
+					instance.fireEvent('deactivate');
+					Keyboard.manager.fireEvent('changed');
+				}
+			}
+			else if (this.manager) {
+				this.manager.deactivate(this);
+			}
+			return this;
+		},
+
+		relenquish: function(){
+			if (this.previous) this.activate(this.previous);
+		},
+
+		//management logic
+		manage: function(instance){
+			if (instance.manager) instance.manager.drop(instance);
+			this.instances.push(instance);
+			instance.manager = this;
+			if (!this.activeKB) this.activate(instance);
+			else this._disable(instance);
+		},
+
+		_disable: function(instance){
+			if (this.activeKB == instance) this.activeKB = null;
+		},
+
+		drop: function(instance){
+			this._disable(instance);
+			this.instances.erase(instance);
+		},
+
+		instances: [],
+
+		trace: function(){
+			Keyboard.trace(this);
+		},
+
+		each: function(fn){
+			Keyboard.each(this, fn);
+		}
+
+	});
+	
+	var parsed = {};
+	var modifiers = ['shift', 'control', 'alt', 'meta'];
+	var regex = /^(?:shift|control|ctrl|alt|meta)$/;
+	
+	Keyboard.parse = function(type, eventType, ignore){
+		if (ignore && ignore.contains(type.toLowerCase())) return type;
+		
+		type = type.toLowerCase().replace(/^(keyup|keydown):/, function($0, $1){
+			eventType = $1;
+			return '';
+		});
+
+		if (!parsed[type]){
+			var key, mods = {};
+			type.split('+').each(function(part){
+				if (regex.test(part)) mods[part] = true;
+				else key = part;
+			});
+
+			mods.control = mods.control || mods.ctrl; // allow both control and ctrl
+			
+			var keys = [];
+			modifiers.each(function(mod){
+				if (mods[mod]) keys.push(mod);
+			});
+			
+			if (key) keys.push(key);
+			parsed[type] = keys.join('+');
+		}
+
+		return eventType + ':' + parsed[type];
+	};
+
+	Keyboard.each = function(keyboard, fn){
+		var current = keyboard || Keyboard.manager;
+		while (current){
+			fn.run(current);
+			current = current.activeKB;
+		}
+	};
+
+	Keyboard.stop = function(event){
+		event.preventKeyboardPropagation = true;
+	};
+
+	Keyboard.manager = new Keyboard({
+		active: true
+	});
+	
+	Keyboard.trace = function(keyboard){
+		keyboard = keyboard || Keyboard.manager;
+		keyboard.enableLog();
+		keyboard.log('the following items have focus: ');
+		Keyboard.each(keyboard, function(current){
+			keyboard.log(document.id(current.widget) || current.wiget || current);
+		});
+	};
+	
+	var handler = function(event){
+		var keys = [];
+		modifiers.each(function(mod){
+			if (event[mod]) keys.push(mod);
+		});
+		
+		if (!regex.test(event.key)) keys.push(event.key);
+		Keyboard.manager.handle(event, event.type + ':' + keys.join('+'));
+	};
+	
+	document.addEvents({
+		'keyup': handler,
+		'keydown': handler
+	});
+
+	Event.Keys.extend({
+		'shift': 16,
+		'control': 17,
+		'alt': 18,
+		'capslock': 20,
+		'pageup': 33,
+		'pagedown': 34,
+		'end': 35,
+		'home': 36,
+		'numlock': 144,
+		'scrolllock': 145,
+		';': 186,
+		'=': 187,
+		',': 188,
+		'-': Browser.Engine.Gecko ? 109 : 189,
+		'.': 190,
+		'/': 191,
+		'`': 192,
+		'[': 219,
+		'\\': 220,
+		']': 221,
+		"'": 222
+	});
+
+})();
+/*
+---
+
+script: HtmlTable.Select.js
+
+description: Builds a stripy, sortable table with methods to add rows. Rows can be selected with the mouse or keyboard navigation.
+
+license: MIT-style license
+
+authors:
+ - Harald Kirschner
+ - Aaron Newton
+
+requires:
+ - /Keyboard
+ - /HtmlTable
+ - /Class.refactor
+ - /Element.Delegation
+
+provides: [HtmlTable.Select]
+
+...
+*/
+
+HtmlTable = Class.refactor(HtmlTable, {
+
+	options: {
+		/*onRowFocus: $empty,
+		onRowUnfocus: $empty,*/
+		useKeyboard: true,
+		classRowSelected: 'table-tr-selected',
+		classRowHovered: 'table-tr-hovered',
+		classSelectable: 'table-selectable',
+		allowMultiSelect: true,
+		selectable: false
+	},
+
+	initialize: function(){
+		this.previous.apply(this, arguments);
+		if (this.occluded) return this.occluded;
+		this.selectedRows = new Elements();
+		this.bound = {
+			mouseleave: this.mouseleave.bind(this),
+			focusRow: this.focusRow.bind(this)
+		};
+		if (this.options.selectable) this.enableSelect();
+	},
+
+	enableSelect: function(){
+		this.selectEnabled = true;
+		this.attachSelects();
+		this.element.addClass(this.options.classSelectable);
+	},
+
+	disableSelect: function(){
+		this.selectEnabled = false;
+		this.attach(false);
+		this.element.removeClass(this.options.classSelectable);
+	},
+
+	attachSelects: function(attach){
+		attach = $pick(attach, true);
+		var method = attach ? 'addEvents' : 'removeEvents';
+		this.element[method]({
+			mouseleave: this.bound.mouseleave
+		});
+		this.body[method]({
+			'click:relay(tr)': this.bound.focusRow
+		});
+		if (this.options.useKeyboard || this.keyboard){
+			if (!this.keyboard) this.keyboard = new Keyboard({
+				events: {
+					down: function(e) {
+						e.preventDefault();
+						this.shiftFocus(1);
+					}.bind(this),
+					up: function(e) {
+						e.preventDefault();
+						this.shiftFocus(-1);
+					}.bind(this),
+					enter: function(e) {
+						e.preventDefault();
+						if (this.hover) this.focusRow(this.hover);
+					}.bind(this)
+				},
+				active: true
+			});
+			this.keyboard[attach ? 'activate' : 'deactivate']();
+		}
+		this.updateSelects();
+	},
+
+	mouseleave: function(){
+		if (this.hover) this.leaveRow(this.hover);
+	},
+
+	focus: function(){
+		if (this.keyboard) this.keyboard.activate();
+	},
+
+	blur: function(){
+		if (this.keyboard) this.keyboard.deactivate();
+	},
+
+	push: function(){
+		var ret = this.previous.apply(this, arguments);
+		this.updateSelects();
+		return ret;
+	},
+
+	updateSelects: function(){
+		Array.each(this.body.rows, function(row){
+			var binders = row.retrieve('binders');
+			if ((binders && this.selectEnabled) || (!binders && !this.selectEnabled)) return;
+			if (!binders){
+				binders = {
+					mouseenter: this.enterRow.bind(this, [row]),
+					mouseleave: this.leaveRow.bind(this, [row])
+				};
+				row.store('binders', binders).addEvents(binders);
+			} else {
+				row.removeEvents(binders);
+			}
+		}, this);
+	},
+
+	enterRow: function(row){
+		if (this.hover) this.hover = this.leaveRow(this.hover);
+		this.hover = row.addClass(this.options.classRowHovered);
+	},
+
+	shiftFocus: function(offset){
+		if (!this.hover) return this.enterRow(this.body.rows[0]);
+		var to = Array.indexOf(this.body.rows, this.hover) + offset;
+		if (to < 0) to = 0;
+		if (to >= this.body.rows.length) to = this.body.rows.length - 1;
+		if (this.hover == this.body.rows[to]) return this;
+		this.enterRow(this.body.rows[to]);
+	},
+
+	leaveRow: function(row){
+		row.removeClass(this.options.classRowHovered);
+	},
+
+	focusRow: function(){
+		var row = arguments[1] || arguments[0]; //delegation passes the event first
+		if (!this.body.getChildren().contains(row)) return;
+		var unfocus = function(row){
+			this.selectedRows.erase(row);
+			row.removeClass(this.options.classRowSelected);
+			this.fireEvent('rowUnfocus', [row, this.selectedRows]);
+		}.bind(this);
+		if (!this.options.allowMultiSelect) this.selectedRows.each(unfocus);
+		if (!this.selectedRows.contains(row)) {
+			this.selectedRows.push(row);
+			row.addClass(this.options.classRowSelected);
+			this.fireEvent('rowFocus', [row, this.selectedRows]);
+		} else {
+			unfocus(row);
+		}
+		return false;
+	},
+
+	selectAll: function(status){
+		status = $pick(status, true);
+		if (!this.options.allowMultiSelect && status) return;
+		if (!status) this.selectedRows.removeClass(this.options.classRowSelected).empty();
+		else this.selectedRows.combine(this.body.rows).addClass(this.options.classRowSelected);
+		return this;
+	},
+
+	selectNone: function(){
+		return this.selectAll(false);
+	}
+
+});
+/*
+---
+
+script: Keyboard.js
+
+description: Enhances Keyboard by adding the ability to name and describe keyboard shortcuts, and the ability to grab shortcuts by name and bind the shortcut to different keys.
+
+license: MIT-style license
+
+authors:
+ - Perrin Westrich
+
+requires:
+ - core:1.2.4/Function
+ - /Keyboard.Extras
+
+provides: [Keyboard.Extras]
+
+...
+*/
+Keyboard.prototype.options.nonParsedEvents.combine(['rebound', 'onrebound']);
+
+Keyboard.implement({
+
+	/*
+		shortcut should be in the format of:
+		{
+			'keys': 'shift+s', // the default to add as an event.
+			'description': 'blah blah blah', // a brief description of the functionality.
+			'handler': function(){} // the event handler to run when keys are pressed.
+		}
+	*/
+	addShortcut: function(name, shortcut) {
+		this.shortcuts = this.shortcuts || [];
+		this.shortcutIndex = this.shortcutIndex || {};
+		
+		shortcut.getKeyboard = $lambda(this);
+		shortcut.name = name;
+		this.shortcutIndex[name] = shortcut;
+		this.shortcuts.push(shortcut);
+		if(shortcut.keys) this.addEvent(shortcut.keys, shortcut.handler);
+		return this;
+	},
+
+	addShortcuts: function(obj){
+		for(var name in obj) this.addShortcut(name, obj[name]);
+		return this;
+	},
+
+	getShortcuts: function(){
+		return this.shortcuts || [];
+	},
+
+	getShortcut: function(name){
+		return (this.shortcutIndex || {})[name];
+	}
+
+});
+
+Keyboard.rebind = function(newKeys, shortcuts){
+	$splat(shortcuts).each(function(shortcut){
+		shortcut.getKeyboard().removeEvent(shortcut.keys, shortcut.handler);
+		shortcut.getKeyboard().addEvent(newKeys, shortcut.handler);
+		shortcut.keys = newKeys;
+		shortcut.getKeyboard().fireEvent('rebound');
+	});
+};
+
+
+Keyboard.getActiveShortcuts = function(keyboard) {
+	var activeKBS = [], activeSCS = [];
+	Keyboard.each(keyboard, [].push.bind(activeKBS));
+	activeKBS.each(function(kb){ activeSCS.extend(kb.getShortcuts()); });
+	return activeSCS;
+};
+
+Keyboard.getShortcut = function(name, keyboard, opts){
+	opts = opts || {};
+	var shortcuts = opts.many ? [] : null,
+		set = opts.many ? function(kb){
+				var shortcut = kb.getShortcut(name);
+				if(shortcut) shortcuts.push(shortcut);
+			} : function(kb) { 
+				if(!shortcuts) shortcuts = kb.getShortcut(name);
+			};
+	Keyboard.each(keyboard, set);
+	return shortcuts;
+};
+
+Keyboard.getShortcuts = function(name, keyboard) {
+	return Keyboard.getShortcut(name, keyboard, { many: true });
+};
+/*
+---
+
+script: Scroller.js
+
+description: Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+
+requires:
+ - core:1.2.4/Events
+ - core:1.2.4/Options
+ - core:1.2.4/Element.Event
+ - core:1.2.4/Element.Dimensions
+
+provides: [Scroller]
+
+...
+*/
+
+var Scroller = new Class({
+
+	Implements: [Events, Options],
+
+	options: {
+		area: 20,
+		velocity: 1,
+		onChange: function(x, y){
+			this.element.scrollTo(x, y);
+		},
+		fps: 50
+	},
+
+	initialize: function(element, options){
+		this.setOptions(options);
+		this.element = document.id(element);
+		this.docBody = document.id(this.element.getDocument().body);
+		this.listener = ($type(this.element) != 'element') ?  this.docBody : this.element;
+		this.timer = null;
+		this.bound = {
+			attach: this.attach.bind(this),
+			detach: this.detach.bind(this),
+			getCoords: this.getCoords.bind(this)
+		};
+	},
+
+	start: function(){
+		this.listener.addEvents({
+			mouseover: this.bound.attach,
+			mouseout: this.bound.detach
+		});
+	},
+
+	stop: function(){
+		this.listener.removeEvents({
+			mouseover: this.bound.attach,
+			mouseout: this.bound.detach
+		});
+		this.detach();
+		this.timer = $clear(this.timer);
+	},
+
+	attach: function(){
+		this.listener.addEvent('mousemove', this.bound.getCoords);
+	},
+
+	detach: function(){
+		this.listener.removeEvent('mousemove', this.bound.getCoords);
+		this.timer = $clear(this.timer);
+	},
+
+	getCoords: function(event){
+		this.page = (this.listener.get('tag') == 'body') ? event.client : event.page;
+		if (!this.timer) this.timer = this.scroll.periodical(Math.round(1000 / this.options.fps), this);
+	},
+
+	scroll: function(){
+		var size = this.element.getSize(), 
+			scroll = this.element.getScroll(), 
+			pos = this.element != this.docBody ? this.element.getOffsets() : {x: 0, y:0}, 
+			scrollSize = this.element.getScrollSize(), 
+			change = {x: 0, y: 0};
+		for (var z in this.page){
+			if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0) {
+				change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity;
+			} else if (this.page[z] + this.options.area > (size[z] + pos[z]) && scroll[z] + size[z] != scrollSize[z]) {
+				change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity;
+			}
+		}
+		if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]);
+	}
+
+});/*
+---
+
+script: Tips.js
+
+description: Class for creating nice tips that follow the mouse cursor when hovering an element.
+
+license: MIT-style license
+
+authors:
+ - Valerio Proietti
+ - Christoph Pojer
+
+requires:
+ - core:1.2.4/Options
+ - core:1.2.4/Events
+ - core:1.2.4/Element.Event
+ - core:1.2.4/Element.Style
+ - core:1.2.4/Element.Dimensions
+ - /MooTools.More
+
+provides: [Tips]
+
+...
+*/
+
+(function(){
+
+var read = function(option, element){
+	return (option) ? ($type(option) == 'function' ? option(element) : element.get(option)) : '';
+};
+
+this.Tips = new Class({
+
+	Implements: [Events, Options],
+
+	options: {
+		/*
+		onAttach: $empty(element),
+		onDetach: $empty(element),
+		*/
+		onShow: function(){
+			this.tip.setStyle('display', 'block');
+		},
+		onHide: function(){
+			this.tip.setStyle('display', 'none');
+		},
+		title: 'title',
+		text: function(element){
+			return element.get('rel') || element.get('href');
+		},
+		showDelay: 100,
+		hideDelay: 100,
+		className: 'tip-wrap',
+		offset: {x: 16, y: 16},
+		windowPadding: {x:0, y:0},
+		fixed: false
+	},
+
+	initialize: function(){
+		var params = Array.link(arguments, {options: Object.type, elements: $defined});
+		this.setOptions(params.options);
+		if (params.elements) this.attach(params.elements);
+		this.container = new Element('div', {'class': 'tip'});
+	},
+
+	toElement: function(){
+		if (this.tip) return this.tip;
+
+		return this.tip = new Element('div', {
+			'class': this.options.className,
+			styles: {
+				position: 'absolute',
+				top: 0,
+				left: 0
+			}
+		}).adopt(
+			new Element('div', {'class': 'tip-top'}),
+			this.container,
+			new Element('div', {'class': 'tip-bottom'})
+		).inject(document.body);
+	},
+
+	attach: function(elements){
+		$$(elements).each(function(element){
+			var title = read(this.options.title, element),
+				text = read(this.options.text, element);
+			
+			element.erase('title').store('tip:native', title).retrieve('tip:title', title);
+			element.retrieve('tip:text', text);
+			this.fireEvent('attach', [element]);
+			
+			var events = ['enter', 'leave'];
+			if (!this.options.fixed) events.push('move');
+			
+			events.each(function(value){
+				var event = element.retrieve('tip:' + value);
+				if (!event) event = this['element' + value.capitalize()].bindWithEvent(this, element);
+				
+				element.store('tip:' + value, event).addEvent('mouse' + value, event);
+			}, this);
+		}, this);
+		
+		return this;
+	},
+
+	detach: function(elements){
+		$$(elements).each(function(element){
+			['enter', 'leave', 'move'].each(function(value){
+				element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value);
+			});
+			
+			this.fireEvent('detach', [element]);
+			
+			if (this.options.title == 'title'){ // This is necessary to check if we can revert the title
+				var original = element.retrieve('tip:native');
+				if (original) element.set('title', original);
+			}
+		}, this);
+		
+		return this;
+	},
+
+	elementEnter: function(event, element){
+		this.container.empty();
+		
+		['title', 'text'].each(function(value){
+			var content = element.retrieve('tip:' + value);
+			if (content) this.fill(new Element('div', {'class': 'tip-' + value}).inject(this.container), content);
+		}, this);
+		
+		$clear(this.timer);
+		this.timer = (function(){
+			this.show(this, element);
+			this.position((this.options.fixed) ? {page: element.getPosition()} : event);
+		}).delay(this.options.showDelay, this);
+	},
+
+	elementLeave: function(event, element){
+		$clear(this.timer);
+		this.timer = this.hide.delay(this.options.hideDelay, this, element);
+		this.fireForParent(event, element);
+	},
+
+	fireForParent: function(event, element){
+		element = element.getParent();
+		if (!element || element == document.body) return;
+		if (element.retrieve('tip:enter')) element.fireEvent('mouseenter', event);
+		else this.fireForParent(event, element);
+	},
+
+	elementMove: function(event, element){
+		this.position(event);
+	},
+
+	position: function(event){
+		if (!this.tip) document.id(this);
+
+		var size = window.getSize(), scroll = window.getScroll(),
+			tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
+			props = {x: 'left', y: 'top'},
+			obj = {};
+		
+		for (var z in props){
+			obj[props[z]] = event.page[z] + this.options.offset[z];
+			if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowPadding[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
+		}
+		
+		this.tip.setStyles(obj);
+	},
+
+	fill: function(element, contents){
+		if(typeof contents == 'string') element.set('html', contents);
+		else element.adopt(contents);
+	},
+
+	show: function(element){
+		if (!this.tip) document.id(this);
+		this.fireEvent('show', [this.tip, element]);
+	},
+
+	hide: function(element){
+		if (!this.tip) document.id(this);
+		this.fireEvent('hide', [this.tip, element]);
+	}
+
+});
+
+})();/*
+---
+
+script: Date.Catalan.js
+
+description: Date messages for Catalan.
+
+license: MIT-style license
+
+authors:
+ - Alfons Sanchez
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Catalan]
+
+...
+*/
+
+MooTools.lang.set('ca-CA', 'Date', {
+
+	months: ['Gener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juli', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Desembre'],
+	days: ['Diumenge', 'Dilluns', 'Dimarts', 'Dimecres', 'Dijous', 'Divendres', 'Dissabte'],
+	//culture's date order: MM/DD/YYYY
+	dateOrder: ['date', 'month', 'year'],
+
+	shortDate: '%d/%m/%Y',
+	shortTime: '%H:%M',
+
+	AM: 'AM',
+	PM: 'PM',
+
+	/* Date.Extras */
+	ordinal: '',
+
+	lessThanMinuteAgo: 'fa menys d`un minut',
+	minuteAgo: 'fa un minut',
+	minutesAgo: 'fa {delta} minuts',
+	hourAgo: 'fa un hora',
+	hoursAgo: 'fa unes {delta} hores',
+	dayAgo: 'fa un dia',
+	daysAgo: 'fa {delta} dies',
+	lessThanMinuteUntil: 'menys d`un minut des d`ara',
+	minuteUntil: 'un minut des d`ara',
+	minutesUntil: '{delta} minuts des d`ara',
+	hourUntil: 'un hora des d`ara',
+	hoursUntil: 'unes {delta} hores des d`ara',
+	dayUntil: '1 dia des d`ara',
+	daysUntil: '{delta} dies des d`ara'
+
+});/*
+---
+
+script: Date.Czech.js
+
+description: Date messages for Czech.
+
+license: MIT-style license
+
+authors:
+ - Jan Černý chemiX
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Czech]
+
+...
+*/
+
+MooTools.lang.set('cs-CZ', 'Date', {
+
+	months: ['Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Červenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec'],
+	days: ['Neděle', 'Pondělí', 'Úterý', 'Středa', 'Čtvrtek', 'Pátek', 'Sobota'],
+	//culture's date order: MM/DD/YYYY
+	dateOrder: ['date', 'month', 'year'],
+	shortDate: '%d/%m/%Y',
+	shortTime: '%H:%M',
+	AM: 'dop.',
+	PM: 'odp.',
+
+	/* Date.Extras */
+	ordinal: function(dayOfMonth){
+		return '.';
+	},
+
+    // TODO : in examples use and fix it
+	lessThanMinuteAgo: 'méně než minutou',
+	minuteAgo: 'přibližně před minutou',
+	minutesAgo: 'před {delta} minutami',
+	hourAgo: 'přibližně před hodinou',
+	hoursAgo: 'před {delta} hodinami',
+	dayAgo: 'před dnem',
+	daysAgo: 'před {delta} dni',
+	lessThanMinuteUntil: 'před méně než minutou',
+	minuteUntil: 'asi před minutou',
+	minutesUntil: ' asi před {delta} minutami',
+	hourUntil: 'asi před hodinou',
+	hoursUntil: 'před {delta} hodinami',
+	dayUntil: 'před dnem',
+	daysUntil: 'před {delta} dni',
+	weekUntil: 'před týdnem',
+	weeksUntil: 'před {delta} týdny',
+	monthUntil: 'před měsícem',
+	monthsUntil: 'před {delta} měsíci',
+	yearUntil: 'před rokem',
+	yearsUntil: 'před {delta} lety'
+
+});
+/*
+---
+
+script: Date.Danish.js
+
+description: Date messages for Danish.
+
+license: MIT-style license
+
+authors:
+ - Martin Overgaard
+ - Henrik Hansen
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Danish]
+
+...
+*/
+ 
+MooTools.lang.set('da-DK', 'Date', {
+
+	months: ['Januar', 'Februa', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
+	days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'],
+	//culture's date order: DD/MM/YYYY
+	dateOrder: ['date', 'month', 'year'],
+
+	AM: 'AM',
+	PM: 'PM',
+
+	shortDate: '%d-%m-%Y',
+	shortTime: '%H:%M',
+
+	/* Date.Extras */
+	ordinal: function(dayOfMonth){
+	  //1st, 2nd, 3rd, etc.
+	  return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)];
+	},
+
+	lessThanMinuteAgo: 'mindre end et minut siden',
+	minuteAgo: 'omkring et minut siden',
+	minutesAgo: '{delta} minutter siden',
+	hourAgo: 'omkring en time siden',
+	hoursAgo: 'omkring {delta} timer siden',
+	dayAgo: '1 dag siden',
+	daysAgo: '{delta} dage siden',
+	weekAgo: '1 uge siden',
+	weeksAgo: '{delta} uger siden',
+	monthAgo: '1 måned siden',
+	monthsAgo: '{delta} måneder siden',
+	yearthAgo: '1 år siden',
+	yearsAgo: '{delta} år siden',
+	lessThanMinuteUntil: 'mindre end et minut fra nu',
+	minuteUntil: 'omkring et minut fra nu',
+	minutesUntil: '{delta} minutter fra nu',
+	hourUntil: 'omkring en time fra nu',
+	hoursUntil: 'omkring {delta} timer fra nu',
+	dayUntil: '1 dag fra nu',
+	daysUntil: '{delta} dage fra nu',
+	weekUntil: '1 uge fra nu',
+	weeksUntil: '{delta} uger fra nu',
+	monthUntil: '1 måned fra nu',
+	monthsUntil: '{delta} måneder fra nu',
+	yearUntil: '1 år fra nu',
+	yearsUntil: '{delta} år fra nu'
+
+});
+/*
+---
+
+script: Date.Dutch.js
+
+description: Date messages in Dutch.
+
+license: MIT-style license
+
+authors:
+ - Lennart Pilon
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Dutch]
+
+...
+*/
+
+MooTools.lang.set('nl-NL', 'Date', {
+
+	months: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
+	days: ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag'],
+	//culture's date order: DD/MM/YYYY
+	dateOrder: ['date', 'month', 'year'],
+
+	AM: 'AM',
+	PM: 'PM',
+
+	shortDate: '%d/%m/%Y',
+	shortTime: '%H:%M',
+
+	/* Date.Extras */
+	ordinal: 'e',
+
+	lessThanMinuteAgo: 'minder dan een minuut geleden',
+	minuteAgo: 'ongeveer een minuut geleden',
+	minutesAgo: 'minuten geleden',
+	hourAgo: 'ongeveer een uur geleden',
+	hoursAgo: 'ongeveer {delta} uur geleden',
+	dayAgo: '{delta} dag geleden',
+	daysAgo: 'dagen geleden',
+	weekAgo: 'een week geleden',
+	weeksAgo: '{delta} weken geleden',
+	monthAgo: 'een maand geleden',
+	monthsAgo: '{delta} maanden geleden',
+	yearAgo: 'een jaar geleden',
+	yearsAgo: '{delta} jaar geleden',
+	lessThanMinuteUntil: 'minder dan een minuut vanaf nu',
+	minuteUntil: 'ongeveer een minuut vanaf nu',
+	minutesUntil: '{delta} minuten vanaf nu',
+	hourUntil: 'ongeveer een uur vanaf nu',
+	hoursUntil: 'ongeveer {delta} uur vanaf nu',
+	dayUntil: '1 dag vanaf nu',
+	daysUntil: '{delta} dagen vanaf nu',
+	weekAgo: 'een week geleden',
+	weeksAgo: '{delta} weken geleden',
+	monthAgo: 'een maand geleden',
+	monthsAgo: '{delta} maanden geleden',
+	yearthAgo: 'een jaar geleden',
+	yearsAgo: '{delta} jaar geleden',
+
+	weekUntil: 'over een week',
+	weeksUntil: 'over {delta} weken',
+	monthUntil: 'over een maand',
+	monthsUntil: 'over {delta} maanden',
+	yearUntil: 'over een jaar',
+	yearsUntil: 'over {delta} jaar' 
+
+});/*
+---
+
+script: Date.English.GB.js
+
+description: Date messages for British English.
+
+license: MIT-style license
+
+authors:
+ - Aaron Newton
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.English.GB]
+
+...
+*/
+
+MooTools.lang.set('en-GB', 'Date', {
+
+	dateOrder: ['date', 'month', 'year'],
+	
+	shortDate: '%d/%m/%Y',
+	shortTime: '%H:%M'
+
+}).set('cascade', ['en-US']);/*
+---
+
+script: Date.Estonian.js
+
+description: Date messages for Estonian.
+
+license: MIT-style license
+
+authors:
+ - Kevin Valdek
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Estonian]
+
+...
+*/
+
+MooTools.lang.set('et-EE', 'Date', {
+
+	months: ['jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni', 'juuli', 'august', 'september', 'oktoober', 'november', 'detsember'],
+	days: ['pühapäev', 'esmaspäev', 'teisipäev', 'kolmapäev', 'neljapäev', 'reede', 'laupäev'],
+	//culture's date order: MM.DD.YYYY
+	dateOrder: ['month', 'date', 'year'],
+
+	AM: 'AM',
+	PM: 'PM',
+
+	shortDate: '%m.%d.%Y',
+	shortTime: '%H:%M',
+
+	/* Date.Extras */
+	ordinal: '',
+
+	lessThanMinuteAgo: 'vähem kui minut aega tagasi',
+	minuteAgo: 'umbes minut aega tagasi',
+	minutesAgo: '{delta} minutit tagasi',
+	hourAgo: 'umbes tund aega tagasi',
+	hoursAgo: 'umbes {delta} tundi tagasi',
+	dayAgo: '1 päev tagasi',
+	daysAgo: '{delta} päeva tagasi',
+	weekAgo: '1 nädal tagasi',
+	weeksAgo: '{delta} nädalat tagasi',
+	monthAgo: '1 kuu tagasi',
+	monthsAgo: '{delta} kuud tagasi',
+	yearAgo: '1 aasta tagasi',
+	yearsAgo: '{delta} aastat tagasi',
+	lessThanMinuteUntil: 'vähem kui minuti aja pärast',
+	minuteUntil: 'umbes minuti aja pärast',
+	minutesUntil: '{delta} minuti pärast',
+	hourUntil: 'umbes tunni aja pärast',
+	hoursUntil: 'umbes {delta} tunni pärast',
+	dayUntil: '1 päeva pärast',
+	daysUntil: '{delta} päeva pärast',
+	weekUntil: '1 nädala pärast',
+	weeksUntil: '{delta} nädala pärast',
+	monthUntil: '1 kuu pärast',
+	monthsUntil: '{delta} kuu pärast',
+	yearUntil: '1 aasta pärast',
+	yearsUntil: '{delta} aasta pärast'
+
+});/*
+---
+
+script: Date.German.js
+
+description: Date messages for German.
+
+license: MIT-style license
+
+authors:
+ - Christoph Pojer
+ - Frank Rossi
+ - Ulrich Petri
+ - Fabian Beiner
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.German]
+
+...
+*/
+
+MooTools.lang.set('de-DE', 'Date', {
+
+	months: ['Januar', 'Februar', 'M&auml;rz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
+	days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
+	//culture's date order: MM/DD/YYYY
+	dateOrder: [ 'date', 'month', 'year', '.'],
+
+	AM: 'vormittags',
+	PM: 'nachmittags',
+
+	shortDate: '%d.%m.%Y',
+	shortTime: '%H:%M',
+
+	/* Date.Extras */
+	ordinal: '.',
+
+	lessThanMinuteAgo: 'Vor weniger als einer Minute',
+	minuteAgo: 'Vor einer Minute',
+	minutesAgo: 'Vor {delta} Minuten',
+	hourAgo: 'Vor einer Stunde',
+	hoursAgo: 'Vor {delta} Stunden',
+	dayAgo: 'Vor einem Tag',
+	daysAgo: 'Vor {delta} Tagen',
+	weekAgo: 'Vor einer Woche',
+	weeksAgo: 'Vor {delta} Wochen',
+	monthAgo: 'Vor einem Monat',
+	monthsAgo: 'Vor {delta} Monaten',
+	yearAgo: 'Vor einem Jahr',
+	yearsAgo: 'Vor {delta} Jahren',
+	lessThanMinuteUntil: 'In weniger als einer Minute',
+	minuteUntil: 'In einer Minute',
+	minutesUntil: 'In {delta} Minuten',
+	hourUntil: 'In ca. einer Stunde',
+	hoursUntil: 'In ca. {delta} Stunden',
+	dayUntil: 'In einem Tag',
+	daysUntil: 'In {delta} Tagen',
+	weekUntil: 'In einer Woche',
+	weeksUntil: 'In {delta} Wochen',
+	monthUntil: 'In einem Monat',
+	monthsUntil: 'In {delta} Monaten',
+	yearUntil: 'In einem Jahr',
+	yearsUntil: 'In {delta} Jahren'
+});/*
+---
+
+script: Date.German.CH.js
+
+description: Date messages for German (Switzerland).
+
+license: MIT-style license
+
+authors:
+ - Michael van der Weg
+
+requires:
+ - /Lang
+ - /Date.German
+
+provides: [Date.German.CH]
+
+...
+*/
+
+MooTools.lang.set('de-CH', 'cascade', ['de-DE']);/*
+---
+
+script: Date.French.js
+
+description: Date messages in French.
+
+license: MIT-style license
+
+authors:
+ - Nicolas Sorosac
+ - Antoine Abt
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.French]
+
+...
+*/
+ 
+MooTools.lang.set('fr-FR', 'Date', {
+
+	months: ['janvier', 'f&eacute;vrier', 'mars', 'avril', 'mai', 'juin', 'juillet', 'ao&ucirc;t', 'septembre', 'octobre', 'novembre', 'd&eacute;cembre'],
+	days: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
+	dateOrder: ['date', 'month', 'year'],
+
+	AM: 'AM',
+	PM: 'PM',
+
+	shortDate: '%d/%m/%Y',
+	shortTime: '%H:%M',
+
+	/* Date.Extras */
+	getOrdinal: function(dayOfMonth){
+	  return (dayOfMonth > 1) ? '' : 'er';
+	},
+
+	lessThanMinuteAgo: 'il y a moins d\'une minute',
+	minuteAgo: 'il y a une minute',
+	minutesAgo: 'il y a {delta} minutes',
+	hourAgo: 'il y a une heure',
+	hoursAgo: 'il y a {delta} heures',
+	dayAgo: 'il y a un jour',
+	daysAgo: 'il y a {delta} jours',
+	weekAgo: 'il y a une semaine',
+	weeksAgo: 'il y a {delta} semaines',
+	monthAgo: 'il y a 1 mois',
+	monthsAgo: 'il y a {delta} mois',
+	yearthAgo: 'il y a 1 an',
+	yearsAgo: 'il y a {delta} ans',
+	lessThanMinuteUntil: 'dans moins d\'une minute',
+	minuteUntil: 'dans une minute',
+	minutesUntil: 'dans {delta} minutes',
+	hourUntil: 'dans une heure',
+	hoursUntil: 'dans {delta} heures',
+	dayUntil: 'dans un jour',
+	daysUntil: 'dans {delta} jours',
+	weekUntil: 'dans 1 semaine',
+	weeksUntil: 'dans {delta} semaines',
+	monthUntil: 'dans 1 mois',
+	monthsUntil: 'dans {delta} mois',
+	yearUntil: 'dans 1 an',
+	yearsUntil: 'dans {delta} ans'
+
+});
+/*
+---
+
+script: Date.Italian.js
+
+description: Date messages for Italian.
+
+license: MIT-style license.
+
+authors:
+ - Andrea Novero
+ - Valerio Proietti
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Italian]
+
+...
+*/
+ 
+MooTools.lang.set('it-IT', 'Date', {
+ 
+	months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
+	days: ['Domenica', 'Luned&igrave;', 'Marted&igrave;', 'Mercoled&igrave;', 'Gioved&igrave;', 'Venerd&igrave;', 'Sabato'],
+	//culture's date order: DD/MM/YYYY
+	dateOrder: ['date', 'month', 'year'],
+
+	AM: 'AM',
+	PM: 'PM',
+
+	shortDate: '%d/%m/%Y',
+	shortTime: '%H.%M',
+
+	/* Date.Extras */
+	ordinal: '&ordm;',
+
+	lessThanMinuteAgo: 'meno di un minuto fa',
+	minuteAgo: 'circa un minuto fa',
+	minutesAgo: 'circa {delta} minuti fa',
+	hourAgo: 'circa un\'ora fa',
+	hoursAgo: 'circa {delta} ore fa',
+	dayAgo: 'circa 1 giorno fa',
+	daysAgo: 'circa {delta} giorni fa',
+	lessThanMinuteUntil: 'tra meno di un minuto',
+	minuteUntil: 'tra circa un minuto',
+	minutesUntil: 'tra circa {delta} minuti',
+	hourUntil: 'tra circa un\'ora',
+	hoursUntil: 'tra circa {delta} ore',
+	dayUntil: 'tra circa un giorno',
+	daysUntil: 'tra circa {delta} giorni'
+
+});/*
+---
+
+script: Date.Norwegian.js
+
+description: Date messages in Norwegian.
+
+license: MIT-style license
+
+authors:
+ - Espen 'Rexxars' Hovlandsdal
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Norwegian]
+
+...
+*/
+
+MooTools.lang.set('no-NO', 'Date', {
+
+	dateOrder: ['date', 'month', 'year'],
+
+	shortDate: '%d.%m.%Y',
+	shortTime: '%H:%M',
+
+	lessThanMinuteAgo: 'kortere enn et minutt siden',
+	minuteAgo: 'omtrent et minutt siden',
+	minutesAgo: '{delta} minutter siden',
+	hourAgo: 'omtrent en time siden',
+	hoursAgo: 'omtrent {delta} timer siden',
+	dayAgo: '{delta} dag siden',
+	daysAgo: '{delta} dager siden'
+
+});/*
+---
+
+script: Date.Polish.js
+
+description: Date messages for Polish.
+
+license: MIT-style license
+
+authors:
+ - Oskar Krawczyk
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Polish]
+
+...
+*/
+
+MooTools.lang.set('pl-PL', 'Date', {
+	months: ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
+	days: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'],
+	dateOrder: ['year', 'month', 'date'],
+	AM: 'nad ranem',
+	PM: 'po południu',
+
+	shortDate: '%Y-%m-%d',
+	shortTime: '%H:%M',
+
+	/* Date.Extras */
+	ordinal: function(dayOfMonth){
+		return (dayOfMonth > 3 && dayOfMonth < 21) ? 'ty' : ['ty', 'szy', 'gi', 'ci', 'ty'][Math.min(dayOfMonth % 10, 4)];
+	},
+
+	lessThanMinuteAgo: 'mniej niż minute temu',
+	minuteAgo: 'około minutę temu',
+	minutesAgo: '{delta} minut temu',
+	hourAgo: 'około godzinę temu',
+	hoursAgo: 'około {delta} godzin temu',
+	dayAgo: 'Wczoraj',
+	daysAgo: '{delta} dni temu',
+	lessThanMinuteUntil: 'za niecałą minutę',
+	minuteUntil: 'za około minutę',
+	minutesUntil: 'za {delta} minut',
+	hourUntil: 'za około godzinę',
+	hoursUntil: 'za około {delta} godzin',
+	dayUntil: 'za 1 dzień',
+	daysUntil: 'za {delta} dni'
+});/*
+---
+
+script: Date.Portuguese.BR.js
+
+description: Date messages in Portuguese-BR (Brazil).
+
+license: MIT-style license
+
+authors:
+ - Fabio Miranda Costa
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Portuguese.BR]
+
+...
+*/
+
+MooTools.lang.set('pt-BR', 'Date', {
+
+	months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
+	days: ['Domingo', 'Segunda-feira', 'Terça-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'Sábado'],
+	//culture's date order: DD/MM/YYYY
+	dateOrder: ['date', 'month', 'year'],
+	shortDate: '%d/%m/%Y',
+	shortTime: '%H:%M',
+
+	/* Date.Extras */
+	ordinal: function(dayOfMonth){
+		//1º, 2º, 3º, etc.
+    	return '&ordm;';
+	},
+
+	lessThanMinuteAgo: 'há menos de um minuto',
+	minuteAgo: 'há cerca de um minuto',
+	minutesAgo: 'há {delta} minutos',
+	hourAgo: 'há cerca de uma hora',
+	hoursAgo: 'há cerca de {delta} horas',
+	dayAgo: 'há um dia',
+	daysAgo: 'há {delta} dias',
+    weekAgo: 'há uma semana',
+	weeksAgo: 'há {delta} semanas',
+	monthAgo: 'há um mês',
+	monthsAgo: 'há {delta} meses',
+	yearAgo: 'há um ano',
+	yearsAgo: 'há {delta} anos',
+	lessThanMinuteUntil: 'em menos de um minuto',
+	minuteUntil: 'em um minuto',
+	minutesUntil: 'em {delta} minutos',
+	hourUntil: 'em uma hora',
+	hoursUntil: 'em {delta} horas',
+	dayUntil: 'em um dia',
+	daysUntil: 'em {delta} dias',
+	weekUntil: 'em uma semana',
+	weeksUntil: 'em {delta} semanas',
+	monthUntil: 'em um mês',
+	monthsUntil: 'em {delta} meses',
+	yearUntil: 'em um ano',
+	yearsUntil: 'em {delta} anos'
+
+});/*
+Script: Date.Russian.js
+	Date messages for Russian.
+
+	License:
+		MIT-style license.
+
+	Authors:
+		Evstigneev Pavel
+*/
+
+MooTools.lang.set('ru-RU-unicode', 'Date', {
+
+	months: ['Январь', 'Февраль', 'Март', 'Ð?прель', 'Май', 'Июнь', 'Июль', 'Ð?вгуÑ?Ñ‚', 'СентÑ?брь', 'ОктÑ?брь', 'Ð?оÑ?брь', 'Декабрь'],
+	days: ['ВоÑ?креÑ?енье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'ПÑ?тница', 'Суббота'],
+	//culture's date order: MM/DD/YYYY
+	dateOrder: ['date', 'month', 'year'],
+	AM: 'AM',
+	PM: 'PM',
+
+	shortDate: '%d/%m/%Y',
+	shortTime: '%H:%M',
+
+
+  /*
+   *  Russian language pluralization rules, taken from CLDR project, http://unicode.org/cldr/
+   *
+   *  one -> n mod 10 is 1 and n mod 100 is not 11;
+   *  few -> n mod 10 in 2..4 and n mod 100 not in 12..14;
+   *  many -> n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
+   *  other -> everything else (example 3.14)
+   */
+
+  pluralize: function (n, one, few, many, other) {
+    var modulo10 = n % 10
+    var modulo100 = n % 100
+
+    if (modulo10 == 1 && modulo100 != 11) {
+      return one;
+    } else if ((modulo10 == 2 || modulo10 == 3 || modulo10 == 4) && !(modulo100 == 12 || modulo100 == 13 || modulo100 == 14)) {
+      return few;
+    } else if (modulo10 == 0 || (modulo10 == 5 || modulo10 == 6 || modulo10 == 7 || modulo10 == 8 || modulo10 == 9) || (modulo100 == 11 || modulo100 == 12 || modulo100 == 13 || modulo100 == 14)) {
+      return many;
+    } else {
+      return other;
+    }
+  },
+
+	/* Date.Extras */
+	ordinal: '',
+	lessThanMinuteAgo: 'меньше минуты назад',
+	minuteAgo: 'минута назад',
+	minutesAgo: function (delta) { return  '{delta} ' + this.pluralize(delta, 'минута', 'минуты', 'минут') + ' назад'},
+	hourAgo: 'чаÑ? назад',
+	hoursAgo: function (delta) { return  '{delta} ' + this.pluralize(delta, 'чаÑ?', 'чаÑ?а', 'чаÑ?ов') + ' назад'},
+	dayAgo: 'вчера',
+	daysAgo: function (delta) { return '{delta} ' + this.pluralize(delta, 'день', 'днÑ?', 'дней') + ' назад' },
+	lessThanMinuteUntil: 'меньше минуты назад',
+	minuteUntil: 'через минуту',
+	minutesUntil: function (delta) { return  'через {delta} ' + this.pluralize(delta, 'чаÑ?', 'чаÑ?а', 'чаÑ?ов') + ''},
+	hourUntil: 'через чаÑ?',
+	hoursUntil: function (delta) { return  'через {delta} ' + this.pluralize(delta, 'чаÑ?', 'чаÑ?а', 'чаÑ?ов') + ''},
+	dayUntil: 'завтра',
+	daysUntil: function (delta) { return 'через {delta} ' + this.pluralize(delta, 'день', 'днÑ?', 'дней') + '' }
+
+});/*
+---
+
+script: Date.Spanish.US.js
+
+description: Date messages for Spanish.
+
+license: MIT-style license
+
+authors:
+ - Ãlfons Sanchez
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Spanish]
+
+...
+*/
+
+MooTools.lang.set('es-ES', 'Date', {
+
+	months: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
+	days: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'],
+	//culture's date order: MM/DD/YYYY
+	dateOrder: ['date', 'month', 'year'],
+	AM: 'AM',
+	PM: 'PM',
+
+	shortDate: '%d/%m/%Y',
+	shortTime: '%H:%M',
+
+	/* Date.Extras */
+	ordinal: '',
+
+	lessThanMinuteAgo: 'hace menos de un minuto',
+	minuteAgo: 'hace un minuto',
+	minutesAgo: 'hace {delta} minutos',
+	hourAgo: 'hace una hora',
+	hoursAgo: 'hace unas {delta} horas',
+	dayAgo: 'hace un día',
+	daysAgo: 'hace {delta} días',
+	weekAgo: 'hace una semana',
+	weeksAgo: 'hace unas {delta} semanas',
+	monthAgo: 'hace un mes',
+	monthsAgo: 'hace {delta} meses',
+	yearAgo: 'hace un año',
+	yearsAgo: 'hace {delta} años',
+	lessThanMinuteUntil: 'menos de un minuto desde ahora',
+	minuteUntil: 'un minuto desde ahora',
+	minutesUntil: '{delta} minutos desde ahora',
+	hourUntil: 'una hora desde ahora',
+	hoursUntil: 'unas {delta} horas desde ahora',
+	dayUntil: 'un día desde ahora',
+	daysUntil: '{delta} días desde ahora',
+	weekUntil: 'una semana desde ahora',
+	weeksUntil: 'unas {delta} semanas desde ahora',
+	monthUntil: 'un mes desde ahora',
+	monthsUntil: '{delta} meses desde ahora',
+	yearUntil: 'un año desde ahora',
+	yearsUntil: '{delta} años desde ahora'
+
+});/*
+---
+
+script: Date.Swedish.js
+
+description: Date messages for Swedish (SE).
+
+license: MIT-style license
+
+authors:
+ - Martin Lundgren
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Swedish]
+
+...
+*/
+
+MooTools.lang.set('sv-SE', 'Date', {
+
+	months: ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december'],
+	days: ['söndag', 'måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag', 'lördag'],
+	// culture's date order: YYYY-MM-DD
+	dateOrder: ['year', 'month', 'date'],
+	AM: '',
+	PM: '',
+
+	shortDate: '%Y-%m-%d',
+	shortTime: '%H:%M',
+
+	/* Date.Extras */
+	ordinal: function(dayOfMonth){
+		// Not used in Swedish
+		return '';
+	},
+
+	lessThanMinuteAgo: 'mindre än en minut sedan',
+	minuteAgo: 'ungefär en minut sedan',
+	minutesAgo: '{delta} minuter sedan',
+	hourAgo: 'ungefär en timme sedan',
+	hoursAgo: 'ungefär {delta} timmar sedan',
+	dayAgo: '1 dag sedan',
+	daysAgo: '{delta} dagar sedan',
+	lessThanMinuteUntil: 'mindre än en minut sedan',
+	minuteUntil: 'ungefär en minut sedan',
+	minutesUntil: '{delta} minuter sedan',
+	hourUntil: 'ungefär en timme sedan',
+	hoursUntil: 'ungefär {delta} timmar sedan',
+	dayUntil: '1 dag sedan',
+	daysUntil: '{delta} dagar sedan'
+
+});/*
+---
+
+script: Date.Ukrainian.js
+
+description: Date messages for Ukrainian.
+
+license: MIT-style license
+
+authors:
+ - Slik
+
+requires:
+ - /Lang
+ - /Date
+
+provides: [Date.Ukrainian]
+
+...
+*/
+
+(function(){
+	var pluralize = function(n, one, few, many, other){
+		var d = (n / 10).toInt();
+		var z = n % 10;
+		var s = (n / 100).toInt();
+
+		if(d == 1 && n > 10) return many;
+		if(z == 1) return one;
+		if(z > 0 && z < 5) return few;
+		return many;
+	};
+
+	MooTools.lang.set('uk-UA', 'Date', {
+			months: ['Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'ВереÑ?ень', 'Жовтень', 'ЛиÑ?топад', 'Грудень'],
+			days: ['Ð?еділÑ?', 'Понеділок', 'Вівторок', 'Середа', 'Четвер', 'П\'Ñ?тницÑ?', 'Субота'],
+			//culture's date order: DD/MM/YYYY
+			dateOrder: ['date', 'month', 'year'],
+			AM: 'до полуднÑ?',
+			PM: 'по полудню',
+
+			shortDate: '%d/%m/%Y',
+			shortTime: '%H:%M',
+
+			/* Date.Extras */
+			ordinal: '',
+			lessThanMinuteAgo: 'меньше хвилини тому',
+			minuteAgo: 'хвилину тому',
+			minutesAgo: function (delta){
+				return '{delta} ' + pluralize(delta, 'хвилину', 'хвилини', 'хвилин') + ' тому';
+			},
+			hourAgo: 'годину тому',
+			hoursAgo: function (delta){
+				return '{delta} ' + pluralize(delta, 'годину', 'години', 'годин') + ' тому';
+			},
+			dayAgo: 'вчора',
+			daysAgo: function (delta){
+				return '{delta} ' + pluralize(delta, 'день', 'днÑ?', 'днів') + ' тому';
+			},
+			weekAgo: 'тиждень тому',
+			weeksAgo: function (delta){
+				return '{delta} ' + pluralize(delta, 'тиждень', 'тижні', 'тижнів') + ' тому';
+			},
+			monthAgo: 'міÑ?Ñ?ць тому',
+			monthsAgo: function (delta){
+				return '{delta} ' + pluralize(delta, 'міÑ?Ñ?ць', 'міÑ?Ñ?ці', 'міÑ?Ñ?ців') + ' тому';
+			},
+			yearAgo: 'рік тому',
+			yearsAgo: function (delta){
+				return '{delta} ' + pluralize(delta, 'рік', 'роки', 'років') + ' тому';
+			},
+			lessThanMinuteUntil: 'за мить',
+			minuteUntil: 'через хвилину',
+			minutesUntil: function (delta){
+				return 'через {delta} ' + pluralize(delta, 'хвилину', 'хвилини', 'хвилин');
+			},
+			hourUntil: 'через годину',
+			hoursUntil: function (delta){
+				return 'через {delta} ' + pluralize(delta, 'годину', 'години', 'годин');
+			},
+			dayUntil: 'завтра',
+			daysUntil: function (delta){
+				return 'через {delta} ' + pluralize(delta, 'день', 'днÑ?', 'днів');
+			},
+			weekUntil: 'через тиждень',
+			weeksUntil: function (delta){
+				return 'через {delta} ' + pluralize(delta, 'тиждень', 'тижні', 'тижнів');
+			},
+			monthUntil: 'через міÑ?Ñ?ць',
+			monthesUntil: function (delta){
+				return 'через {delta} ' + pluralize(delta, 'міÑ?Ñ?ць', 'міÑ?Ñ?ці', 'міÑ?Ñ?ців');
+			},
+			yearUntil: 'через рік',
+			yearsUntil: function (delta){
+				return 'через {delta} ' + pluralize(delta, 'рік', 'роки', 'років');
+			}
+	});
+
+})();/*
+---
+
+script: Form.Validator.Arabic.js
+
+description: Form.Validator messages in Arabic.
+
+license: MIT-style license
+
+authors:
+ - Chafik Barbar
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Arabic]
+
+...
+*/
+
+MooTools.lang.set('ar', 'Form.Validator', {
+	required:'هذا الحقل مطلوب.',
+	minLength:'رجاءً إدخال {minLength}  أحرÙ? على الأقل (تم إدخال {length} أحرÙ?).',
+	maxLength:'الرجاء عدم إدخال أكثر من {maxLength} أحرÙ? (تم إدخال {length} أحرÙ?).',
+	integer:'الرجاء إدخال عدد صحيح Ù?ÙŠ هذا الحقل. أي رقم ذو كسر عشري أو مئوي (مثال 1.25 ) غير مسموح.',
+	numeric:'الرجاء إدخال قيم رقمية Ù?ÙŠ هذا الحقل (مثال "1" أو "1.1" أو "-1" أو "-1.1").',
+	digits:'الرجاء أستخدام قيم رقمية وعلامات ترقيمية Ù?قط Ù?ÙŠ هذا الحقل (مثال, رقم هاتÙ? مع نقطة أو شحطة)',
+	alpha:'الرجاء أستخدام أحرÙ? Ù?قط (ا-ÙŠ) Ù?ÙŠ هذا الحقل. أي Ù?راغات أو علامات غير مسموحة.',
+	alphanum:'الرجاء أستخدام أحرÙ? Ù?قط (ا-ÙŠ) أو أرقام (0-9) Ù?قط Ù?ÙŠ هذا الحقل. أي Ù?راغات أو علامات غير مسموحة.',
+	dateSuchAs:'الرجاء إدخال تاريخ صحيح كالتالي {date}',
+	dateInFormatMDY:'الرجاء إدخال تاريخ صحيح (مثال, 31-12-1999)',
+	email:'الرجاء إدخال بريد إلكتروني صحيح.',
+	url:'الرجاء إدخال عنوان إلكتروني صحيح مثل http://www.google.com',
+	currencyDollar:'الرجاء إدخال قيمة $ صحيحة.  مثال, 100.00$',
+	oneRequired:'الرجاء إدخال قيمة Ù?ÙŠ أحد هذه الحقول على الأقل.',
+	errorPrefix: 'خطأ: ',
+	warningPrefix: 'تحذير: '
+}).set('ar', 'Date', {
+	dateOrder: ['date', 'month', 'year', '/']
+});/*
+---
+
+script: Form.Validator.Catalan.js
+
+description: Date messages for Catalan.
+
+license: MIT-style license
+
+authors:
+ - Miquel Hudin
+ - Alfons Sanchez
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Catalan]
+
+...
+*/
+
+MooTools.lang.set('ca-CA', 'Form.Validator', {
+
+	required:'Aquest camp es obligatori.',
+	minLength:'Per favor introdueix al menys {minLength} caracters (has introduit {length} caracters).',
+	maxLength:'Per favor introdueix no mes de {maxLength} caracters (has introduit {length} caracters).',
+	integer:'Per favor introdueix un nombre enter en aquest camp. Nombres amb decimals (p.e. 1,25) no estan permesos.',
+	numeric:'Per favor introdueix sols valors numerics en aquest camp (p.e. "1" o "1,1" o "-1" o "-1,1").',
+	digits:'Per favor usa sols numeros i puntuacio en aquest camp (per exemple, un nombre de telefon amb guions i punts no esta permes).',
+	alpha:'Per favor utilitza lletres nomes (a-z) en aquest camp. No s´admiteixen espais ni altres caracters.',
+	alphanum:'Per favor, utilitza nomes lletres (a-z) o numeros (0-9) en aquest camp. No s´admiteixen espais ni altres caracters.',
+	dateSuchAs:'Per favor introdueix una data valida com {date}',
+	dateInFormatMDY:'Per favor introdueix una data valida com DD/MM/YYYY (p.e. "31/12/1999")',
+	email:'Per favor, introdueix una adreça de correu electronic valida. Per exemple,  "fred at domain.com".',
+	url:'Per favor introdueix una URL valida com http://www.google.com.',
+	currencyDollar:'Per favor introdueix una quantitat valida de €. Per exemple €100,00 .',
+	oneRequired:'Per favor introdueix alguna cosa per al menys una d´aquestes entrades.',
+	errorPrefix: 'Error: ',
+	warningPrefix: 'Avis: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'No poden haver espais en aquesta entrada.',
+	reqChkByNode: 'No hi han elements seleccionats.',
+	requiredChk: 'Aquest camp es obligatori.',
+	reqChkByName: 'Per favor selecciona una {label}.',
+	match: 'Aquest camp necessita coincidir amb el camp {matchName}',
+	startDate: 'la data de inici',
+	endDate: 'la data de fi',
+	currendDate: 'la data actual',
+	afterDate: 'La data deu ser igual o posterior a {label}.',
+	beforeDate: 'La data deu ser igual o anterior a {label}.',
+	startMonth: 'Per favor selecciona un mes d´orige',
+	sameMonth: 'Aquestes dos dates deuen estar dins del mateix mes - deus canviar una o altra.'
+
+});/*
+---
+
+script: Form.Validator.Czech.js
+
+description: Form Validator messages for Czech.
+
+license: MIT-style license
+
+authors:
+ - Jan Černý chemiX
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Czech]
+
+...
+*/
+
+MooTools.lang.set('cs-CZ', 'Form.Validator', {
+
+	required:'Tato položka je povinná.',
+	minLength:'Zadejte prosím alespoň {minLength} znaků (napsáno {length} znaků).',
+	maxLength:'Zadejte prosím méně než {maxLength} znaků (nápsáno {length} znaků).',
+	integer:'Zadejte prosím celé Ä?íslo. Desetinná Ä?ísla (napÅ™. 1.25) nejsou povolena.',
+	numeric:'Zadejte jen Ä?íselné hodnoty  (tj. "1" nebo "1.1" nebo "-1" nebo "-1.1").',
+	digits:'Zadejte prosím pouze Ä?ísla a interpunkÄ?ní znaménka(například telefonní Ä?íslo s pomlÄ?kami nebo teÄ?kami je povoleno).',
+	alpha:'Zadejte prosím pouze písmena (a-z). Mezery nebo jiné znaky nejsou povoleny.',
+	alphanum:'Zadejte prosím pouze písmena (a-z) nebo Ä?íslice (0-9). Mezery nebo jiné znaky nejsou povoleny.',
+	dateSuchAs:'Zadejte prosím platné datum jako {date}',
+	dateInFormatMDY:'Zadejte prosím platné datum jako MM / DD / RRRR (tj. "12/31/1999")',
+	email:'Zadejte prosím platnou e-mailovou adresu. Například "fred at domain.com".',
+	url:'Zadejte prosím platnou URL adresu jako http://www.google.com.',
+	currencyDollar:'Zadejte prosím platnou Ä?ástku. Například $100.00.',
+	oneRequired:'Zadejte prosím alespoň jednu hodnotu pro tyto položky.',
+	errorPrefix: 'Chyba: ',
+	warningPrefix: 'Upozornění: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'V této položce nejsou povoleny mezery',
+	reqChkByNode: 'Nejsou vybrány žádné položky.',
+	requiredChk: 'Tato položka je vyžadována.',
+	reqChkByName: 'Prosím vyberte {label}.',
+	match: 'Tato položka se musí shodovat s položkou {matchName}',
+	startDate: 'datum zahájení',
+	endDate: 'datum ukonÄ?ení',
+	currendDate: 'aktuální datum',
+	afterDate: 'Datum by mělo být stejné nebo větší než {label}.',
+	beforeDate: 'Datum by mělo být stejné nebo menší než {label}.',
+	startMonth: 'Vyberte poÄ?áteÄ?ní mÄ›síc.',
+	sameMonth: 'Tyto dva datumy musí být ve stejném měsíci - změňte jeden z nich.',
+    creditcard: 'Zadané Ä?íslo kreditní karty je neplatné. Prosím opravte ho. Bylo zadáno {length} Ä?ísel.'
+
+});
+/*
+---
+
+script: Form.Validator.Chinese.js
+
+description: Form.Validator messages in chinese (both simplified and traditional).
+
+license: MIT-style license
+
+authors:
+ - 陈桂军 - guidy <at> ixuer [dot] net
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Chinese]
+
+...
+*/
+
+/*
+In Chinese:
+------------
+需�指出的是:
+简体中文适用于中国大陆,
+�体中文适用于香港�澳门和�湾�。
+简体中文和�体中文在字体和语法上有很多的��之处。
+
+我�以确�简体中文语言包的准确性,
+但对于�体中文,我�以��用户�以准确的�解,但无法��语�符�他们的阅读习惯。
+如果您�能确认的�,�以�使用简体中文语言包,因为它是最通用的。
+
+In English:
+------------
+It should be noted that:
+Simplified  Chinese apply to mainland Chinese,
+Traditional Chinese apply to Hong Kong, Macao and Taiwan Province.
+There are a lot of different from Simplified  Chinese and Traditional Chinese , Contains font and syntax .
+
+I can assure Simplified Chinese language pack accuracy .
+For Traditional Chinese, I can only guarantee that users can understand, but not necessarily in line with their reading habits.
+If you are unsure, you can only use the simplified Chinese language pack, as it is the most common.
+
+*/
+
+// Simplified Chinese
+MooTools.lang.set('zhs-CN', 'Form.Validator', {
+	required:'这是必填项。',
+	minLength:'请至少输入 {minLength} 个字符 (已输入 {length} 个)。',
+	maxLength:'最多�能输入 {maxLength} 个字符 (已输入 {length} 个)。',
+	integer:'请输入一个整数,�能包��数点。例如:"1", "200"。',
+	numeric:'请输入一个数字,例如:"1", "1.1", "-1", "-1.1"。',
+	digits:'这里�能接�数字和标点的输入,标点�以是:"(", ")", ".", ":", "-", "+", "#"和空格。',
+	alpha:'请输入 A-Z 的 26 个字�,�能包�空格或任何其他字符。',
+	alphanum:'请输入 A-Z 的 26 个字�或 0-9 的 10 个数字,�能包�空格或任何其他字符。',
+	dateSuchAs:'请输入�法的日期格�,如:{date}。',
+	dateInFormatMDY:'请输入�法的日期格�,例如:MM/DD/YYYY ("12/31/1999")。',
+	email:'请输入�法的电�信箱地�,例如:"fred at domain.com"。',
+	url:'请输入�法的 Url 地�,例如:http://www.google.com。',
+	currencyDollar:'请输入�法的货�符�,例如:¥',
+	oneRequired:'请至少选择一项。',
+	errorPrefix: '错误:',
+	warningPrefix: '警告:'
+});
+
+// Traditional Chinese
+MooTools.lang.set('zht-CN', 'Form.Validator', {
+	required:'這是必填項。',
+	minLength:'請至少�入 {minLength} 個字符(已�入 {length} 個)。',
+	maxLength:'最多�能�入 {maxLength} 個字符(已�入 {length} 個)。',
+	integer:'請�入一個整數,�能包��數點。例如:"1", "200"。',
+	numeric:'請�入一個數字,例如:"1", "1.1", "-1", "-1.1"。',
+	digits:'這裡�能接�數字和標點的�入,標點�以是:"(", ")", ".", ":", "-", "+", "#"和空格。',
+	alpha:'請�入 A-Z 的 26 個字�,�能包�空格或任何其他字符。',
+	alphanum:'請�入 A-Z 的 26 個字�或 0-9 的 10 個數字,�能包�空格或任何其他字符。',
+	dateSuchAs:'請�入�法的日期格�,如:{date}。',
+	dateInFormatMDY:'請�入�法的日期格�,例如:MM/DD/YYYY ("12/31/1999")。',
+	email:'請�入�法的電�信箱地�,例如:"fred at domain.com"。',
+	url:'請�入�法的 Url 地�,例如:http://www.google.com。',
+	currencyYuan:'請�入�法的貨幣符號,例如:¥',
+	oneRequired:'請至少�擇一項。',
+	errorPrefix: '錯誤:',
+	warningPrefix: '警告:'
+});
+
+Form.Validator.add('validate-currency-yuan', {
+	errorMsg: function(){
+		return Form.Validator.getMsg('currencyYuan');
+	},
+	test: function(element) {
+		// [ï¿¥]1[##][,###]+[.##]
+		// [ï¿¥]1###+[.##]
+		// [ï¿¥]0.##
+		// [ï¿¥].##
+		return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^ï¿¥?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value'));
+	}
+});
+/*
+---
+
+script: Form.Validator.Dutch.js
+
+description: Form.Validator messages in Dutch.
+
+license: MIT-style license
+
+authors:
+ - Lennart Pilon
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Dutch]
+
+...
+*/
+
+MooTools.lang.set('nl-NL', 'Form.Validator', {
+	required:'Dit veld is verplicht.',
+	minLength:'Vul minimaal {minLength} karakters in (je hebt {length} karakters ingevoerd).',
+	maxLength:'Vul niet meer dan {maxLength} karakters in (je hebt {length} karakters ingevoerd).',
+	integer:'Vul een getal in. Getallen met decimalen (bijvoorbeeld 1,25) zijn niet toegestaan.',
+	numeric:'Vul alleen numerieke waarden in (bijvoorbeeld. "1" of "1.1" of "-1" of "-1.1").',
+	digits:'Vul alleen nummers en leestekens in (bijvoorbeeld een telefoonnummer met een streepje).',
+	alpha:'Vul alleen letters in (a-z). Spaties en andere karakters zijn niet toegestaan.',
+	alphanum:'Vul alleen letters in (a-z) of nummers (0-9). Spaties en andere karakters zijn niet toegestaan.',
+	dateSuchAs:'Vul een geldige datum in, zoals {date}',
+	dateInFormatMDY:'Vul een geldige datum, in het formaat MM/DD/YYYY (bijvoorbeeld "12/31/1999")',
+	email:'Vul een geldig e-mailadres in. Bijvoorbeeld "fred at domein.nl".',
+	url:'Vul een geldige URL in, zoals http://www.google.nl.',
+	currencyDollar:'Vul een geldig $ bedrag in. Bijvoorbeeld $100.00 .',
+	oneRequired:'Vul iets in bij minimaal een van de invoervelden.',
+	warningPrefix: 'Waarschuwing: ',
+	errorPrefix: 'Fout: '
+});/*
+---
+
+script: Form.Validator.Estonian.js
+
+description: Date messages for Estonian.
+
+license: MIT-style license
+
+authors:
+ - Kevin Valdek
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Estonian]
+
+...
+*/
+
+MooTools.lang.set('et-EE', 'Form.Validator', {
+
+	required:'Väli peab olema täidetud.',
+	minLength:'Palun sisestage vähemalt {minLength} tähte (te sisestasite {length} tähte).',
+	maxLength:'Palun ärge sisestage rohkem kui {maxLength} tähte (te sisestasite {length} tähte).',
+	integer:'Palun sisestage väljale täisarv. Kümnendarvud (näiteks 1.25) ei ole lubatud.',
+	numeric:'Palun sisestage ainult numbreid väljale (näiteks "1", "1.1", "-1" või "-1.1").',
+	digits:'Palun kasutage ainult numbreid ja kirjavahemärke (telefoninumbri sisestamisel on lubatud kasutada kriipse ja punkte).',
+	alpha:'Palun kasutage ainult tähti (a-z). Tühikud ja teised sümbolid on keelatud.',
+	alphanum:'Palun kasutage ainult tähti (a-z) või numbreid (0-9). Tühikud ja teised sümbolid on keelatud.',
+	dateSuchAs:'Palun sisestage kehtiv kuupäev kujul {date}',
+	dateInFormatMDY:'Palun sisestage kehtiv kuupäev kujul MM.DD.YYYY (näiteks: "12.31.1999").',
+	email:'Palun sisestage kehtiv e-maili aadress (näiteks: "fred at domain.com").',
+	url:'Palun sisestage kehtiv URL (näiteks: http://www.google.com).',
+	currencyDollar:'Palun sisestage kehtiv $ summa (näiteks: $100.00).',
+	oneRequired:'Palun sisestage midagi vähemalt ühele antud väljadest.',
+	errorPrefix: 'Viga: ',
+	warningPrefix: 'Hoiatus: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'Väli ei tohi sisaldada tühikuid.',
+	reqChkByNode: 'Ükski väljadest pole valitud.',
+	requiredChk: 'Välja täitmine on vajalik.',
+	reqChkByName: 'Palun valige üks {label}.',
+	match: 'Väli peab sobima {matchName} väljaga',
+	startDate: 'algkuupäev',
+	endDate: 'lõppkuupäev',
+	currendDate: 'praegune kuupäev',
+	afterDate: 'Kuupäev peab olema võrdne või pärast {label}.',
+	beforeDate: 'Kuupäev peab olema võrdne või enne {label}.',
+	startMonth: 'Palun valige algkuupäev.',
+	sameMonth: 'Antud kaks kuupäeva peavad olema samas kuus - peate muutma ühte kuupäeva.'
+
+});/*
+---
+
+script: Form.Validator.German.js
+
+description: Date messages for German.
+
+license: MIT-style license
+
+authors: 
+ - Frank Rossi
+ - Ulrich Petri
+ - Fabian Beiner
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.German]
+
+...
+*/
+
+MooTools.lang.set('de-DE', 'Form.Validator', {
+
+	required: 'Dieses Eingabefeld muss ausgef&uuml;llt werden.',
+	minLength: 'Geben Sie bitte mindestens {minLength} Zeichen ein (Sie haben nur {length} Zeichen eingegeben).',
+	maxLength: 'Geben Sie bitte nicht mehr als {maxLength} Zeichen ein (Sie haben {length} Zeichen eingegeben).',
+	integer: 'Geben Sie in diesem Eingabefeld bitte eine ganze Zahl ein. Dezimalzahlen (z.B. &quot;1.25&quot;) sind nicht erlaubt.',
+	numeric: 'Geben Sie in diesem Eingabefeld bitte nur Zahlenwerte (z.B. &quot;1&quot;, &quot;1.1&quot;, &quot;-1&quot; oder &quot;-1.1&quot;) ein.',
+	digits: 'Geben Sie in diesem Eingabefeld bitte nur Zahlen und Satzzeichen ein (z.B. eine Telefonnummer mit Bindestrichen und Punkten ist erlaubt).',
+	alpha: 'Geben Sie in diesem Eingabefeld bitte nur Buchstaben (a-z) ein. Leerzeichen und andere Zeichen sind nicht erlaubt.',
+	alphanum: 'Geben Sie in diesem Eingabefeld bitte nur Buchstaben (a-z) und Zahlen (0-9) ein. Leerzeichen oder andere Zeichen sind nicht erlaubt.',
+	dateSuchAs: 'Geben Sie bitte ein g&uuml;ltiges Datum ein (z.B. &quot;{date}&quot;).',
+	dateInFormatMDY: 'Geben Sie bitte ein g&uuml;ltiges Datum im Format TT.MM.JJJJ ein (z.B. &quot;31.12.1999&quot;).',
+	email: 'Geben Sie bitte eine g&uuml;ltige E-Mail-Adresse ein (z.B. &quot;max at mustermann.de&quot;).',
+	url: 'Geben Sie bitte eine g&uuml;ltige URL ein (z.B. &quot;http://www.google.de&quot;).',
+	currencyDollar: 'Geben Sie bitte einen g&uuml;ltigen Betrag in EURO ein (z.B. 100.00&#8364;).',
+	oneRequired: 'Bitte f&uuml;llen Sie mindestens ein Eingabefeld aus.',
+	errorPrefix: 'Fehler: ',
+	warningPrefix: 'Warnung: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'Es darf kein Leerzeichen in diesem Eingabefeld sein.',
+	reqChkByNode: 'Es wurden keine Elemente gew&auml;hlt.',
+	requiredChk: 'Dieses Feld muss ausgef&uuml;llt werden.',
+	reqChkByName: 'Bitte w&auml;hlen Sie ein {label}.',
+	match: 'Dieses Eingabefeld muss mit dem {matchName} Eingabefeld &uuml;bereinstimmen.',
+	startDate: 'Das Anfangsdatum',
+	endDate: 'Das Enddatum',
+	currendDate: 'Das aktuelle Datum',
+	afterDate: 'Das Datum sollte zur gleichen Zeit oder sp&auml;ter sein als {label}.',
+	beforeDate: 'Das Datum sollte zur gleichen Zeit oder fr&uuml;her sein als {label}.',
+	startMonth: 'W&auml;hlen Sie bitte einen Anfangsmonat',
+	sameMonth: 'Diese zwei Datumsangaben m&uuml;ssen im selben Monat sein - Sie m&uuml;ssen eines von beiden ver&auml;ndern.',
+	creditcard: 'Die eingegebene Kreditkartennummer ist ung&uuml;ltig. Bitte &uuml;berpr&uuml;fen Sie diese und versuchen Sie es erneut. {length} Zahlen eingegeben.'
+});
+/*
+---
+
+script: Form.Validator.German.CH.js
+
+description: Date messages for German (Switzerland).
+ 
+license: MIT-style license
+ 
+authors:
+ - Michael van der Weg
+
+requires:
+ - /Lang
+ - /Form.Validator.German
+
+provides: [Form.Validator.German.CH]
+
+...
+*/
+ 
+MooTools.lang.set('de-CH', 'Form.Validator', {
+ 
+  required:'Dieses Feld ist obligatorisch.',
+  minLength:'Geben Sie bitte mindestens {minLength} Zeichen ein (Sie haben {length} Zeichen eingegeben).',
+  maxLength:'Bitte geben Sie nicht mehr als {maxLength} Zeichen ein (Sie haben {length} Zeichen eingegeben).',
+  integer:'Geben Sie bitte eine ganze Zahl ein. Dezimalzahlen (z.B. 1.25) sind nicht erlaubt.',
+  numeric:'Geben Sie bitte nur Zahlenwerte in dieses Eingabefeld ein (z.B. &quot;1&quot;, &quot;1.1&quot;, &quot;-1&quot; oder &quot;-1.1&quot;).',
+  digits:'Benutzen Sie bitte nur Zahlen und Satzzeichen in diesem Eingabefeld (erlaubt ist z.B. eine Telefonnummer mit Bindestrichen und Punkten).',
+  alpha:'Benutzen Sie bitte nur Buchstaben (a-z) in diesem Feld. Leerzeichen und andere Zeichen sind nicht erlaubt.',
+  alphanum:'Benutzen Sie bitte nur Buchstaben (a-z) und Zahlen (0-9) in diesem Eingabefeld. Leerzeichen und andere Zeichen sind nicht erlaubt.',
+  dateSuchAs:'Geben Sie bitte ein g&uuml;ltiges Datum ein. Wie zum Beispiel {date}',
+  dateInFormatMDY:'Geben Sie bitte ein g&uuml;ltiges Datum ein. Wie zum Beispiel TT.MM.JJJJ (z.B. &quot;31.12.1999&quot;)',
+  email:'Geben Sie bitte eine g&uuml;ltige E-Mail Adresse ein. Wie zum Beispiel &quot;maria at bernasconi.ch&quot;.',
+  url:'Geben Sie bitte eine g&uuml;ltige URL ein. Wie zum Beispiel http://www.google.ch.',
+  currencyDollar:'Geben Sie bitte einen g&uuml;ltigen Betrag in Schweizer Franken ein. Wie zum Beispiel 100.00 CHF .',
+  oneRequired:'Machen Sie f&uuml;r mindestens eines der Eingabefelder einen Eintrag.',
+  errorPrefix: 'Fehler: ',
+  warningPrefix: 'Warnung: ',
+ 
+  //Form.Validator.Extras
+ 
+  noSpace: 'In diesem Eingabefeld darf kein Leerzeichen sein.',
+  reqChkByNode: 'Es wurden keine Elemente gew&auml;hlt.',
+  requiredChk: 'Dieses Feld ist obligatorisch.',
+  reqChkByName: 'Bitte w&auml;hlen Sie ein {label}.',
+  match: 'Dieses Eingabefeld muss mit dem Feld {matchName} &uuml;bereinstimmen.',
+  startDate: 'Das Anfangsdatum',
+  endDate: 'Das Enddatum',
+  currendDate: 'Das aktuelle Datum',
+  afterDate: 'Das Datum sollte zur gleichen Zeit oder sp&auml;ter sein {label}.',
+  beforeDate: 'Das Datum sollte zur gleichen Zeit oder fr&uuml;her sein {label}.',
+  startMonth: 'W&auml;hlen Sie bitte einen Anfangsmonat',
+  sameMonth: 'Diese zwei Datumsangaben m&uuml;ssen im selben Monat sein - Sie m&uuml;ssen eine von beiden ver&auml;ndern.'
+ 
+});/*
+---
+
+script: Form.Validator.French.js
+
+description: Form.Validator messages in French.
+
+license: MIT-style license
+
+authors: 
+ - Miquel Hudin
+ - Nicolas Sorosac <nicolas <dot> sorosac <at> gmail <dot> com>
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.French]
+
+...
+*/
+ 
+MooTools.lang.set('fr-FR', 'Form.Validator', {
+  required:'Ce champ est obligatoire.',
+  minLength:'Veuillez saisir un minimum de {minLength} caract&egrave;re(s) (vous avez saisi {length} caract&egrave;re(s)).',
+  maxLength:'Veuillez saisir un maximum de {maxLength} caract&egrave;re(s) (vous avez saisi {length} caract&egrave;re(s)).',
+  integer:'Veuillez saisir un nombre entier dans ce champ. Les nombres d&eacute;cimaux (ex : "1,25") ne sont pas autoris&eacute;s.',
+  numeric:'Veuillez saisir uniquement des chiffres dans ce champ (ex : "1" ou "1,1" ou "-1" ou "-1,1").',
+  digits:'Veuillez saisir uniquement des chiffres et des signes de ponctuation dans ce champ (ex : un num&eacute;ro de t&eacute;l&eacute;phone avec des traits d\'union est autoris&eacute;).',
+  alpha:'Veuillez saisir uniquement des lettres (a-z) dans ce champ. Les espaces ou autres caract&egrave;res ne sont pas autoris&eacute;s.',
+  alphanum:'Veuillez saisir uniquement des lettres (a-z) ou des chiffres (0-9) dans ce champ. Les espaces ou autres caract&egrave;res ne sont pas autoris&eacute;s.',
+  dateSuchAs:'Veuillez saisir une date correcte comme {date}',
+  dateInFormatMDY:'Veuillez saisir une date correcte, au format JJ/MM/AAAA (ex : "31/11/1999").',
+  email:'Veuillez saisir une adresse de courrier &eacute;lectronique. Par example "fred at domaine.com".',
+  url:'Veuillez saisir une URL, comme http://www.google.com.',
+  currencyDollar:'Veuillez saisir une quantit&eacute; correcte. Par example 100,00&euro;.',
+  oneRequired:'Veuillez s&eacute;lectionner au moins une de ces options.',
+  errorPrefix: 'Erreur : ',
+  warningPrefix: 'Attention : ',
+  
+  //Form.Validator.Extras
+ 
+  noSpace: 'Ce champ n\'accepte pas les espaces.',
+  reqChkByNode: 'Aucun &eacute;l&eacute;ment n\'est s&eacute;lectionn&eacute;.',
+  requiredChk: 'Ce champ est obligatoire.',
+  reqChkByName: 'Veuillez s&eacute;lectionner un(e) {label}.',
+  match: 'Ce champ doit correspondre avec le champ {matchName}.',
+  startDate: 'date de d&eacute;but',
+  endDate: 'date de fin',
+  currendDate: 'date actuelle',
+  afterDate: 'La date doit &ecirc;tre identique ou post&eacute;rieure &agrave; {label}.',
+  beforeDate: 'La date doit &ecirc;tre identique ou ant&eacute;rieure &agrave; {label}.',
+  startMonth: 'Veuillez s&eacute;lectionner un mois de d&eacute;but.',
+  sameMonth: 'Ces deux dates doivent &ecirc;tre dans le m&ecirc;me mois - vous devez en modifier une.'
+ 
+});/*
+---
+
+script: Form.Validator.Italian.js
+
+description: Form.Validator messages in Italian.
+
+license: MIT-style license
+
+authors:
+ - Leonardo Laureti
+ - Andrea Novero
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Italian]
+
+...
+*/
+ 
+MooTools.lang.set('it-IT', 'Form.Validator', {
+
+	required:'Il campo &egrave; obbligatorio.',
+	minLength:'Inserire almeno {minLength} caratteri (ne sono stati inseriti {length}).',
+	maxLength:'Inserire al massimo {maxLength} caratteri (ne sono stati inseriti {length}).',
+	integer:'Inserire un numero intero. Non sono consentiti decimali (es.: 1.25).',
+	numeric:'Inserire solo valori numerici (es.: "1" oppure "1.1" oppure "-1" oppure "-1.1").',
+	digits:'Inserire solo numeri e caratteri di punteggiatura. Per esempio &egrave; consentito un numero telefonico con trattini o punti.',
+	alpha:'Inserire solo lettere (a-z). Non sono consentiti spazi o altri caratteri.',
+	alphanum:'Inserire solo lettere (a-z) o numeri (0-9). Non sono consentiti spazi o altri caratteri.',
+	dateSuchAs:'Inserire una data valida del tipo {date}',
+	dateInFormatMDY:'Inserire una data valida nel formato MM/GG/AAAA (es.: "12/31/1999")',
+	email:'Inserire un indirizzo email valido. Per esempio "nome at dominio.com".',
+	url:'Inserire un indirizzo valido. Per esempio "http://www.dominio.com".',
+	currencyDollar:'Inserire un importo valido. Per esempio "$100.00".',
+	oneRequired:'Completare almeno uno dei campi richiesti.',
+	errorPrefix: 'Errore: ',
+	warningPrefix: 'Attenzione: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'Non sono consentiti spazi.',
+	reqChkByNode: 'Nessuna voce selezionata.',
+	requiredChk: 'Il campo &egrave; obbligatorio.',
+	reqChkByName: 'Selezionare un(a) {label}.',
+	match: 'Il valore deve corrispondere al campo {matchName}',
+	startDate: 'data d\'inizio',
+	endDate: 'data di fine',
+	currendDate: 'data attuale',
+	afterDate: 'La data deve corrispondere o essere successiva al {label}.',
+	beforeDate: 'La data deve corrispondere o essere precedente al {label}.',
+	startMonth: 'Selezionare un mese d\'inizio',
+	sameMonth: 'Le due date devono essere dello stesso mese - occorre modificarne una.'
+
+});/*
+---
+
+script: Form.Validator.Norwegian.js
+
+description: Form.Validator messages in Norwegian.
+
+license: MIT-style license
+
+authors:
+ - Espen 'Rexxars' Hovlandsdal
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Norwegian]
+
+...
+*/
+
+MooTools.lang.set('no-NO', 'Form.Validator', {
+   required:'Dette feltet er påkrevd.',
+   minLength:'Vennligst skriv inn minst {minLength} tegn (du skrev {length} tegn).',
+   maxLength:'Vennligst skriv inn maksimalt {maxLength} tegn (du skrev {length} tegn).',
+   integer:'Vennligst skriv inn et tall i dette feltet. Tall med desimaler (for eksempel 1,25) er ikke tillat.',
+   numeric:'Vennligst skriv inn kun numeriske verdier i dette feltet (for eksempel "1", "1.1", "-1" eller "-1.1").',
+   digits:'Vennligst bruk kun nummer og skilletegn i dette feltet.',
+   alpha:'Vennligst bruk kun bokstaver (a-z) i dette feltet. Ingen mellomrom eller andre tegn er tillat.',
+   alphanum:'Vennligst bruk kun bokstaver (a-z) eller nummer (0-9) i dette feltet. Ingen mellomrom eller andre tegn er tillat.',
+   dateSuchAs:'Vennligst skriv inn en gyldig dato, som {date}',
+   dateInFormatMDY:'Vennligst skriv inn en gyldig dato, i formatet MM/DD/YYYY (for eksempel "12/31/1999")',
+   email:'Vennligst skriv inn en gyldig epost-adresse. For eksempel "espen at domene.no".',
+   url:'Vennligst skriv inn en gyldig URL, for eksempel http://www.google.no.',
+   currencyDollar:'Vennligst fyll ut et gyldig $ beløp. For eksempel $100.00 .',
+   oneRequired:'Vennligst fyll ut noe i minst ett av disse feltene.',
+   errorPrefix: 'Feil: ',
+   warningPrefix: 'Advarsel: '
+});/*
+---
+
+script: Form.Validator.Polish.js
+
+description: Date messages for Polish.
+
+license: MIT-style license
+
+authors:
+ - Oskar Krawczyk
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Polish]
+
+...
+*/
+
+MooTools.lang.set('pl-PL', 'Form.Validator', {
+
+	required:'To pole jest wymagane.',
+	minLength:'Wymagane jest przynajmniej {minLength} znaków (wpisanych zostało tylko {length}).',
+	maxLength:'Dozwolone jest nie więcej niż {maxLength} znaków (wpisanych zostało {length})',
+	integer:'To pole wymaga liczb całych. Liczby dziesiętne (np. 1.25) są niedozwolone.',
+	numeric:'Prosimy używać tylko numerycznych wartości w tym polu (np. "1", "1.1", "-1" lub "-1.1").',
+	digits:'Prosimy używać liczb oraz zankow punktuacyjnych w typ polu (dla przykładu, przy numerze telefonu myślniki i kropki są dozwolone).',
+	alpha:'Prosimy używać tylko liter (a-z) w tym polu. Spacje oraz inne znaki są niedozwolone.',
+	alphanum:'Prosimy używać tylko liter (a-z) lub liczb (0-9) w tym polu. Spacje oraz inne znaki są niedozwolone.',
+	dateSuchAs:'Prosimy podać prawidłową datę w formacie: {date}',
+	dateInFormatMDY:'Prosimy podać poprawną date w formacie DD.MM.RRRR (i.e. "12.01.2009")',
+	email:'Prosimy podać prawidłowy adres e-mail, np. "jan at domena.pl".',
+	url:'Prosimy podać prawidłowy adres URL, np. http://www.google.pl.',
+	currencyDollar:'Prosimy podać prawidłową sumę w PLN. Dla przykładu: 100.00 PLN.',
+	oneRequired:'Prosimy wypełnić chociaż jedno z pól.',
+	errorPrefix: 'BÅ‚Ä…d: ',
+	warningPrefix: 'Uwaga: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'W tym polu nie mogą znajdować się spacje.',
+	reqChkByNode: 'Brak zaznaczonych elementów.',
+	requiredChk: 'To pole jest wymagane.',
+	reqChkByName: 'Prosimy wybrać z {label}.',
+	match: 'To pole musi być takie samo jak {matchName}',
+	startDate: 'data poczÄ…tkowa',
+	endDate: 'data końcowa',
+	currendDate: 'aktualna data',
+	afterDate: 'Podana data poinna być taka sama lub po {label}.',
+	beforeDate: 'Podana data poinna być taka sama lub przed {label}.',
+	startMonth: 'Prosimy wybrać początkowy miesiąc.',
+	sameMonth: 'Te dwie daty muszą być w zakresie tego samego miesiąca - wymagana jest zmiana któregoś z pól.'
+
+});/*
+---
+
+script: Form.Validator.Portuguese.js
+
+description: Form.Validator messages in Portuguese.
+
+license: MIT-style license
+
+authors:
+ - Miquel Hudin
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Portuguese]
+
+...
+*/
+
+MooTools.lang.set('pt-PT', 'Form.Validator', {
+	required:'Este campo é necessário.',
+	minLength:'Digite pelo menos{minLength} caracteres (comprimento {length} caracteres).',
+	maxLength:'Não insira mais de {maxLength} caracteres (comprimento {length} caracteres).',
+	integer:'Digite um número inteiro neste domínio. Com números decimais (por exemplo, 1,25), não são permitidas.',
+	numeric:'Digite apenas valores numéricos neste domínio (p.ex., "1" ou "1.1" ou "-1" ou "-1,1").',
+	digits:'Por favor, use números e pontuação apenas neste campo (p.ex., um número de telefone com traços ou pontos é permitida).',
+	alpha:'Por favor use somente letras (a-z), com nesta área. Não utilize espaços nem outros caracteres são permitidos.',
+	alphanum:'Use somente letras (a-z) ou números (0-9) neste campo. Não utilize espaços nem outros caracteres são permitidos.',
+	dateSuchAs:'Digite uma data válida, como {date}',
+	dateInFormatMDY:'Digite uma data válida, como DD/MM/YYYY (p.ex. "31/12/1999")',
+	email:'Digite um endereço de email válido. Por exemplo "fred at domain.com".',
+	url:'Digite uma URL válida, como http://www.google.com.',
+	currencyDollar:'Digite um valor válido $. Por exemplo $ 100,00. ',
+	oneRequired:'Digite algo para pelo menos um desses insumos.',
+	errorPrefix: 'Erro: ',
+	warningPrefix: 'Aviso: '
+
+}).set('pt-PT', 'Date', {
+	dateOrder: ['date', 'month', 'year', '/']
+});/*
+---
+
+script: Form.Validator.Portuguese.BR.js
+
+description: Form.Validator messages in Portuguese-BR.
+
+license: MIT-style license
+
+authors:
+ - Fábio Miranda Costa
+
+requires:
+ - /Lang
+ - /Form.Validator.Portuguese
+
+provides: [Form.Validator.Portuguese.BR]
+
+...
+*/
+
+MooTools.lang.set('pt-BR', 'Form.Validator', {
+
+	required: 'Este campo é obrigatório.',
+	minLength: 'Digite pelo menos {minLength} caracteres (tamanho atual: {length}).',
+	maxLength: 'Não digite mais de {maxLength} caracteres (tamanho atual: {length}).',
+	integer: 'Por favor digite apenas um número inteiro neste campo. Não são permitidos números decimais (por exemplo, 1,25).',
+	numeric: 'Por favor digite apenas valores numéricos neste campo (por exemplo, "1" ou "1.1" ou "-1" ou "-1,1").',
+	digits: 'Por favor use apenas números e pontuação neste campo (por exemplo, um número de telefone com traços ou pontos é permitido).',
+	alpha: 'Por favor use somente letras (a-z). Espaço e outros caracteres não são permitidos.',
+	alphanum: 'Use somente letras (a-z) ou números (0-9) neste campo. Espaço e outros caracteres não são permitidos.',
+	dateSuchAs: 'Digite uma data válida, como {date}',
+	dateInFormatMDY: 'Digite uma data válida, como DD/MM/YYYY (por exemplo, "31/12/1999")',
+	email: 'Digite um endereço de email válido. Por exemplo "nome at dominio.com".',
+	url: 'Digite uma URL válida. Exemplo: http://www.google.com.',
+	currencyDollar: 'Digite um valor em dinheiro válido. Exemplo: R$100,00 .',
+	oneRequired: 'Digite algo para pelo menos um desses campos.',
+	errorPrefix: 'Erro: ',
+	warningPrefix: 'Aviso: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'Não é possível digitar espaços neste campo.',
+	reqChkByNode: 'Não foi selecionado nenhum item.',
+	requiredChk: 'Este campo é obrigatório.',
+	reqChkByName: 'Por favor digite um {label}.',
+	match: 'Este campo deve ser igual ao campo {matchName}.',
+	startDate: 'a data inicial',
+	endDate: 'a data final',
+	currendDate: 'a data atual',
+	afterDate: 'A data deve ser igual ou posterior a {label}.',
+	beforeDate: 'A data deve ser igual ou anterior a {label}.',
+	startMonth: 'Por favor selecione uma data inicial.',
+	sameMonth: 'Estas duas datas devem ter o mesmo mês - você deve modificar uma das duas.',
+	creditcard: 'O número do cartão de crédito informado é inválido. Por favor verifique o valor e tente novamente. {length} números informados.'
+
+});/*
+---
+
+script: Form.Validator.Russian.js
+
+description: Form.Validator messages in Russian (utf-8 and cp1251).
+
+license: MIT-style license
+
+authors:
+ - Chernodarov Egor
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Russian]
+
+...
+*/
+
+MooTools.lang.set('ru-RU-unicode', 'Form.Validator', {
+	required:'Это поле обÑ?зательно к заполнению.',
+	minLength:'ПожалуйÑ?та, введите хотÑ? бы {minLength} Ñ?имволов (Ð’Ñ‹ ввели {length}).',
+	maxLength:'ПожалуйÑ?та, введите не больше {maxLength} Ñ?имволов (Ð’Ñ‹ ввели {length}).',
+	integer:'ПожалуйÑ?та, введите в Ñ?то поле чиÑ?ло. Дробные чиÑ?ла (например 1.25) тут не разрешены.',
+	numeric:'ПожалуйÑ?та, введите в Ñ?то поле чиÑ?ло (например "1" или "1.1", или "-1", или "-1.1").',
+	digits:'Ð’ Ñ?том поле Ð’Ñ‹ можете иÑ?пользовать только цифры и знаки пунктуации (например, телефонный номер Ñ?о знаками дефиÑ?а или Ñ? точками).',
+	alpha:'Ð’ Ñ?том поле можно иÑ?пользовать только латинÑ?кие буквы (a-z). Пробелы и другие Ñ?имволы запрещены.',
+	alphanum:'Ð’ Ñ?том поле можно иÑ?пользовать только латинÑ?кие буквы (a-z) и цифры (0-9). Пробелы и другие Ñ?имволы запрещены.',
+	dateSuchAs:'ПожалуйÑ?та, введите корректную дату {date}',
+	dateInFormatMDY:'ПожалуйÑ?та, введите дату в формате ММ/ДД/ГГГГ (например "12/31/1999")',
+	email:'ПожалуйÑ?та, введите корректный емейл-адреÑ?. ДлÑ? примера "fred at domain.com".',
+	url:'ПожалуйÑ?та, введите правильную Ñ?Ñ?ылку вида http://www.google.com.',
+	currencyDollar:'ПожалуйÑ?та, введите Ñ?умму в долларах. Ð?апример: $100.00 .',
+	oneRequired:'ПожалуйÑ?та, выберите хоть что-нибудь в одном из Ñ?тих полей.',
+	errorPrefix: 'Ошибка: ',
+	warningPrefix: 'Внимание: '
+});
+
+//translation in windows-1251 codepage
+MooTools.lang.set('ru-RU', 'Form.Validator', {
+	required:'�òî ïîëå îáÿçàòåëüíî ê çàïîëíåíèþ.',
+	minLength:'�îæàëóéñòà, ââåäèòå õîòÿ áû {minLength} ñèìâîëîâ (Âû ââåëè {length}).',
+	maxLength:'�îæàëóéñòà, ââåäèòå íå áîëüøå {maxLength} ñèìâîëîâ (Âû ââåëè {length}).',
+	integer:'�îæàëóéñòà, ââåäèòå â ýòî ïîëå ÷èñëî. Äðîáíûå ÷èñëà (íàïðèìåð 1.25) òóò íå ðàçðåøåíû.',
+	numeric:'�îæàëóéñòà, ââåäèòå â ýòî ïîëå ÷èñëî (íàïðèìåð "1" èëè "1.1", èëè "-1", èëè "-1.1").',
+	digits:' ýòîì ïîëå Âû ìîæåòå èñïîëüçîâàòü òîëüêî öèôðû è çíàêè ïóíêòóàöèè (íàïðèìåð, òåëåôîííûé íîìåð ñî çíàêàìè äåôèñà èëè ñ òî÷êàìè).',
+	alpha:' ýòîì ïîëå ìîæíî èñïîëüçîâàòü òîëüêî ëàòèíñêèå áóêâû (a-z). �ðîáåëû è äðóãèå ñèìâîëû çàïðåùåíû.',
+	alphanum:' ýòîì ïîëå ìîæíî èñïîëüçîâàòü òîëüêî ëàòèíñêèå áóêâû (a-z) è öèôðû (0-9). �ðîáåëû è äðóãèå ñèìâîëû çàïðåùåíû.',
+	dateSuchAs:'�îæàëóéñòà, ââåäèòå êîððåêòíóþ äàòó {date}',
+	dateInFormatMDY:'�îæàëóéñòà, ââåäèòå äàòó â ôîðìàòå ÌÌ/ÄÄ/ÃÃÃà (íàïðèìåð "12/31/1999")',
+	email:'�îæàëóéñòà, ââåäèòå êîððåêòíûé åìåéë-àäðåñ. Äëÿ ïðèìåðà "fred at domain.com".',
+	url:'�îæàëóéñòà, ââåäèòå ïðàâèëüíóþ ññûëêó âèäà http://www.google.com.',
+	currencyDollar:'�îæàëóéñòà, ââåäèòå ñóììó â äîëëàðàõ. �àïðèìåð: $100.00 .',
+	oneRequired:'�îæàëóéñòà, âûáåðèòå õîòü ÷òî-íèáóäü â îäíîì èç ýòèõ ïîëåé.',
+	errorPrefix: 'Îøèáêà: ',
+	warningPrefix: 'Âíèìàíèå: '
+});/*
+---
+
+script: Form.Validator.Spanish.js
+
+description: Date messages for Spanish.
+
+license: MIT-style license
+
+authors:
+ - Ãlfons Sanchez
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Spanish]
+
+...
+*/
+
+MooTools.lang.set('es-ES', 'Form.Validator', {
+
+	required:'Este campo es obligatorio.',
+	minLength:'Por favor introduce al menos {minLength} caracteres (has introducido {length} caracteres).',
+	maxLength:'Por favor introduce no m&aacute;s de {maxLength} caracteres (has introducido {length} caracteres).',
+	integer:'Por favor introduce un n&uacute;mero entero en este campo. N&uacute;meros con decimales (p.e. 1,25) no se permiten.',
+	numeric:'Por favor introduce solo valores num&eacute;ricos en este campo (p.e. "1" o "1,1" o "-1" o "-1,1").',
+	digits:'Por favor usa solo n&uacute;meros y puntuaci&oacute;n en este campo (por ejemplo, un n&uacute;mero de tel&eacute;fono con guiones y puntos no esta permitido).',
+	alpha:'Por favor usa letras solo (a-z) en este campo. No se admiten espacios ni otros caracteres.',
+	alphanum:'Por favor, usa solo letras (a-z) o n&uacute;meros (0-9) en este campo. No se admiten espacios ni otros caracteres.',
+	dateSuchAs:'Por favor introduce una fecha v&aacute;lida como {date}',
+	dateInFormatMDY:'Por favor introduce una fecha v&aacute;lida como DD/MM/YYYY (p.e. "31/12/1999")',
+	email:'Por favor, introduce una direcci&oacute;n de email v&aacute;lida. Por ejemplo,  "fred at domain.com".',
+	url:'Por favor introduce una URL v&aacute;lida como http://www.google.com.',
+	currencyDollar:'Por favor introduce una cantidad v&aacute;lida de €. Por ejemplo €100,00 .',
+	oneRequired:'Por favor introduce algo para por lo menos una de estas entradas.',
+	errorPrefix: 'Error: ',
+	warningPrefix: 'Aviso: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'No pueden haber espacios en esta entrada.',
+	reqChkByNode: 'No hay elementos seleccionados.',
+	requiredChk: 'Este campo es obligatorio.',
+	reqChkByName: 'Por favor selecciona una {label}.',
+	match: 'Este campo necesita coincidir con el campo {matchName}',
+	startDate: 'la fecha de inicio',
+	endDate: 'la fecha de fin',
+	currendDate: 'la fecha actual',
+	afterDate: 'La fecha debe ser igual o posterior a {label}.',
+	beforeDate: 'La fecha debe ser igual o anterior a {label}.',
+	startMonth: 'Por favor selecciona un mes de origen',
+	sameMonth: 'Estas dos fechas deben estar en el mismo mes - debes cambiar una u otra.'
+
+});
+/*
+---
+
+script: Form.Validator.Swedish.js
+
+description: Date messages for Swedish.
+
+license: MIT-style license
+
+authors:
+ - Martin Lundgren
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Swedish]
+
+...
+*/
+
+MooTools.lang.set('sv-SE', 'Form.Validator', {
+
+	required:'Fältet är obligatoriskt.',
+	minLength:'Ange minst {minLength} tecken (du angav {length} tecken).',
+	maxLength:'Ange högst {maxLength} tecken (du angav {length} tecken). ',
+	integer:'Ange ett heltal i fältet. Tal med decimaler (t.ex. 1,25) är inte tillåtna.',
+	numeric:'Ange endast numeriska värden i detta fält (t.ex. "1" eller "1.1" eller "-1" eller "-1,1").',
+	digits:'Använd endast siffror och skiljetecken i detta fält (till exempel ett telefonnummer med bindestreck tillåtet).',
+	alpha:'Använd endast bokstäver (a-ö) i detta fält. Inga mellanslag eller andra tecken är tillåtna.',
+	alphanum:'Använd endast bokstäver (a-ö) och siffror (0-9) i detta fält. Inga mellanslag eller andra tecken är tillåtna.',
+	dateSuchAs:'Ange ett giltigt datum som t.ex. {date}',
+	dateInFormatMDY:'Ange ett giltigt datum som t.ex. YYYY-MM-DD (i.e. "1999-12-31")',
+	email:'Ange en giltig e-postadress. Till exempel "erik at domain.com".',
+	url:'Ange en giltig webbadress som http://www.google.com.',
+	currencyDollar:'Ange en giltig belopp. Exempelvis 100,00.',
+	oneRequired:'Vänligen ange minst ett av dessa alternativ.',
+	errorPrefix: 'Fel: ',
+	warningPrefix: 'Varning: ',
+
+	//Form.Validator.Extras
+
+	noSpace: 'Det får inte finnas några mellanslag i detta fält.',
+	reqChkByNode: 'Inga objekt är valda.',
+	requiredChk: 'Detta är ett obligatoriskt fält.',
+	reqChkByName: 'Välj en {label}.',
+	match: 'Detta fält måste matcha {matchName}',
+	startDate: 'startdatumet',
+	endDate: 'slutdatum',
+	currendDate: 'dagens datum',
+	afterDate: 'Datumet bör vara samma eller senare än {label}.',
+	beforeDate: 'Datumet bör vara samma eller tidigare än {label}.',
+	startMonth: 'Välj en start månad',
+	sameMonth: 'Dessa två datum måste vara i samma månad - du måste ändra det ena eller det andra.'
+
+});/*
+---
+
+script: Form.Validator.Ukrainian.js
+
+description: Form.Validator messages in Ukrainian (utf-8).
+
+license: MIT-style license
+
+authors:
+ - Slik
+
+requires:
+ - /Lang
+ - /Form.Validator
+
+provides: [Form.Validator.Ukrainian]
+
+...
+*/
+
+MooTools.lang.set('uk-UA', 'Form.Validator', {
+	required:'Це поле повинне бути заповненим.',
+	minLength:'Введіть хоча б {minLength} Ñ?имволів (Ви ввели {length}).',
+	maxLength:'КількіÑ?Ñ‚ÑŒ Ñ?имволів не може бути більше {maxLength} (Ви ввели {length}).',
+	integer:'Введіть в це поле чиÑ?ло. Дробові чиÑ?ла (наприклад 1.25) не дозволені.',
+	numeric:'Введіть в це поле чиÑ?ло (наприклад "1" або "1.1", або "-1", або "-1.1").',
+	digits:'Ð’ цьому полі ви можете викориÑ?товувати лише цифри Ñ– знаки пунктіації (наприклад, телефонний номер з знаками дефізу або з крапками).',
+	alpha:'Ð’ цьому полі можна викориÑ?товувати лише латинÑ?ькі літери (a-z). Пробіли Ñ– інші Ñ?имволи заборонені.',
+	alphanum:'Ð’ цьому полі можна викориÑ?товувати лише латинÑ?ькі літери (a-z) Ñ– цифри (0-9). Пробіли Ñ– інші Ñ?имволи заборонені.',
+	dateSuchAs:'Введіть коректну дату {date}.',
+	dateInFormatMDY:'Введіть дату в форматі ММ/ДД/РРРР (наприклад "12/31/2009").',
+	email:'Введіть коректну адреÑ?у електронної пошти (наприклад "name at domain.com").',
+	url:'Введіть коректне інтернет-поÑ?иланнÑ? (наприклад http://www.google.com).',
+	currencyDollar:'Введіть Ñ?уму в доларах (наприклад "$100.00").',
+	oneRequired:'Заповніть одне з полів.',
+	errorPrefix: 'Помилка: ',
+	warningPrefix: 'Увага: '
+});
 /*
-Script: Core.js
-	MooTools - My Object Oriented JavaScript Tools.
+---
 
-License:
-	MIT-style license.
+name: Common
 
-Copyright:
-	Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).
+description: Jx namespace with methods and classes common to most Jx widgets
 
-Code & Documentation:
-	[The MooTools production team](http://mootools.net/developers/).
+license: MIT-style license.
 
-Inspiration:
-	- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
-	- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
-*/
+requires:
+ - Core/Class
+ - Core/Element
+ - Core/Browser
+ - Core/Element.Style
+ - Core/Request
+ - Core/Class.Extras
+ - More/Class.Binds
+ - Core/Array
+ - Core/Element.Event
+ - Core/Element.Dimensions
+ - More/Element.Measure
+ - More/Mootools.Lang
+ - Core/Slick.Finder
+ - Core/Slick.Parser
 
-var MooTools = {
-	'version': '1.2.2',
-	'build': 'f0491d62fbb7e906789aa3733d6a67d43e5af7c9'
-};
+provides: [Jx]
 
-var Native = function(options){
-	options = options || {};
-	var name = options.name;
-	var legacy = options.legacy;
-	var protect = options.protect;
-	var methods = options.implement;
-	var generics = options.generics;
-	var initialize = options.initialize;
-	var afterImplement = options.afterImplement || function(){};
-	var object = initialize || legacy;
-	generics = generics !== false;
+css:
+ - license
+ - reset
+ - common
 
-	object.constructor = Native;
-	object.$family = {name: 'native'};
-	if (legacy && initialize) object.prototype = legacy.prototype;
-	object.prototype.constructor = object;
+images:
+ - a_pixel.png
 
-	if (name){
-		var family = name.toLowerCase();
-		object.prototype.$family = {name: family};
-		Native.typize(object, family);
-	}
+...
+ */
+// $Id: common.js 976 2010-09-02 18:57:12Z pagameba $
+/**
+ * Function: $jx
+ * dereferences a DOM Element to a JxLib object if possible and returns
+ * a reference to the object, or null if not defined.
+ */
+function $jx(id) {
+  var widget = null;
+  id = document.id(id);
+  if (id) {
+    widget = id.retrieve('jxWidget');
+    if (!widget && id != document.body) {
+      widget = $jx(id.getParent());
+    }
+  }
+  return widget;
+}
 
-	var add = function(obj, name, method, force){
-		if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
-		if (generics) Native.genericize(obj, name, protect);
-		afterImplement.call(obj, name, method);
-		return obj;
-	};
+/**
+ * Class: Jx
+ * Jx is a global singleton object that contains the entire Jx library
+ * within it.  All Jx functions, attributes and classes are accessed
+ * through the global Jx object.  Jx should not create any other
+ * global variables, if you discover that it does then please report
+ * it as a bug
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
 
-	object.alias = function(a1, a2, a3){
-		if (typeof a1 == 'string'){
-			if ((a1 = this.prototype[a1])) return add(this, a2, a1, a3);
-		}
-		for (var a in a1) this.alias(a, a1[a], a2);
-		return this;
-	};
+/* firebug console supressor for IE/Safari/Opera */
+window.addEvent('load',
+function() {
+    if (! ("console" in window)) {
+        var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
+        "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"],
+            i;
 
-	object.implement = function(a1, a2, a3){
-		if (typeof a1 == 'string') return add(this, a1, a2, a3);
-		for (var p in a1) add(this, p, a1[p], a2);
-		return this;
-	};
-
-	if (methods) object.implement(methods);
-
-	return object;
-};
-
-Native.genericize = function(object, property, check){
-	if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
-		var args = Array.prototype.slice.call(arguments);
-		return object.prototype[property].apply(args.shift(), args);
-	};
-};
-
-Native.implement = function(objects, properties){
-	for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
-};
-
-Native.typize = function(object, family){
-	if (!object.type) object.type = function(item){
-		return ($type(item) === family);
-	};
-};
-
-(function(){
-	var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
-	for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});
-
-	var types = {'boolean': Boolean, 'native': Native, 'object': Object};
-	for (var t in types) Native.typize(types[t], t);
-
-	var generics = {
-		'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
-		'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
-	};
-	for (var g in generics){
-		for (var i = generics[g].length; i--;) Native.genericize(window[g], generics[g][i], true);
-	}
-})();
-
-var Hash = new Native({
-
-	name: 'Hash',
-
-	initialize: function(object){
-		if ($type(object) == 'hash') object = $unlink(object.getClean());
-		for (var key in object) this[key] = object[key];
-		return this;
-	}
-
+        window.console = {};
+        for (i = 0; i < names.length; ++i) {
+            window.console[names[i]] = function() {};
+        }
+    }
 });
 
-Hash.implement({
 
-	forEach: function(fn, bind){
-		for (var key in this){
-			if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
-		}
-	},
-
-	getClean: function(){
-		var clean = {};
-		for (var key in this){
-			if (this.hasOwnProperty(key)) clean[key] = this[key];
-		}
-		return clean;
-	},
-
-	getLength: function(){
-		var length = 0;
-		for (var key in this){
-			if (this.hasOwnProperty(key)) length++;
-		}
-		return length;
-	}
-
-});
-
-Hash.alias('forEach', 'each');
-
-Array.implement({
-
-	forEach: function(fn, bind){
-		for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
-	}
-
-});
-
-Array.alias('forEach', 'each');
-
-function $A(iterable){
-	if (iterable.item){
-		var l = iterable.length, array = new Array(l);
-		while (l--) array[l] = iterable[l];
-		return array;
-	}
-	return Array.prototype.slice.call(iterable);
+// add mutator that sets jxFamily when creating a class so we can check
+// its type
+Class.Mutators.Family = function(self, name) {
+    if ($defined(name)) {
+        self.jxFamily = name;
+        return self;
+    }
+    else if(!$defined(this.prototype.jxFamily)) {
+        this.implement({
+            'jxFamily': self
+        });
+    }
 };
 
-function $arguments(i){
-	return function(){
-		return arguments[i];
-	};
-};
+// this replaces the mootools $unlink method with our own version that
+// avoids infinite recursion on Jx objects.
+function $unlink(object) {
+    if (object && object.jxFamily) {
+        return object;
+    }
+    var unlinked, p, i, l;
+    switch ($type(object)) {
+    case 'object':
+        unlinked = {};
+        for (p in object) unlinked[p] = $unlink(object[p]);
+        break;
+    case 'hash':
+        unlinked = new Hash(object);
+        break;
+    case 'array':
+        unlinked = [];
+        for (i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
+        break;
+    default:
+        return object;
+    }
+    return unlinked;
+}
 
-function $chk(obj){
-	return !!(obj || obj === 0);
-};
+/**
+ * Override of mootools-core 1.3's typeOf operator to prevent infinite recursion
+ * when doing typeOf on JxLib objects.
+ *
+var typeOf = this.typeOf = function(item){
+    if (item == null) return 'null';
+    if (item.jxFamily) return item.jxFamily;
+    if (item.$family) return item.$family();
 
-function $clear(timer){
-	clearTimeout(timer);
-	clearInterval(timer);
-	return null;
-};
+    if (item.nodeName){
+        if (item.nodeType == 1) return 'element';
+        if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
+    } else if (typeof item.length == 'number'){
+        if (item.callee) return 'arguments';
+        if ('item' in item) return 'collection';
+    }
 
-function $defined(obj){
-	return (obj != undefined);
+    return typeof item;
 };
 
-function $each(iterable, fn, bind){
-	var type = $type(iterable);
-	((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
+this.$type = function(object){
+    var type = typeOf(object);
+    if (type == 'elements') return 'array';
+    return (type == 'null') ? false : type;
 };
-
-function $empty(){};
-
-function $extend(original, extended){
-	for (var key in (extended || {})) original[key] = extended[key];
-	return original;
-};
-
-function $H(object){
-	return new Hash(object);
-};
-
-function $lambda(value){
-	return (typeof value == 'function') ? value : function(){
-		return value;
-	};
-};
-
-function $merge(){
-	var args = Array.slice(arguments);
-	args.unshift({});
-	return $mixin.apply(null, args);
-};
-
-function $mixin(mix){
-	for (var i = 1, l = arguments.length; i < l; i++){
-		var object = arguments[i];
-		if ($type(object) != 'object') continue;
-		for (var key in object){
-			var op = object[key], mp = mix[key];
-			mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $mixin(mp, op) : $unlink(op);
-		}
-	}
-	return mix;
-};
-
-function $pick(){
-	for (var i = 0, l = arguments.length; i < l; i++){
-		if (arguments[i] != undefined) return arguments[i];
-	}
-	return null;
-};
-
-function $random(min, max){
-	return Math.floor(Math.random() * (max - min + 1) + min);
-};
-
-function $splat(obj){
-	var type = $type(obj);
-	return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
-};
-
-var $time = Date.now || function(){
-	return +new Date;
-};
-
-function $try(){
-	for (var i = 0, l = arguments.length; i < l; i++){
-		try {
-			return arguments[i]();
-		} catch(e){}
-	}
-	return null;
-};
-
-function $type(obj){
-	if (obj == undefined) return false;
-	if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
-	if (obj.nodeName){
-		switch (obj.nodeType){
-			case 1: return 'element';
-			case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
-		}
-	} else if (typeof obj.length == 'number'){
-		if (obj.callee) return 'arguments';
-		else if (obj.item) return 'collection';
-	}
-	return typeof obj;
-};
-
-function $unlink(object){
-	var unlinked;
-	switch ($type(object)){
-		case 'object':
-			unlinked = {};
-			for (var p in object) unlinked[p] = $unlink(object[p]);
-		break;
-		case 'hash':
-			unlinked = new Hash(object);
-		break;
-		case 'array':
-			unlinked = [];
-			for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
-		break;
-		default: return object;
-	}
-	return unlinked;
-};
-/*
-Script: Browser.js
-	The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.
-
-License:
-	MIT-style license.
 */
 
-var Browser = $merge({
+/* Setup global namespace.  It is possible to set the global namespace
+ * prior to including jxlib.  This would typically be required only if
+ * the auto-detection of the jxlib base URL would fail.  For instance,
+ * if you combine jxlib with other javascript libraries into a single file
+ * build and call it something without jxlib in the file name, then the
+ * detection of baseURL would fail.  If this happens to you, try adding
+ * Jx = { baseURL: '/path/to/jxlib/'; }
+ * where the path to jxlib contains a file called a_pixel.png (it doesn't
+ * have to include jxlib, just the a_pixel.png file).
+ */
+if (typeof Jx === 'undefined') {
+  var Jx = {};
+}
 
-	Engine: {name: 'unknown', version: 0},
+/**
+ * APIProperty: {String} baseURL
+ * This is the URL that Jx was loaded from, it is
+ * automatically calculated from the script tag
+ * src property that included Jx.
+ *
+ * Note that this assumes that you are loading Jx
+ * from a js/ or lib/ folder in parallel to the
+ * images/ folder that contains the various images
+ * needed by Jx components.  If you have a different
+ * folder structure, you can define Jx's base
+ * by including the following before including
+ * the jxlib javascript file:
+ *
+ * (code)
+ * Jx = {
+ *    baseURL: 'some/path'
+ * }
+ * (end)
+ */
+if (!$defined(Jx.baseURL)) {
+  (function() {
+    var aScripts = document.getElementsByTagName('SCRIPT'),
+        i, s, n, file;
+    for (i = 0; i < aScripts.length; i++) {
+      s = aScripts[i].src;
+      n = s.lastIndexOf('/');
+      file = s.slice(n+1,s.length-1);
+      if (file.contains('jxlib')) {
+        Jx.baseURL = s.slice(0,n);
+        break;
+      }
+    }
+  })();
+}
+/**
+ * APIProperty: {Image} aPixel
+ * aPixel is a single transparent pixel and is the only image we actually
+ * use directly in JxLib code.  If you want to use your own transparent pixel
+ * image or use it from a different location than the install of jxlib
+ * javascript files, you can manually declare it before including jxlib code
+ * (code)
+ * Jx = {
+ *   aPixel: new Element('img', {
+ *     alt: '',
+ *     title: '',
+ *     width: 1,
+ *     height: 1,
+ *     src: 'path/to/a/transparent.png'
+ *   });
+ * }
+ * (end)
+ */
+if (!$defined(Jx.aPixel)) {
+  Jx.aPixel = new Element('img', {
+    alt:'',
+    title:'',
+    src: Jx.baseURL +'/a_pixel.png'
+  });
+}
 
-	Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},
+/**
+ * APIProperty: {Boolean} isAir
+ * indicates if JxLib is running in an Adobe Air environment.  This is
+ * normally auto-detected but you can manually set it by declaring the Jx
+ * namespace before including jxlib:
+ * (code)
+ * Jx = {
+ *   isAir: true
+ * }
+ * (end)
+ */
+if (!$defined(Jx.isAir)) {
+  (function() {
+    /**
+     * Determine if we're running in Adobe AIR.
+     */
+    var aScripts = document.getElementsByTagName('SCRIPT'),
+        src = aScripts[0].src;
+    if (src.contains('app:')) {
+      Jx.isAir = true;
+    } else {
+      Jx.isAir = false;
+    }
+  })();
+}
 
-	Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},
-
-	Plugins: {},
-
-	Engines: {
-
-		presto: function(){
-			return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
-		},
-
-		trident: function(){
-			return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? 5 : 4);
-		},
-
-		webkit: function(){
-			return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
-		},
-
-		gecko: function(){
-			return (document.getBoxObjectFor == undefined) ? false : ((document.getElementsByClassName) ? 19 : 18);
-		}
-
-	}
-
-}, Browser || {});
-
-Browser.Platform[Browser.Platform.name] = true;
-
-Browser.detect = function(){
-
-	for (var engine in this.Engines){
-		var version = this.Engines[engine]();
-		if (version){
-			this.Engine = {name: engine, version: version};
-			this.Engine[engine] = this.Engine[engine + version] = true;
-			break;
-		}
-	}
-
-	return {name: engine, version: version};
-
+/**
+ * APIMethod: setLanguage
+ * set the current language to be used by Jx widgets.  This uses the MooTools
+ * lang module.  If an invalid or missing language is requested, the default
+ * rules of MooTools.lang will be used (revert to en-US at time of writing).
+ *
+ * Parameters:
+ * {String} language identifier, the language to set.
+ */
+Jx.setLanguage = function(lang) {
+  Jx.lang = lang;
+  MooTools.lang.setLanguage(Jx.lang);
 };
 
-Browser.detect();
+/**
+ * APIProperty: {String} lang
+ * Checks to see if Jx.lang is already set. If not, it sets it to the default
+ * 'en-US'. We will then set the Motools.lang language to this setting
+ * automatically.
+ *
+ * The language can be changed on the fly at anytime by calling
+ * Jx.setLanguage().
+ * By default all Jx.Widget subclasses will listen for the langChange event of
+ * the Mootools.lang class. It will then call a method, changeText(), if it
+ * exists on the particular widget. You will be able to disable listening for
+ * these changes by setting the Jx.Widget option useLang to false.
+ */
+if (!$defined(Jx.lang)) {
+  Jx.lang = 'en-US';
+}
 
-Browser.Request = function(){
-	return $try(function(){
-		return new XMLHttpRequest();
-	}, function(){
-		return new ActiveXObject('MSXML2.XMLHTTP');
-	});
-};
+Jx.setLanguage(Jx.lang);
 
-Browser.Features.xhr = !!(Browser.Request());
-
-Browser.Plugins.Flash = (function(){
-	var version = ($try(function(){
-		return navigator.plugins['Shockwave Flash'].description;
-	}, function(){
-		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
-	}) || '0 r0').match(/\d+/g);
-	return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
-})();
-
-function $exec(text){
-	if (!text) return text;
-	if (window.execScript){
-		window.execScript(text);
-	} else {
-		var script = document.createElement('script');
-		script.setAttribute('type', 'text/javascript');
-		script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
-		document.head.appendChild(script);
-		document.head.removeChild(script);
-	}
-	return text;
+/**
+ * APIMethod: applyPNGFilter
+ *
+ * Static method that applies the PNG Filter Hack for IE browsers
+ * when showing 24bit PNG's.  Used automatically for img tags with
+ * a class of png24.
+ *
+ * The filter is applied using a nifty feature of IE that allows javascript to
+ * be executed as part of a CSS style rule - this ensures that the hack only
+ * gets applied on IE browsers.
+ *
+ * The CSS that triggers this hack is only in the ie6.css files of the various
+ * themes.
+ *
+ * Parameters:
+ * object {Object} the object (img) to which the filter needs to be applied.
+ */
+Jx.applyPNGFilter = function(o) {
+    var t = Jx.aPixel.src, 
+        s;
+    if (o.src != t) {
+        s = o.src;
+        o.src = t;
+        o.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + s + "',sizingMethod='scale')";
+    }
 };
 
-Native.UID = 1;
-
-var $uid = (Browser.Engine.trident) ? function(item){
-	return (item.uid || (item.uid = [Native.UID++]))[0];
-} : function(item){
-	return item.uid || (item.uid = Native.UID++);
+/**
+ * NOTE: We should consider moving the image loading code into a separate
+ * class. Perhaps as Jx.Preloader which could extend Jx.Object
+ */
+Jx.imgQueue = [];
+//The queue of images to be loaded
+Jx.imgLoaded = {};
+//a hash table of images that have been loaded and cached
+Jx.imagesLoading = 0;
+//counter for number of concurrent image loads
+/**
+ * APIMethod: addToImgQueue
+ * Request that an image be set to a DOM IMG element src attribute.  This puts
+ * the image into a queue and there are private methods to manage that queue
+ * and limit image loading to 2 at a time.
+ *
+ * Parameters:
+ * obj - {Object} an object containing an element and src
+ * property, where element is the element to update and src
+ * is the url to the image.
+ */
+Jx.addToImgQueue = function(obj) {
+    if (Jx.imgLoaded[obj.src]) {
+        //if this image was already requested (i.e. it's in cache) just set it directly
+        obj.element.src = obj.src;
+    } else {
+        //otherwise stick it in the queue
+        Jx.imgQueue.push(obj);
+        Jx.imgLoaded[obj.src] = true;
+    }
+    //start the queue management process
+    Jx.checkImgQueue();
 };
 
-var Window = new Native({
-
-	name: 'Window',
-
-	legacy: (Browser.Engine.trident) ? null: window.Window,
-
-	initialize: function(win){
-		$uid(win);
-		if (!win.Element){
-			win.Element = $empty;
-			if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
-			win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
-		}
-		win.document.window = win;
-		return $extend(win, Window.Prototype);
-	},
-
-	afterImplement: function(property, value){
-		window[property] = Window.Prototype[property] = value;
-	}
-
-});
-
-Window.Prototype = {$family: {name: 'window'}};
-
-new Window(window);
-
-var Document = new Native({
-
-	name: 'Document',
-
-	legacy: (Browser.Engine.trident) ? null: window.Document,
-
-	initialize: function(doc){
-		$uid(doc);
-		doc.head = doc.getElementsByTagName('head')[0];
-		doc.html = doc.getElementsByTagName('html')[0];
-		if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
-			doc.execCommand("BackgroundImageCache", false, true);
-		});
-		if (Browser.Engine.trident) doc.window.attachEvent('onunload', function() {
-			doc.window.detachEvent('onunload', arguments.callee);
-			doc.head = doc.html = doc.window = null;
-		});
-		return $extend(doc, Document.Prototype);
-	},
-
-	afterImplement: function(property, value){
-		document[property] = Document.Prototype[property] = value;
-	}
-
-});
-
-Document.Prototype = {$family: {name: 'document'}};
-
-new Document(document);
-/*
-Script: Array.js
-	Contains Array Prototypes like each, contains, and erase.
-
-License:
-	MIT-style license.
-*/
-
-Array.implement({
-
-	every: function(fn, bind){
-		for (var i = 0, l = this.length; i < l; i++){
-			if (!fn.call(bind, this[i], i, this)) return false;
-		}
-		return true;
-	},
-
-	filter: function(fn, bind){
-		var results = [];
-		for (var i = 0, l = this.length; i < l; i++){
-			if (fn.call(bind, this[i], i, this)) results.push(this[i]);
-		}
-		return results;
-	},
-
-	clean: function() {
-		return this.filter($defined);
-	},
-
-	indexOf: function(item, from){
-		var len = this.length;
-		for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
-			if (this[i] === item) return i;
-		}
-		return -1;
-	},
-
-	map: function(fn, bind){
-		var results = [];
-		for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
-		return results;
-	},
-
-	some: function(fn, bind){
-		for (var i = 0, l = this.length; i < l; i++){
-			if (fn.call(bind, this[i], i, this)) return true;
-		}
-		return false;
-	},
-
-	associate: function(keys){
-		var obj = {}, length = Math.min(this.length, keys.length);
-		for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
-		return obj;
-	},
-
-	link: function(object){
-		var result = {};
-		for (var i = 0, l = this.length; i < l; i++){
-			for (var key in object){
-				if (object[key](this[i])){
-					result[key] = this[i];
-					delete object[key];
-					break;
-				}
-			}
-		}
-		return result;
-	},
-
-	contains: function(item, from){
-		return this.indexOf(item, from) != -1;
-	},
-
-	extend: function(array){
-		for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
-		return this;
-	},
-	
-	getLast: function(){
-		return (this.length) ? this[this.length - 1] : null;
-	},
-
-	getRandom: function(){
-		return (this.length) ? this[$random(0, this.length - 1)] : null;
-	},
-
-	include: function(item){
-		if (!this.contains(item)) this.push(item);
-		return this;
-	},
-
-	combine: function(array){
-		for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
-		return this;
-	},
-
-	erase: function(item){
-		for (var i = this.length; i--; i){
-			if (this[i] === item) this.splice(i, 1);
-		}
-		return this;
-	},
-
-	empty: function(){
-		this.length = 0;
-		return this;
-	},
-
-	flatten: function(){
-		var array = [];
-		for (var i = 0, l = this.length; i < l; i++){
-			var type = $type(this[i]);
-			if (!type) continue;
-			array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
-		}
-		return array;
-	},
-
-	hexToRgb: function(array){
-		if (this.length != 3) return null;
-		var rgb = this.map(function(value){
-			if (value.length == 1) value += value;
-			return value.toInt(16);
-		});
-		return (array) ? rgb : 'rgb(' + rgb + ')';
-	},
-
-	rgbToHex: function(array){
-		if (this.length < 3) return null;
-		if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
-		var hex = [];
-		for (var i = 0; i < 3; i++){
-			var bit = (this[i] - 0).toString(16);
-			hex.push((bit.length == 1) ? '0' + bit : bit);
-		}
-		return (array) ? hex : '#' + hex.join('');
-	}
-
-});
-/*
-Script: Function.js
-	Contains Function Prototypes like create, bind, pass, and delay.
-
-License:
-	MIT-style license.
-*/
-
-Function.implement({
-
-	extend: function(properties){
-		for (var property in properties) this[property] = properties[property];
-		return this;
-	},
-
-	create: function(options){
-		var self = this;
-		options = options || {};
-		return function(event){
-			var args = options.arguments;
-			args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
-			if (options.event) args = [event || window.event].extend(args);
-			var returns = function(){
-				return self.apply(options.bind || null, args);
-			};
-			if (options.delay) return setTimeout(returns, options.delay);
-			if (options.periodical) return setInterval(returns, options.periodical);
-			if (options.attempt) return $try(returns);
-			return returns();
-		};
-	},
-
-	run: function(args, bind){
-		return this.apply(bind, $splat(args));
-	},
-
-	pass: function(args, bind){
-		return this.create({bind: bind, arguments: args});
-	},
-
-	bind: function(bind, args){
-		return this.create({bind: bind, arguments: args});
-	},
-
-	bindWithEvent: function(bind, args){
-		return this.create({bind: bind, arguments: args, event: true});
-	},
-
-	attempt: function(args, bind){
-		return this.create({bind: bind, arguments: args, attempt: true})();
-	},
-
-	delay: function(delay, bind, args){
-		return this.create({bind: bind, arguments: args, delay: delay})();
-	},
-
-	periodical: function(periodical, bind, args){
-		return this.create({bind: bind, arguments: args, periodical: periodical})();
-	}
-
-});
-/*
-Script: Number.js
-	Contains Number Prototypes like limit, round, times, and ceil.
-
-License:
-	MIT-style license.
-*/
-
-Number.implement({
-
-	limit: function(min, max){
-		return Math.min(max, Math.max(min, this));
-	},
-
-	round: function(precision){
-		precision = Math.pow(10, precision || 0);
-		return Math.round(this * precision) / precision;
-	},
-
-	times: function(fn, bind){
-		for (var i = 0; i < this; i++) fn.call(bind, i, this);
-	},
-
-	toFloat: function(){
-		return parseFloat(this);
-	},
-
-	toInt: function(base){
-		return parseInt(this, base || 10);
-	}
-
-});
-
-Number.alias('times', 'each');
-
-(function(math){
-	var methods = {};
-	math.each(function(name){
-		if (!Number[name]) methods[name] = function(){
-			return Math[name].apply(null, [this].concat($A(arguments)));
-		};
-	});
-	Number.implement(methods);
-})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
-/*
-Script: String.js
-	Contains String Prototypes like camelCase, capitalize, test, and toInt.
-
-License:
-	MIT-style license.
-*/
-
-String.implement({
-
-	test: function(regex, params){
-		return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
-	},
-
-	contains: function(string, separator){
-		return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
-	},
-
-	trim: function(){
-		return this.replace(/^\s+|\s+$/g, '');
-	},
-
-	clean: function(){
-		return this.replace(/\s+/g, ' ').trim();
-	},
-
-	camelCase: function(){
-		return this.replace(/-\D/g, function(match){
-			return match.charAt(1).toUpperCase();
-		});
-	},
-
-	hyphenate: function(){
-		return this.replace(/[A-Z]/g, function(match){
-			return ('-' + match.charAt(0).toLowerCase());
-		});
-	},
-
-	capitalize: function(){
-		return this.replace(/\b[a-z]/g, function(match){
-			return match.toUpperCase();
-		});
-	},
-
-	escapeRegExp: function(){
-		return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
-	},
-
-	toInt: function(base){
-		return parseInt(this, base || 10);
-	},
-
-	toFloat: function(){
-		return parseFloat(this);
-	},
-
-	hexToRgb: function(array){
-		var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
-		return (hex) ? hex.slice(1).hexToRgb(array) : null;
-	},
-
-	rgbToHex: function(array){
-		var rgb = this.match(/\d{1,3}/g);
-		return (rgb) ? rgb.rgbToHex(array) : null;
-	},
-
-	stripScripts: function(option){
-		var scripts = '';
-		var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
-			scripts += arguments[1] + '\n';
-			return '';
-		});
-		if (option === true) $exec(scripts);
-		else if ($type(option) == 'function') option(scripts, text);
-		return text;
-	},
-
-	substitute: function(object, regexp){
-		return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
-			if (match.charAt(0) == '\\') return match.slice(1);
-			return (object[name] != undefined) ? object[name] : '';
-		});
-	}
-
-});
-/*
-Script: Hash.js
-	Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
-
-License:
-	MIT-style license.
-*/
-
-Hash.implement({
-
-	has: Object.prototype.hasOwnProperty,
-
-	keyOf: function(value){
-		for (var key in this){
-			if (this.hasOwnProperty(key) && this[key] === value) return key;
-		}
-		return null;
-	},
-
-	hasValue: function(value){
-		return (Hash.keyOf(this, value) !== null);
-	},
-
-	extend: function(properties){
-		Hash.each(properties, function(value, key){
-			Hash.set(this, key, value);
-		}, this);
-		return this;
-	},
-
-	combine: function(properties){
-		Hash.each(properties, function(value, key){
-			Hash.include(this, key, value);
-		}, this);
-		return this;
-	},
-
-	erase: function(key){
-		if (this.hasOwnProperty(key)) delete this[key];
-		return this;
-	},
-
-	get: function(key){
-		return (this.hasOwnProperty(key)) ? this[key] : null;
-	},
-
-	set: function(key, value){
-		if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
-		return this;
-	},
-
-	empty: function(){
-		Hash.each(this, function(value, key){
-			delete this[key];
-		}, this);
-		return this;
-	},
-
-	include: function(key, value){
-		if (this[key] == undefined) this[key] = value;
-		return this;
-	},
-
-	map: function(fn, bind){
-		var results = new Hash;
-		Hash.each(this, function(value, key){
-			results.set(key, fn.call(bind, value, key, this));
-		}, this);
-		return results;
-	},
-
-	filter: function(fn, bind){
-		var results = new Hash;
-		Hash.each(this, function(value, key){
-			if (fn.call(bind, value, key, this)) results.set(key, value);
-		}, this);
-		return results;
-	},
-
-	every: function(fn, bind){
-		for (var key in this){
-			if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
-		}
-		return true;
-	},
-
-	some: function(fn, bind){
-		for (var key in this){
-			if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
-		}
-		return false;
-	},
-
-	getKeys: function(){
-		var keys = [];
-		Hash.each(this, function(value, key){
-			keys.push(key);
-		});
-		return keys;
-	},
-
-	getValues: function(){
-		var values = [];
-		Hash.each(this, function(value){
-			values.push(value);
-		});
-		return values;
-	},
-
-	toQueryString: function(base){
-		var queryString = [];
-		Hash.each(this, function(value, key){
-			if (base) key = base + '[' + key + ']';
-			var result;
-			switch ($type(value)){
-				case 'object': result = Hash.toQueryString(value, key); break;
-				case 'array':
-					var qs = {};
-					value.each(function(val, i){
-						qs[i] = val;
-					});
-					result = Hash.toQueryString(qs, key);
-				break;
-				default: result = key + '=' + encodeURIComponent(value);
-			}
-			if (value != undefined) queryString.push(result);
-		});
-
-		return queryString.join('&');
-	}
-
-});
-
-Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
-/*
-Script: Event.js
-	Contains the Event Native, to make the event object completely crossbrowser.
-
-License:
-	MIT-style license.
-*/
-var Event = new Native({
-
-	name: 'Event',
-
-	initialize: function(event, win){
-		win = win || window;
-		var doc = win.document;
-		event = event || win.event;
-		if (event.$extended) return event;
-		this.$extended = true;
-		var type = event.type;
-		var target = event.target || event.srcElement;
-		while (target && target.nodeType == 3) target = target.parentNode;
-
-		if (type.test(/key/)){
-			var code = event.which || event.keyCode;
-			var key = Event.Keys.keyOf(code);
-			if (type == 'keydown'){
-				var fKey = code - 111;
-				if (fKey > 0 && fKey < 13) key = 'f' + fKey;
-			}
-			key = key || String.fromCharCode(code).toLowerCase();
-		} else if (type.match(/(click|mouse|menu)/i)){
-			doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
-			var page = {
-				x: event.pageX || event.clientX + doc.scrollLeft,
-				y: event.pageY || event.clientY + doc.scrollTop
-			};
-			var client = {
-				x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
-				y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
-			};
-			if (type.match(/DOMMouseScroll|mousewheel/)){
-				var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
-			}
-			var rightClick = (event.which == 3) || (event.button == 2);
-			var related = null;
-			if (type.match(/over|out/)){
-				switch (type){
-					case 'mouseover': related = event.relatedTarget || event.fromElement; break;
-					case 'mouseout': related = event.relatedTarget || event.toElement;
-				}
-				if (!(function(){
-					while (related && related.nodeType == 3) related = related.parentNode;
-					return true;
-				}).create({attempt: Browser.Engine.gecko})()) related = false;
-			}
-		}
-
-		return $extend(this, {
-			event: event,
-			type: type,
-
-			page: page,
-			client: client,
-			rightClick: rightClick,
-
-			wheel: wheel,
-
-			relatedTarget: related,
-			target: target,
-
-			code: code,
-			key: key,
-
-			shift: event.shiftKey,
-			control: event.ctrlKey,
-			alt: event.altKey,
-			meta: event.metaKey
-		});
-	}
-
-});
-
-Event.Keys = new Hash({
-	'enter': 13,
-	'up': 38,
-	'down': 40,
-	'left': 37,
-	'right': 39,
-	'esc': 27,
-	'space': 32,
-	'backspace': 8,
-	'tab': 9,
-	'delete': 46
-});
-
-Event.implement({
-
-	stop: function(){
-		return this.stopPropagation().preventDefault();
-	},
-
-	stopPropagation: function(){
-		if (this.event.stopPropagation) this.event.stopPropagation();
-		else this.event.cancelBubble = true;
-		return this;
-	},
-
-	preventDefault: function(){
-		if (this.event.preventDefault) this.event.preventDefault();
-		else this.event.returnValue = false;
-		return this;
-	}
-
-});
-/*
-Script: Class.js
-	Contains the Class Function for easily creating, extending, and implementing reusable Classes.
-
-License:
-	MIT-style license.
-*/
-
-function Class(params){
-	
-	if (params instanceof Function) params = {initialize: params};
-	
-	var newClass = function(){
-		Object.reset(this);
-		if (newClass._prototyping) return this;
-		this._current = $empty;
-		var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
-		delete this._current; delete this.caller;
-		return value;
-	}.extend(this);
-	
-	newClass.implement(params);
-	
-	newClass.constructor = Class;
-	newClass.prototype.constructor = newClass;
-
-	return newClass;
-
+/**
+ * APIMethod: checkImgQueue
+ *
+ * An internal method that ensures no more than 2 images are loading at a
+ * time.
+ */
+Jx.checkImgQueue = function() {
+    while (Jx.imagesLoading < 2 && Jx.imgQueue.length > 0) {
+        Jx.loadNextImg();
+    }
 };
 
-Function.prototype.protect = function(){
-	this._protected = true;
-	return this;
+/**
+ * Method: loadNextImg
+ *
+ * An internal method actually populate the DOM element with the image source.
+ */
+Jx.loadNextImg = function() {
+    var obj = Jx.imgQueue.shift();
+    if (obj) {
+        ++Jx.imagesLoading;
+        obj.element.onload = function() {--Jx.imagesLoading;
+            Jx.checkImgQueue();
+        };
+        obj.element.onerror = function() {--Jx.imagesLoading;
+            Jx.checkImgQueue();
+        };
+        obj.element.src = obj.src;
+    }
 };
 
-Object.reset = function(object, key){
-		
-	if (key == null){
-		for (var p in object) Object.reset(object, p);
-		return object;
-	}
-	
-	delete object[key];
-	
-	switch ($type(object[key])){
-		case 'object':
-			var F = function(){};
-			F.prototype = object[key];
-			var i = new F;
-			object[key] = Object.reset(i);
-		break;
-		case 'array': object[key] = $unlink(object[key]); break;
-	}
-	
-	return object;
-	
+/**
+ * APIMethod: getNumber
+ * safely parse a number and return its integer value.  A NaN value
+ * returns 0.  CSS size values are also parsed correctly.
+ *
+ * Parameters:
+ * n - {Mixed} the string or object to parse.
+ *
+ * Returns:
+ * {Integer} the integer value that the parameter represents
+ */
+Jx.getNumber = function(n, def) {
+    var result = n === null || isNaN(parseInt(n, 10)) ? (def || 0) : parseInt(n, 10);
+    return result;
 };
 
-new Native({name: 'Class', initialize: Class}).extend({
-
-	instantiate: function(F){
-		F._prototyping = true;
-		var proto = new F;
-		delete F._prototyping;
-		return proto;
-	},
-	
-	wrap: function(self, key, method){
-		if (method._origin) method = method._origin;
-		
-		return function(){
-			if (method._protected && this._current == null) throw new Error('The method "' + key + '" cannot be called.');
-			var caller = this.caller, current = this._current;
-			this.caller = current; this._current = arguments.callee;
-			var result = method.apply(this, arguments);
-			this._current = current; this.caller = caller;
-			return result;
-		}.extend({_owner: self, _origin: method, _name: key});
-
-	}
-	
-});
-
-Class.implement({
-	
-	implement: function(key, value){
-		
-		if ($type(key) == 'object'){
-			for (var p in key) this.implement(p, key[p]);
-			return this;
-		}
-		
-		var mutator = Class.Mutators[key];
-		
-		if (mutator){
-			value = mutator.call(this, value);
-			if (value == null) return this;
-		}
-		
-		var proto = this.prototype;
-
-		switch ($type(value)){
-			
-			case 'function':
-				if (value._hidden) return this;
-				proto[key] = Class.wrap(this, key, value);
-			break;
-			
-			case 'object':
-				var previous = proto[key];
-				if ($type(previous) == 'object') $mixin(previous, value);
-				else proto[key] = $unlink(value);
-			break;
-			
-			case 'array':
-				proto[key] = $unlink(value);
-			break;
-			
-			default: proto[key] = value;
-
-		}
-		
-		return this;
-
-	}
-	
-});
-
-Class.Mutators = {
-	
-	Extends: function(parent){
-
-		this.parent = parent;
-		this.prototype = Class.instantiate(parent);
-
-		this.implement('parent', function(){
-			var name = this.caller._name, previous = this.caller._owner.parent.prototype[name];
-			if (!previous) throw new Error('The method "' + name + '" has no parent.');
-			return previous.apply(this, arguments);
-		}.protect());
-
-	},
-
-	Implements: function(items){
-		$splat(items).each(function(item){
-			if (item instanceof Function) item = Class.instantiate(item);
-			this.implement(item);
-		}, this);
-
-	}
-	
+/**
+ * APIMethod: getPageDimensions
+ * return the dimensions of the browser client area.
+ *
+ * Returns:
+ * {Object} an object containing a width and height property
+ * that represent the width and height of the browser client area.
+ */
+Jx.getPageDimensions = function() {
+    return {
+        width: window.getWidth(),
+        height: window.getHeight()
+    };
 };
-/*
-Script: Class.Extras.js
-	Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
 
-License:
-	MIT-style license.
-*/
-
-var Chain = new Class({
-
-	$chain: [],
-
-	chain: function(){
-		this.$chain.extend(Array.flatten(arguments));
-		return this;
-	},
-
-	callChain: function(){
-		return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
-	},
-
-	clearChain: function(){
-		this.$chain.empty();
-		return this;
-	}
-
-});
-
-var Events = new Class({
-
-	$events: {},
-
-	addEvent: function(type, fn, internal){
-		type = Events.removeOn(type);
-		if (fn != $empty){
-			this.$events[type] = this.$events[type] || [];
-			this.$events[type].include(fn);
-			if (internal) fn.internal = true;
-		}
-		return this;
-	},
-
-	addEvents: function(events){
-		for (var type in events) this.addEvent(type, events[type]);
-		return this;
-	},
-
-	fireEvent: function(type, args, delay){
-		type = Events.removeOn(type);
-		if (!this.$events || !this.$events[type]) return this;
-		this.$events[type].each(function(fn){
-			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
-		}, this);
-		return this;
-	},
-
-	removeEvent: function(type, fn){
-		type = Events.removeOn(type);
-		if (!this.$events[type]) return this;
-		if (!fn.internal) this.$events[type].erase(fn);
-		return this;
-	},
-
-	removeEvents: function(events){
-		if ($type(events) == 'object'){
-			for (var type in events) this.removeEvent(type, events[type]);
-			return this;
-		}
-		if (events) events = Events.removeOn(events);
-		for (var type in this.$events){
-			if (events && events != type) continue;
-			var fns = this.$events[type];
-			for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
-		}
-		return this;
-	}
-
-});
-
-Events.removeOn = function(string){
-	return string.replace(/^on([A-Z])/, function(full, first) {
-		return first.toLowerCase();
-	});
+/**
+ * APIMethod: type
+ * safely return the type of an object using the mootools type system
+ *
+ * Returns:
+ * {Object} an object containing a width and height property
+ * that represent the width and height of the browser client area.
+ */
+Jx.type = function(obj) {
+  return typeof obj == 'undefined' ? false : obj.jxFamily || $type(obj);
 };
 
-var Options = new Class({
+(function($) {
+    // Wrapper for document.id
 
-	setOptions: function(){
-		this.options = $merge.run([this.options].extend(arguments));
-		if (!this.addEvent) return this;
-		for (var option in this.options){
-			if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
-			this.addEvent(option, this.options[option]);
-			delete this.options[option];
-		}
-		return this;
-	}
+    /**
+     * Class: Element
+     *
+     * Element is a global object provided by the mootools library.  The
+     * functions documented here are extensions to the Element object provided
+     * by Jx to make cross-browser compatibility easier to achieve.  Most of
+     * the methods are measurement related.
+     *
+     * While the code in these methods has been converted to use MooTools
+     * methods, there may be better MooTools methods to use to accomplish
+     * these things.
+     * Ultimately, it would be nice to eliminate most or all of these and find
+     * the MooTools equivalent or convince MooTools to add them.
+     *
+     * NOTE: Many of these methods can be replaced with mootools-more's
+     * Element.Measure
+     */
+    Element.implement({
+        /**
+         * APIMethod: getBoxSizing
+         * return the box sizing of an element, one of 'content-box' or
+         *'border-box'.
+         *
+         * Parameters:
+         * elem - {Object} the element to get the box sizing of.
+         *
+         * Returns:
+         * {String} the box sizing of the element.
+         */
+        getBoxSizing: function() {
+            var result = 'content-box',
+                cm,
+                sizing;
+            if (Browser.Engine.trident || Browser.Engine.presto) {
+                cm = document["compatMode"];
+                if (cm == "BackCompat" || cm == "QuirksMode") {
+                    result = 'border-box';
+                } else {
+                    result = 'content-box';
+                }
+            } else {
+                if (arguments.length === 0) {
+                    node = document.documentElement;
+                }
+                sizing = this.getStyle("-moz-box-sizing");
+                if (!sizing) {
+                    sizing = this.getStyle("box-sizing");
+                }
+                result = (sizing ? sizing: 'content-box');
+            }
+            return result;
+        },
+        /**
+         * APIMethod: getContentBoxSize
+         * return the size of the content area of an element.  This is the
+         * size of the element less margins, padding, and borders.
+         *
+         * Parameters:
+         * elem - {Object} the element to get the content size of.
+         *
+         * Returns:
+         * {Object} an object with two properties, width and height, that
+         * are the size of the content area of the measured element.
+         */
+        getContentBoxSize: function() {
+            var s = this.getSizes(['padding', 'border']);
+            return {
+                width: this.offsetWidth - s.padding.left - s.padding.right - s.border.left - s.border.right,
+                height: this.offsetHeight - s.padding.bottom - s.padding.top - s.border.bottom - s.border.top
+            };
+        },
+        /**
+         * APIMethod: getBorderBoxSize
+         * return the size of the border area of an element.  This is the size
+         * of the element less margins.
+         *
+         * Parameters:
+         * elem - {Object} the element to get the border sizing of.
+         *
+         * Returns:
+         * {Object} an object with two properties, width and height, that
+         * are the size of the border area of the measured element.
+         */
+        getBorderBoxSize: function() {
+            return {
+                width: this.offsetWidth,
+                height: this.offsetHeight
+            };
+        },
 
-});
-/*
-Script: Element.js
-	One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser,
-	time-saver methods to let you easily work with HTML Elements.
+        /**
+         * APIMethod: getMarginBoxSize
+         * return the size of the margin area of an element.  This is the size
+         * of the element plus margins.
+         *
+         * Parameters:
+         * elem - {Object} the element to get the margin sizing of.
+         *
+         * Returns:
+         * {Object} an object with two properties, width and height, that
+         * are the size of the margin area of the measured element.
+         */
+        getMarginBoxSize: function() {
+            var s = this.getSizes(['margin']);
+            return {
+                width: this.offsetWidth + s.margin.left + s.margin.right,
+                height: this.offsetHeight + s.margin.top + s.margin.bottom
+            };
+        },
+        /**
+         * APIMethod: getSizes
+         * measure the size of various styles on various edges and return
+         * the values.
+         *
+         * Parameters:
+         * styles - array, the styles to compute.  By default, this is
+         * ['padding', 'border','margin'].  If you don't need all the styles,
+         * just request the ones you need to minimize compute time required.
+         * edges - array, the edges to compute styles for.  By default,  this
+         * is ['top','right','bottom','left'].  If you don't need all the
+         * edges, then request the ones you need to minimize compute time.
+         *
+         * Returns:
+         * {Object} an object with one member for each requested style.  Each
+         * style member is an object containing members for each requested
+         * edge. Values are the computed style for each edge in pixels.
+         */
+        getSizes: function(which, edges) {
+            which = which || ['padding', 'border', 'margin'];
+            edges = edges || ['left', 'top', 'right', 'bottom'];
+            var result = {},
+                e,
+                n;
+            which.each(function(style) {
+                result[style] = {};
+                edges.each(function(edge) {
+                    e = (style == 'border') ? edge + '-width': edge;
+                    n = this.getStyle(style + '-' + e);
+                    result[style][edge] = n === null || isNaN(parseInt(n, 10)) ? 0: parseInt(n, 10);
+                },
+                this);
+            },
+            this);
+            return result;
+        },
+        /**
+         * APIMethod: setContentBoxSize
+         * set either or both of the width and height of an element to
+         * the provided size.  This function ensures that the content
+         * area of the element is the requested size and the resulting
+         * size of the element may be larger depending on padding and
+         * borders.
+         *
+         * Parameters:
+         * elem - {Object} the element to set the content area of.
+         * size - {Object} an object with a width and/or height property that
+         * is the size to set the content area of the element to.
+         */
+        setContentBoxSize: function(size) {
+            var m,
+                width,
+                height;
+            if (this.getBoxSizing() == 'border-box') {
+                m = this.measure(function() {
+                    return this.getSizes(['padding', 'border']);
+                });
+                if ($defined(size.width)) {
+                    width = size.width + m.padding.left + m.padding.right + m.border.left + m.border.right;
+                    if (width < 0) {
+                        width = 0;
+                    }
+                    this.setStyle('width', width);
+                }
+                if ($defined(size.height)) {
+                    height = size.height + m.padding.top + m.padding.bottom + m.border.top + m.border.bottom;
+                    if (height < 0) {
+                        height = 0;
+                    }
+                    this.setStyle('height', height);
+                }
+            } else {
+                if ($defined(size.width) && size.width >= 0) {
+                  this.setStyle('width', width);
+                }
+                if ($defined(size.height) && size.height >= 0) {
+                  this.setStyle('height', height);
+                }
+            }
+        },
+        /**
+         * APIMethod: setBorderBoxSize
+         * set either or both of the width and height of an element to
+         * the provided size.  This function ensures that the border
+         * size of the element is the requested size and the resulting
+         * content areaof the element may be larger depending on padding and
+         * borders.
+         *
+         * Parameters:
+         * elem - {Object} the element to set the border size of.
+         * size - {Object} an object with a width and/or height property that
+         * is the size to set the content area of the element to.
+         */
+        setBorderBoxSize: function(size) {
+            var m, 
+                width, 
+                height;
+            if (this.getBoxSizing() == 'content-box') {
+                m = this.measure(function() {
+                    return this.getSizes();
+                });
 
-License:
-	MIT-style license.
-*/
+                if ($defined(size.width)) {
+                    width = size.width - m.padding.left - m.padding.right - m.border.left - m.border.right - m.margin.left - m.margin.right;
+                    if (width < 0) {
+                        width = 0;
+                    }
+                    this.setStyle('width', width);
+                }
+                if ($defined(size.height)) {
+                    height = size.height - m.padding.top - m.padding.bottom - m.border.top - m.border.bottom - m.margin.top - m.margin.bottom;
+                    if (height < 0) {
+                        height = 0;
+                    }
+                    this.setStyle('height', height);
+                }
+            } else {
+                if ($defined(size.width) && size.width >= 0) {
+                  this.setStyle('width', width);
+                }
+                if ($defined(size.height) && size.height >= 0) {
+                  this.setStyle('height', height);
+                }
+            }
+        },
 
-var Element = new Native({
+        /**
+         * APIMethod: descendantOf
+         * determines if the element is a descendent of the reference node.
+         *
+         * Parameters:
+         * node - {HTMLElement} the reference node
+         *
+         * Returns:
+         * {Boolean} true if the element is a descendent, false otherwise.
+         */
+        descendantOf: function(node) {
+            var parent = document.id(this.parentNode);
+            while (parent != node && parent && parent.parentNode && parent.parentNode != parent) {
+                parent = document.id(parent.parentNode);
+            }
+            return parent == node;
+        },
 
-	name: 'Element',
-
-	legacy: window.Element,
-
-	initialize: function(tag, props){
-		var konstructor = Element.Constructors.get(tag);
-		if (konstructor) return konstructor(props);
-		if (typeof tag == 'string') return document.newElement(tag, props);
-		return $(tag).set(props);
-	},
-
-	afterImplement: function(key, value){
-		Element.Prototype[key] = value;
-		if (Array[key]) return;
-		Elements.implement(key, function(){
-			var items = [], elements = true;
-			for (var i = 0, j = this.length; i < j; i++){
-				var returns = this[i][key].apply(this[i], arguments);
-				items.push(returns);
-				if (elements) elements = ($type(returns) == 'element');
-			}
-			return (elements) ? new Elements(items) : items;
-		});
-	}
-
-});
-
-Element.Prototype = {$family: {name: 'element'}};
-
-Element.Constructors = new Hash;
-
-var IFrame = new Native({
-
-	name: 'IFrame',
-
-	generics: false,
-
-	initialize: function(){
-		var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
-		var props = params.properties || {};
-		var iframe = $(params.iframe) || false;
-		var onload = props.onload || $empty;
-		delete props.onload;
-		props.id = props.name = $pick(props.id, props.name, iframe.id, iframe.name, 'IFrame_' + $time());
-		iframe = new Element(iframe || 'iframe', props);
-		var onFrameLoad = function(){
-			var host = $try(function(){
-				return iframe.contentWindow.location.host;
-			});
-			if (host && host == window.location.host){
-				var win = new Window(iframe.contentWindow);
-				new Document(iframe.contentWindow.document);
-				$extend(win.Element.prototype, Element.Prototype);
-			}
-			onload.call(iframe.contentWindow, iframe.contentWindow.document);
-		};
-		(window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
-		return iframe;
-	}
-
-});
-
-var Elements = new Native({
-
-	initialize: function(elements, options){
-		options = $extend({ddup: true, cash: true}, options);
-		elements = elements || [];
-		if (options.ddup || options.cash){
-			var uniques = {}, returned = [];
-			for (var i = 0, l = elements.length; i < l; i++){
-				var el = $.element(elements[i], !options.cash);
-				if (options.ddup){
-					if (uniques[el.uid]) continue;
-					uniques[el.uid] = true;
-				}
-				returned.push(el);
-			}
-			elements = returned;
-		}
-		return (options.cash) ? $extend(elements, this) : elements;
-	}
-
-});
-
-Elements.implement({
-
-	filter: function(filter, bind){
-		if (!filter) return this;
-		return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
-			return item.match(filter);
-		} : filter, bind));
-	}
-
-});
-
-Document.implement({
-
-	newElement: function(tag, props){
-		if (Browser.Engine.trident && props){
-			['name', 'type', 'checked'].each(function(attribute){
-				if (!props[attribute]) return;
-				tag += ' ' + attribute + '="' + props[attribute] + '"';
-				if (attribute != 'checked') delete props[attribute];
-			});
-			tag = '<' + tag + '>';
-		}
-		return $.element(this.createElement(tag)).set(props);
-	},
-
-	newTextNode: function(text){
-		return this.createTextNode(text);
-	},
-
-	getDocument: function(){
-		return this;
-	},
-
-	getWindow: function(){
-		return this.window;
-	}
-
-});
-
-Window.implement({
-
-	$: function(el, nocash){
-		if (el && el.$family && el.uid) return el;
-		var type = $type(el);
-		return ($[type]) ? $[type](el, nocash, this.document) : null;
-	},
-
-	$$: function(selector){
-		if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
-		var elements = [];
-		var args = Array.flatten(arguments);
-		for (var i = 0, l = args.length; i < l; i++){
-			var item = args[i];
-			switch ($type(item)){
-				case 'element': elements.push(item); break;
-				case 'string': elements.extend(this.document.getElements(item, true));
-			}
-		}
-		return new Elements(elements);
-	},
-
-	getDocument: function(){
-		return this.document;
-	},
-
-	getWindow: function(){
-		return this;
-	}
-
-});
-
-$.string = function(id, nocash, doc){
-	id = doc.getElementById(id);
-	return (id) ? $.element(id, nocash) : null;
-};
-
-$.element = function(el, nocash){
-	$uid(el);
-	if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
-		var proto = Element.Prototype;
-		for (var p in proto) el[p] = proto[p];
-	};
-	return el;
-};
-
-$.object = function(obj, nocash, doc){
-	if (obj.toElement) return $.element(obj.toElement(doc), nocash);
-	return null;
-};
-
-$.textnode = $.whitespace = $.window = $.document = $arguments(0);
-
-Native.implement([Element, Document], {
-
-	getElement: function(selector, nocash){
-		return $(this.getElements(selector, true)[0] || null, nocash);
-	},
-
-	getElements: function(tags, nocash){
-		tags = tags.split(',');
-		var elements = [];
-		var ddup = (tags.length > 1);
-		tags.each(function(tag){
-			var partial = this.getElementsByTagName(tag.trim());
-			(ddup) ? elements.extend(partial) : elements = partial;
-		}, this);
-		return new Elements(elements, {ddup: ddup, cash: !nocash});
-	}
-
-});
-
-(function(){
-
-var collected = {}, storage = {};
-var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};
-
-var get = function(uid){
-	return (storage[uid] || (storage[uid] = {}));
-};
-
-var clean = function(item, retain){
-	if (!item) return;
-	var uid = item.uid;
-	if (Browser.Engine.trident){
-		if (item.clearAttributes){
-			var clone = retain && item.cloneNode(false);
-			item.clearAttributes();
-			if (clone) item.mergeAttributes(clone);
-		} else if (item.removeEvents){
-			item.removeEvents();
-		}
-		if ((/object/i).test(item.tagName)){
-			for (var p in item){
-				if (typeof item[p] == 'function') item[p] = $empty;
-			}
-			Element.dispose(item);
-		}
-	}	
-	if (!uid) return;
-	collected[uid] = storage[uid] = null;
-};
-
-var purge = function(){
-	Hash.each(collected, clean);
-	if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
-	if (window.CollectGarbage) CollectGarbage();
-	collected = {}; storage = {};
-};
-
-var walk = function(element, walk, start, match, all, nocash){
-	var el = element[start || walk];
-	var elements = [];
-	while (el){
-		if (el.nodeType == 1 && (!match || Element.match(el, match))){
-			if (!all) return $(el, nocash);
-			elements.push(el);
-		}
-		el = el[walk];
-	}
-	return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
-};
-
-var attributes = {
-	'html': 'innerHTML',
-	'class': 'className',
-	'for': 'htmlFor',
-	'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
-};
-var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
-var camels = ['value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
-
-bools = bools.associate(bools);
-
-Hash.extend(attributes, bools);
-Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));
-
-var inserters = {
-
-	before: function(context, element){
-		if (element.parentNode) element.parentNode.insertBefore(context, element);
-	},
-
-	after: function(context, element){
-		if (!element.parentNode) return;
-		var next = element.nextSibling;
-		(next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
-	},
-
-	bottom: function(context, element){
-		element.appendChild(context);
-	},
-
-	top: function(context, element){
-		var first = element.firstChild;
-		(first) ? element.insertBefore(context, first) : element.appendChild(context);
-	}
-
-};
-
-inserters.inside = inserters.bottom;
-
-Hash.each(inserters, function(inserter, where){
-
-	where = where.capitalize();
-
-	Element.implement('inject' + where, function(el){
-		inserter(this, $(el, true));
-		return this;
-	});
-
-	Element.implement('grab' + where, function(el){
-		inserter($(el, true), this);
-		return this;
-	});
-
-});
-
-Element.implement({
-
-	set: function(prop, value){
-		switch ($type(prop)){
-			case 'object':
-				for (var p in prop) this.set(p, prop[p]);
-				break;
-			case 'string':
-				var property = Element.Properties.get(prop);
-				(property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
-		}
-		return this;
-	},
-
-	get: function(prop){
-		var property = Element.Properties.get(prop);
-		return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
-	},
-
-	erase: function(prop){
-		var property = Element.Properties.get(prop);
-		(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
-		return this;
-	},
-
-	setProperty: function(attribute, value){
-		var key = attributes[attribute];
-		if (value == undefined) return this.removeProperty(attribute);
-		if (key && bools[attribute]) value = !!value;
-		(key) ? this[key] = value : this.setAttribute(attribute, '' + value);
-		return this;
-	},
-
-	setProperties: function(attributes){
-		for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
-		return this;
-	},
-
-	getProperty: function(attribute){
-		var key = attributes[attribute];
-		var value = (key) ? this[key] : this.getAttribute(attribute, 2);
-		return (bools[attribute]) ? !!value : (key) ? value : value || null;
-	},
-
-	getProperties: function(){
-		var args = $A(arguments);
-		return args.map(this.getProperty, this).associate(args);
-	},
-
-	removeProperty: function(attribute){
-		var key = attributes[attribute];
-		(key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
-		return this;
-	},
-
-	removeProperties: function(){
-		Array.each(arguments, this.removeProperty, this);
-		return this;
-	},
-
-	hasClass: function(className){
-		return this.className.contains(className, ' ');
-	},
-
-	addClass: function(className){
-		if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
-		return this;
-	},
-
-	removeClass: function(className){
-		this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
-		return this;
-	},
-
-	toggleClass: function(className){
-		return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
-	},
-
-	adopt: function(){
-		Array.flatten(arguments).each(function(element){
-			element = $(element, true);
-			if (element) this.appendChild(element);
-		}, this);
-		return this;
-	},
-
-	appendText: function(text, where){
-		return this.grab(this.getDocument().newTextNode(text), where);
-	},
-
-	grab: function(el, where){
-		inserters[where || 'bottom']($(el, true), this);
-		return this;
-	},
-
-	inject: function(el, where){
-		inserters[where || 'bottom'](this, $(el, true));
-		return this;
-	},
-
-	replaces: function(el){
-		el = $(el, true);
-		el.parentNode.replaceChild(this, el);
-		return this;
-	},
-
-	wraps: function(el, where){
-		el = $(el, true);
-		return this.replaces(el).grab(el, where);
-	},
-
-	getPrevious: function(match, nocash){
-		return walk(this, 'previousSibling', null, match, false, nocash);
-	},
-
-	getAllPrevious: function(match, nocash){
-		return walk(this, 'previousSibling', null, match, true, nocash);
-	},
-
-	getNext: function(match, nocash){
-		return walk(this, 'nextSibling', null, match, false, nocash);
-	},
-
-	getAllNext: function(match, nocash){
-		return walk(this, 'nextSibling', null, match, true, nocash);
-	},
-
-	getFirst: function(match, nocash){
-		return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
-	},
-
-	getLast: function(match, nocash){
-		return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
-	},
-
-	getParent: function(match, nocash){
-		return walk(this, 'parentNode', null, match, false, nocash);
-	},
-
-	getParents: function(match, nocash){
-		return walk(this, 'parentNode', null, match, true, nocash);
-	},
-	
-	getSiblings: function(match, nocash) {
-		return this.getParent().getChildren(match, nocash).erase(this);
-	},
-
-	getChildren: function(match, nocash){
-		return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
-	},
-
-	getWindow: function(){
-		return this.ownerDocument.window;
-	},
-
-	getDocument: function(){
-		return this.ownerDocument;
-	},
-
-	getElementById: function(id, nocash){
-		var el = this.ownerDocument.getElementById(id);
-		if (!el) return null;
-		for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
-			if (!parent) return null;
-		}
-		return $.element(el, nocash);
-	},
-
-	getSelected: function(){
-		return new Elements($A(this.options).filter(function(option){
-			return option.selected;
-		}));
-	},
-
-	getComputedStyle: function(property){
-		if (this.currentStyle) return this.currentStyle[property.camelCase()];
-		var computed = this.getDocument().defaultView.getComputedStyle(this, null);
-		return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
-	},
-
-	toQueryString: function(){
-		var queryString = [];
-		this.getElements('input, select, textarea', true).each(function(el){
-			if (!el.name || el.disabled) return;
-			var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
-				return opt.value;
-			}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
-			$splat(value).each(function(val){
-				if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
-			});
-		});
-		return queryString.join('&');
-	},
-
-	clone: function(contents, keepid){
-		contents = contents !== false;
-		var clone = this.cloneNode(contents);
-		var clean = function(node, element){
-			if (!keepid) node.removeAttribute('id');
-			if (Browser.Engine.trident){
-				node.clearAttributes();
-				node.mergeAttributes(element);
-				node.removeAttribute('uid');
-				if (node.options){
-					var no = node.options, eo = element.options;
-					for (var j = no.length; j--;) no[j].selected = eo[j].selected;
-				}
-			}
-			var prop = props[element.tagName.toLowerCase()];
-			if (prop && element[prop]) node[prop] = element[prop];
-		};
-
-		if (contents){
-			var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
-			for (var i = ce.length; i--;) clean(ce[i], te[i]);
-		}
-
-		clean(clone, this);
-		return $(clone);
-	},
-
-	destroy: function(){
-		Element.empty(this);
-		Element.dispose(this);
-		clean(this, true);
-		return null;
-	},
-
-	empty: function(){
-		$A(this.childNodes).each(function(node){
-			Element.destroy(node);
-		});
-		return this;
-	},
-
-	dispose: function(){
-		return (this.parentNode) ? this.parentNode.removeChild(this) : this;
-	},
-
-	hasChild: function(el){
-		el = $(el, true);
-		if (!el) return false;
-		if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
-		return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
-	},
-
-	match: function(tag){
-		return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
-	}
-
-});
-
-Native.implement([Element, Window, Document], {
-
-	addListener: function(type, fn){
-		if (type == 'unload'){
-			var old = fn, self = this;
-			fn = function(){
-				self.removeListener('unload', fn);
-				old();
-			};
-		} else {
-			collected[this.uid] = this;
-		}
-		if (this.addEventListener) this.addEventListener(type, fn, false);
-		else this.attachEvent('on' + type, fn);
-		return this;
-	},
-
-	removeListener: function(type, fn){
-		if (this.removeEventListener) this.removeEventListener(type, fn, false);
-		else this.detachEvent('on' + type, fn);
-		return this;
-	},
-
-	retrieve: function(property, dflt){
-		var storage = get(this.uid), prop = storage[property];
-		if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
-		return $pick(prop);
-	},
-
-	store: function(property, value){
-		var storage = get(this.uid);
-		storage[property] = value;
-		return this;
-	},
-
-	eliminate: function(property){
-		var storage = get(this.uid);
-		delete storage[property];
-		return this;
-	}
-
-});
-
-window.addListener('unload', purge);
-
-})();
-
-Element.Properties = new Hash;
-
-Element.Properties.style = {
-
-	set: function(style){
-		this.style.cssText = style;
-	},
-
-	get: function(){
-		return this.style.cssText;
-	},
-
-	erase: function(){
-		this.style.cssText = '';
-	}
-
-};
-
-Element.Properties.tag = {
-
-	get: function(){
-		return this.tagName.toLowerCase();
-	}
-
-};
-
-Element.Properties.html = (function(){
-	var wrapper = document.createElement('div');
-
-	var translations = {
-		table: [1, '<table>', '</table>'],
-		select: [1, '<select>', '</select>'],
-		tbody: [2, '<table><tbody>', '</tbody></table>'],
-		tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
-	};
-	translations.thead = translations.tfoot = translations.tbody;
-
-	var html = {
-		set: function(){
-			var html = Array.flatten(arguments).join('');
-			var wrap = Browser.Engine.trident && translations[this.get('tag')];
-			if (wrap){
-				var first = wrapper;
-				first.innerHTML = wrap[1] + html + wrap[2];
-				for (var i = wrap[0]; i--;) first = first.firstChild;
-				this.empty().adopt(first.childNodes);
-			} else {
-				this.innerHTML = html;
-			}
-		}
-	};
-
-	html.erase = html.set;
-
-	return html;
-})();
-
-if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
-	get: function(){
-		if (this.innerText) return this.innerText;
-		var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
-		var text = temp.innerText;
-		temp.destroy();
-		return text;
-	}
-};
+        /**
+         * APIMethod: findElement
+         * search the parentage of the element to find an element of the given
+         * tag name.
+         *
+         * Parameters:
+         * type - {String} the tag name of the element type to search for
+         *
+         * Returns:
+         * {HTMLElement} the first node (this one or first parent) with the
+         * requested tag name or false if none are found.
+         */
+        findElement: function(type) {
+            var o = this,
+                tagName = o.tagName;
+            while (o.tagName != type && o && o.parentNode && o.parentNode != o) {
+                o = document.id(o.parentNode);
+            }
+            return o.tagName == type ? o: false;
+        }
+    });
+    /**
+     * Class: Array
+     * Extensions to the javascript array object
+     */
+    Array.implement({
+        /**
+         * APIMethod: swap
+         * swaps 2 elements of an array
+         *
+         * Parameters:
+         * a - the first position to swap
+         * b - the second position to swap
+         */
+        'swap': function(a, b) {
+            var temp = this[a];
+            this[a] = this[b];
+            this[b] = temp;
+        }
+    });
+})(document.id || $);
+// End Wrapper for document.id
 /*
-Script: Element.Event.js
-	Contains Element methods for dealing with events, and custom Events.
+---
 
-License:
-	MIT-style license.
-*/
+name: Jx.Styles
 
-Element.Properties.events = {set: function(events){
-	this.addEvents(events);
-}};
+description: A singleton object useful for dynamically creating and manipulating CSS styles
 
-Native.implement([Element, Window, Document], {
+license: MIT-style license.
 
-	addEvent: function(type, fn){
-		var events = this.retrieve('events', {});
-		events[type] = events[type] || {'keys': [], 'values': []};
-		if (events[type].keys.contains(fn)) return this;
-		events[type].keys.push(fn);
-		var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
-		if (custom){
-			if (custom.onAdd) custom.onAdd.call(this, fn);
-			if (custom.condition){
-				condition = function(event){
-					if (custom.condition.call(this, event)) return fn.call(this, event);
-					return true;
-				};
-			}
-			realType = custom.base || realType;
-		}
-		var defn = function(){
-			return fn.call(self);
-		};
-		var nativeEvent = Element.NativeEvents[realType];
-		if (nativeEvent){
-			if (nativeEvent == 2){
-				defn = function(event){
-					event = new Event(event, self.getWindow());
-					if (condition.call(self, event) === false) event.stop();
-				};
-			}
-			this.addListener(realType, defn);
-		}
-		events[type].values.push(defn);
-		return this;
-	},
+requires:
+ - Jx
 
-	removeEvent: function(type, fn){
-		var events = this.retrieve('events');
-		if (!events || !events[type]) return this;
-		var pos = events[type].keys.indexOf(fn);
-		if (pos == -1) return this;
-		events[type].keys.splice(pos, 1);
-		var value = events[type].values.splice(pos, 1)[0];
-		var custom = Element.Events.get(type);
-		if (custom){
-			if (custom.onRemove) custom.onRemove.call(this, fn);
-			type = custom.base || type;
-		}
-		return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
-	},
+provides: [Jx.Styles]
 
-	addEvents: function(events){
-		for (var event in events) this.addEvent(event, events[event]);
-		return this;
-	},
+...
+ */
+/**
+ * Class: Jx.Styles
+ * Dynamic stylesheet class. Used for creating and manipulating dynamic
+ * stylesheets.
+ *
+ * TBD: should we handle the case of putting the same selector in a stylesheet
+ * twice?  Right now the code that stores the index of each rule on the
+ * stylesheet is not really safe for that when combined with delete or get
+ *
+ * This is a singleton and should be called directly, like so:
+ *
+ * (code)
+ *   // create a rule that turns all para text red and 15px.
+ *   var rule = Jx.Styles.insertCssRule("p", "color: red;", "myStyle");
+ *   rule.style.fontSize = "15px";
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ * Additional code by Paul Spencer
+ *
+ * This file is licensed under an MIT style license
+ *
+ * Inspired by dojox.html.styles, VisitSpy by nwhite,
+ * http://www.hunlock.com/blogs/Totally_Pwn_CSS_with_Javascript
+ *
+ */
+Jx.Styles = new(new Class({
+    /**
+     * dynamicStyleMap - <Hash> used to keep a reference to dynamically
+     * created style sheets for quick access
+     */
+    dynamicStyleMap: new Hash(),
+    /**
+     * APIMethod: getCssRule
+     * retrieve a reference to a CSS rule in a specific style sheet based on
+     * its selector.  If the rule does not exist, create it.
+     *
+     * Parameters:
+     * selector - <String> the CSS selector for the rule
+     * styleSheetName - <String> the name of the sheet to get the rule from
+     *
+     * Returns:
+     * <CSSRule> - the requested rule
+     */
+    getCssRule: function(selector, styleSheetName) {
+        var ss = this.getDynamicStyleSheet(styleSheetName),
+            rule = null,
+            i;
+        if (ss.indicies) {
+            i = ss.indicies.indexOf(selector);
+            if (i == -1) {
+                rule = this.insertCssRule(selector, '', styleSheetName);
+            } else {
+                if (Browser.Engine.trident) {
+                    rule = ss.sheet.rules[i];
+                } else {
+                    rule = ss.sheet.cssRules[i];
+                }
+            }
+        }
+        return rule;
+    },
+    /**
+     * APIMethod: insertCssRule
+     * insert a new dynamic rule into the given stylesheet.  If no name is
+     * given for the stylesheet then the default stylesheet is used.
+     *
+     * Parameters:
+     * selector - <String> the CSS selector for the rule
+     * declaration - <String> CSS-formatted rules to include.  May be empty,
+     * in which case you may want to use the returned rule object to
+     * manipulate styles
+     * styleSheetName - <String> the name of the sheet to place the rules in,
+     * or empty to put them in a default sheet.
+     *
+     * Returns:
+     * <CSSRule> - a CSS Rule object with properties that are browser
+     * dependent.  In general, you can use rule.styles to set any CSS
+     * properties in the same way that you would set them on a DOM object.
+     */
+    insertCssRule: function (selector, declaration, styleSheetName) {
+        var ss = this.getDynamicStyleSheet(styleSheetName),
+            rule,
+            text = selector + " {" + declaration + "}",
+            index;
+        if (Browser.Engine.trident) {
+            if (declaration == '') {
+                //IE requires SOME text for the declaration. Passing '{}' will
+                //create an empty rule.
+                declaration = '{}';
+            }
+            index = ss.styleSheet.addRule(selector,declaration);
+            rule = ss.styleSheet.rules[index];
+        } else {
+            ss.sheet.insertRule(text, ss.indicies.length);
+            rule = ss.sheet.cssRules[ss.indicies.length];
+        }
+        ss.indicies.push(selector);
+        return rule;
+    },
+    /**
+     * APIMethod: removeCssRule
+     * removes a CSS rule from the named stylesheet.
+     *
+     * Parameters:
+     * selector - <String> the CSS selector for the rule
+     * styleSheetName - <String> the name of the sheet to remove the rule
+     * from,  or empty to remove them from the default sheet.
+     *
+     * Returns:
+     * <Boolean> true if the rule was removed, false if it was not.
+     */
+    removeCssRule: function (selector, styleSheetName) {
+        var ss = this.getDynamicStyleSheet(styleSheetName),
+            i = ss.indicies.indexOf(selector),
+            result = false;
+        ss.indicies.splice(i, 1);
+        if (Browser.Engine.trident) {
+            ss.removeRule(i);
+            result = true;
+        } else {
+            ss.sheet.deleteRule(i);
+            result = true;
+        }
+        return result;
+    },
+    /**
+     * APIMethod: getDynamicStyleSheet
+     * return a reference to a styleSheet based on its title.  If the sheet
+     * does not already exist, it is created.
+     *
+     * Parameter:
+     * name - <String> the title of the stylesheet to create or obtain
+     *
+     * Returns:
+     * <StyleSheet> a StyleSheet object with browser dependent capabilities.
+     */
+    getDynamicStyleSheet: function (name) {
+        name = (name) ? name : 'default';
+        if (!this.dynamicStyleMap.has(name)) {
+            var sheet = new Element('style').set('type', 'text/css').inject(document.head);
+            sheet.indicies = [];
+            this.dynamicStyleMap.set(name, sheet);
+        }
+        return this.dynamicStyleMap.get(name);
+    },
+    /**
+     * APIMethod: enableStyleSheet
+     * enable a style sheet
+     *
+     * Parameters:
+     * name - <String> the title of the stylesheet to enable
+     */
+    enableStyleSheet: function (name) {
+        this.getDynamicStyleSheet(name).disabled = false;
+    },
+    /**
+     * APIMethod: disableStyleSheet
+     * enable a style sheet
+     *
+     * Parameters:
+     * name - <String> the title of the stylesheet to disable
+     */
+    disableStyleSheet: function (name) {
+        this.getDynamicStyleSheet(name).disabled = true;
+    },
+    /**
+     * APIMethod: removeStyleSheet
+     * Removes a style sheet
+     *
+     * Parameters:
+     * name = <String> the title of the stylesheet to remove
+     */
+    removeStyleSheet: function (name) {
+      this.disableStyleSheet(name);
+      this.getDynamicStyleSheet(name).dispose();
+      this.dynamicStyleMap.erase(name);
+    },
+    /**
+     * APIMethod: isStyleSheetDefined
+     * Determined if the passed in name is a defined dynamic style sheet.
+     *
+     * Parameters:
+     * name = <String> the title of the stylesheet to remove
+     */
+    isStyleSheetDefined: function (name) {
+      return this.dynamicStyleMap.has(name);
+    }
+}))();/*
+---
 
-	removeEvents: function(events){
-		if ($type(events) == 'object'){
-			for (var type in events) this.removeEvent(type, events[type]);
-			return this;
-		}
-		var attached = this.retrieve('events');
-		if (!attached) return this;
-		if (!events){
-			for (var type in attached) this.removeEvents(type);
-			this.eliminate('events');
-		} else if (attached[events]){
-			while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
-			attached[events] = null;
-		}
-		return this;
-	},
+name: Jx.Object
 
-	fireEvent: function(type, args, delay){
-		var events = this.retrieve('events');
-		if (!events || !events[type]) return this;
-		events[type].keys.each(function(fn){
-			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
-		}, this);
-		return this;
-	},
+description: Base class for all other object in the JxLib framework.
 
-	cloneEvents: function(from, type){
-		from = $(from);
-		var fevents = from.retrieve('events');
-		if (!fevents) return this;
-		if (!type){
-			for (var evType in fevents) this.cloneEvents(from, evType);
-		} else if (fevents[type]){
-			fevents[type].keys.each(function(fn){
-				this.addEvent(type, fn);
-			}, this);
-		}
-		return this;
-	}
+license: MIT-style license.
 
-});
+requires:
+ - Jx
 
-Element.NativeEvents = {
-	click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
-	mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
-	mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
-	keydown: 2, keypress: 2, keyup: 2, //keyboard
-	focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
-	load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
-	error: 1, abort: 1, scroll: 1 //misc
-};
+provides: [Jx.Object]
 
-(function(){
+...
+ */
+// $Id: object.js 976 2010-09-02 18:57:12Z pagameba $
+/**
+ * Class: Jx.Object
+ * Base class for all other object in the JxLib framework. This class
+ * implements both mootools mixins Events and Options so the rest of the
+ * classes don't need to.
+ *
+ * The Initialization Pipeline:
+ * Jx.Object provides a default initialize method to construct new instances
+ * of objects that inherit from it.  No sub-class should override initialize
+ * unless you know exactly what you're doing.  Instead, the initialization
+ * pipeline provides an init() method that is intended to be overridden in
+ * sub-classes to provide class-specific initialization as part of the
+ * initialization pipeline.
+ *
+ * The basic initialization pipeline for a Jx.Object is to parse the
+ * parameters provided to initialize(), separate out options from other formal
+ * parameters based on the parameters property of the class, call init() and
+ * initialize plugins.
+ *
+ * Parsing Parameters:
+ * Because each sub-class no longer has an initialize method, it no longer has
+ * direct access to parameters passed to the constructor.  Instead, a
+ * sub-class is expected to provide a parameters attribute with an array of
+ * parameter names in the order expected.  Jx.Object will enumerate the
+ * attributes passed to its initialize method and automatically place them
+ * in the options object under the appropriate key (the value from the
+ * array).  Parameters not found will not be present or will be null.
+ *
+ * The default parameters are a single options object which is merged with
+ * the options attribute of the class.
+ *
+ * Calling Init:
+ * Jx.Object fires the event 'preInit' before calling the init() method,
+ * calls the init() method, then fires the 'postInit' event.  It is expected
+ * that most sub-class specific initialization will happen in the init()
+ * method.  A sub-class may hook preInit and postInit events to perform tasks
+ * in one of two ways.
+ *
+ * First, simply send onPreInit and onPostInit functions via the options
+ * object as follows (they could be standalone functions or functions of
+ * another object setup using .bind())
+ *
+ * (code)
+ * var preInit = function () {}
+ * var postInit = function () {}
+ *
+ * var options = {
+ *   onPreInit: preInit,
+ *   onPostInit: postInit,
+ *   ...other options...
+ * };
+ *
+ * var dialog = new Jx.Dialog(options);
+ * (end)
+ *
+ * The second method you can use is to override the initialize method
+ *
+ * (code)
+ * var MyClass = new Class({
+ *   Family: 'MyClass',
+ *   initialize: function() {
+ *     this.addEvent('preInit', this.preInit.bind(this));
+ *     this.addEvent('postInit', this.postInit.bind(this));
+ *     this.parent.apply(this, arguments);
+ *   },
+ *   preInit: function() {
+ *     // something just before init() is called
+ *   },
+ *   postInit: function() {
+ *     // something just after init() is called
+ *   },
+ *   init: function() {
+ *     this.parent();
+ *     // initialization code here
+ *   }
+ * });
+ * (end)
+ *
+ * When the object finishes initializing itself (including the plugin
+ * initialization) it will fire off the initializeDone event. You can hook
+ * into this event in the same way as the events mentioned above.
+ *
+ * Plugins:
+ * Plugins provide pieces of additional, optional, functionality. They are not
+ * necessary for the proper function of an object. All plugins should be
+ * located in the Jx.Plugin namespace and they should be further segregated by
+ * applicable object. While all objects can support plugins, not all of them
+ * have the automatic instantiation of applicable plugins turned on. In order
+ * to turn this feature on for an object you need to set the pluginNamespace
+ * property of the object. The following is an example of setting the
+ * property:
+ *
+ * (code)
+ * var MyClass = new Class({
+ *   Extends: Jx.Object,
+ *   pluginNamespace: 'MyClass'
+ * };
+ * (end)
+ *
+ * The absence of this property does not mean you cannot attach a plugin to an
+ * object. It simply means that you can't have Jx.Object create the
+ * plugin for you.
+ *
+ * There are four ways to attach a plugin to an object. First, simply
+ * instantiate the plugin yourself and call its attach() method (other class
+ * options left out for the sake of simplicity):
+ *
+ * (code)
+ * var MyGrid = new Jx.Grid();
+ * var APlugin = new Jx.Plugin.Grid.Selector();
+ * APlugin.attach(MyGrid);
+ * (end)
+ *
+ * Second, you can instantiate the plugin first and pass it to the object
+ * through the plugins array in the options object.
+ *
+ * (code)
+ * var APlugin = new Jx.Plugin.Grid.Selector();
+ * var MyGrid = new Jx.Grid({plugins: [APlugin]});
+ * (end)
+ *
+ * The third way is to pass the information needed to instantiate the plugin
+ * in the plugins array of the options object:
+ *
+ * (code)
+ * var MyGrid = new Jx.Grid({
+ *   plugins: [{
+ *      name: 'Selector',
+ *      options: {}    //options needed to create this plugin
+ *   },{
+ *      name: 'Sorter',
+ *      options: {}
+ *   }]
+ * });
+ * (end)
+ *
+ * The final way, if the plugin has no options, is to pass the name of the
+ * plugin as a simple string in the plugins array.
+ *
+ * (code)
+ * var MyGrid = new Jx.Grid({
+ *   plugins: ['Selector','Sorter']
+ * });
+ * (end)
+ *
+ * Part of the process of initializing plugins is to call prePluginInit() and
+ * postPluginInit(). These events provide you access to the object just before
+ * and after the plugins are initialized and/or attached to the object using
+ * methods 2 and 3 above. You can hook into these in the same way that you
+ * hook into the preInit() and postInit() events.
+ *
+ * Destroying Jx.Object Instances:
+ * Jx.Object provides a destroy method that cleans up potential memory leaks
+ * when you no longer need an object.  Sub-classes are expected to implement
+ * a cleanup() method that provides specific cleanup code for each
+ * sub-class.  Remember to call this.parent() when providing a cleanup()
+ * method. Destroy will also fire off 2 events: preDestroy and postDestroy.
+ * You can hook into these methods in the same way as the init or plugin
+ * events.
+ *
+ * The Family Attribute:
+ * the Family attribute of a class is used internally by JxLib to identify Jx
+ * objects within mootools.  The actual value of Family is unimportant to Jx.
+ * If you do not provide a Family, a class will inherit it's base class family
+ * up to Jx.Object.  Family is useful when debugging as you will be able to
+ * identify the family in the firebug inspector, but is not as useful for
+ * coding purposes as it does not allow for inheritance.
+ *
+ * Events:
+ *
+ * preInit
+ * postInit
+ * prePluginInit
+ * postPluginInit
+ * initializeDone
+ * preDestroy
+ * postDestroy
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Object = new Class({
+    Family: "Jx.Object",
+    Implements: [Options, Events],
+    plugins: null,
+    pluginNamespace: 'Other',
+    /**
+     * Constructor: Jx.Object
+     * create a new instance of Jx.Object
+     *
+     * Parameters:
+     * options - {Object} optional parameters for creating an object.
+     */
+    parameters: ['options'],
 
-var $check = function(event){
-	var related = event.relatedTarget;
-	if (related == undefined) return true;
-	if (related === false) return false;
-	return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
-};
+    options: {
+      /**
+       * Option: useLang
+       * Turns on this widget's ability to react to changes in
+       * the default language. Handy for changing text out on the fly.
+       *
+       * TODO: Should this be enabled or disabled by default?
+       */
+      useLang: true,
+      /**
+       * Option: plugins
+       * {Array} an array of plugins to add to the object.
+       */
+      plugins: null
+    },
 
-Element.Events = new Hash({
+    bound: null,
 
-	mouseenter: {
-		base: 'mouseover',
-		condition: $check
-	},
+    initialize: function(){
+        this.plugins = new Hash();
+        this.bound = {};
+        //normalize arguments
+        var numArgs = arguments.length,
+            options = {},
+            parameters = this.parameters,
+            numParams,
+            index;
 
-	mouseleave: {
-		base: 'mouseout',
-		condition: $check
-	},
+        if (numArgs > 0) {
+            if (numArgs === 1
+                    && (Jx.type(arguments[0])==='object' || Jx.type(arguments[0])==='Hash')
+                    && parameters.length === 1
+                    && parameters[0] === 'options') {
+                options = arguments[0];
+            } else {
+                numParams = parameters.length;
+                index;
+                if (numParams <= numArgs) {
+                    index = numParams;
+                } else {
+                    index = numArgs;
+                }
+                for (var i = 0; i < index; i++) {
+                    if (parameters[i] === 'options') {
+                        $extend(options, arguments[i]);
+                    } else {
+                        options[parameters[i]] = arguments[i];
+                    }
+                }
+            }
+        }
 
-	mousewheel: {
-		base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
-	}
+        this.setOptions(options);
 
-});
+        this.bound.changeText = this.changeText.bind(this);
+        if (this.options.useLang) {
+            MooTools.lang.addEvent('langChange', this.bound.changeText);
+        }
 
-})();
-/*
-Script: Element.Style.js
-	Contains methods for interacting with the styles of Elements in a fashionable way.
+        this.fireEvent('preInit');
+        this.init();
+        this.fireEvent('postInit');
+        this.fireEvent('prePluginInit');
+        this.initPlugins();
+        this.fireEvent('postPluginInit');
+        this.fireEvent('initializeDone');
+    },
 
-License:
-	MIT-style license.
-*/
+    /**
+     * Method: initPlugins
+     * internal function to initialize plugins on object creation
+     */
+    initPlugins: function () {
+        var p;
+        // pluginNamespace must be defined in order to pass plugins to the
+        // object
+        if ($defined(this.pluginNamespace)) {
+            if ($defined(this.options.plugins)
+                    && Jx.type(this.options.plugins) === 'array') {
+                this.options.plugins.each(function (plugin) {
+                    if (plugin instanceof Jx.Plugin) {
+                        plugin.attach(this);
+                        this.plugins.set(plugin.name, plugin);
+                    } else if (Jx.type(plugin) === 'object') {
+                        // All plugin-enabled objects should define a
+                        // pluginNamespace member variable
+                        // that is used for locating the plugins. The default
+                        // namespace is 'Other' for
+                        // now until we come up with a better idea
+                      if ($defined(Jx.Plugin[this.pluginNamespace][plugin.name.capitalize()])) {
+                        p = new Jx.Plugin[this.pluginNamespace][plugin.name.capitalize()](plugin.options);
+                      } else {
+                        p = new Jx.Adaptor[this.pluginNamespace][plugin.name.capitalize()](plugin.options);
+                      }
+                        p.attach(this);
+                    } else if (Jx.type(plugin) === 'string') {
+                        //this is a name for a plugin.
+                      if ($defined(Jx.Plugin[this.pluginNamespace][plugin.capitalize()])) {
+                        p = new Jx.Plugin[this.pluginNamespace][plugin.capitalize()]();
+                      } else {
+                        p = new Jx.Adaptor[this.pluginNamespace][plugin.capitalize()]();
+                      }
+                        p.attach(this);
+                    }
+                }, this);
+            }
+        }
+    },
 
-Element.Properties.styles = {set: function(styles){
-	this.setStyles(styles);
-}};
+    /**
+     * APIMethod: destroy
+     * destroy a Jx.Object, safely cleaning up any potential memory
+     * leaks along the way.  Uses the cleanup method of an object to
+     * actually do the cleanup.
+     * Emits the preDestroy event before cleanup and the postDestroy event
+     * after cleanup.
+     */
+    destroy: function () {
+        this.fireEvent('preDestroy');
+        this.cleanup();
+        this.fireEvent('postDestroy');
+    },
 
-Element.Properties.opacity = {
+    /**
+     * Method: cleanup
+     * to be implemented by subclasses to do the actual work of destroying
+     * an object.
+     */
+    cleanup: function () {
+        //detach plugins
+        if (this.plugins.getLength > 0) {
+            this.plugins.each(function (plugin) {
+                plugin.detach();
+                plugin.destroy();
+            }, this);
+        }
+        this.plugins.empty();
+        MooTools.lang.removeEvent('langChange', this.bound.changeText);
+        this.bound = null;
+    },
 
-	set: function(opacity, novisibility){
-		if (!novisibility){
-			if (opacity == 0){
-				if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
-			} else {
-				if (this.style.visibility != 'visible') this.style.visibility = 'visible';
-			}
-		}
-		if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
-		if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
-		this.style.opacity = opacity;
-		this.store('opacity', opacity);
-	},
+    /**
+     * Method: init
+     * virtual initialization method to be implemented by sub-classes
+     */
+    init: $empty,
 
-	get: function(){
-		return this.retrieve('opacity', 1);
-	}
+    /**
+     * APIMethod: registerPlugin
+     * This method is called by a plugin that has its attach method
+     * called.
+     *
+     * Parameters:
+     * plugin - the plugin to register with this object
+     */
+    registerPlugin: function (plugin) {
+        if (!this.plugins.has(plugin.name)) {
+            this.plugins.set(plugin.name,  plugin);
+        }
+    },
+    /**
+     * APIMethod: deregisterPlugin
+     * his method is called by a plugin that has its detach method
+     * called.
+     *
+     * Parameters:
+     * plugin - the plugin to deregister.
+     */
+    deregisterPlugin: function (plugin) {
+        if (this.plugins.has(plugin.name)) {
+            this.plugins.erase(plugin.name);
+        }
+    },
 
-};
+    /**
+     * APIMethod: getPlugin
+     * Allows a developer to get a reference to a plugin with only the
+     * name of the plugin.
+     *
+     * Parameters:
+     * name - the name of the plugin as defined in the plugin's name property
+     */
+    getPlugin: function (name) {
+        if (this.plugins.has(name)) {
+            return this.plugins.get(name);
+        }
+    },
 
-Element.implement({
+    /**
+     * APIMethod: getText
+     *
+     * returns the text for a jx.widget used in a label.
+     *
+     * Parameters:
+     * val - <String> || <Function> || <Object> = { set: '', key: ''[, value: ''] } for a MooTools.lang object
+     */
+    getText: function(val) {
+      var result = '';
+      if (Jx.type(val) == 'string' || Jx.type(val) == 'number') {
+        result = val;
+      } else if (Jx.type(val) == 'function') {
+        result = val();
+      } else if (Jx.type(val) == 'object' && $defined(val.set) && $defined(val.key)) {
+        // COMMENT: just an idea how a localization object could be stored to the instance if needed somewhere else and options change?
+        this.i18n = val;
+        if($defined(val.value)) {
+          result = MooTools.lang.get(val.set, val.key)[val.value];
+        }else{
+          result = MooTools.lang.get(val.set, val.key);
+        }
+      }
+      return result;
+    },
 
-	setOpacity: function(value){
-		return this.set('opacity', value, true);
-	},
-
-	getOpacity: function(){
-		return this.get('opacity');
-	},
-
-	setStyle: function(property, value){
-		switch (property){
-			case 'opacity': return this.set('opacity', parseFloat(value));
-			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
-		}
-		property = property.camelCase();
-		if ($type(value) != 'string'){
-			var map = (Element.Styles.get(property) || '@').split(' ');
-			value = $splat(value).map(function(val, i){
-				if (!map[i]) return '';
-				return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
-			}).join(' ');
-		} else if (value == String(Number(value))){
-			value = Math.round(value);
-		}
-		this.style[property] = value;
-		return this;
-	},
-
-	getStyle: function(property){
-		switch (property){
-			case 'opacity': return this.get('opacity');
-			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
-		}
-		property = property.camelCase();
-		var result = this.style[property];
-		if (!$chk(result)){
-			result = [];
-			for (var style in Element.ShortStyles){
-				if (property != style) continue;
-				for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
-				return result.join(' ');
-			}
-			result = this.getComputedStyle(property);
-		}
-		if (result){
-			result = String(result);
-			var color = result.match(/rgba?\([\d\s,]+\)/);
-			if (color) result = result.replace(color[0], color[0].rgbToHex());
-		}
-		if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result, 10)))){
-			if (property.test(/^(height|width)$/)){
-				var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
-				values.each(function(value){
-					size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
-				}, this);
-				return this['offset' + property.capitalize()] - size + 'px';
-			}
-			if ((Browser.Engine.presto) && String(result).test('px')) return result;
-			if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
-		}
-		return result;
-	},
-
-	setStyles: function(styles){
-		for (var style in styles) this.setStyle(style, styles[style]);
-		return this;
-	},
-
-	getStyles: function(){
-		var result = {};
-		Array.each(arguments, function(key){
-			result[key] = this.getStyle(key);
-		}, this);
-		return result;
-	}
-
+    /**
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     *
+     * Parameters:
+     * lang - the language being changed to or that had it's data set of
+     *    translations changed.
+     */
+    changeText : $empty
 });
-
-Element.Styles = new Hash({
-	left: '@px', top: '@px', bottom: '@px', right: '@px',
-	width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
-	backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
-	fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
-	margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
-	borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
-	zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
-});
-
-Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};
-
-['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
-	var Short = Element.ShortStyles;
-	var All = Element.Styles;
-	['margin', 'padding'].each(function(style){
-		var sd = style + direction;
-		Short[style][sd] = All[sd] = '@px';
-	});
-	var bd = 'border' + direction;
-	Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
-	var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
-	Short[bd] = {};
-	Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
-	Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
-	Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
-});
 /*
-Script: Element.Dimensions.js
-	Contains methods to work with size, scroll, or positioning of Elements and the window object.
+---
 
-License:
-	MIT-style license.
+name: Locale.English.US
 
-Credits:
-	- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
-	- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
-*/
+description: Default translations of text strings used in JX for US english (en-US)
 
-(function(){
+license: MIT-style license.
 
-Element.implement({
+requires:
+ - More/Lang
 
-	scrollTo: function(x, y){
-		if (isBody(this)){
-			this.getWindow().scrollTo(x, y);
-		} else {
-			this.scrollLeft = x;
-			this.scrollTop = y;
-		}
-		return this;
-	},
+provides: [Locale.English.US]
 
-	getSize: function(){
-		if (isBody(this)) return this.getWindow().getSize();
-		return {x: this.offsetWidth, y: this.offsetHeight};
-	},
-
-	getScrollSize: function(){
-		if (isBody(this)) return this.getWindow().getScrollSize();
-		return {x: this.scrollWidth, y: this.scrollHeight};
-	},
-
-	getScroll: function(){
-		if (isBody(this)) return this.getWindow().getScroll();
-		return {x: this.scrollLeft, y: this.scrollTop};
-	},
-
-	getScrolls: function(){
-		var element = this, position = {x: 0, y: 0};
-		while (element && !isBody(element)){
-			position.x += element.scrollLeft;
-			position.y += element.scrollTop;
-			element = element.parentNode;
-		}
-		return position;
-	},
-
-	getOffsetParent: function(){
-		var element = this;
-		if (isBody(element)) return null;
-		if (!Browser.Engine.trident) return element.offsetParent;
-		while ((element = element.parentNode) && !isBody(element)){
-			if (styleString(element, 'position') != 'static') return element;
-		}
-		return null;
-	},
-
-	getOffsets: function(){		
-		if (Browser.Engine.trident){
-			var bound = this.getBoundingClientRect(), html = this.getDocument().documentElement;
-			var isFixed = styleString(this, 'position') == 'fixed';
-			return {
-				x: bound.left + ((isFixed) ? 0 : html.scrollLeft) - html.clientLeft,
-				y: bound.top +  ((isFixed) ? 0 : html.scrollTop)  - html.clientTop
-			};
-		}
-
-		var element = this, position = {x: 0, y: 0};
-		if (isBody(this)) return position;
-
-		while (element && !isBody(element)){
-			position.x += element.offsetLeft;
-			position.y += element.offsetTop;
-
-			if (Browser.Engine.gecko){
-				if (!borderBox(element)){
-					position.x += leftBorder(element);
-					position.y += topBorder(element);
-				}
-				var parent = element.parentNode;
-				if (parent && styleString(parent, 'overflow') != 'visible'){
-					position.x += leftBorder(parent);
-					position.y += topBorder(parent);
-				}
-			} else if (element != this && Browser.Engine.webkit){
-				position.x += leftBorder(element);
-				position.y += topBorder(element);
-			}
-
-			element = element.offsetParent;
-		}
-		if (Browser.Engine.gecko && !borderBox(this)){
-			position.x -= leftBorder(this);
-			position.y -= topBorder(this);
-		}
-		return position;
-	},
-
-	getPosition: function(relative){
-		if (isBody(this)) return {x: 0, y: 0};
-		var offset = this.getOffsets(), scroll = this.getScrolls();
-		var position = {x: offset.x - scroll.x, y: offset.y - scroll.y};
-		var relativePosition = (relative && (relative = $(relative))) ? relative.getPosition() : {x: 0, y: 0};
-		return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
-	},
-
-	getCoordinates: function(element){
-		if (isBody(this)) return this.getWindow().getCoordinates();
-		var position = this.getPosition(element), size = this.getSize();
-		var obj = {left: position.x, top: position.y, width: size.x, height: size.y};
-		obj.right = obj.left + obj.width;
-		obj.bottom = obj.top + obj.height;
-		return obj;
-	},
-
-	computePosition: function(obj){
-		return {left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top')};
-	},
-
-	position: function(obj){
-		return this.setStyles(this.computePosition(obj));
-	}
-
-});
-
-Native.implement([Document, Window], {
-
-	getSize: function(){
-		if (Browser.Engine.presto || Browser.Engine.webkit) {
-			var win = this.getWindow();
-			return {x: win.innerWidth, y: win.innerHeight};
-		}
-		var doc = getCompatElement(this);
-		return {x: doc.clientWidth, y: doc.clientHeight};
-	},
-
-	getScroll: function(){
-		var win = this.getWindow(), doc = getCompatElement(this);
-		return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
-	},
-
-	getScrollSize: function(){
-		var doc = getCompatElement(this), min = this.getSize();
-		return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
-	},
-
-	getPosition: function(){
-		return {x: 0, y: 0};
-	},
-
-	getCoordinates: function(){
-		var size = this.getSize();
-		return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
-	}
-
-});
-
-// private methods
-
-var styleString = Element.getComputedStyle;
-
-function styleNumber(element, style){
-	return styleString(element, style).toInt() || 0;
-};
-
-function borderBox(element){
-	return styleString(element, '-moz-box-sizing') == 'border-box';
-};
-
-function topBorder(element){
-	return styleNumber(element, 'border-top-width');
-};
-
-function leftBorder(element){
-	return styleNumber(element, 'border-left-width');
-};
-
-function isBody(element){
-	return (/^(?:body|html)$/i).test(element.tagName);
-};
-
-function getCompatElement(element){
-	var doc = element.getDocument();
-	return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
-};
-
-})();
-
-//aliases
-
-Native.implement([Window, Document, Element], {
-
-	getHeight: function(){
-		return this.getSize().y;
-	},
-
-	getWidth: function(){
-		return this.getSize().x;
-	},
-
-	getScrollTop: function(){
-		return this.getScroll().y;
-	},
-
-	getScrollLeft: function(){
-		return this.getScroll().x;
-	},
-
-	getScrollHeight: function(){
-		return this.getScrollSize().y;
-	},
-
-	getScrollWidth: function(){
-		return this.getScrollSize().x;
-	},
-
-	getTop: function(){
-		return this.getPosition().y;
-	},
-
-	getLeft: function(){
-		return this.getPosition().x;
-	}
-
-});
-/*
-Script: Selectors.js
-	Adds advanced CSS Querying capabilities for targeting elements. Also includes pseudoselectors support.
-
-License:
-	MIT-style license.
-*/
-
-Native.implement([Document, Element], {
-
-	getElements: function(expression, nocash){
-		expression = expression.split(',');
-		var items, local = {};
-		for (var i = 0, l = expression.length; i < l; i++){
-			var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
-			if (i != 0 && elements.item) elements = $A(elements);
-			items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
-		}
-		return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
-	}
-
-});
-
-Element.implement({
-
-	match: function(selector){
-		if (!selector || (selector == this)) return true;
-		var tagid = Selectors.Utils.parseTagAndID(selector);
-		var tag = tagid[0], id = tagid[1];
-		if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
-		var parsed = Selectors.Utils.parseSelector(selector);
-		return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
-	}
-
-});
-
-var Selectors = {Cache: {nth: {}, parsed: {}}};
-
-Selectors.RegExps = {
-	id: (/#([\w-]+)/),
-	tag: (/^(\w+|\*)/),
-	quick: (/^(\w+|\*)$/),
-	splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
-	combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
-};
-
-Selectors.Utils = {
-
-	chk: function(item, uniques){
-		if (!uniques) return true;
-		var uid = $uid(item);
-		if (!uniques[uid]) return uniques[uid] = true;
-		return false;
-	},
-
-	parseNthArgument: function(argument){
-		if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
-		var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
-		if (!parsed) return false;
-		var inta = parseInt(parsed[1], 10);
-		var a = (inta || inta === 0) ? inta : 1;
-		var special = parsed[2] || false;
-		var b = parseInt(parsed[3], 10) || 0;
-		if (a != 0){
-			b--;
-			while (b < 1) b += a;
-			while (b >= a) b -= a;
-		} else {
-			a = b;
-			special = 'index';
-		}
-		switch (special){
-			case 'n': parsed = {a: a, b: b, special: 'n'}; break;
-			case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
-			case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
-			case 'first': parsed = {a: 0, special: 'index'}; break;
-			case 'last': parsed = {special: 'last-child'}; break;
-			case 'only': parsed = {special: 'only-child'}; break;
-			default: parsed = {a: (a - 1), special: 'index'};
-		}
-
-		return Selectors.Cache.nth[argument] = parsed;
-	},
-
-	parseSelector: function(selector){
-		if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
-		var m, parsed = {classes: [], pseudos: [], attributes: []};
-		while ((m = Selectors.RegExps.combined.exec(selector))){
-			var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
-			if (cn){
-				parsed.classes.push(cn);
-			} else if (pn){
-				var parser = Selectors.Pseudo.get(pn);
-				if (parser) parsed.pseudos.push({parser: parser, argument: pa});
-				else parsed.attributes.push({name: pn, operator: '=', value: pa});
-			} else if (an){
-				parsed.attributes.push({name: an, operator: ao, value: av});
-			}
-		}
-		if (!parsed.classes.length) delete parsed.classes;
-		if (!parsed.attributes.length) delete parsed.attributes;
-		if (!parsed.pseudos.length) delete parsed.pseudos;
-		if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
-		return Selectors.Cache.parsed[selector] = parsed;
-	},
-
-	parseTagAndID: function(selector){
-		var tag = selector.match(Selectors.RegExps.tag);
-		var id = selector.match(Selectors.RegExps.id);
-		return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
-	},
-
-	filter: function(item, parsed, local){
-		var i;
-		if (parsed.classes){
-			for (i = parsed.classes.length; i--; i){
-				var cn = parsed.classes[i];
-				if (!Selectors.Filters.byClass(item, cn)) return false;
-			}
-		}
-		if (parsed.attributes){
-			for (i = parsed.attributes.length; i--; i){
-				var att = parsed.attributes[i];
-				if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
-			}
-		}
-		if (parsed.pseudos){
-			for (i = parsed.pseudos.length; i--; i){
-				var psd = parsed.pseudos[i];
-				if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
-			}
-		}
-		return true;
-	},
-
-	getByTagAndID: function(ctx, tag, id){
-		if (id){
-			var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
-			return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
-		} else {
-			return ctx.getElementsByTagName(tag);
-		}
-	},
-
-	search: function(self, expression, local){
-		var splitters = [];
-
-		var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
-			splitters.push(m1);
-			return ':)' + m2;
-		}).split(':)');
-
-		var items, filtered, item;
-
-		for (var i = 0, l = selectors.length; i < l; i++){
-
-			var selector = selectors[i];
-
-			if (i == 0 && Selectors.RegExps.quick.test(selector)){
-				items = self.getElementsByTagName(selector);
-				continue;
-			}
-
-			var splitter = splitters[i - 1];
-
-			var tagid = Selectors.Utils.parseTagAndID(selector);
-			var tag = tagid[0], id = tagid[1];
-
-			if (i == 0){
-				items = Selectors.Utils.getByTagAndID(self, tag, id);
-			} else {
-				var uniques = {}, found = [];
-				for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
-				items = found;
-			}
-
-			var parsed = Selectors.Utils.parseSelector(selector);
-
-			if (parsed){
-				filtered = [];
-				for (var m = 0, n = items.length; m < n; m++){
-					item = items[m];
-					if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
-				}
-				items = filtered;
-			}
-
-		}
-
-		return items;
-
-	}
-
-};
-
-Selectors.Getters = {
-
-	' ': function(found, self, tag, id, uniques){
-		var items = Selectors.Utils.getByTagAndID(self, tag, id);
-		for (var i = 0, l = items.length; i < l; i++){
-			var item = items[i];
-			if (Selectors.Utils.chk(item, uniques)) found.push(item);
-		}
-		return found;
-	},
-
-	'>': function(found, self, tag, id, uniques){
-		var children = Selectors.Utils.getByTagAndID(self, tag, id);
-		for (var i = 0, l = children.length; i < l; i++){
-			var child = children[i];
-			if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
-		}
-		return found;
-	},
-
-	'+': function(found, self, tag, id, uniques){
-		while ((self = self.nextSibling)){
-			if (self.nodeType == 1){
-				if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
-				break;
-			}
-		}
-		return found;
-	},
-
-	'~': function(found, self, tag, id, uniques){
-		while ((self = self.nextSibling)){
-			if (self.nodeType == 1){
-				if (!Selectors.Utils.chk(self, uniques)) break;
-				if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
-			}
-		}
-		return found;
-	}
-
-};
-
-Selectors.Filters = {
-
-	byTag: function(self, tag){
-		return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
-	},
-
-	byID: function(self, id){
-		return (!id || (self.id && self.id == id));
-	},
-
-	byClass: function(self, klass){
-		return (self.className && self.className.contains(klass, ' '));
-	},
-
-	byPseudo: function(self, parser, argument, local){
-		return parser.call(self, argument, local);
-	},
-
-	byAttribute: function(self, name, operator, value){
-		var result = Element.prototype.getProperty.call(self, name);
-		if (!result) return (operator == '!=');
-		if (!operator || value == undefined) return true;
-		switch (operator){
-			case '=': return (result == value);
-			case '*=': return (result.contains(value));
-			case '^=': return (result.substr(0, value.length) == value);
-			case '$=': return (result.substr(result.length - value.length) == value);
-			case '!=': return (result != value);
-			case '~=': return result.contains(value, ' ');
-			case '|=': return result.contains(value, '-');
-		}
-		return false;
-	}
-
-};
-
-Selectors.Pseudo = new Hash({
-
-	// w3c pseudo selectors
-
-	checked: function(){
-		return this.checked;
-	},
+...
+ */
+MooTools.lang.set('en-US', 'Jx', {
 	
-	empty: function(){
-		return !(this.innerText || this.textContent || '').length;
+	'widget': {
+		busyMessage: 'Working ...'
 	},
-
-	not: function(selector){
-		return !Element.match(this, selector);
+	'colorpalette': {
+		alphaLabel: 'alpha (%)'
 	},
-
-	contains: function(text){
-		return (this.innerText || this.textContent || '').contains(text);
+	notice: {
+		closeTip: 'close this notice'
 	},
-
-	'first-child': function(){
-		return Selectors.Pseudo.index.call(this, 0);
+	progressbar: {
+		messageText: 'Loading...',
+		progressText: '{progress} of {total}'
 	},
-
-	'last-child': function(){
-		var element = this;
-		while ((element = element.nextSibling)){
-			if (element.nodeType == 1) return false;
-		}
-		return true;
+	field: {
+		requiredText: '*'
 	},
-
-	'only-child': function(){
-		var prev = this;
-		while ((prev = prev.previousSibling)){
-			if (prev.nodeType == 1) return false;
-		}
-		var next = this;
-		while ((next = next.nextSibling)){
-			if (next.nodeType == 1) return false;
-		}
-		return true;
+	file: {
+		browseLabel: 'Browse...'
 	},
-
-	'nth-child': function(argument, local){
-		argument = (argument == undefined) ? 'n' : argument;
-		var parsed = Selectors.Utils.parseNthArgument(argument);
-		if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
-		var count = 0;
-		local.positions = local.positions || {};
-		var uid = $uid(this);
-		if (!local.positions[uid]){
-			var self = this;
-			while ((self = self.previousSibling)){
-				if (self.nodeType != 1) continue;
-				count ++;
-				var position = local.positions[$uid(self)];
-				if (position != undefined){
-					count = position + count;
-					break;
-				}
-			}
-			local.positions[uid] = count;
-		}
-		return (local.positions[uid] % parsed.a == parsed.b);
+	'formatter.boolean': {
+		'true': 'Yes',
+		'false': 'No'
 	},
-
-	// custom pseudo selectors
-
-	index: function(index){
-		var element = this, count = 0;
-		while ((element = element.previousSibling)){
-			if (element.nodeType == 1 && ++count > index) return false;
-		}
-		return (count == index);
+	'formatter.currency': {
+		sign: '$'
 	},
-
-	even: function(argument, local){
-		return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
+	'formatter.number': {
+		decimalSeparator: '.',
+    thousandsSeparator: ','
 	},
-
-	odd: function(argument, local){
-		return Selectors.Pseudo['nth-child'].call(this, '2n', local);
+	splitter: {
+		barToolTip: 'drag this bar to resize'
 	},
-	
-	selected: function() {
-		return this.selected;
-	}
-
-});
-/*
-Script: Domready.js
-	Contains the domready custom event.
-
-License:
-	MIT-style license.
-*/
-
-Element.Events.domready = {
-
-	onAdd: function(fn){
-		if (Browser.loaded) fn.call(this);
-	}
-
-};
-
-(function(){
-
-	var domready = function(){
-		if (Browser.loaded) return;
-		Browser.loaded = true;
-		window.fireEvent('domready');
-		document.fireEvent('domready');
-	};
-
-	if (Browser.Engine.trident){
-		var temp = document.createElement('div');
-		(function(){
-			($try(function(){
-				temp.doScroll('left');
-				return $(temp).inject(document.body).set('html', 'temp').dispose();
-			})) ? domready() : arguments.callee.delay(50);
-		})();
-	} else if (Browser.Engine.webkit && Browser.Engine.version < 525){
-		(function(){
-			(['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
-		})();
-	} else {
-		window.addEvent('load', domready);
-		document.addEvent('DOMContentLoaded', domready);
-	}
-
-})();
-/*
-Script: JSON.js
-	JSON encoder and decoder.
-
-License:
-	MIT-style license.
-
-See Also:
-	<http://www.json.org/>
-*/
-
-var JSON = new Hash({
-
-	$specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
-
-	$replaceChars: function(chr){
-		return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
+  panelset: {
+    barToolTip: 'drag this bar to resize'
+  },
+	panel: {
+		collapseTooltip: 'Collapse/Expand Panel',
+    collapseLabel: 'Collapse',
+    expandLabel: 'Expand',
+    maximizeTooltip: 'Maximize Panel',
+    maximizeLabel: 'Maximize',
+    restoreTooltip: 'Restore Panel',
+    restoreLabel: 'Restore',
+    closeTooltip: 'Close Panel',
+    closeLabel: 'Close'
 	},
-
-	encode: function(obj){
-		switch ($type(obj)){
-			case 'string':
-				return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
-			case 'array':
-				return '[' + String(obj.map(JSON.encode).filter($defined)) + ']';
-			case 'object': case 'hash':
-				var string = [];
-				Hash.each(obj, function(value, key){
-					var json = JSON.encode(value);
-					if (json) string.push(JSON.encode(key) + ':' + json);
-				});
-				return '{' + string + '}';
-			case 'number': case 'boolean': return String(obj);
-			case false: return 'null';
-		}
-		return null;
+	confirm: {
+		affirmativeLabel: 'Yes',
+    negativeLabel: 'No'
 	},
-
-	decode: function(string, secure){
-		if ($type(string) != 'string' || !string.length) return null;
-		if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
-		return eval('(' + string + ')');
-	}
-
-});
-
-Native.implement([Hash, Array, String, Number], {
-
-	toJSON: function(){
-		return JSON.encode(this);
-	}
-
-});
-/*
-Script: Cookie.js
-	Class for creating, loading, and saving browser Cookies.
-
-License:
-	MIT-style license.
-
-Credits:
-	Based on the functions by Peter-Paul Koch (http://quirksmode.org).
-*/
-
-var Cookie = new Class({
-
-	Implements: Options,
-
-	options: {
-		path: false,
-		domain: false,
-		duration: false,
-		secure: false,
-		document: document
+	dialog: {
+		resizeToolTip: 'Resize dialog'
 	},
-
-	initialize: function(key, options){
-		this.key = key;
-		this.setOptions(options);
+	message: {
+		okButton: 'Ok'
 	},
-
-	write: function(value){
-		value = encodeURIComponent(value);
-		if (this.options.domain) value += '; domain=' + this.options.domain;
-		if (this.options.path) value += '; path=' + this.options.path;
-		if (this.options.duration){
-			var date = new Date();
-			date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
-			value += '; expires=' + date.toGMTString();
-		}
-		if (this.options.secure) value += '; secure';
-		this.options.document.cookie = this.key + '=' + value;
-		return this;
+	prompt: {
+		okButton: 'Ok',
+		cancelButton: 'Cancel'
 	},
-
-	read: function(){
-		var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
-		return (value) ? decodeURIComponent(value[1]) : null;
+	upload: {
+		buttonText: 'Upload Files'
 	},
-
-	dispose: function(){
-		new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
-		return this;
-	}
-
-});
-
-Cookie.write = function(key, value, options){
-	return new Cookie(key, options).write(value);
-};
-
-Cookie.read = function(key){
-	return new Cookie(key).read();
-};
-
-Cookie.dispose = function(key, options){
-	return new Cookie(key, options).dispose();
-};
-/*
-Script: Swiff.js
-	Wrapper for embedding SWF movies. Supports (and fixes) External Interface Communication.
-
-License:
-	MIT-style license.
-
-Credits:
-	Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
-*/
-
-var Swiff = new Class({
-
-	Implements: [Options],
-
-	options: {
-		id: null,
-		height: 1,
-		width: 1,
-		container: null,
-		properties: {},
-		params: {
-			quality: 'high',
-			allowScriptAccess: 'always',
-			wMode: 'transparent',
-			swLiveConnect: true
-		},
-		callBacks: {},
-		vars: {}
+	'plugin.resize': {
+	  tooltip: 'Drag to resize, double click to auto-size.'
 	},
+  'plugin.editor': {
+    submitButton: 'Save',
+    cancelButton: 'Cancel'
+  }
+});/*
+---
 
-	toElement: function(){
-		return this.object;
-	},
+name: Jx.Widget
 
-	initialize: function(path, options){
-		this.instance = 'Swiff_' + $time();
+description: Base class for all widgets (visual classes) in the JxLib Framework.
 
-		this.setOptions(options);
-		options = this.options;
-		var id = this.id = options.id || this.instance;
-		var container = $(options.container);
+license: MIT-style license.
 
-		Swiff.CallBacks[this.instance] = {};
+requires:
+ - Jx.Object
+ - Locale.English.US
 
-		var params = options.params, vars = options.vars, callBacks = options.callBacks;
-		var properties = $extend({height: options.height, width: options.width}, options.properties);
+provides: [Jx.Widget]
 
-		var self = this;
+css:
+ - chrome
 
-		for (var callBack in callBacks){
-			Swiff.CallBacks[this.instance][callBack] = (function(option){
-				return function(){
-					return option.apply(self.object, arguments);
-				};
-			})(callBacks[callBack]);
-			vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
-		}
+images:
+ - spinner_16.gif
+ - spinner_24.gif
 
-		params.flashVars = Hash.toQueryString(vars);
-		if (Browser.Engine.trident){
-			properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
-			params.movie = path;
-		} else {
-			properties.type = 'application/x-shockwave-flash';
-			properties.data = path;
-		}
-		var build = '<object id="' + id + '"';
-		for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
-		build += '>';
-		for (var param in params){
-			if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
-		}
-		build += '</object>';
-		this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
-	},
+optional:
+ - More/Spinner
 
-	replaces: function(element){
-		element = $(element, true);
-		element.parentNode.replaceChild(this.toElement(), element);
-		return this;
-	},
+...
+ */
+// $Id: widget.js 976 2010-09-02 18:57:12Z pagameba $
+/**
+ * Class: Jx.Widget
+ * Base class for all widgets (visual classes) in the JxLib Framework. This
+ * class extends <Jx.Object> and adds the Chrome, ContentLoader, Addable, and
+ * AutoPosition mixins from the original framework.
+ *
+ * ContentLoader:
+ *
+ * ContentLoader functionality provides a consistent
+ * mechanism for descendants of Jx.Widget to load content in one of
+ * four different ways:
+ *
+ * o using an existing element, by id
+ *
+ * o using an existing element, by object reference
+ *
+ * o using an HTML string
+ *
+ * o using a URL to get the content remotely
+ *
+ * Chrome:
+ *
+ * Chrome is the extraneous visual element that provides the look and feel to
+ * some elements i.e. dialogs.  Chrome is added inside the element specified
+ * but may bleed outside the element to provide drop shadows etc.  This is
+ * done by absolutely positioning the chrome objects in the container based on
+ * calculations using the margins, borders, and padding of the jxChrome
+ * class and the element it is added to.
+ *
+ * Chrome can consist of either pure CSS border and background colors, or
+ * a background-image on the jxChrome class.  Using a background-image on
+ * the jxChrome class creates four images inside the chrome container that
+ * are positioned in the top-left, top-right, bottom-left and bottom-right
+ * corners of the chrome container and are sized to fill 50% of the width
+ * and height.  The images are positioned and clipped such that the
+ * appropriate corners of the chrome image are displayed in those locations.
+ *
+ * Busy States:
+ *
+ * Any widget can be set as temporarily busy by calling the setBusy(true)
+ * method and then as idle by calling setBusy(false).  By default, busy
+ * widgets display an event mask that prevents them from being clicked and
+ * a spinner image with a message.  By default, there are two configurations
+ * for the spinner image and message, one for 'small' widgets like buttons
+ * and inputs, and one for larger widgets like panels and dialogs.  The
+ * framework automatically chooses the most appropriate configuration so you
+ * don't need to worry about it unless you want to customize it.
+ *
+ * You can disable this behaviour entirely by setting busyMask: false in the
+ * widget options when creating the widget.
+ *
+ * The mask and spinner functionality is provided by the MooTools Spinner
+ * class.  You can use any options documented for Spinner or Mask by setting
+ * the maskOptions option when creating a widget.
+ *
+ * Events:
+ * Jx.Widget has several events called during it's lifetime (in addition to
+ * the ones for its base class <Jx.Object>).
+ *
+ * preRender - called before rendering begins
+ * postRender - called after rendering is done
+ * deferRender - called when the deferRender option is set to true. The first
+ *      two events (pre- and post- render will NOT be called if deferRender is
+ *      set to true).
+ * contentLoaded - called after content has been loaded successfully
+ * contentLoadFailed - called if content can not be loaded for some reason
+ * addTo - called when a widget is added to another element or widget
+ * busy - called just before the busy mask is rendered/removed
+ *
+ * MooTools.Lang Keys:
+ * widget.busyMessage - sets the message of the waiter component when used
+ */
+Jx.Widget = new Class({
+    Family: "Jx.Widget",
+    Extends: Jx.Object,
 
-	inject: function(element){
-		$(element, true).appendChild(this.toElement());
-		return this;
-	},
+    options: {
+        /* Option: id
+         * (optional) {String} an HTML ID to assign to the widget
+         */
+        id: null,
+        /**
+         * Option: content
+         * content may be an HTML element reference, the id of an HTML element
+         * already in the DOM, or an HTML string that becomes the inner HTML
+         * of the element.
+         */
+        content: null,
+        /**
+         * Option: contentURL
+         * the URL to load content from
+         */
+        contentURL: null,
+        /**
+         * Option: loadOnDemand
+         * {boolean} ajax content will only be loaded if the action is requested
+         * (like loading the content into a tab when activated)
+         */
+        loadOnDemand : false,
+        /**
+         * Option: cacheContent
+         * {boolean} determine whether the content should be loaded every time
+         * or if it's being cached
+         */
+        cacheContent : true,
+        /**
+         * Option: template
+         * the default HTML structure of this widget.  The default template
+         * is just a div with a class of jxWidget in the base class
+         */
+        template: '<div class="jxWidget"></div>',
+        /**
+         * Option: busyClass
+         * {String} a CSS class name to apply to busy mask when a widget is
+         * set as busy.  The default is 'jxBusy'.
+         */
+        busyClass: 'jxBusy',
+        /**
+         * Option: busyMask
+         * {Object} an object of options to pass to the MooTools Spinner
+         * when masking a busy object.  Set to false if you do not want
+         * to use the busy mask.
+         */
+        busyMask: {
+          'class': 'jxSpinner jxSpinnerLarge',
+          img: {'class':'jxSpinnerImage'},
+          content: {'class':'jxSpinnerContent'},
+          messageContainer: {'class':'jxSpinnerMessage'},
+          useIframeShim: true,
+          iframeShimOptions: {
+            className: 'jxIframeShim'
+          },
+          fx: true
+        },
+        /**
+         * Option: deferRender
+         * Used to defer rendering of a widget to a later time. Useful when
+         * we need data or other information not at hand at the moment
+         * of Widget instantiation. If set to true, the user will need to call
+         * render() at some later time. The only drawback to doing so will be
+         * the loss of preRender and postRender events.
+         */
+        deferRender: false
+    },
 
-	remote: function(){
-		return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
-	}
+    /**
+     * Property: classes
+     * {<Hash>} a hash of object properties to CSS class names used to
+     * automatically extract references to important DOM elements when
+     * processing a widget template.  This allows developers to provide custom
+     * HTML structures without affecting the functionality of widgets.
+     */
+    classes: new Hash({
+        domObj: 'jxWidget'
+    }),
 
-});
+    /**
+     * Property: busy
+     * {Boolean} is the widget currently busy?  This should be considered
+     * an internal property, use the API methods <Jx.Widget::setBusy> and
+     * <Jx.Widget::isBusy> to manage the busy state of a widget.
+     */
+    busy: false,
 
-Swiff.CallBacks = {};
+    /**
+     * Property: domObj
+     * The HTMLElement that represents this widget.
+     */
+    domObj: null,
 
-Swiff.remote = function(obj, fn){
-	var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
-	return eval(rs);
-};
-/*
-Script: Fx.js
-	Contains the basic animation logic to be extended by all other Fx Classes.
+    /**
+     * Property: contentIsLoaded
+     * {Boolean} tracks the load state of the content, specifically useful
+     * in the case of remote content.
+     */
+    contentIsLoaded: false,
 
-License:
-	MIT-style license.
-*/
+    /**
+     * Property: chrome
+     * the DOM element that contains the chrome
+     */
+    chrome: null,
 
-var Fx = new Class({
+    /**
+     * Method: init
+     * sets up the base widget code and runs the render function.  Called
+     * by the Jx.Object framework for object initialization, should not be
+     * called directly.
+     */
+    init: function(){
+        if (!this.options.deferRender) {
+            this.fireEvent('preRender');
+            this.render();
+            this.fireEvent('postRender');
+        } else {
+            this.fireEvent('deferRender');
+        }
+    },
 
-	Implements: [Chain, Events, Options],
+    /**
+     * APIMethod: loadContent
+     *
+     * triggers loading of content based on options set for the current
+     * object.
+     *
+     * Parameters:
+     * element - {Object} the element to insert the content into
+     *
+     * Events:
+     *
+     * ContentLoader adds the following events to an object.  You can
+     * register for these events using the addEvent method or by providing
+     * callback functions via the on{EventName} properties in the options
+     * object
+     *
+     * contentLoaded - called when the content has been loaded.  If the
+     *     content is not asynchronous then this is called before loadContent
+     *     returns.
+     * contentLoadFailed - called if the content fails to load, primarily
+     *     useful when using the contentURL method of loading content.
+     */
+    loadContent: function(element) {
+        var c,
+            options = this.options,
+            timeout;
+        element = document.id(element);
+        if (options.content) {
+            if (options.content.domObj) {
+                c = document.id(options.content.domObj);
+            } else {
+                c = document.id(options.content);
+            }
+            if (c) {
+                if (options.content.addTo) {
+                    options.content.addTo(element);
+                } else {
+                    element.appendChild(c);
+                }
+                this.contentIsLoaded = true;
+            } else {
+                element.innerHTML = options.content;
+                this.contentIsLoaded = true;
+            }
+        } else if (options.contentURL) {
+            this.contentIsLoaded = false;
+            this.req = new Request({
+                url: options.contentURL,
+                method:'get',
+                evalScripts:true,
+                onRequest:(function() {
+                  if(options.loadOnDemand) {
+                      this.setBusy(true);
+                  }
+                }).bind(this),
+                onSuccess:(function(html) {
+                    element.innerHTML = html;
+                    this.contentIsLoaded = true;
+                    if (Jx.isAir){
+                        $clear(this.reqTimeout);
+                    }
+                    this.setBusy(false);
+                    this.fireEvent('contentLoaded', this);
+                }).bind(this),
+                onFailure: (function(){
+                    this.contentIsLoaded = true;
+                    this.fireEvent('contentLoadFailed', this);
+                    this.setBusy(false);
+                }).bind(this),
+                headers: {'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT'}
+            });
+            this.req.send();
+            if (Jx.isAir) {
+                timeout = $defined(options.timeout) ? options.timeout : 10000;
+                this.reqTimeout = this.checkRequest.delay(timeout, this);
+            }
+        } else {
+            this.contentIsLoaded = true;
+        }
+        if (options.contentId) {
+            element.id = this.options.contentId;
+        }
+        if (this.contentIsLoaded) {
+            this.fireEvent('contentLoaded', this);
+        }
+    },
 
-	options: {
-		/*
-		onStart: $empty,
-		onCancel: $empty,
-		onComplete: $empty,
-		*/
-		fps: 50,
-		unit: false,
-		duration: 500,
-		link: 'ignore'
-	},
+    /**
+     * APIMethod: position
+     * positions an element relative to another element
+     * based on the provided options.  Positioning rules are
+     * a string with two space-separated values.  The first value
+     * references the parent element and the second value references
+     * the thing being positioned.  In general, multiple rules can be
+     * considered by passing an array of rules to the horizontal and
+     * vertical options.  The position method will attempt to position
+     * the element in relation to the relative element using the rules
+     * specified in the options.  If the element does not fit in the
+     * viewport using the rule, then the next rule is attempted.  If
+     * all rules fail, the last rule is used and element may extend
+     * outside the viewport.  Horizontal and vertical rules are
+     * processed independently.
+     *
+     * Horizontal Positioning:
+     * Horizontal values are 'left', 'center', 'right', and numeric values.
+     * Some common rules are:
+     * o 'left left' is interpreted as aligning the left
+     * edge of the element to be positioned with the left edge of the
+     * reference element.
+     * o 'right right' aligns the two right edges.
+     * o 'right left' aligns the left edge of the element to the right of
+     * the reference element.
+     * o 'left right' aligns the right edge of the element to the left
+     * edge of the reference element.
+     *
+     * Vertical Positioning:
+     * Vertical values are 'top', 'center', 'bottom', and numeric values.
+     * Some common rules are:
+     * o 'top top' is interpreted as aligning the top
+     * edge of the element to be positioned with the top edge of the
+     * reference element.
+     * o 'bottom bottom' aligns the two bottom edges.
+     * o 'bottom top' aligns the top edge of the element to the bottom of
+     * the reference element.
+     * o 'top bottom' aligns the bottom edge of the element to the top
+     * edge of the reference element.
+     *
+     * Parameters:
+     * element - the element to position
+     * relative - the element to position relative to
+     * options - the positioning options, see list below.
+     *
+     * Options:
+     * horizontal - the horizontal positioning rule to use to position the
+     *    element.  Valid values are 'left', 'center', 'right', and a numeric
+     *    value.  The default value is 'center center'.
+     * vertical - the vertical positioning rule to use to position the
+     *    element.  Valid values are 'top', 'center', 'bottom', and a numeric
+     *    value.  The default value is 'center center'.
+     * offsets - an object containing numeric pixel offset values for the
+     *    object being positioned as top, right, bottom and left properties.
+     */
+    position: function(element, relative, options) {
+        element = document.id(element);
+        relative = document.id(relative);
+        var hor = $splat(options.horizontal || ['center center']),
+            ver = $splat(options.vertical || ['center center']),
+            offsets = $merge({top:0,right:0,bottom:0,left:0}, options.offsets || {}),
+            coords = relative.getCoordinates(), //top, left, width, height,
+            page, 
+            scroll,
+            size,
+            left,
+            rigbht,
+            top,
+            bottom,
+            n,
+            parts;
+        if (!document.id(element.parentNode) || element.parentNode ==  document.body) {
+            page = Jx.getPageDimensions();
+            scroll = document.id(document.body).getScroll();
+        } else {
+            page = document.id(element.parentNode).getContentBoxSize(); //width, height
+            scroll = document.id(element.parentNode).getScroll();
+        }
+        if (relative == document.body) {
+            // adjust coords for the scroll offsets to make the object
+            // appear in the right part of the page.
+            coords.left += scroll.x;
+            coords.top += scroll.y;
+        } else if (element.parentNode == relative) {
+            // if the element is opening *inside* its relative, we want
+            // it to position correctly within it so top/left becomes
+            // the reference system.
+            coords.left = 0;
+            coords.top = 0;
+        }
+        size = element.getMarginBoxSize(); //width, height
+        if (!hor.some(function(opt) {
+            parts = opt.split(' ');
+            if (parts.length != 2) {
+                return false;
+            }
+            if (!isNaN(parseInt(parts[0],10))) {
+                n = parseInt(parts[0],10);
+                if (n>=0) {
+                    left = n;
+                } else {
+                    left = coords.left + coords.width + n;
+                }
+            } else {
+                switch(parts[0]) {
+                    case 'right':
+                        left = coords.left + coords.width;
+                        break;
+                    case 'center':
+                        left = coords.left + Math.round(coords.width/2);
+                        break;
+                    case 'left':
+                    default:
+                        left = coords.left;
+                        break;
+                }
+            }
+            if (!isNaN(parseInt(parts[1],10))) {
+                n = parseInt(parts[1],10);
+                if (n<0) {
+                    right = left + n;
+                    left = right - size.width;
+                } else {
+                    left += n;
+                    right = left + size.width;
+                }
+                right = coords.left + coords.width + parseInt(parts[1],10);
+                left = right - size.width;
+            } else {
+                switch(parts[1]) {
+                    case 'left':
+                        left -= offsets.left;
+                        right = left + size.width;
+                        break;
+                    case 'right':
+                        left += offsets.right;
+                        right = left;
+                        left = left - size.width;
+                        break;
+                    case 'center':
+                    default:
+                        left = left - Math.round(size.width/2);
+                        right = left + size.width;
+                        break;
+                }
+            }
+            return (left >= scroll.x && right <= scroll.x + page.width);
+        })) {
+            // all failed, snap the last position onto the page as best
+            // we can - can't do anything if the element is wider than the
+            // space available.
+            if (right > page.width) {
+                left = scroll.x + page.width - size.width;
+            }
+            if (left < 0) {
+                left = 0;
+            }
+        }
+        element.setStyle('left', left);
 
-	initialize: function(options){
-		this.subject = this.subject || this;
-		this.setOptions(options);
-		this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
-		var wait = this.options.wait;
-		if (wait === false) this.options.link = 'cancel';
-	},
+        if (!ver.some(function(opt) {
+          parts = opt.split(' ');
+          if (parts.length != 2) {
+            return false;
+          }
+          if (!isNaN(parseInt(parts[0],10))) {
+            top = parseInt(parts[0],10);
+          } else {
+            switch(parts[0]) {
+              case 'bottom':
+                top = coords.top + coords.height;
+                break;
+              case 'center':
+                top = coords.top + Math.round(coords.height/2);
+                break;
+              case 'top':
+              default:
+                top = coords.top;
+                break;
+            }
+          }
+          if (!isNaN(parseInt(parts[1],10))) {
+              var n = parseInt(parts[1],10);
+              if (n>=0) {
+                  top += n;
+                  bottom = top + size.height;
+              } else {
+                  bottom = top + n;
+                  top = bottom - size.height;
+              }
+          } else {
+              switch(parts[1]) {
+                  case 'top':
+                      top -= offsets.top;
+                      bottom = top + size.height;
+                      break;
+                  case 'bottom':
+                      top += offsets.bottom;
+                      bottom = top;
+                      top = top - size.height;
+                      break;
+                  case 'center':
+                  default:
+                      top = top - Math.round(size.height/2);
+                      bottom = top + size.height;
+                      break;
+              }
+          }
+          return (top >= scroll.y && bottom <= scroll.y + page.height);
+      })) {
+          // all failed, snap the last position onto the page as best
+          // we can - can't do anything if the element is higher than the
+          // space available.
+          if (bottom > page.height) {
+              top = scroll.y + page.height - size.height;
+          }
+          if (top < 0) {
+              top = 0;
+          }
+      }
+      element.setStyle('top', top);
 
-	getTransition: function(){
-		return function(p){
-			return -(Math.cos(Math.PI * p) - 1) / 2;
-		};
-	},
+      /* update the jx layout if necessary */
+      var jxl = element.retrieve('jxLayout');
+      if (jxl) {
+          jxl.options.left = left;
+          jxl.options.top = top;
+      }
+    },
 
-	step: function(){
-		var time = $time();
-		if (time < this.time + this.options.duration){
-			var delta = this.transition((time - this.time) / this.options.duration);
-			this.set(this.compute(this.from, this.to, delta));
-		} else {
-			this.set(this.compute(this.from, this.to, 1));
-			this.complete();
-		}
-	},
+    /**
+     * Method: makeChrome
+     * create chrome on an element.
+     *
+     * Parameters:
+     * element - {HTMLElement} the element to put the chrome on.
+     */
+    makeChrome: function(element) {
+        var c = new Element('div', {
+                'class':'jxChrome',
+                events: {
+                  contextmenu: function(e) { e.stop(); }
+                }
+              }),
+            src;
 
-	set: function(now){
-		return now;
-	},
+        /* add to element so we can get the background image style */
+        element.adopt(c);
 
-	compute: function(from, to, delta){
-		return Fx.compute(from, to, delta);
-	},
+        /* pick up any offset because of chrome, set
+         * through padding on the chrome object.  Other code can then
+         * make use of these offset values to fix positioning.
+         */
+        this.chromeOffsets = c.measure(function() {
+            return this.getSizes(['padding']).padding;
+        });
+        c.setStyle('padding', 0);
 
-	check: function(){
-		if (!this.timer) return true;
-		switch (this.options.link){
-			case 'cancel': this.cancel(); return true;
-			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
-		}
-		return false;
-	},
+        /* get the chrome image from the background image of the element */
+        /* the app: protocol check is for adobe air support */
+        src = c.getStyle('backgroundImage');
+        if (src != null) {
+          if (!(src.contains('http://') || src.contains('https://') || src.contains('file://') || src.contains('app:/'))) {
+              src = null;
+          } else {
+              src = src.slice(4,-1);
+              /* this only seems to be IE and Opera, but they add quotes
+               * around the url - yuck
+               */
+              if (src.charAt(0) == '"') {
+                  src = src.slice(1,-1);
+              }
 
-	start: function(from, to){
-		if (!this.check(from, to)) return this;
-		this.from = from;
-		this.to = to;
-		this.time = 0;
-		this.transition = this.getTransition();
-		this.startTimer();
-		this.onStart();
-		return this;
-	},
+              /* and remove the background image */
+              c.setStyle('backgroundImage', 'none');
 
-	complete: function(){
-		if (this.stopTimer()) this.onComplete();
-		return this;
-	},
+              /* make chrome */
+              ['TR','TL','BL','BR'].each(function(s){
+                  c.adopt(
+                      new Element('div',{
+                          'class':'jxChrome'+s
+                      }).adopt(
+                      new Element('img',{
+                          'class':'png24',
+                          src:src,
+                          alt: '',
+                          title: ''
+                      })));
+              }, this);
+          }
+        }
+        /* create a shim so selects don't show through the chrome */
+        if ($defined(window.IframeShim)) {
+          this.shim = new IframeShim(c, {className: 'jxIframeShim'});
+        }
 
-	cancel: function(){
-		if (this.stopTimer()) this.onCancel();
-		return this;
-	},
+        /* remove from DOM so the other resizing logic works as expected */
+        c.dispose();
+        this.chrome = c;
+    },
 
-	onStart: function(){
-		this.fireEvent('start', this.subject);
-	},
+    /**
+     * APIMethod: showChrome
+     * show the chrome on an element.  This creates the chrome if necessary.
+     * If the chrome has been previously created and not removed, you can
+     * call this without an element and it will just resize the chrome within
+     * its existing element.  You can also pass in a different element from
+     * which the chrome was previously attached to and it will move the chrome
+     * to the new element.
+     *
+     * Parameters:
+     * element - {HTMLElement} the element to show the chrome on.
+     */
+    showChrome: function(element) {
+        element = document.id(element) || document.id(this);
+        if (element) {
+            if (!this.chrome) {
+                this.makeChrome(element);
+                element.addClass('jxHasChrome');
+            }
+            this.resizeChrome(element);
+            if (this.shim) {
+              this.shim.show();
+            }
+            if (element && this.chrome.parentNode !== element) {
+                element.adopt(this.chrome);
+                this.chrome.setStyle('z-index',-1);
+            }
+        }
+    },
 
-	onComplete: function(){
-		this.fireEvent('complete', this.subject);
-		if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
-	},
+    /**
+     * APIMethod: hideChrome
+     * removes the chrome from the DOM.  If you do this, you can't
+     * call showChrome with no arguments.
+     */
+    hideChrome: function() {
+        if (this.chrome) {
+            if (this.shim) {
+              this.shim.hide();
+            }
+            this.chrome.parentNode.removeClass('jxHasChrome');
+            this.chrome.dispose();
+        }
+    },
 
-	onCancel: function(){
-		this.fireEvent('cancel', this.subject).clearChain();
-	},
+    /**
+     * APIMethod: resizeChrome
+     * manually resize the chrome on an element.
+     *
+     * Parameters:
+     * element: {DOMElement} the element to resize the chrome for
+     */
+    resizeChrome: function(o) {
+        if (this.chrome && Browser.Engine.trident4) {
+            this.chrome.setContentBoxSize(document.id(o).getBorderBoxSize());
+            if (this.shim) {
+              this.shim.position();
+            }
+        }
+    },
 
-	pause: function(){
-		this.stopTimer();
-		return this;
-	},
+    /**
+     * APIMethod: addTo
+     * adds the object to the DOM relative to another element.  If you use
+     * 'top' or 'bottom' then the element is added to the relative
+     * element (becomes a child node).  If you use 'before' or 'after'
+     * then the element is inserted adjacent to the reference node.
+     *
+     * Parameters:
+     * reference - {Object} the DOM element or id of a DOM element
+     * to append the object relative to
+     * where - {String} where to append the element in relation to the
+     * reference node.  Can be 'top', 'bottom', 'before' or 'after'.
+     * The default is 'bottom'.
+     *
+     * Returns:
+     * the object itself, which is useful for chaining calls together
+     */
+    addTo: function(reference, where) {
+        var el = document.id(this.addable) || document.id(this.domObj);
+        if (el) {
+            if (reference instanceof Jx.Widget && $defined(reference.add)) {
+                reference.add(el);
+            } else {
+                ref = document.id(reference);
+                el.inject(ref,where);
+            }
+            this.fireEvent('addTo',this);
+        }
+        return this;
+    },
 
-	resume: function(){
-		this.startTimer();
-		return this;
-	},
+    /**
+     * APIMethod: toElement
+     * return a DOM element reference for this widget, by default this
+     * returns the local domObj reference.  This is used by the mootools
+     * framework with the document.id() or $() methods allowing you to
+     * manipulate a Jx.Widget sub class as if it were a DOM element.
+     *
+     * (code)
+     * var button = new Jx.Button({label: 'test'});
+     * $(button).inject('someElement');
+     * (end)
+     */
+    toElement: function() {
+        return this.domObj;
+    },
 
-	stopTimer: function(){
-		if (!this.timer) return false;
-		this.time = $time() - this.time;
-		this.timer = $clear(this.timer);
-		return true;
-	},
+    /**
+     * APIMethod: processTemplate
+     * This function pulls the needed elements from a provided template
+     *
+     * Parameters:
+     * template - the template to use in grabbing elements
+     * classes - an array of class names to use in grabbing elements
+     * container - the container to add the template into
+     *
+     * Returns:
+     * a hash object containing the requested Elements keyed by the class
+     * names
+     */
+    processTemplate: function(template,classes,container){
+        var h = new Hash(),
+            element,
+            el;
+        if ($defined(container)){
+            element = container.set('html',template);
+        } else {
+            element = new Element('div',{html:template});
+        }
+        classes.each(function(klass){
+            el = element.getElement('.'+klass);
+            if ($defined(el)){
+                h.set(klass,el);
+            }
+        });
+        return h;
+    },
 
-	startTimer: function(){
-		if (this.timer) return false;
-		this.time = $time() - this.time;
-		this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
-		return true;
-	}
+    /**
+     * Method: generateId
+     * Used to generate a unique ID for Jx Widgets.
+     */
+    generateId: function(prefix){
+        prefix = (prefix) ? prefix : 'jx-';
+        var uid = $uid(this);
+        delete this.uid;
+        return prefix + uid;
+    },
 
-});
+    /**
+     * APIMethod: dispose
+     * remove the widget from the DOM
+     */
+    dispose: function(){
+        var el = document.id(this.addable) || document.id(this.domObj);
+        if (el) {
+            el.dispose();
+        }
+    },
 
-Fx.compute = function(from, to, delta){
-	return (to - from) * delta + from;
-};
+    /**
+     * Method: cleanup
+     * destroy the widget and clean up any potential memory leaks
+     */
+    cleanup: function(){
+        if ($defined(this.domObj)) {
+            this.domObj.eliminate('jxWidget');
+            this.domObj.destroy();
+        }
+        if ($defined(this.addable)) {
+            this.addable.destroy();
+        }
+        if ($defined(this.domA)) {
+            this.domA.destroy();
+        }
+        if ($defined(this.classes)) {
+          this.classes.each(function(v, k) {
+            this[k] = null;
+          }, this);
+        }
+        this.elements.empty();
+        this.elements = null;
+        this.parent();
+    },
 
-Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};
-/*
-Script: Fx.CSS.js
-	Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.
+    /**
+     * Method: render
+     * render the widget, internal function called by the framework.
+     */
+    render: function() {
+        this.elements = this.processElements(this.options.template,
+            this.classes);
+        if ($defined(this.domObj)) {
+          if ( $defined(this.options.id)) {
+            this.domObj.set('id', this.options.id);
+          }
+          //TODO: Should we autogenerate an id when one is not provided? like so...
+          // this.domObj.set('id',this.generateId());
+          this.domObj.store('jxWidget', this);
+        }
+    },
 
-License:
-	MIT-style license.
-*/
+    /**
+     * Property: elements
+     * a hash of elements extracted by processing the widget template
+     */
+    elements: null,
 
-Fx.CSS = new Class({
+    /**
+     * Method: processElements
+     * process the template of the widget and populate the elements hash
+     * with any objects.  Also set any object references based on the classes
+     * hash.
+     */
+    processElements: function(template, classes) {
+        var keys = classes.getValues();
+        elements = this.processTemplate(template, keys);
+        classes.each(function(value, key) {
+            if (key != 'elements' && elements.get(value)) {
+                this[key] = elements.get(value);
+            }
+        }, this);
+        return elements;
+    },
 
-	Extends: Fx,
+    /**
+     * APIMethod: isBusy
+     * indicate if the widget is currently busy or not
+     *
+     * Returns:
+     * {Boolean} true if busy, false otherwise.
+     */
+    isBusy: function() {
+      return this.busy;
+    },
 
-	//prepares the base from/to object
+    /**
+     * APIMethod: setBusy
+     * set the busy state of the widget
+     *
+     * Parameters:
+     * busy         - {Boolean} true to set the widget as busy, false to set it as idle.
+     * message      - {String||Jx Localized Object} (Optional) set a custom message directly
+     *                next to the loading icon. Default is {set:'Jx',key:'widget',value:'busyMessage'}
+     * forceMessage - {Boolean} force displaying a message for larger areas than 60px of height
+     */
+    setBusy: function(state, message, forceMessage) {
+      if (this.busy == state) {
+        return;
+      }
+      var options = this.options,
+          z,
+          size,
+          opts,
+          domObj = this.domObj;
+      message = $defined(message) ? message : {
+        set:'Jx',
+        key:'widget',
+        value:'busyMessage'
+      };
+      forceMessage = $defined(forceMessage) ? forceMessage : false;
+      this.busy = state;
+      this.fireEvent('busy', state);
+      if (state) {
+        if (options.busyClass) {
+          domObj.addClass(options.busyClass);
+        }
+        if (options.busyMask && domObj.spin) {
+          /* put the spinner above the element in the z-index */
+          z = Jx.getNumber(domObj.getStyle('z-index'));
+          opts = {
+            style: {
+              'z-index': z+1
+            }
+          };
+          /* switch to the small size if the element is less than
+           * 60 pixels high
+           */
+          size = domObj.getBorderBoxSize();
+          if (size.height < 60 || forceMessage) {
+            opts['class'] = 'jxSpinner jxSpinnerSmall';
+            opts.img = null;
+            opts.message = new Element('p',{
+              'class':'jxSpinnerMessage',
+              html: '<span class="jxSpinnerImage"></span>'+this.getText(message)
+            });
+          }
+          opts = $merge(options.busyMask, opts);
+          domObj.get('spinner', opts).show(!options.busyMask.fx);
+        }
+      } else {
+        if (options.busyClass) {
+          domObj.removeClass(options.busyClass);
+        }
+        if (options.busyMask && this.domObj.unspin) {
+          domObj.get('spinner').hide(!options.busyMask.fx);
+        }
+      }
+    },
 
-	prepare: function(element, property, values){
-		values = $splat(values);
-		var values1 = values[1];
-		if (!$chk(values1)){
-			values[1] = values[0];
-			values[0] = element.getStyle(property);
-		}
-		var parsed = values.map(this.parse);
-		return {from: parsed[0], to: parsed[1]};
-	},
+    /**
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     *
+     * Parameters:
+     * lang - {string} the language being changed to or that had it's data set of
+     *    translations changed.
+     */
+    changeText: function (lang) {
+        //if the mask is being used then recreate it. The code will pull
+        //the new text automatically
+        if (this.busy) {
+            this.setBusy(false);
+            this.setBusy(true);
+        }
+    },
 
-	//parses a value into an array
+    /**
+     * APIMethod: stack
+     * stack this widget in the z-index of the DOM relative to other stacked
+     * objects.
+     *
+     * Parameters:
+     * el - {DOMElement} optional, the element to stack.  By default, the
+     * element to stack is the one returned by the toElement method which
+     * is typically this.domObj unless the method has been overloaded.
+     */
+    stack: function(el) {
+      Jx.Stack.stack(el || document.id(this));
+    },
 
-	parse: function(value){
-		value = $lambda(value)();
-		value = (typeof value == 'string') ? value.split(' ') : $splat(value);
-		return value.map(function(val){
-			val = String(val);
-			var found = false;
-			Fx.CSS.Parsers.each(function(parser, key){
-				if (found) return;
-				var parsed = parser.parse(val);
-				if ($chk(parsed)) found = {value: parsed, parser: parser};
-			});
-			found = found || {value: val, parser: Fx.CSS.Parsers.String};
-			return found;
-		});
-	},
-
-	//computes by a from and to prepared objects, using their parsers.
-
-	compute: function(from, to, delta){
-		var computed = [];
-		(Math.min(from.length, to.length)).times(function(i){
-			computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
-		});
-		computed.$family = {name: 'fx:css:value'};
-		return computed;
-	},
-
-	//serves the value as settable
-
-	serve: function(value, unit){
-		if ($type(value) != 'fx:css:value') value = this.parse(value);
-		var returned = [];
-		value.each(function(bit){
-			returned = returned.concat(bit.parser.serve(bit.value, unit));
-		});
-		return returned;
-	},
-
-	//renders the change to an element
-
-	render: function(element, property, value, unit){
-		element.setStyle(property, this.serve(value, unit));
-	},
-
-	//searches inside the page css to find the values for a selector
-
-	search: function(selector){
-		if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
-		var to = {};
-		Array.each(document.styleSheets, function(sheet, j){
-			var href = sheet.href;
-			if (href && href.contains('://') && !href.contains(document.domain)) return;
-			var rules = sheet.rules || sheet.cssRules;
-			Array.each(rules, function(rule, i){
-				if (!rule.style) return;
-				var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
-					return m.toLowerCase();
-				}) : null;
-				if (!selectorText || !selectorText.test('^' + selector + '$')) return;
-				Element.Styles.each(function(value, style){
-					if (!rule.style[style] || Element.ShortStyles[style]) return;
-					value = String(rule.style[style]);
-					to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
-				});
-			});
-		});
-		return Fx.CSS.Cache[selector] = to;
-	}
-
+    /**
+     * APIMethod: unstack
+     * remove this widget from the stack.
+     *
+     * Parameters:
+     * el - {DOMElement} optional, the element to unstack.  By default, the
+     * element to unstack is the one returned by the toElement method which
+     * is typically this.domObj unless the method has been overloaded.
+     */
+    unstack: function(el) {
+      Jx.Stack.unstack(el = el || document.id(this));
+    }
 });
 
-Fx.CSS.Cache = {};
 
-Fx.CSS.Parsers = new Hash({
+/**
+ * It seems AIR never returns an XHR that "fails" by not finding the
+ * appropriate file when run in the application sandbox and retrieving a local
+ * file. This affects Jx.ContentLoader in that a "failed" event is never fired.
+ *
+ * To fix this, I've added a timeout that waits about 10 seconds or so in the code above
+ * for the XHR to return, if it hasn't returned at the end of the timeout, we cancel the
+ * XHR and fire the failure event.
+ *
+ * This code only gets added if we're in AIR.
+ */
+if (Jx.isAir){
+    Jx.Widget.implement({
+        /**
+         * Method: checkRequest
+         * Is fired after a delay to check the request to make sure it's not
+         * failing in AIR.
+         */
+        checkRequest: function(){
+            if (this.req.xhr.readyState === 1) {
+                //we still haven't gotten the file. Cancel and fire the
+                //failure
+                $clear(this.reqTimeout);
+                this.req.cancel();
+                this.contentIsLoaded = true;
+                this.fireEvent('contentLoadFailed', this);
+            }
+        }
+    });
+}/*
+---
 
-	Color: {
-		parse: function(value){
-			if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
-			return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
-		},
-		compute: function(from, to, delta){
-			return from.map(function(value, i){
-				return Math.round(Fx.compute(from[i], to[i], delta));
-			});
-		},
-		serve: function(value){
-			return value.map(Number);
-		}
-	},
+name: Jx.Selection
 
-	Number: {
-		parse: parseFloat,
-		compute: Fx.compute,
-		serve: function(value, unit){
-			return (unit) ? value + unit : value;
-		}
-	},
+description: A class to manage selection across multiple list objects
 
-	String: {
-		parse: $lambda(false),
-		compute: $arguments(1),
-		serve: $arguments(0)
-	}
+license: MIT-style license.
 
-});
-/*
-Script: Fx.Tween.js
-	Formerly Fx.Style, effect to transition any CSS property for an element.
+requires:
+ - Jx.Object
 
-License:
-	MIT-style license.
-*/
+provides: [Jx.Selection]
 
-Fx.Tween = new Class({
+...
+ */
+// $Id: selection.js 976 2010-09-02 18:57:12Z pagameba $
+/**
+ * Class: Jx.Selection
+ *
+ * Manage selection of objects.
+ *
+ * Example:
+ * (code)
+ * var selection = new Jx.Selection();
+ * (end)
+ *
+ * Events:
+ * select - fired when an item is added to the selection.  This event may be
+ *    changed by passing the eventToFire option when creating the selection
+ *    object.
+ * unselect - fired when an item is removed from the selection.  This event
+ *    may be changed by passing the eventToFire option when creating the
+ *    selection object.
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
 
-	Extends: Fx.CSS,
 
-	initialize: function(element, options){
-		this.element = this.subject = $(element);
-		this.parent(options);
-	},
+Jx.Selection = new Class({
+    Family: 'Jx.Selection',
+    Extends: Jx.Object,
+    options: {
+        /**
+         * Option: eventToFire
+         * Allows the developer to change the event that is fired in case one
+         * object is using multiple selectionManager instances.  The default
+         * is to use 'select' and 'unselect'.  To modify the event names,
+         * pass different values:
+         * (code)
+         * new Jx.Selection({
+         *   eventToFire: {
+         *     select: 'newSelect',
+         *     unselect: 'newUnselect'
+         *   }
+         * });
+         * (end)
+         */
+        eventToFire: {
+            select: 'select',
+            unselect: 'unselect'
+        },
+        /**
+         * APIProperty: selectClass
+         * the CSS class name to add to the wrapper element when it is
+         * selected
+         */
+        selectClass: 'jxSelected',
+        /**
+         * Option: selectMode
+         * {string} default single.  May be single or multiple.  In single
+         * mode only one item may be selected.  Selecting a new item will
+         * implicitly unselect the currently selected item.
+         */
+        selectMode: 'single',
+        /**
+         * Option: selectToggle
+         * {Boolean} Default true.  Selection of a selected item will unselect
+         * it.
+         */
+        selectToggle: true,
+        /**
+         * Option: minimumSelection
+         * {Integer} Default 0.  The minimum number of items that must be
+         * selected.  If set to a number higher than 0, items added to a list
+         * are automatically selected until this minimum is met.  The user may
+         * not unselect items if unselecting them will drop the total number
+         * of items selected below the minimum.
+         */
+        minimumSelection: 0
+    },
 
-	set: function(property, now){
-		if (arguments.length == 1){
-			now = property;
-			property = this.property || this.options.property;
-		}
-		this.render(this.element, property, now, this.options.unit);
-		return this;
-	},
+    /**
+     * Property: selection
+     * {Array} an array holding the current selection
+     */
+    selection: null,
 
-	start: function(property, from, to){
-		if (!this.check(property, from, to)) return this;
-		var args = Array.flatten(arguments);
-		this.property = this.options.property || args.shift();
-		var parsed = this.prepare(this.element, this.property, args);
-		return this.parent(parsed.from, parsed.to);
-	}
+    /**
+     * Constructor: Jx.Selection
+     * create a new instance of Jx.Selection
+     *
+     * Parameters:
+     * options - {Object} options for the new instance
+     */
+    init: function () {
+        this.selection = [];
+        this.parent();
+    },
 
-});
+    cleanup: function() {
+      this.selection = null;
+      this.parent();
+    },
 
-Element.Properties.tween = {
+    /**
+     * APIMethod: defaultSelect
+     * select an item if the selection does not yet contain the minimum
+     * number of selected items.  Uses <Jx.Selection::select> to select
+     * the item, so the same criteria is applied to the item if it is
+     * to be selected.
+     */
+    defaultSelect: function(item) {
+        if (this.selection.length < this.options.minimumSelection) {
+            this.select(item);
+        }
+    },
 
-	set: function(options){
-		var tween = this.retrieve('tween');
-		if (tween) tween.cancel();
-		return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
-	},
+    /**
+     * APIMethod: select
+     * select an item.
+     *
+     * Parameters:
+     * item - {DOMElement} a DOM element or an element ID.
+     */
+    select: function (item) {
+        var options = this.options,
+            selection = this.selection;
+        item = document.id(item);
+        if (options.selectMode === 'multiple') {
+            if (selection.contains(item)) {
+                this.unselect(item);
+            } else {
+                document.id(item).addClass(options.selectClass);
+                selection.push(item);
+                this.fireEvent(options.eventToFire.select, item);
+            }
+        } else if (options.selectMode == 'single') {
+            if (!this.selection.contains(item)) {
+                document.id(item).addClass(options.selectClass);
+                selection.push(item);
+                if (selection.length > 1) {
+                    this.unselect(selection[0]);
+                }
+                this.fireEvent(options.eventToFire.select, item);
+            } else {
+                if (options.selectToggle) {
+                  this.unselect(item);
+                }
+            }
+        }
+    },
 
-	get: function(options){
-		if (options || !this.retrieve('tween')){
-			if (options || !this.retrieve('tween:options')) this.set('tween', options);
-			this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
-		}
-		return this.retrieve('tween');
-	}
+    /**
+     * APIMethod: unselect
+     * remove an item from the selection.  The item must already be in the
+     * selection.
+     *
+     * Parameters:
+     * item - {DOMElement} a DOM element or an element ID.
+     */
+    unselect: function (item) {
+        var selection = this.selection,
+            options = this.options;
+        if (selection.contains(item) &&
+            selection.length > options.minimumSelection) {
+            document.id(item).removeClass(options.selectClass);
+            selection.erase(item);
+            this.fireEvent(options.eventToFire.unselect, [item, this]);
+        }
+    },
 
-};
+    /**
+     * APIMethod: selected
+     * returns the items in the current selection.
+     *
+     * Returns:
+     * {Array} an array of DOM elements in the current selection
+     */
+    selected: function () {
+        return this.selection;
+    },
 
-Element.implement({
-
-	tween: function(property, from, to){
-		this.get('tween').start(arguments);
-		return this;
-	},
-
-	fade: function(how){
-		var fade = this.get('tween'), o = 'opacity', toggle;
-		how = $pick(how, 'toggle');
-		switch (how){
-			case 'in': fade.start(o, 1); break;
-			case 'out': fade.start(o, 0); break;
-			case 'show': fade.set(o, 1); break;
-			case 'hide': fade.set(o, 0); break;
-			case 'toggle':
-				var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
-				fade.start(o, (flag) ? 0 : 1);
-				this.store('fade:flag', !flag);
-				toggle = true;
-			break;
-			default: fade.start(o, arguments);
-		}
-		if (!toggle) this.eliminate('fade:flag');
-		return this;
-	},
-
-	highlight: function(start, end){
-		if (!end){
-			end = this.retrieve('highlight:original', this.getStyle('background-color'));
-			end = (end == 'transparent') ? '#fff' : end;
-		}
-		var tween = this.get('tween');
-		tween.start('background-color', start || '#ffff88', end).chain(function(){
-			this.setStyle('background-color', this.retrieve('highlight:original'));
-			tween.callChain();
-		}.bind(this));
-		return this;
-	}
-
-});
-/*
-Script: Fx.Morph.js
-	Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.
-
-License:
-	MIT-style license.
-*/
-
-Fx.Morph = new Class({
-
-	Extends: Fx.CSS,
-
-	initialize: function(element, options){
-		this.element = this.subject = $(element);
-		this.parent(options);
-	},
-
-	set: function(now){
-		if (typeof now == 'string') now = this.search(now);
-		for (var p in now) this.render(this.element, p, now[p], this.options.unit);
-		return this;
-	},
-
-	compute: function(from, to, delta){
-		var now = {};
-		for (var p in from) now[p] = this.parent(from[p], to[p], delta);
-		return now;
-	},
-
-	start: function(properties){
-		if (!this.check(properties)) return this;
-		if (typeof properties == 'string') properties = this.search(properties);
-		var from = {}, to = {};
-		for (var p in properties){
-			var parsed = this.prepare(this.element, p, properties[p]);
-			from[p] = parsed.from;
-			to[p] = parsed.to;
-		}
-		return this.parent(from, to);
-	}
-
-});
-
-Element.Properties.morph = {
-
-	set: function(options){
-		var morph = this.retrieve('morph');
-		if (morph) morph.cancel();
-		return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
-	},
-
-	get: function(options){
-		if (options || !this.retrieve('morph')){
-			if (options || !this.retrieve('morph:options')) this.set('morph', options);
-			this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
-		}
-		return this.retrieve('morph');
-	}
-
-};
-
-Element.implement({
-
-	morph: function(props){
-		this.get('morph').start(props);
-		return this;
-	}
-
-});
-/*
-Script: Fx.Transitions.js
-	Contains a set of advanced transitions to be used with any of the Fx Classes.
-
-License:
-	MIT-style license.
-
-Credits:
-	Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
-*/
-
-Fx.implement({
-
-	getTransition: function(){
-		var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
-		if (typeof trans == 'string'){
-			var data = trans.split(':');
-			trans = Fx.Transitions;
-			trans = trans[data[0]] || trans[data[0].capitalize()];
-			if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
-		}
-		return trans;
-	}
-
-});
-
-Fx.Transition = function(transition, params){
-	params = $splat(params);
-	return $extend(transition, {
-		easeIn: function(pos){
-			return transition(pos, params);
-		},
-		easeOut: function(pos){
-			return 1 - transition(1 - pos, params);
-		},
-		easeInOut: function(pos){
-			return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
-		}
-	});
-};
-
-Fx.Transitions = new Hash({
-
-	linear: $arguments(0)
-
-});
-
-Fx.Transitions.extend = function(transitions){
-	for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
-};
-
-Fx.Transitions.extend({
-
-	Pow: function(p, x){
-		return Math.pow(p, x[0] || 6);
-	},
-
-	Expo: function(p){
-		return Math.pow(2, 8 * (p - 1));
-	},
-
-	Circ: function(p){
-		return 1 - Math.sin(Math.acos(p));
-	},
-
-	Sine: function(p){
-		return 1 - Math.sin((1 - p) * Math.PI / 2);
-	},
-
-	Back: function(p, x){
-		x = x[0] || 1.618;
-		return Math.pow(p, 2) * ((x + 1) * p - x);
-	},
-
-	Bounce: function(p){
-		var value;
-		for (var a = 0, b = 1; 1; a += b, b /= 2){
-			if (p >= (7 - 4 * a) / 11){
-				value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
-				break;
-			}
-		}
-		return value;
-	},
-
-	Elastic: function(p, x){
-		return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
-	}
-
-});
-
-['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
-	Fx.Transitions[transition] = new Fx.Transition(function(p){
-		return Math.pow(p, [i + 2]);
-	});
-});
-/*
-Script: Request.js
-	Powerful all purpose Request Class. Uses XMLHTTPRequest.
-
-License:
-	MIT-style license.
-*/
-
-var Request = new Class({
-
-	Implements: [Chain, Events, Options],
-
-	options: {/*
-		onRequest: $empty,
-		onComplete: $empty,
-		onCancel: $empty,
-		onSuccess: $empty,
-		onFailure: $empty,
-		onException: $empty,*/
-		url: '',
-		data: '',
-		headers: {
-			'X-Requested-With': 'XMLHttpRequest',
-			'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
-		},
-		async: true,
-		format: false,
-		method: 'post',
-		link: 'ignore',
-		isSuccess: null,
-		emulation: true,
-		urlEncoded: true,
-		encoding: 'utf-8',
-		evalScripts: false,
-		evalResponse: false,
-		noCache: false
-	},
-
-	initialize: function(options){
-		this.xhr = new Browser.Request();
-		this.setOptions(options);
-		this.options.isSuccess = this.options.isSuccess || this.isSuccess;
-		this.headers = new Hash(this.options.headers);
-	},
-
-	onStateChange: function(){
-		if (this.xhr.readyState != 4 || !this.running) return;
-		this.running = false;
-		this.status = 0;
-		$try(function(){
-			this.status = this.xhr.status;
-		}.bind(this));
-		if (this.options.isSuccess.call(this, this.status)){
-			this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
-			this.success(this.response.text, this.response.xml);
-		} else {
-			this.response = {text: null, xml: null};
-			this.failure();
-		}
-		this.xhr.onreadystatechange = $empty;
-	},
-
-	isSuccess: function(){
-		return ((this.status >= 200) && (this.status < 300));
-	},
-
-	processScripts: function(text){
-		if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
-		return text.stripScripts(this.options.evalScripts);
-	},
-
-	success: function(text, xml){
-		this.onSuccess(this.processScripts(text), xml);
-	},
-
-	onSuccess: function(){
-		this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
-	},
-
-	failure: function(){
-		this.onFailure();
-	},
-
-	onFailure: function(){
-		this.fireEvent('complete').fireEvent('failure', this.xhr);
-	},
-
-	setHeader: function(name, value){
-		this.headers.set(name, value);
-		return this;
-	},
-
-	getHeader: function(name){
-		return $try(function(){
-			return this.xhr.getResponseHeader(name);
-		}.bind(this));
-	},
-
-	check: function(){
-		if (!this.running) return true;
-		switch (this.options.link){
-			case 'cancel': this.cancel(); return true;
-			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
-		}
-		return false;
-	},
-
-	send: function(options){
-		if (!this.check(options)) return this;
-		this.running = true;
-
-		var type = $type(options);
-		if (type == 'string' || type == 'element') options = {data: options};
-
-		var old = this.options;
-		options = $extend({data: old.data, url: old.url, method: old.method}, options);
-		var data = options.data, url = options.url, method = options.method;
-
-		switch ($type(data)){
-			case 'element': data = $(data).toQueryString(); break;
-			case 'object': case 'hash': data = Hash.toQueryString(data);
-		}
-
-		if (this.options.format){
-			var format = 'format=' + this.options.format;
-			data = (data) ? format + '&' + data : format;
-		}
-
-		if (this.options.emulation && ['put', 'delete'].contains(method)){
-			var _method = '_method=' + method;
-			data = (data) ? _method + '&' + data : _method;
-			method = 'post';
-		}
-
-		if (this.options.urlEncoded && method == 'post'){
-			var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
-			this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
-		}
-
-		if(this.options.noCache) {
-			var noCache = "noCache=" + new Date().getTime();
-			data = (data) ? noCache + '&' + data : noCache;
-		}
-
-
-		if (data && method == 'get'){
-			url = url + (url.contains('?') ? '&' : '?') + data;
-			data = null;
-		}
-
-
-		this.xhr.open(method.toUpperCase(), url, this.options.async);
-
-		this.xhr.onreadystatechange = this.onStateChange.bind(this);
-
-		this.headers.each(function(value, key){
-			try {
-				this.xhr.setRequestHeader(key, value);
-			} catch (e){
-				this.fireEvent('exception', [key, value]);
-			}
-		}, this);
-
-		this.fireEvent('request');
-		this.xhr.send(data);
-		if (!this.options.async) this.onStateChange();
-		return this;
-	},
-
-	cancel: function(){
-		if (!this.running) return this;
-		this.running = false;
-		this.xhr.abort();
-		this.xhr.onreadystatechange = $empty;
-		this.xhr = new Browser.Request();
-		this.fireEvent('cancel');
-		return this;
-	}
-
-});
-
-(function(){
-
-var methods = {};
-['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
-	methods[method] = function(){
-		var params = Array.link(arguments, {url: String.type, data: $defined});
-		return this.send($extend(params, {method: method.toLowerCase()}));
-	};
-});
-
-Request.implement(methods);
-
-})();/*
-Script: Request.HTML.js
-	Extends the basic Request Class with additional methods for interacting with HTML responses.
-
-License:
-	MIT-style license.
-*/
-
-Request.HTML = new Class({
-
-	Extends: Request,
-
-	options: {
-		update: false,
-		append: false,
-		evalScripts: true,
-		filter: false
-	},
-
-	processHTML: function(text){
-		var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
-		text = (match) ? match[1] : text;
-
-		var container = new Element('div');
-
-		return $try(function(){
-			var root = '<root>' + text + '</root>', doc;
-			if (Browser.Engine.trident){
-				doc = new ActiveXObject('Microsoft.XMLDOM');
-				doc.async = false;
-				doc.loadXML(root);
-			} else {
-				doc = new DOMParser().parseFromString(root, 'text/xml');
-			}
-			root = doc.getElementsByTagName('root')[0];
-			if (!root) return;
-			for (var i = 0, k = root.childNodes.length; i < k; i++){
-				var child = Element.clone(root.childNodes[i], true, true);
-				if (child) container.grab(child);
-			}
-			return container;
-		}) || container.set('html', text);
-	},
-
-	success: function(text){
-		var options = this.options, response = this.response;
-
-		response.html = text.stripScripts(function(script){
-			response.javascript = script;
-		});
-
-		var temp = this.processHTML(response.html);
-
-		response.tree = temp.childNodes;
-		response.elements = temp.getElements('*');
-
-		if (options.filter) response.tree = response.elements.filter(options.filter);
-		if (options.update) $(options.update).empty().set('html', response.html);
-		else if (options.append) $(options.append).adopt(temp.getChildren());
-		if (options.evalScripts) $exec(response.javascript);
-
-		this.onSuccess(response.tree, response.elements, response.html, response.javascript);
-	}
-
-});
-
-Element.Properties.send = {
-
-	set: function(options){
-		var send = this.retrieve('send');
-		if (send) send.cancel();
-		return this.eliminate('send').store('send:options', $extend({
-			data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
-		}, options));
-	},
-
-	get: function(options){
-		if (options || !this.retrieve('send')){
-			if (options || !this.retrieve('send:options')) this.set('send', options);
-			this.store('send', new Request(this.retrieve('send:options')));
-		}
-		return this.retrieve('send');
-	}
-
-};
-
-Element.Properties.load = {
-
-	set: function(options){
-		var load = this.retrieve('load');
-		if (load) load.cancel();
-		return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
-	},
-
-	get: function(options){
-		if (options || ! this.retrieve('load')){
-			if (options || !this.retrieve('load:options')) this.set('load', options);
-			this.store('load', new Request.HTML(this.retrieve('load:options')));
-		}
-		return this.retrieve('load');
-	}
-
-};
-
-Element.implement({
-
-	send: function(url){
-		var sender = this.get('send');
-		sender.send({data: this, url: url || sender.options.url});
-		return this;
-	},
-
-	load: function(){
-		this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
-		return this;
-	}
-
-});
-/*
-Script: Request.JSON.js
-	Extends the basic Request Class with additional methods for sending and receiving JSON data.
-
-License:
-	MIT-style license.
-*/
-
-Request.JSON = new Class({
-
-	Extends: Request,
-
-	options: {
-		secure: true
-	},
-
-	initialize: function(options){
-		this.parent(options);
-		this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
-	},
-
-	success: function(text){
-		this.response.json = JSON.decode(text, this.options.secure);
-		this.onSuccess(this.response.json, text);
-	}
-
-});
-MooTools.More = {
-	'version': '1.2.2.1'
-};/*
-Script: MooTools.Lang.js
-	Provides methods for localization.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-*/
-
-(function(){
-
-	var data = {
-		language: 'en-US',
-		languages: {
-			'en-US': {}
-		},
-		cascades: ['en-US']
-	};
-	
-	var cascaded;
-
-	MooTools.lang = new Events();
-
-	$extend(MooTools.lang, {
-
-		setLanguage: function(lang){
-			if (!data.languages[lang]) return this;
-			data.language = lang;
-			this.load();
-			this.fireEvent('langChange', lang);
-			return this;
-		},
-
-		load: function() {
-			var langs = this.cascade(this.getCurrentLanguage());
-			cascaded = {};
-			$each(langs, function(set, setName){
-				cascaded[setName] = this.lambda(set);
-			}, this);
-		},
-
-		getCurrentLanguage: function(){
-			return data.language;
-		},
-
-		addLanguage: function(lang){
-			data.languages[lang] = data.languages[lang] || {};
-			return this;
-		},
-
-		cascade: function(lang){
-			var cascades = (data.languages[lang] || {}).cascades || [];
-			cascades.combine(data.cascades);
-			cascades.erase(lang).push(lang);
-			var langs = cascades.map(function(lng){
-				return data.languages[lng];
-			}, this);
-			return $merge.apply(this, langs);
-		},
-
-		lambda: function(set) {
-			(set || {}).get = function(key, args){
-				return $lambda(set[key]).apply(this, $splat(args));
-			};
-			return set;
-		},
-
-		get: function(set, key, args){
-			if (cascaded && cascaded[set]) return (key ? cascaded[set].get(key, args) : cascaded[set]);
-		},
-
-		set: function(lang, set, members){
-			this.addLanguage(lang);
-			langData = data.languages[lang];
-			if (!langData[set]) langData[set] = {};
-			$extend(langData[set], members);
-			if (lang == this.getCurrentLanguage()){
-				this.load();
-				this.fireEvent('langChange', lang);
-			}
-			return this;
-		},
-
-		list: function(){
-			return Hash.getKeys(data.languages);
-		}
-
-	});
-
-})();/*
-Script: Log.js
-	Provides basic logging functionality for plugins to implement.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Guillermo Rauch
-*/
-
-var Log = new Class({
-	
-	log: function(){
-		Log.logger.call(this, arguments);
-	}
-	
-});
-
-Log.logged = [];
-
-Log.logger = function(){
-	if(window.console && console.log) console.log.apply(console, arguments);
-	else Log.logged.push(arguments);
-};/*
-Script: Class.Refactor.js
-	Extends a class onto itself with new property, preserving any items attached to the class's namespace.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-*/
-
-Class.refactor = function(original, refactors){
-
-	$each(refactors, function(item, name){
-		var origin = original.prototype[name];
-		if (origin && (origin = origin._origin) && typeof item == 'function') original.implement(name, function(){
-			var old = this.previous;
-			this.previous = origin;
-			var value = item.apply(this, arguments);
-			this.previous = old;
-			return value;
-		}); else original.implement(name, item);
-	});
-
-	return original;
-
-};/*
-Script: Class.Binds.js
-	Automagically binds specified methods in a class to the instance of the class.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-*/
-
-Class.Mutators.Binds = function(binds){
-    return binds;
-};
-
-Class.Mutators.initialize = function(initialize){
-	return function(){
-		$splat(this.Binds).each(function(name){
-			var original = this[name];
-			if (original) this[name] = original.bind(this);
-		}, this);
-		return initialize.apply(this, arguments);
-	};
-};/*
-Script: Class.Occlude.js
-	Prevents a class from being applied to a DOM element twice.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-*/
-
-Class.Occlude = new Class({
-
-	occlude: function(property, element){
-		element = $(element || this.element);
-		var instance = element.retrieve(property || this.property);
-		if (instance && !$defined(this.occluded)){
-			this.occluded = instance;
-		} else {
-			this.occluded = false;
-			element.store(property || this.property, this);
-		}
-		return this.occluded;
-	}
-
+    /**
+     * APIMethod: isSelected
+     * test if an item is in the current selection.
+     *
+     * Parameters:
+     * item - {DOMElement} a DOM element or an element ID.
+     *
+     * Returns:
+     * {Boolean} true if the current selection contains the item, false
+     * otherwise
+     */
+    isSelected: function(item) {
+        return this.selection.contains(item);
+    }
 });/*
-Script: Chain.Wait.js
-	Adds a method to inject pauses between chained events.
+---
 
-	License:
-		MIT-style license.
+name: Jx.List
 
-	Authors:
-		Aaron Newton
-*/
+description: A class that is used to manage lists of DOM elements
 
-(function(){
+license: MIT-style license.
 
-	var wait = {
-		wait: function(duration){
-			return this.chain(function(){
-				this.callChain.delay($pick(duration, 500), this);
-			}.bind(this));
-		}
-	};
+requires:
+ - Jx.Object
+ - Jx.Selection
 
-	Chain.implement(wait);
+provides: [Jx.List]
 
-	if (window.Fx){
-		Fx.implement(wait);
-		['Css', 'Tween', 'Elements'].each(function(cls){
-			if (Fx[cls]) Fx[cls].implement(wait);
-		});
-	}
+...
+ */
+// $Id: list.js 976 2010-09-02 18:57:12Z pagameba $
+/**
+ * Class: Jx.List
+ *
+ * Manage a list of DOM elements and provide an API and events for managing
+ * those items within a container.  Works with Jx.Selection to manage
+ * selection of items in the list.  You have two options for managing
+ * selections.  The first, and default, option is to specify select: true
+ * in the constructor options and any of the <Jx.Selection> options as well.
+ * This will create a default Jx.Selection object to manage selections.  The
+ * second option is to pass a Jx.Selection object as the third constructor
+ * argument.  This allows sharing selection between multiple lists.
+ *
+ * Example:
+ * (code)
+ * var list = new Jx.List('container',{
+ *   hover: true,
+ *   select: true,
+ *   onSelect: function(el) {
+ *     alert(el.get('html'));
+ *   }
+ * });
+ * list.add(new Element('li', {html:'1'}));
+ * list.add(new Element('li', {html:'2'}));
+ * list.add(new Element('li', {html:'3'}));
+ *
+ * (end)
+ *
+ * Events:
+ * add - fired when an item is added
+ * remove - fired when an item is removed
+ * mouseenter - fired when the user mouses over an element
+ * mouseleave - fired when the user mouses out of an element
+ * select - fired when an item is selected
+ * unselect - fired when an item is selected
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.List = new Class({
+    Family: 'Jx.List',
+    Extends: Jx.Object,
+    /**
+     * Constructor: Jx.List
+     * create a new instance of Jx.List
+     *
+     * Parameters:
+     * container - {Mixed} an element reference or id of an element that will
+     * contain the items in the list
+     * options - {Object} an object containing optional parameters
+     * selection - {<Jx.Selection>} null or a Jx.Selection object. If the
+     * select option is set to true, then list will use this selection object
+     * to track selections or create its own if no selection object is
+     * supplied.
+     */
+    parameters: ['container', 'options', 'selection'],
+    /* does this object own the selection object (and should clean it up) */
+    ownsSelection: false,
+    /**
+     * APIProperty: container
+     * the element that will contain items as they are added
+     */
+    container: null,
+    /**
+     * APIProperty: selection
+     * <Jx.Selection> a selection object if selection is enabled
+     */
+    selection: null,
+    options: {
+        /**
+         * Option: items
+         * an array of items to add to the list right away
+         */
+        items: null,
+        /**
+         * Option: hover
+         * {Boolean} default false.  If set to true, the wrapper element will
+         * obtain the defined hoverClass if set and mouseenter/mouseleave
+         * events will be emitted when the user hovers over and out of elements
+         */
+        hover: false,
+        /**
+         * Option: hoverClass
+         * the CSS class name to add to the wrapper element when the mouse is
+         * over an item
+         */
+        hoverClass: 'jxHover',
 
-	try {
-		Element.implement({
-			chains: function(effects){
-				$splat($pick(effects, ['tween', 'morph', 'reveal'])).each(function(effect){
-					effect = this.get(effect);
-					if (!effect) return;
-					effect.setOptions({
-						link:'chain'
-					});
-				}, this);
-				return this;
-			},
-			pauseFx: function(duration, effect){
-				this.chains(effect).get($pick(effect, 'tween')).wait(duration);
-				return this;
-			}
-		});
-	} catch(e){}
+        /**
+         * Option: press
+         * {Boolean} default false.  If set to true, the wrapper element will
+         * obtain the defined pressClass if set and mousedown/mouseup
+         * events will be emitted when the user clicks on elements
+         */
+        press: false,
+        /**
+         * Option: pressedClass
+         * the CSS class name to add to the wrapper element when the mouse is
+         * down on an item
+         */
+        pressClass: 'jxPressed',
 
-})();/*
-Script: Array.Extras.js
-	Extends the Array native object to include useful methods to work with arrays.
+        /**
+         * Option: select
+         * {Boolean} default false.  If set to true, the wrapper element will
+         * obtain the defined selectClass if set and select/unselect events
+         * will be emitted when items are selected and unselected.  For other
+         * selection objects, see <Jx.Selection>
+         */
+        select: false
+    },
 
-	License:
-		MIT-style license.
+    /**
+     * Method: init
+     * internal method to initialize this object
+     */
+    init: function() {
+        this.container = document.id(this.options.container);
+        this.container.store('jxList', this);
 
-	Authors:
-		Christoph Pojer
+        var target = this,
+            options = this.options,
+            isEnabled = function(el) {
+                var item = el.retrieve('jxListTargetItem') || el;
+                return !item.hasClass('jxDisabled');
+            },
+            isSelectable = function(el) {
+                var item = el.retrieve('jxListTargetItem') || el;
+                return !item.hasClass('jxUnselectable');
+            };
+        this.bound = $merge(this.bound, {
+            mousedown: function() {
+                if (isEnabled(this)) {
+                    this.addClass(options.pressClass);
+                    target.fireEvent('mousedown', this, target);
+                }
+            },
+            mouseup: function() {
+                if (isEnabled(this)) {
+                    this.removeClass(options.pressClass);
+                    target.fireEvent('mouseup', this, target);
+                }
+            },
+            mouseenter: function() {
+                if (isEnabled(this)) {
+                    this.addClass(options.hoverClass);
+                    target.fireEvent('mouseenter', this, target);
+                }
+            },
+            mouseleave: function() {
+                if (isEnabled(this)) {
+                    this.removeClass(options.hoverClass);
+                    target.fireEvent('mouseleave', this, target);
+                }
+            },
+            keydown: function(e) {
+                if (e.key == 'enter' && isEnabled(this)) {
+                    this.addClass('jxPressed');
+                }
+            },
+            keyup: function(e) {
+                if (e.key == 'enter' && isEnabled(this)) {
+                    this.removeClass('jxPressed');
+                }
+            },
+            click: function (e) {
+                if (target.selection &&
+                    isEnabled(this) &&
+                    isSelectable(this)) {
+                    target.selection.select(this, target);
+                }
+                target.fireEvent('click', this, target);
+            },
+            select: function(item) {
+                if (isEnabled(item)) {
+                    var itemTarget = item.retrieve('jxListTargetItem') || item;
+                    target.fireEvent('select', itemTarget);
+                }
+            },
+            unselect: function(item) {
+                if (isEnabled(item)) {
+                    var itemTarget = item.retrieve('jxListTargetItem') || item;
+                    target.fireEvent('unselect', itemTarget);
+                }
+            },
+            contextmenu: function(e) {
+              var cm = this.retrieve('jxContextMenu');
+              if (cm) {
+                cm.show(e);
+                this.removeClass(options.pressClass);
+              }
+              e.stop();
+            }
+        });
 
-*/
-Array.implement({
+        if (options.selection) {
+            this.setSelection(options.selection);
+            options.select = true;
+        } else if (options.select) {
+            this.selection = new Jx.Selection(options);
+            this.ownsSelection = true;
+        }
 
-	min: function(){
-		return Math.min.apply(null, this);
-	},
+        if ($defined(options.items)) {
+            this.add(options.items);
+        }
+    },
 
-	max: function(){
-		return Math.max.apply(null, this);
-	},
+    /**
+     * Method: cleanup
+     * destroy the list and release anything it references
+     */
+    cleanup: function() {
+        this.container.getChildren().each(function(item){
+            this.remove(item);
+        }, this);
+        if (this.selection && this.ownsSelection) {
+            this.selection.removeEvents();
+            this.selection.destroy();
+        }
+        this.setSelection(null);
+        this.container.eliminate('jxList');
+        var bound = this.bound;
+        bound.mousedown=null;
+        bound.mouseup=null;
+        bound.mouseenter=null;
+        bound.mouseleave=null;
+        bound.keydown=null;
+        bound.keyup=null;
+        bound.click=null;
+        bound.select=null;
+        bound.unselect=null;
+        bound.contextmenu=null;
+        this.parent();
+    },
 
-	average: function(){
-		return this.length ? this.sum() / this.length : 0;
-	},
+    /**
+     * APIMethod: add
+     * add an item to the list of items at the specified position
+     *
+     * Parameters:
+     * item - {mixed} the object to add, a DOM element or an
+     * object that provides a getElement method.  An array of items may also
+     * be provided.  All items are inserted sequentially at the indicated
+     * position.
+     * position - {mixed} optional, the position to add the element, either
+     * an integer position in the list or another item to place this item
+     * after
+     */
+    add: function(item, position) {
+        if (Jx.type(item) == 'array') {
+            item.each(function(what){
+              this.add(what, position);
+            }.bind(this) );
+            return;
+        }
+        /* the element being wrapped */
+        var el = document.id(item),
+            target = el.retrieve('jxListTarget') || el,
+            bound = this.bound,
+            options = this.options,
+            container = this.container;
+        if (target) {
+            target.store('jxListTargetItem', el);
+            target.addEvents({
+              contextmenu: this.bound.contextmenu
+            });
+            if (options.press && options.pressClass) {
+                target.addEvents({
+                    mousedown: bound.mousedown,
+                    mouseup: bound.mouseup,
+                    keyup: bound.keyup,
+                    keydown: bound.keydown
+                });
+            }
+            if (options.hover && options.hoverClass) {
+                target.addEvents({
+                    mouseenter: bound.mouseenter,
+                    mouseleave: bound.mouseleave
+                });
+            }
+            if (this.selection) {
+                target.addEvents({
+                    click: bound.click
+                });
+            }
+            if ($defined(position)) {
+                if ($type(position) == 'number') {
+                    if (position < container.childNodes.length) {
+                        el.inject(container.childNodes[position],'before');
+                    } else {
+                        el.inject(container, 'bottom');
+                    }
+                } else if (container.hasChild(position)) {
+                    el.inject(position,'after');
+                }
+                this.fireEvent('add', item, this);
+            } else {
+                el.inject(container, 'bottom');
+                this.fireEvent('add', item, this);
+            }
+            if (this.selection) {
+                this.selection.defaultSelect(el);
+            }
+        }
+    },
+    /**
+     * APIMethod: remove
+     * remove an item from the list of items
+     *
+     * Parameters:
+     * item - {mixed} the item to remove or the index of the item to remove.
+     * An array of items may also be provided.
+     *
+     * Returns:
+     * {mixed} the item that was removed or null if the item is not a member
+     * of this list.
+     */
+    remove: function(item) {
+        var el = document.id(item),
+            target;
+        if (el && this.container.hasChild(el)) {
+            this.unselect(el, true);
+            el.dispose();
+            target = el.retrieve('jxListTarget') || el;
+            target.removeEvents(this.bound);
+            this.fireEvent('remove', item, this);
+            return item;
+        }
+        return null;
+    },
+    /**
+     * APIMethod: replace
+     * replace one item with another
+     *
+     * Parameters:
+     * item - {mixed} the item to replace or the index of the item to replace
+     * withItem - {mixed} the object, DOM element, Jx.Object or an object
+     * implementing getElement to add
+     *
+     * Returns:
+     * {mixed} the item that was removed
+     */
+    replace: function(item, withItem) {
+        if (this.container.hasChild(item)) {
+            this.add(withItem, item);
+            this.remove(item);
+        }
+    },
+    /**
+     * APIMethod: indexOf
+     * find the index of an item in the list
+     *
+     * Parameters:
+     * item - {mixed} the object, DOM element, Jx.Object or an object
+     * implementing getElement to find the index of
+     *
+     * Returns:
+     * {integer} the position of the item or -1 if not found
+     */
+    indexOf: function(item) {
+        return $A(this.container.childNodes).indexOf(item);
+    },
+    /**
+     * APIMethod: count
+     * returns the number of items in the list
+     */
+    count: function() {
+        return this.container.childNodes.length;
+    },
+    /**
+     * APIMethod: items
+     * returns an array of the items in the list
+     */
+    items: function() {
+        return $A(this.container.childNodes);
+    },
+    /**
+     * APIMethod: each
+     * applies the supplied function to each item
+     *
+     * Parameters:
+     * func - {function} the function to apply, it will receive the item and
+     * index of the item as parameters
+     * context - {object} the context to execute the function in, null by
+     * default.
+     */
+    each: function(f, context) {
+        $A(this.container.childNodes).each(f, context);
+    },
+    /**
+     * APIMethod: select
+     * select an item
+     *
+     * Parameters:
+     * item - {mixed} the object to select, a DOM element, a Jx.Object, or an
+     * object that provides a getElement method.  An array of items may also be
+     * provided.
+     */
+    select: function(item) {
+        if (this.selection) {
+            this.selection.select(item);
+        }
+    },
+    /**
+     * APIMethod: unselect
+     * unselect an item or items
+     *
+     * Parameters:
+     * item - {mixed} the object to select, a DOM element, a Jx.Object, or an
+     * object that provides a getElement method.  An array of elements may also
+     * be provided.
+     * force - {Boolean} force deselection even if this violates the minimum
+     * selection constraint (used internally when removing items)
+     */
+    unselect: function(item, force) {
+        if (this.selection) {
+            this.selection.unselect(item);
+        }
+    },
+    /**
+     * APIMethod: selected
+     * returns the selected item or items
+     *
+     * Returns:
+     * {mixed} the selected item or an array of selected items
+     */
+    selected: function() {
+        return this.selection ? this.selection.selected : [];
+    },
+    /**
+     * APIMethod: empty
+     * clears all of the items from the list
+     */
+    empty: function(){
+        this.container.getChildren().each(function(item){
+            this.remove(item);
+        }, this);
+    },
+    /**
+     * APIMethod: setSelection
+     * sets the <Jx.Selection> object that this list will use for selection
+     * events.
+     *
+     * Parameters:
+     * {<Jx.Selection>} the selection object, or null to remove it.
+     */
+    setSelection: function(selection) {
+        var sel = this.selection;
+        if (sel == selection) return;
 
-	sum: function(){
-		var result = 0, l = this.length;
-		if (l){
-			do {
-				result += this[--l];
-			} while (l);
-		}
-		return result;
-	},
+        if (sel) {
+            sel.removeEvents(this.bound);
+            if (this.ownsSelection) {
+                sel.destroy();
+                this.ownsSelection = false;
+            }
+        }
 
-	unique: function(){
-		return [].combine(this);
-	}
+        this.selection = selection;
+        if (selection) {
+            selection.addEvents({
+                select: this.bound.select,
+                unselect: this.bound.unselect
+            });
+        }
+    }
 
 });/*
-Script: Date.js
-	Extends the Date native object to include methods useful in managing dates.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Stack
 
-	Authors:
-		Aaron Newton
-		Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/
-		Harald Kirshner - mail [at] digitarald.de; http://digitarald.de
+description: A singleton object for managing a global z-index stack for widgets that need to order themselves in the z-index of the page relative to other such widgets.
 
-*/
+license: MIT-style license.
 
-(function(){
+requires:
+ - Jx
 
-new Native({name: 'Date', initialize: Date, protect: true});
+provides: [Jx.Stack]
 
-['now','parse','UTC'].each(function(method){
-	Native.genericize(Date, method, true);
-});
+...
+ */
+/**
+ * Class: Jx.Stack
+ * Manage the zIndex of widgets
+ *
+ * This is a singleton and should be called directly, like so:
+ *
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2010 Paul Spencer
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Stack = new(new Class({
+  /**
+   * Property: els
+   * {Array} the elements in the stack
+   */
+  els: [],
 
-Date.Methods = {};
+  /**
+   * Property: base
+   * {Integer} the base z-index value of the first element in the stack
+   */
+  base: 1000,
 
-['Date', 'Day', 'FullYear', 'Hours', 'Milliseconds', 'Minutes', 'Month', 'Seconds', 'Time', 'TimezoneOffset',
-	'Week', 'Timezone', 'GMTOffset', 'DayOfYear', 'LastMonth', 'UTCDate', 'UTCDay', 'UTCFullYear',
-	'AMPM', 'UTCHours', 'UTCMilliseconds', 'UTCMinutes', 'UTCMonth', 'UTCSeconds'].each(function(method){
-	Date.Methods[method.toLowerCase()] = method;
-});
+  /**
+   * Property: increment
+   * {Integer} the amount to increment the z-index between elements of the
+   * stack
+   */
+  increment: 100,
 
-$each({
-	ms: 'Milliseconds',
-	year: 'FullYear',
-	min: 'Minutes',
-	mo: 'Month',
-	sec: 'Seconds',
-	hr: 'Hours'
-}, function(value, key){
-	Date.Methods[key] = value;
-});
+  /**
+   * APIMethod: stack
+   * push an element onto the stack and set its z-index appropriately
+   *
+   * Parameters:
+   * el - {DOMElement} a DOM element to push on the stack
+   */
+  stack: function(el) {
+    this.unstack(el);
+    this.els.push(el);
+    this.setZIndex(el, this.els.length-1);
+  },
 
-var zeroize = function(what, length){
-	return '0'.repeat(length - what.toString().length) + what;
-};
+  /**
+   * APIMethod: unstack
+   * pull an element off the stack and reflow the z-index of the remaining
+   * elements in the stack if necessary
+   *
+   * Parameters:
+   * el - {DOMElement} the DOM element to pull off the stack
+   */
+  unstack: function(el) {
+    var elements = this.els;
+    if (elements.contains(el)) {
+      el.setStyle('z-index', '');
+      var idx = elements.indexOf(el);
+      elements.erase(el);
+      for (var i=idx; i<elements.length; i++) {
+        this.setZIndex(elements[i], i);
+      }
+    }
+  },
 
-Date.implement({
+  /**
+   * Method: setZIndex
+   * set the z-index of an element based on its position in the stack
+   *
+   * Parameters:
+   * el - {DOMElement} the element to set the z-index for
+   * idx - {Integer} optional, the index to assume for this object
+   */
+  setZIndex: function(obj, idx) {
+    idx = idx || this.els.indexOf(obj);
+    if (idx !== false) {
+      document.id(obj).setStyle('z-index', this.base + (idx*this.increment));
+    }
+  }
 
-	set: function(prop, value){
-		switch ($type(prop)){
-			case 'object':
-				for (var p in prop) this.set(p, prop[p]);
-				break;
-			case 'string':
-				prop = prop.toLowerCase();
-				var m = Date.Methods;
-				if (m[prop]) this['set' + m[prop]](value);
-		}
-		return this;
-	},
+}))();/*
+name: Locale.German
 
-	get: function(key){
-		key = key.toLowerCase();
-		var m = Date.Methods;
-		if (m[key]) return this['get' + m[key]]();
-		return null;
-	},
+description: Default translations of text strings used in JX for German (Germany) (de-DE)
 
-	clone: function(){
-		return new Date(this.get('time'));
-	},
+license: MIT-style license.
 
-	increment: function(interval, times){
-		return this.multiply(interval, times);
-	},
+requires:
+ - More/Lang
 
-	decrement: function(interval, times){
-		return this.multiply(interval, times, false);
-	},
+provides: [Locale.German]
 
-	multiply: function(interval, times, increment){
-		interval = interval || 'day';
-		times = $pick(times, 1);
-		increment = $pick(increment, true);
-		var multiplier = increment ? 1 : -1;
-		var month = this.format('%m').toInt() - 1;
-		var year = this.format('%Y').toInt();
-		var time = this.get('time');
-		var offset = 0;
-		switch (interval) {
-				case 'year':
-					times.times(function(val) {
-						if (Date.isLeapYear(year+val) && month > 1 && multiplier > 0) val++;
-						if (Date.isLeapYear(year+val) && month <= 1 && multiplier < 0) val--;
-						offset += Date.units.year(year+val);
-					});
-					break;
-				case 'month':
-					times.times(function(val){
-						if (multiplier < 0) val++;
-						var mo = month+(val * multiplier);
-						var year = year;
-						if (mo < 0) {
-							year--;
-							mo = 12+mo;
-						}
-						if (mo > 11 || mo < 0) {
-							year += (mo / 12).toInt() * multiplier;
-							mo = mo % 12;
-						}
-						offset += Date.units.month(mo, year);
-					});
-					break;
-				case 'day':
-					return this.set('date', this.get('date')+(multiplier*times));
-				default:
-					offset = Date.units[interval]() * times;
-					break;
-		}
-		this.set('time', time + (offset * multiplier));
-		return this;
-	},
+...
+ */
 
-	isLeapYear: function(){
-		return Date.isLeapYear(this.get('year'));
-	},
-
-	clearTime: function(){
-		['hr', 'min', 'sec', 'ms'].each(function(t){
-			this.set(t, 0);
-		}, this);
-		return this;
-	},
-
-	diff: function(d, resolution){
-		resolution = resolution || 'day';
-		if ($type(d) == 'string') d = Date.parse(d);
-		switch (resolution){
-			case 'year':
-				return d.format('%Y').toInt() - this.format('%Y').toInt();
-				break;
-			case 'month':
-				var months = (d.format('%Y').toInt() - this.format('%Y').toInt())*12;
-				return months + d.format('%m').toInt() - this.format('%m').toInt();
-				break;
-			default:
-				var diff = d.get('time') - this.get('time');
-				if (diff < 0 && Date.units[resolution]() > (-1*(diff))) return 0;
-				else if (diff >= 0 && diff < Date.units[resolution]()) return 0;
-				return ((d.get('time') - this.get('time')) / Date.units[resolution]()).round();
-		}
-		return null;
-	},
-
-	getWeek: function(){
-		var day = (new Date(this.get('year'), 0, 1)).get('date');
-		return Math.round((this.get('dayofyear') + (day > 3 ? day - 4 : day + 3)) / 7);
-	},
-
-	getTimezone: function(){
-		return this.toString()
-			.replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/, '$1')
-			.replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, '$1$2$3');
-	},
-
-	getGMTOffset: function(){
-		var off = this.get('timezoneOffset');
-		return ((off > 0) ? '-' : ' + ')
-			+ zeroize(Math.floor(Math.abs(off) / 60), 2)
-			+ zeroize(off % 60, 2);
-	},
-
-	parse: function(str){
-		this.set('time', Date.parse(str));
-		return this;
-	},
-
-	isValid: function(date) {
-		return !!(date || this).valueOf();
-	},
-
-	format: function(f){
-		if (!this.isValid()) return 'invalid date';
-		f = f || '%x %X';
-		//replace short-hand with actual format
-		f = ({
-			db: '%Y-%m-%d %H:%M:%S',
-			compact: '%Y%m%dT%H%M%S',
-			iso8601: '%Y-%m-%dT%H:%M:%S%T',
-			rfc822: '%a, %d %b %Y %H:%M:%S %Z',
-			'short': '%d %b %H:%M',
-			'long': '%B %d, %Y %H:%M'
-		})[f.toLowerCase()] || f;
-		var d = this;
-		return f.replace(/\%([aAbBcdHIjmMpSUWwxXyYTZ\%])/g,
-			function($1, $2){
-				switch ($2){
-					case 'a': return Date.getMsg('days')[d.get('day')].substr(0, 3);
-					case 'A': return Date.getMsg('days')[d.get('day')];
-					case 'b': return Date.getMsg('months')[d.get('month')].substr(0, 3);
-					case 'B': return Date.getMsg('months')[d.get('month')];
-					case 'c': return d.toString();
-					case 'd': return zeroize(d.get('date'), 2);
-					case 'H': return zeroize(d.get('hr'), 2);
-					case 'I': return ((d.get('hr') % 12) || 12);
-					case 'j': return zeroize(d.get('dayofyear'), 3);
-					case 'm': return zeroize((d.get('mo') + 1), 2);
-					case 'M': return zeroize(d.get('min'), 2);
-					case 'p': return Date.getMsg(d.get('hr') < 12 ? 'AM' : 'PM');
-					case 'S': return zeroize(d.get('seconds'), 2);
-					case 'U': return zeroize(d.get('week'), 2);
-					case 'W': throw new Error('%W is not supported yet');
-					case 'w': return d.get('day');
-					case 'x': return d.format(Date.getMsg('shortDate'));
-					case 'X': return d.format(Date.getMsg('shortTime'));
-					case 'y': return d.get('year').toString().substr(2);
-					case 'Y': return d.get('year');
-					case 'T': return d.get('GMTOffset');
-					case 'Z': return d.get('Timezone');
-					case '%': return '%';
-				}
-				return $2;
-			}
-		);
-	},
-
-	setAMPM: function(ampm){
-		ampm = ampm.toUpperCase();
-		if (this.format('%H').toInt() > 11 && ampm == 'AM')
-			return this.decrement('hour', 12);
-		else if (this.format('%H').toInt() < 12 && ampm == 'PM')
-			return this.increment('hour', 12);
-		return this;
-	}
-
+MooTools.lang.set('de-DE', 'Date', {
+  // need to overwrite 'M&auml;rz' to 'März' for jx.select fields
+  months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember']
 });
 
-Date.alias('diff', 'compare');
-Date.alias('format', 'strftime');
+MooTools.lang.set('de-DE', 'Jx', {
 
-var nativeParse = Date.parse;
-
-var daysInMonth = function(monthIndex, year){
-	if (Date.isLeapYear(year.toInt()) && monthIndex === 1) return 29;
-	return [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][monthIndex];
-};
-
-
-$extend(Date, {
-
-	getMsg: function(key, args) {
-		return MooTools.lang.get('Date', key, args);
+	'widget': {
+		busyMessage: 'Arbeite ...'
 	},
-
-	units: {
-		ms: $lambda(1),
-		second: $lambda(1000),
-		minute: $lambda(60000),
-		hour: $lambda(3600000),
-		day: $lambda(86400000),
-		week: $lambda(608400000),
-		month: function(monthIndex, year){
-			var d = new Date();
-			return daysInMonth($pick(monthIndex,d.format('%m').toInt()), $pick(year,d.format('%Y').toInt())) * 86400000;
-		},
-		year: function(year){
-			year = year || new Date().format('%Y').toInt();
-			return Date.isLeapYear(year.toInt()) ? 31622400000 : 31536000000;
-		}
+	'colorpalette': {
+		alphaLabel: 'alpha (%)'
 	},
-
-	isLeapYear: function(yr){
-		return new Date(yr , 1, 29).getDate() == 29;
+	notice: {
+		closeTip: 'Notiz schließen'
 	},
-
-	fixY2K: function(d){
-		if (!isNaN(d)){
-			var newDate = new Date(d);
-			if (newDate.get('year') < 2000 && d.toString().indexOf(newDate.get('year')) < 0) newDate.increment('year', 100);
-			return newDate;
-		} else {
-			return d;
-		}
+	progressbar: {
+		messageText: 'Lade...',
+		progressText: '{progress} von {total}'
 	},
-
-	parse: function(from){
-		var t = $type(from);
-		if (t == 'number') return new Date(from);
-		if (t != 'string') return from;
-		if (!from.length) return null;
-		var parsed;
-		Date.parsePatterns.each(function(pattern, i){
-			if (parsed) return;
-			var r = pattern.re.exec(from);
-			if (r) parsed = pattern.handler(r);
-		});
-		return parsed || new Date(nativeParse(from));
+	field: {
+		requiredText: '*'
 	},
-
-	parseDay: function(day, num){
-		var ret = -1;
-		switch ($type(day)){
-			case 'number':
-				ret = Date.getMsg('days')[day - 1] || false;
-				if (!ret) throw new Error('Invalid day index value must be between 1 and 7');
-				break;
-			case 'string':
-				var match = Date.getMsg('days').filter(function(name){
-					return this.test(name);
-				}, new RegExp('^' + day, 'i'));
-				if (!match.length) throw new Error('Invalid day string');
-				if (match.length > 1) throw new Error('Ambiguous day');
-				ret = match[0];
-		}
-		return (num) ? Date.getMsg('days').indexOf(ret) : ret;
+	file: {
+		browseLabel: 'Durchsuchen...'
 	},
-
-	parseMonth: function(month, num){
-		var ret = -1;
-		switch ($type(month)){
-			case 'object':
-				ret = Date.getMsg('months')[month.get('mo')];
-				break;
-			case 'number':
-				ret = Date.getMsg('months')[month - 1] || false;
-				if (!ret) throw new Error('Invalid month index value must be between 1 and 12:' + index);
-				break;
-			case 'string':
-				var match = Date.getMsg('months').filter(function(name){
-					return this.test(name);
-				}, new RegExp('^' + month, 'i'));
-				if (!match.length) throw new Error('Invalid month string');
-				if (match.length > 1) throw new Error('Ambiguous month');
-				ret = match[0];
-		}
-		return (num) ? Date.getMsg('months').indexOf(ret) : ret;
+	'formatter.boolean': {
+		'true': 'Ja',
+		'false': 'Nein'
 	},
-
-	parseUTC: function(value){
-		var localDate = new Date(value);
-		var utcSeconds = Date.UTC(localDate.get('year'), localDate.get('mo'),
-		localDate.get('date'), localDate.get('hr'), localDate.get('min'), localDate.get('sec'));
-		return new Date(utcSeconds);
+	'formatter.currency': {
+		sign: '€'
 	},
-
-	orderIndex: function(unit){
-		return Date.getMsg('dateOrder').indexOf(unit) + 1;
+	'formatter.number': {
+		decimalSeparator: ',',
+    thousandsSeparator: '.'
 	},
-
-	parsePatterns: [
-		{
-			//"1999-12-31"
-			re: /^(\d{4})[\.\-\/](\d{1,2})[\.\-\/](\d{1,2})$/,
-			handler: function(bits){
-				return new Date(bits[1], bits[2] - 1, bits[3]);
-			}
-		},
-		{
-			//"1999-12-31 23:59:59"
-			re: /^(\d{4})[\.\-\/](\d{1,2})[\.\-\/](\d{1,2})\s(\d{1,2}):(\d{1,2})(?:\:(\d{1,2}))?(\w{2})?$/,
-			handler: function(bits){
-				var d = new Date(bits[1], bits[2] - 1, bits[3]);
-				d.set('hr', bits[4]);
-				d.set('min', bits[5]);
-				d.set('sec', bits[6] || 0);
-				if (bits[7]) d.set('ampm', bits[7]);
-				return d;
-			}
-		},
-		{
-			//"12.31.08", "12-31-08", "12/31/08", "12.31.2008", "12-31-2008", "12/31/2008"
-			re: /^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})$/,
-			handler: function(bits){
-				var d = new Date(bits[Date.orderIndex('year')],
-								 bits[Date.orderIndex('month')] - 1,
-								 bits[Date.orderIndex('date')]);
-				return Date.fixY2K(d);
-			}
-		},
-		//"12.31.08", "12-31-08", "12/31/08", "12.31.2008", "12-31-2008", "12/31/2008"
-		//above plus "10:45pm" ex: 12.31.08 10:45pm
-		{
-			re: /^(\d{1,2})[\.\-\/](\d{1,2})[\.\-\/](\d{2,4})\s(\d{1,2})[:\.](\d{1,2})(?:[\:\.](\d{1,2}))?(\w{2})?$/,
-			handler: function(bits){
-				var d = new Date(bits[Date.orderIndex('year')],
-								 bits[Date.orderIndex('month')] - 1,
-								 bits[Date.orderIndex('date')]);
-				d.set('hr', bits[4]);
-				d.set('min', bits[5]);
-				d.set('sec', bits[6] || 0);
-				if (bits[7]) d.set('ampm', bits[7]);
-				return Date.fixY2K(d);
-			}
-		}
-	]
-
-});
-
-})();/*
-Script: Date.Extras.js
-	Extends the Date native object to include extra methods (on top of those in Date.js).
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-
-*/
-
-['LastDayOfMonth', 'Ordinal'].each(function(method){
-	Date.Methods[method.toLowerCase()] = method;
-});
-
-
-Date.implement({
-
-	timeDiffInWords: function(relative_to){
-		return Date.distanceOfTimeInWords(this, relative_to || new Date);
+	splitter: {
+		barToolTip: 'Ziehen Sie diese Leiste um die Größe zu verändern'
 	},
-
-	getOrdinal: function(dayOfMonth){
-		return Date.getMsg('ordinal', dayOfMonth || this.get('date'));
+	panelset: {
+		barToolTip: 'Ziehen Sie diese Leiste um die Größe zu verändern'
 	},
-
-	getDayOfYear: function(){
-		return ((Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 1, 0, 0, 0)
-			- Date.UTC(this.getFullYear(), 0, 1, 0, 0, 0) ) / Date.units.day());
+	panel: {
+        collapseTooltip: 'Panel ein-/ausklappen', //colB
+        collapseLabel: 'Einklappen',  //colM
+        expandLabel: 'Ausklappen', //colM
+        maximizeTooltip: 'Panel maximieren',
+        maximizeLabel: 'maximieren',
+        restoreTooltip: 'Panel wieder herstellen', //maxB
+        restoreLabel: 'wieder herstellen', //maxM
+        closeTooltip: 'Panel schließen', //closeB
+        closeLabel: 'Schließen' //closeM
 	},
-
-	getLastDayOfMonth: function(){
-		var ret = this.clone();
-		ret.setMonth(ret.getMonth() + 1, 0);
-		return ret.getDate();
-	}
-
-});
-
-Date.alias('timeDiffInWords', 'timeAgoInWords');
-
-$extend(Date, {
-
-	distanceOfTimeInWords: function(fromTime, toTime){
-		return this.getTimePhrase(((toTime.getTime() - fromTime.getTime()) / 1000).toInt(), fromTime, toTime);
+	confirm: {
+		affirmativeLabel: 'Ja',
+    negativeLabel: 'Nein'
 	},
-
-	getTimePhrase: function(delta, fromTime, toTime){
-		var getPhrase = function(){
-			var suffix;
-			if (delta >= 0){
-				suffix = 'Ago';
-			} else {
-				delta = delta * -1;
-				suffix = 'Until';
-			}
-			if (delta < 60){
-				return Date.getMsg('lessThanMinute' + suffix, delta);
-			} else if (delta < 120){
-				return Date.getMsg('minute' + suffix, delta);
-			} else if (delta < (45 * 60)){
-				delta = (delta / 60).round();
-				return Date.getMsg('minutes' + suffix, delta);
-			} else if (delta < (90 * 60)){
-				return Date.getMsg('hour' + suffix, delta);
-			} else if (delta < (24 * 60 * 60)){
-				delta = (delta / 3600).round();
-				return Date.getMsg('hours' + suffix, delta);
-			} else if (delta < (48 * 60 * 60)){
-				return Date.getMsg('day' + suffix, delta);
-			} else {
-				delta = (delta / 86400).round();
-				return Date.getMsg('days' + suffix, delta);
-			}
-		};
-		return getPhrase().substitute({delta: delta});
-	}
-
-});
-
-
-Date.parsePatterns.extend([
-
-	{
-		// yyyy-mm-ddTHH:MM:SS-0500 (ISO8601) i.e.2007-04-17T23:15:22Z
-		// inspired by: http://delete.me.uk/2005/03/iso8601.html
-		re: /^(\d{4})(?:-?(\d{2})(?:-?(\d{2})(?:[T ](\d{2})(?::?(\d{2})(?::?(\d{2})(?:\.(\d+))?)?)?(?:Z|(?:([-+])(\d{2})(?::?(\d{2}))?)?)?)?)?)?$/,
-		handler: function(bits){
-			var offset = 0;
-			var d = new Date(bits[1], 0, 1);
-			if (bits[3]) d.set('date', bits[3]);
-			if (bits[2]) d.set('mo', bits[2] - 1);
-			if (bits[4]) d.set('hr', bits[4]);
-			if (bits[5]) d.set('min', bits[5]);
-			if (bits[6]) d.set('sec', bits[6]);
-			if (bits[7]) d.set('ms', ('0.' + bits[7]).toInt() * 1000);
-			if (bits[9]){
-				offset = (bits[9].toInt() * 60) + bits[10].toInt();
-				offset *= ((bits[8] == '-') ? 1 : -1);
-			}
-			//offset -= d.getTimezoneOffset();
-			d.setTime((d * 1) + (offset * 60 * 1000).toInt());
-			return d;
-		}
+	dialog: {
+		label: 'Neues Fenster'
 	},
-
-	{
-		//"today"
-		re: /^tod/i,
-		handler: function(){
-			return new Date();
-		}
+	message: {
+		okButton: 'Ok'
 	},
-
-	{
-		//"tomorow"
-		re: /^tom/i,
-		handler: function(){
-			return new Date().increment();
-		}
+	prompt: {
+		okButton: 'Ok',
+		cancelButton: 'Abbrechen'
 	},
-
-	{
-		//"yesterday"
-		re: /^yes/i,
-		handler: function(){
-			return new Date().decrement();
-		}
+	upload: {
+		buttonText: 'Dateien hochladen'
 	},
-
-	{
-		//4th, 23rd
-		re: /^(\d{1,2})(st|nd|rd|th)?$/i,
-		handler: function(bits){
-			var d = new Date();
-			d.set('date', bits[1].toInt());
-			return d;
-		}
+	'plugin.resize': {
+	  tooltip: 'Klicken um Größe zu verändern. Doppelklick für automatische Anpassung.'
 	},
-
-	{
-		//4th Jan, 23rd May
-		re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+)$/i,
-		handler: function(bits){
-			var d = new Date();
-			d.set('mo', Date.parseMonth(bits[2], true), bits[1].toInt());
-			return d;
-		}
-	},
-
-	{
-		//4th Jan 2000, 23rd May 2004
-		re: /^(\d{1,2})(?:st|nd|rd|th)? (\w+),? (\d{4})$/i,
-		handler: function(bits){
-			var d = new Date();
-			d.set('mo', Date.parseMonth(bits[2], true), bits[1].toInt());
-			d.setYear(bits[3]);
-			return d;
-		}
-	},
-
-	{
-		//Jan 4th
-		re: /^(\w+) (\d{1,2})(?:st|nd|rd|th)?,? (\d{4})$/i,
-		handler: function(bits){
-			var d = new Date();
-			d.set('mo', Date.parseMonth(bits[1], true), bits[2].toInt());
-			d.setYear(bits[3]);
-			return d;
-		}
-	},
-
-	{
-		//Jan 4th 2003
-		re: /^next (\w+)$/i,
-		handler: function(bits){
-			var d = new Date();
-			var day = d.getDay();
-			var newDay = Date.parseDay(bits[1], true);
-			var addDays = newDay - day;
-			if (newDay <= day){
-				addDays += 7;
-			}
-			d.set('date', d.getDate() + addDays);
-			return d;
-		}
-	},
-
-	{
-		//4 May 08:12
-		re: /^\d+\s[a-zA-z]..\s\d.\:\d.$/,
-		handler: function(bits){
-			var d = new Date();
-			bits = bits[0].split(' ');
-			d.set('date', bits[0]);
-			var m;
-			Date.getMsg('months').each(function(mo, i){
-				if (new RegExp('^' + bits[1]).test(mo)) m = i;
-			});
-			d.set('mo', m);
-			d.set('hr', bits[2].split(':')[0]);
-			d.set('min', bits[2].split(':')[1]);
-			d.set('ms', 0);
-			return d;
-		}
-	},
-
-	{
-		re: /^last (\w+)$/i,
-		handler: function(bits){
-			return Date.parse('next ' + bits[0]).decrement('day', 7);
-		}
-	}
-
-]);/*
-Script: Hash.Extras.js
-	Extends the Hash native object to include getFromPath which allows a path notation to child elements.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-*/
-
-Hash.implement({
-
-	getFromPath: function(notation){
-		var source = this.getClean();
-		notation.replace(/\[([^\]]+)\]|\.([^.[]+)|[^[.]+/g, function(match){
-			if (!source) return null;
-			var prop = arguments[2] || arguments[1] || arguments[0];
-			source = (prop in source) ? source[prop] : null;
-			return match;
-		});
-		return source;
-	},
-
-	cleanValues: function(method){
-		method = method || $defined;
-		this.each(function(v, k){
-			if (!method(v)) this.erase(k);
-		}, this);
-		return this;
-	},
-
-	run: function(){
-		var args = arguments;
-		this.each(function(v, k){
-			if ($type(v) == 'function') v.run(args);
-		});
-	}
-
+  'plugin.editor': {
+    submitButton: 'Speichern',
+    cancelButton: 'Abbrechen'
+  }
+});/*
+---
+
+name: Locale.Russian
+
+description: Default translations of text strings used in JX for Russia (Russia) (ru-RU)
+
+license: MIT-style license.
+
+requires:
+ - More/Lang
+
+provides: [Locale.Russian]
+
+...
+ */
+MooTools.lang.set('ru-RU-unicode', 'Jx', {
+	
+	'widget': {
+		busyMessage: 'Обработка...'
+	},
+	'colorpalette': {
+		alphaLabel: 'alpha (%)'
+	},
+	notice: {
+		closeTip: 'закрыть Ñ?то Ñ?ообщение'
+	},
+	progressbar: {
+		messageText: 'Загрузка...',
+		progressText: '{progress} из {total}'
+	},
+	field: {
+		requiredText: '*'
+	},
+	file: {
+		browseLabel: 'Выбрать...'
+	},
+	'formatter.boolean': {
+		'true': 'Да',
+		'false': 'Ð?ет'
+	},
+	'formatter.currency': {
+		sign: 'Ñ€.'
+	},
+	'formatter.number': {
+		decimalSeparator: ',',
+    thousandsSeparator: ' '
+	},
+	splitter: {
+		barToolTip: 'потÑ?ни, чтобы изменить размер'
+	},
+	panelset: {
+		barToolTip: 'потÑ?ни, чтобы изменить размер'
+	},
+	panel: {
+		collapseTooltip: 'Свернуть/Развернуть Панель',
+    collapseLabel: 'Свернуть',
+    expandLabel: 'Развернуть',
+    maximizeTooltip: 'Увеличить Панель',
+    maximizeLabel: 'Увеличить',
+    restoreTooltip: 'ВоÑ?Ñ?тановить Панель',
+    restoreLabel: 'ВоÑ?Ñ?тановить',
+    closeTooltip: 'Закрыть Панель',
+    closeLabel: 'Закрыть'
+	},
+	confirm: {
+		affirmativeLabel: 'Да',
+    negativeLabel: 'Ð?ет'
+	},
+	dialog: {
+		resizeToolTip: 'Изменить размер'
+	},
+	message: {
+		okButton: 'Ок'
+	},
+	prompt: {
+		okButton: 'Ок',
+		cancelButton: 'Отмена'
+	},
+	upload: {
+		buttonText: 'Загрузка файла'
+	},
+	'plugin.resize': {
+	  tooltip: 'ПотÑ?ни, чтобы изменить, двойной щелчок длÑ? авто размера.'
+	},
+  'plugin.editor': {
+    submitButton: 'Сохранить',
+    cancelButton: 'Отмена'
+  }
 });/*
-Script: String.Extras.js
-	Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc).
+---
 
-	License:
-		MIT-style license.
+name: Jx.Record
 
-	Authors:
-		Aaron Newton
-		Guillermo Rauch
+description: The basic record implementation. A store uses records to handle and manipulate data.
 
-*/
+license: MIT-style license.
 
-(function(){
-  
-var special = ['À','à','�','á','Â','â','Ã','ã','Ä','ä','Ã…','Ã¥','Ä‚','ă','Ä„','Ä…','Ć','ć','ÄŒ','�','Ç','ç', 'ÄŽ','�','�','Ä‘', 'È','è','É','é','Ê','ê','Ë','ë','Äš','Ä›','Ę','Ä™', 'Äž','ÄŸ','ÃŒ','ì','�','í','ÃŽ','î','�','ï', 'Ĺ','ĺ','Ľ','ľ','�','Å‚', 'Ñ','ñ','Ň','ň','Ã…Æ’','Å„','Ã’','ò','Ó','ó','Ãâ€�','ô','Õ','õ','Ö','ö','Ø','ø','Å‘','Ã…Ëœ','Ã…â„¢','Ã…â€
 �','ŕ','Š','š','Ş','ş','Ś','ś', 'Ť','ť','Ť','ť','Ţ','ţ','Ù','ù','Ú','ú','Û','û','Ü','ü','Ů','ů', 'Ÿ','ÿ','ý','�','Ž','ž','Ź','ź','Ż','ż', 'Þ','þ','�','ð','ß','Œ','œ','Æ','æ','µ'];
+requires:
+ - Jx.Object
 
-var standard = ['A','a','A','a','A','a','A','a','Ae','ae','A','a','A','a','A','a','C','c','C','c','C','c','D','d','D','d', 'E','e','E','e','E','e','E','e','E','e','E','e','G','g','I','i','I','i','I','i','I','i','L','l','L','l','L','l', 'N','n','N','n','N','n', 'O','o','O','o','O','o','O','o','Oe','oe','O','o','o', 'R','r','R','r', 'S','s','S','s','S','s','T','t','T','t','T','t', 'U','u','U','u','U','u','Ue','ue','U','u','Y','y','Y','y','Z','z','Z','z','Z','z','TH','th','DH','dh','ss','OE','oe','AE','ae','u'];
+provides: [Jx.Record]
 
-var tidymap = {
-	"[\xa0\u2002\u2003\u2009]": " ",
-	"\xb7": "*",
-	"[\u2018\u2019]": "'",
-	"[\u201c\u201d]": '"',
-	"\u2026": "...",
-	"\u2013": "-",
-	"\u2014": "--",
-	"\uFFFD": "&raquo;"
-};
+...
+ */
+// $Id: record.js 977 2010-09-02 18:57:42Z pagameba $
+/**
+ * Class: Jx.Record
+ *
+ * Extends: <Jx.Object>
+ *
+ * This class is used as a representation (or container) for a single row
+ * of data in a <Jx.Store>. It is not usually directly instantiated by the
+ * developer but rather by the store itself.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Record = new Class({
 
-String.implement({
+    Extends: Jx.Object,
+    Family: 'Jx.Record',
 
-	standardize: function(){
-		var text = this;
-		special.each(function(ch, i){
-			text = text.replace(new RegExp(ch, 'g'), standard[i]);
-		});
-		return text;
-	},
+    options: {
+        /**
+         * Option: separator
+         * The separator to pass to the comparator
+         * constructor (<Jx.Compare>) - defaults to '.'
+         */
+        separator : '.',
 
-	repeat: function(times){
-		return new Array(times + 1).join(this);
-	},
+        primaryKey: null
+    },
+    /**
+     * Property: data
+     * The data for this record
+     */
+    data: null,
+    /**
+     * Property: state
+     * used to determine the state of this record. When not null (meaning no
+     * changes were made) this should be one of
+     *
+     * - Jx.Record.UPDATE
+     * - Jx.Record.DELETE
+     * - Jx.Record.INSERT
+     */
+    state: null,
+    /**
+     * Property: columns
+     * Holds a reference to the columns for this record. These are usually
+     * passed to the record from the store. This should be an array of objects
+     * where the objects represent the columns. The object should take the form:
+     *
+     * (code)
+     * {
+     *     name: <column name>,
+     *     type: <column type>,
+     *     ..additional options required by the record implementation...
+     * }
+     * (end)
+     *
+     * The type of the column should be one of alphanumeric, numeric, date,
+     * boolean, or currency.
+     */
+    columns: null,
 
-	pad: function(length, str, dir){
-		if (this.length >= length) return this;
-		str = str || ' ';
-		var pad = str.repeat(length - this.length).substr(0, length - this.length);
-		if (!dir || dir == 'right') return this + pad;
-		if (dir == 'left') return pad + this;
-		return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil());
-	},
+    parameters: ['store', 'columns', 'data', 'options'],
 
-	stripTags: function(){
-		return this.replace(/<\/?[^>]+>/gi, '');
-	},
+    init: function () {
+        this.parent();
+        if ($defined(this.options.columns)) {
+            this.columns = this.options.columns;
+        }
 
-	tidy: function(){
-		var txt = this.toString();
-		$each(tidymap, function(value, key){
-			txt = txt.replace(new RegExp(key, 'g'), value);
-		});
-		return txt;
-	}
+        if ($defined(this.options.data)) {
+            this.processData(this.options.data);
+        } else {
+            this.data = new Hash();
+        }
 
-});
+        if ($defined(this.options.store)) {
+            this.store = this.options.store;
+        }
 
-})();/*
-Script: String.QueryString.js
-	...
+    },
+    /**
+     * APIMethod: get
+     * returns the value of the requested column. Can be programmed to handle
+     * pseudo-columns (such as the primaryKey column implemented in this base
+     * record).
+     *
+     * Parameters:
+     * column - the string, index, or object of the requested column
+     */
+    get: function (column) {
+        var type = Jx.type(column);
+        if (type !== 'object') {
+            if (column === 'primaryKey') {
+                column = this.resolveCol(this.options.primaryKey);
+            } else {
+                column = this.resolveCol(column);
+            }
+        }
+        if (this.data.has(column.name)) {
+            return this.data.get(column.name);
+        } else {
+            return null;
+        }
+    },
+    /**
+     * APIMethod: set
+     * Sets a given value into the requested column.
+     *
+     *  Parameters:
+     *  column - the object, index, or string name of the target column
+     *  data - the data to add to the column
+     */
+    set: function (column, data) {
+        var type = Jx.type(column),
+            oldValue;
+        if (type !== 'object') {
+            column = this.resolveCol(column);
+        }
 
-	License:
-		MIT-style license.
+        if (!$defined(this.data)) {
+            this.data = new Hash();
+        }
 
-	Authors:
-		Sebastian Markbåge, Aaron Newton, Lennart Pilon, Valerio Proietti
-*/
-String.implement({
+        oldValue = this.get(column);
+        this.data.set(column.name, data);
+        this.state = Jx.Record.UPDATE;
+        return [column.name, oldValue, data];
+        //this.store.fireEvent('storeColumnChanged', [this, column.name, oldValue, data]);
 
-	parseQueryString: function(){
-		var vars = this.split(/[&;]/), res = {};
-		if (vars.length) vars.each(function(val){
-			var index = val.indexOf('='),
-				keys = index < 0 ? [''] : val.substr(0, index).match(/[^\]\[]+/g),
-				value = decodeURIComponent(val.substr(index + 1)),
-				obj = res;
-			keys.each(function(key, i){
-				var current = obj[key];
-				if(i < keys.length - 1)
-					obj = obj[key] = current || {};
-				else if($type(current) == 'array')
-					current.push(value);
-				else
-					obj[key] = $defined(current) ? [current, value] : value;
-			});
-		});
-		return res;
-	},
+    },
+    /**
+     * APIMethod: equals
+     * Compares the value of a particular column with a given value
+     *
+     * Parameters:
+     * column - the column to compare with (either column name or index)
+     * value - the value to compare to.
+     *
+     * Returns:
+     * True | False depending on the outcome of the comparison.
+     */
+    equals: function (column, value) {
+        if (column === 'primaryKey') {
+            column = this.resolveCol(this.options.primaryKey);
+        } else {
+            column = this.resolveCol(column);
+        }
+        if (!this.data.has(column.name)) {
+            return null;
+        } else {
+            if (!$defined(this.comparator)) {
+                this.comparator = new Jx.Compare({
+                    separator : this.options.separator
+                });
+            }
+            var fn = this.comparator[column.type].bind(this.comparator);
+            return (fn(this.get(column), value) === 0);
+        }
+    },
+    /**
+     * Method: processData
+     * This method takes the data passed in and puts it into the form the
+     * record needs it in. This default implementation does nothing but
+     * assign the data to the data property but it can be overridden in
+     * subclasses to massge the data in any way needed.
+     *
+     * Parameters:
+     * data - the data to process
+     */
+    processData: function (data) {
+        this.data = $H(data);
+    },
 
-	cleanQueryString: function(method){
-		return this.split('&').filter(function(val){
-			var index = val.indexOf('='),
-			key = index < 0 ? '' : val.substr(0, index),
-			value = val.substr(index + 1);
-			return method ? method.run([key, value]) : $chk(value);
-		}).join('&');
-	}
-
-});/*
-Script: URI.js
-	Provides methods useful in managing the window location and uris.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Sebastian Markbåge, Aaron Newton
-*/
-
-var URI = new Class({
-
-	Implements: Options,
-
-	/*
-	options: {
-		base: false
-	},
-	*/
-
-	regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/,
-	parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'],
-	schemes: { http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0 },
-
-	initialize: function(uri, options){
-		this.setOptions(options);
-		var base = this.options.base || URI.base;
-		uri = uri || base;
-		if (uri && uri.parsed)
-			this.parsed = $unlink(uri.parsed);
-		else
-			this.set('value', uri.href || uri.toString(), base ? new URI(base) : false);
-	},
-
-	parse: function(value, base){
-		var bits = value.match(this.regex);
-		if (!bits) return false;
-		bits.shift();
-		return this.merge(bits.associate(this.parts), base);
-	},
-
-	merge: function(bits, base){
-		if (!bits.scheme && !base.scheme) return false;
-		if (base){
-			this.parts.every(function(part){
-				if (bits[part]) return false;
-				bits[part] = base[part] || '';
-				return true;
-			});
-		}
-		bits.port = bits.port || this.schemes[bits.scheme.toLowerCase()];
-		bits.directory = bits.directory ? this.parseDirectory(bits.directory, base ? base.directory : '') : '/';
-		return bits;
-	},
-
-	parseDirectory: function(directory, baseDirectory) {
-		directory = (directory.substr(0, 1) == '/' ? '' : (baseDirectory || '/')) + directory;
-		if (!directory.test(URI.regs.directoryDot)) return directory;
-		var result = [];
-		directory.replace(URI.regs.endSlash, '').split('/').each(function(dir){
-			if (dir == '..' && result.length > 0) result.pop();
-			else if (dir != '.') result.push(dir);
-		});
-		return result.join('/') + '/';
-	},
-
-	combine: function(bits){
-		return bits.value || bits.scheme + '://' +
-			(bits.user ? bits.user + (bits.password ? ':' + bits.password : '') + '@' : '') +
-			(bits.host || '') + (bits.port && bits.port != this.schemes[bits.scheme] ? ':' + bits.port : '') +
-			(bits.directory || '/') + (bits.file || '') +
-			(bits.query ? '?' + bits.query : '') +
-			(bits.fragment ? '#' + bits.fragment : '');
-	},
-
-	set: function(part, value, base){
-		if (part == 'value'){
-			var scheme = value.match(URI.regs.scheme);
-			if (scheme) scheme = scheme[1];
-			if (scheme && !$defined(this.schemes[scheme.toLowerCase()])) this.parsed = { scheme: scheme, value: value };
-			else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value });
-		} else {
-			this.parsed[part] = value;
-		}
-		return this;
-	},
-
-	get: function(part, base){
-		switch(part){
-			case 'value': return this.combine(this.parsed, base ? base.parsed : false);
-			case 'data' : return this.getData();
-		}
-		return this.parsed[part] || undefined;
-	},
-
-	go: function(){
-		document.location.href = this.toString();
-	},
-
-	toURI: function(){
-		return this;
-	},
-
-	getData: function(key, part){
-		var qs = this.get(part || 'query');
-		if (!$chk(qs)) return key ? null : {};
-		var obj = qs.parseQueryString();
-		return key ? obj[key] : obj;
-	},
-
-	setData: function(values, merge, part){
-		if ($type(arguments[0]) == 'string'){ 
-			values = this.getData(); 
-			values[arguments[0]] = arguments[1]; 
-		} else if (merge) {
-			values = $merge(this.getData(), values);
-		}
-		return this.set(part || 'query', Hash.toQueryString(values));
-	},
-
-	clearData: function(part){
-		return this.set(part || 'query', '');
-	}
-
+    /**
+     * Method: resolveCol
+     * Determines which column is being asked for and returns it.
+     *
+     * Parameters:
+     * col - a number referencing a column in the store
+     *
+     * Returns:
+     * the column object referred to
+     */
+    resolveCol : function (col) {
+        var t = Jx.type(col);
+        if (t === 'number') {
+            col = this.columns[col];
+        } else if (t === 'string') {
+            this.columns.each(function (column) {
+                if (column.name === col) {
+                    col = column;
+                }
+            }, this);
+        }
+        return col;
+    },
+    /**
+     * APIMethod: asHash
+     * Returns the data for this record as a Hash
+     */
+    asHash: function() {
+        return this.data;
+    }
 });
 
-['toString', 'valueOf'].each(function(method){
-	URI.prototype[method] = function(){
-		return this.get('value');
-	};
-});
+Jx.Record.UPDATE = 1;
+Jx.Record.DELETE = 2;
+Jx.Record.INSERT = 3;/*
+---
 
+name: Jx.Store
 
-URI.regs = {
-	endSlash: /\/$/,
-	scheme: /^(\w+):/,
-	directoryDot: /\.\/|\.$/
-};
+description: An implementation of a basic data store.
 
-URI.base = new URI($$('base[href]').getLast(), { base: document.location });
+license: MIT-style license.
 
-String.implement({
+requires:
+ - Jx.Object
+ - Jx.Record
 
-	toURI: function(options){ return new URI(this, options); }
+provides: [Jx.Store]
 
-});/*
-Script: URI.Relative.js
-	Extends the URI class to add methods for computing relative and absolute urls.
+...
+ */
+// $Id: store.js 992 2010-10-07 19:28:37Z pagameba $
+/**
+ * Class: Jx.Store
+ *
+ * Extends: <Jx.Object>
+ *
+ * This class is the  store. It keeps track of data. It
+ * allows adding, deleting, iterating, sorting etc...
+ *
+ * For the most part the store is pretty "dumb" meaning it
+ * starts with very limited functionality. Actually, it can't
+ * even load data by itself. Instead, it needs to have protocols,
+ * strategies, and a record class passed to it that it can use.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Store = new Class({
 
-	License:
-		MIT-style license.
+    Family: 'Jx.Store',
+    Extends: Jx.Object,
 
-	Authors:
-		Sebastian Markbåge
-*/
+    options: {
+        /**
+         * Option: id
+         * the identifier for this store
+         */
+        id : null,
+        /**
+         * Option: columns
+         * an array listing the columns of the store in order of their
+         * appearance in the data object formatted as an object
+         *      {name: 'column name', type: 'column type'}
+         * where type can be one of alphanumeric, numeric, date, boolean,
+         * or currency.
+         */
+        columns : [],
+        /**
+         * Option: protocol
+         * The protocol to use for communication in this store. The store
+         * itself doesn't actually use it but it is accessed by the strategies
+         * to do their work. This option is required and the store won't work
+         * without it.
+         */
+        protocol: null,
+        /**
+         * Option: strategies
+         * This is an array of instantiated strategy objects that will work
+         * on this store. They provide many services such as loading data,
+         * paging data, saving, and sorting (and anything else you may need
+         * can be written). If none are passed in it will use the default
+         * Jx.Store.Strategy.Full
+         */
+        strategies: null,
+        /**
+         * Option: record
+         * This is a Jx.Store.Record instance or one of its subclasses. This is
+         * the class that will be used to hold each individual record in the
+         * store. Don't pass in a instance of the class but rather the class
+         * name itself. If none is passed in it will default to Jx.Record
+         */
+        record: null,
+        /**
+         * Option: recordOptions
+         * Options to pass to each record as it's created.
+         */
+        recordOptions: {
+            primaryKey: null
+        }
+    },
 
-URI = Class.refactor(URI, {
+    /**
+     * Property: data
+     * Holds the data for this store
+     */
+    data : null,
+    /**
+     * Property: index
+     * Holds the current position of the store relative to the data and the pageIndex.
+     * Zero-based index.
+     */
+    index : 0,
+    /**
+     * APIProperty: id
+     * The id of this store.
+     */
+    id : null,
+    /**
+     * Property: loaded
+     * Tells whether the store has been loaded or not
+     */
+    loaded: false,
+    /**
+     * Property: ready
+     * Used to determine if the store is completely initialized.
+     */
+    ready: false,
 
-	combine: function(bits, base){
-		if (!base || bits.scheme != base.scheme || bits.host != base.host || bits.port != base.port)
-			return this.previous.apply(this, arguments);
-		var end = bits.file + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : '');
+    /**
+     * Method: init
+     * initialize the store, should be called by sub-classes
+     */
+    init: function () {
+        this.parent();
 
-		if (!base.directory) return (bits.directory || (bits.file ? '' : './')) + end;
+        if ($defined(this.options.id)) {
+            this.id = this.options.id;
+        }
 
-		var baseDir = base.directory.split('/'),
-			relDir = bits.directory.split('/'),
-			path = '',
-			offset;
+        if (!$defined(this.options.protocol)) {
+            this.ready = false;
+            return;
+        } else {
+            this.protocol = this.options.protocol;
+        }
 
-		var i = 0;
-		for(offset = 0; offset < baseDir.length && offset < relDir.length && baseDir[offset] == relDir[offset]; offset++);
-		for(i = 0; i < baseDir.length - offset - 1; i++) path += '../';
-		for(i = offset; i < relDir.length - 1; i++) path += relDir[i] + '/';
+        this.strategies = new Hash();
 
-		return (path || (bits.file ? '' : './')) + end;
-	},
+        if ($defined(this.options.strategies)) {
+            this.options.strategies.each(function(strategy){
+                this.addStrategy(strategy);
+            },this);
+        } else {
+            var strategy = new Jx.Store.Strategy.Full();
+            this.addStrategy(strategy);
+        }
 
-	toAbsolute: function(base){
-		base = new URI(base);
-		if (base) base.set('directory', '').set('file', '');
-		return this.toRelative(base);
-	},
+        if ($defined(this.options.record)) {
+            this.record = this.options.record;
+        } else {
+            this.record = Jx.Record;
+        }
 
-	toRelative: function(base){
-		return this.get('value', new URI(base));
-	}
 
-});/*
-Script: Element.Forms.js
-	Extends the Element native object to include methods useful in managing inputs.
+    },
 
-	License:
-		MIT-style license.
+    /**
+     * Method: cleanup
+     * avoid memory leaks when a store is destroyed, should be called
+     * by sub-classes if overridden
+     */
+    cleanup: function () {
+        this.strategies.each(function(strategy){
+            strategy.destroy();
+        },this);
+        this.strategies = null;
+        this.protocol.destroy();
+        this.protocol = null;
+        this.record = null;
+    },
+    /**
+     * APIMethod: getStrategy
+     * returns the named strategy if it is present, null otherwise.
+     *
+     * Parameters:
+     * name - the name of the strategy we're looking for
+     */
+    getStrategy: function (name) {
+        if (this.strategies.has(name)) {
+            return this.strategies.get(name);
+        }
+        return null;
+    },
+    /**
+     * APIMethod: addStrategy
+     * Allows the addition of strategies after store initialization. Handy to
+     * have if some other class needs a strategy that is not present.
+     *
+     * Parameters:
+     * strategy - the strategy to add to the store
+     */
+    addStrategy: function (strategy) {
+        this.strategies.set(strategy.name, strategy);
+        strategy.setStore(this);
+        strategy.activate();
+    },
+    /**
+     * APIMethod: load
+     * used to load the store. It simply fires an event that the strategies
+     * are listening for.
+     *
+     * Parameters:
+     * params - a hash of parameters passed to the strategy for determining
+     *     what records to load.
+     */
+    load: function (params) {
+        this.fireEvent('storeLoad', params);
+    },
+    /**
+     * APIMethod: empty
+     * Clears the store of data
+     */
+    empty: function () {
+        if ($defined(this.data)) {
+            this.data.empty();
+        }
+    },
 
-	Authors:
-		Aaron Newton
+    /**
+     * APIMethod: hasNext
+     * Determines if there are more records past the current
+     * one.
+     *
+     * Returns: true | false (Null if there's a problem)
+     */
+    hasNext : function () {
+        if ($defined(this.data)) {
+            return this.index < this.data.length - 1;
+        }
+        return null;
+    },
 
-*/
-Element.implement({
+    /**
+     * APIMethod: hasPrevious
+     * Determines if there are records before the current
+     * one.
+     *
+     * Returns: true | false
+     */
+    hasPrevious : function () {
+        if ($defined(this.data)) {
+            return this.index > 0;
+        }
+        return null;
+    },
 
-	tidy: function(){
-		this.set('value', this.get('value').tidy());
-	},
+    /**
+     * APIMethod: valid
+     * Tells us if the current index has any data (i.e. that the
+     * index is valid).
+     *
+     * Returns: true | false
+     */
+    valid : function () {
+        return ($defined(this.data) && $defined(this.data[this.index]));
+    },
 
-	getTextInRange: function(start, end){
-		return this.get('value').substring(start, end);
-	},
+    /**
+     * APIMethod: next
+     * Moves the store to the next record
+     *
+     * Returns: nothing | null if error
+     */
+    next : function () {
+        if ($defined(this.data)) {
+            this.index++;
+            if (this.index === this.data.length) {
+                this.index = this.data.length - 1;
+            }
+            this.fireEvent('storeMove', this);
+            return true;
+        } else {
+            return null;
+        }
+    },
 
-	getSelectedText: function(){
-		if (document.selection && document.selection.createRange) return document.selection.createRange().text;
-		return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd());
-	},
+    /**
+     * APIMethod: previous
+     * moves the store to the previous record
+     *
+     * Returns: nothing | null if error
+     *
+     */
+    previous : function () {
+        if ($defined(this.data)) {
+            this.index--;
+            if (this.index < 0) {
+                this.index = 0;
+            }
+            this.fireEvent('storeMove', this);
+            return true;
+        } else {
+            return null;
+        }
+    },
 
-	getSelectedRange: function() {
-		if ($defined(this.selectionStart)) return {start: this.selectionStart, end: this.selectionEnd};
-		var pos = {start: 0, end: 0};
-		var range = this.getDocument().selection.createRange();
-		if (!range || range.parentElement() != this) return pos;
-		var dup = range.duplicate();
-		if (this.type == 'text') {
-			pos.start = 0 - dup.moveStart('character', -100000);
-			pos.end = pos.start + range.text.length;
-		} else {
-			var value = this.get('value');
-			var offset = value.length - value.match(/[\n\r]*$/)[0].length;
-			dup.moveToElementText(this);
-			dup.setEndPoint('StartToEnd', range);
-			pos.end = offset - dup.text.length;
-			dup.setEndPoint('StartToStart', range);
-			pos.start = offset - dup.text.length;
-		}
-		return pos;
-	},
+    /**
+     * APIMethod: first
+     * Moves the store to the first record
+     *
+     * Returns: nothing | null if error
+     *
+     */
+    first : function () {
+        if ($defined(this.data)) {
+            this.index = 0;
+            this.fireEvent('storeMove', this);
+            return true;
+        } else {
+            return null;
+        }
+    },
 
-	getSelectionStart: function(){
-		return this.getSelectedRange().start;
-	},
+    /**
+     * APIMethod: last
+     * Moves to the last record in the store
+     *
+     * Returns: nothing | null if error
+     */
+    last : function () {
+        if ($defined(this.data)) {
+            this.index = this.data.length - 1;
+            this.fireEvent('storeMove', this);
+            return true;
+        } else {
+            return null;
+        }
+    },
 
-	getSelectionEnd: function(){
-		return this.getSelectedRange().end;
-	},
+    /**
+     * APIMethod: count
+     * Returns the number of records in the store
+     *
+     * Returns: an integer indicating the number of records in the store or null
+     * if there's an error
+     */
+    count : function () {
+        if ($defined(this.data)) {
+            return this.data.length;
+        }
+        return null;
+    },
 
-	setCaretPosition: function(pos){
-		if (pos == 'end') pos = this.get('value').length;
-		this.selectRange(pos, pos);
-		return this;
-	},
+    /**
+     * APIMethod: getPosition
+     * Tells us where we are in the store
+     *
+     * Returns: an integer indicating the position in the store or null if
+     * there's an error
+     */
+    getPosition : function () {
+        if ($defined(this.data)) {
+            return this.index;
+        }
+        return null;
+    },
 
-	getCaretPosition: function(){
-		return this.getSelectedRange().start;
-	},
+    /**
+     * APIMethod: moveTo
+     * Moves the index to a specific record in the store
+     *
+     * Parameters:
+     * index - the record to move to
+     *
+     * Returns: true - if successful false - if not successful null - on error
+     */
+    moveTo : function (index) {
+        if ($defined(this.data) && index >= 0 && index < this.data.length) {
+            this.index = index;
+            this.fireEvent('storeMove', this);
+            return true;
+        } else if (!$defined(this.data)) {
+            return null;
+        } else {
+            return false;
+        }
+    },
+    /**
+     * APIMethod: each
+     * allows iteration through the store's records.
+     * NOTE: this function is untested
+     *
+     * Parameters:
+     * fn - the function to execute for each record
+     * bind - the scope of the function
+     * ignoreDeleted - flag that tells the function whether to ignore records
+     *                  marked as deleted.
+     */
+    each: function (fn, bind, ignoreDeleted) {
+        if ($defined(this.data)) {
+          var data;
+          if (ignoreDeleted) {
+              data = this.data.filter(function (record) {
+                  return record.state !== Jx.Record.DELETE;
+              }, this);
+          } else {
+              data = this.data;
+          }
+          data.each(fn, bind);
+        }
+    },
+    /**
+     * APIMethod: get
+     * gets the data for the specified column
+     *
+     * Parameters:
+     * column - indicator of the column to set. Either a string (the name of
+     *          the column) or an integer (the index of the column in the
+     *          record).
+     * index - the index of the record in the internal array. Optional.
+     *          defaults to the current index.
+     */
+    get: function (column, index) {
+        if (!$defined(index)) {
+            index = this.index;
+        }
+        return this.data[index].get(column);
+    },
+    /**
+     * APIMethod: set
+     * Sets the passed data for a particular column on the indicated record.
+     *
+     * Parameters:
+     * column - indicator of the column to set. Either a string (the name of
+     *          the column) or an integer (the index of the column in the
+     *          record).
+     * data - the data to set in the column of the record
+     * index - the index of the record in the internal array. Optional.
+     *          defaults to the current index.
+     */
+    set: function (column, data, index) {
+        if (!$defined(index)) {
+            index = this.index;
+        }
+        var ret = this.data[index].set(column, data);
+        ret.reverse();
+        ret.push(index);
+        ret.reverse();
+        //fire event with array [index, column, oldvalue, newValue]
+        this.fireEvent('storeColumnChanged', ret);
+    },
+    /**
+     * APIMethod: refresh
+     * Simply fires the storeRefresh event for strategies to listen for.
+     */
+    refresh: function () {
+        this.fireEvent('storeRefresh', this);
+    },
+    /**
+     * APIMethod: addRecord
+     * Adds given data to the end of the current store.
+     *
+     * Parameters:
+     * data - The data to use in creating a record. This should be in whatever
+     *        form Jx.Store.Record, or the current subclass, needs it in.
+     * position - whether the record is added to the 'top' or 'bottom' of the
+     *      store.
+     * insert - flag whether this is an "insert"
+     */
+    addRecord: function (data, position, insert) {
+        if (!$defined(this.data)) {
+            this.data = [];
+        }
 
-	selectRange: function(start, end){
-		if (this.createTextRange){
-			var value = this.get('value');
-			var diff = value.substr(start, end - start).replace(/\r/g, '').length;
-			start = value.substr(0, start).replace(/\r/g, '').length;
-			var range = this.createTextRange();
-			range.collapse(true);
-			range.moveEnd('character', start + diff);
-			range.moveStart('character', start);
-			range.select();
-		} else {
-			this.focus();
-			this.setSelectionRange(start, end);
-		}
-		return this;
-	},
+        position = $defined(position)? position : 'bottom';
 
-	insertAtCursor: function(value, select){
-		var pos = this.getSelectedRange();
-		var text = this.get('value');
-		this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length));
-		if ($pick(select, true)) this.selectRange(pos.start, pos.start + value.length);
-		else this.setCaretPosition(pos.start + value.length);
-		return this;
-	},
+        var record = data;
+        if (!(data instanceof Jx.Record)) {
+            record = new (this.record)(this, this.options.columns, data, this.options.recordOptions);
+        }
+        if (insert) {
+            record.state = Jx.Record.INSERT;
+        }
+        if (position === 'top') {
+            //some literature claims that .shift() and .unshift() don't work reliably in IE
+            //so we do it this way.
+            this.data.reverse();
+            this.data.push(record);
+            this.data.reverse();
+        } else {
+            this.data.push(record);
+        }
+        this.fireEvent('storeRecordAdded', [this, record, position]);
+    },
+    /**
+     * APIMethod: addRecords
+     * Used to add multiple records to the store at one time.
+     *
+     * Parameters:
+     * data - an array of data to add.
+     * position - 'top' or 'bottom'. Indicates whether to add at the top or
+     * the bottom of the store
+     */
+    addRecords: function (data, position) {
+        var def = $defined(data),
+            type = Jx.type(data);
+        if (def && type === 'array') {
+            this.fireEvent('storeBeginAddRecords', this);
+            //if position is top, reverse the array or we'll add them in the
+            // wrong order.
+            if (position === 'top') {
+                data.reverse();
+            }
+            data.each(function(d){
+                this.addRecord(d, position);
+            },this);
+            this.fireEvent('storeEndAddRecords', this);
+            return true;
+        }
+        return false;
+    },
 
-	insertAroundCursor: function(options, select){
-		options = $extend({
-			before: '',
-			defaultMiddle: '',
-			after: ''
-		}, options);
-		var value = this.getSelectedText() || options.defaultMiddle;
-		var pos = this.getSelectedRange();
-		var text = this.get('value');
-		if (pos.start == pos.end){
-			this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length));
-			this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length);
-		} else {
-			var current = text.substring(pos.start, pos.end);
-			this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length));
-			var selStart = pos.start + options.before.length;
-			if ($pick(select, true)) this.selectRange(selStart, selStart + current.length);
-			else this.setCaretPosition(selStart + text.length);
-		}
-		return this;
-	}
+    /**
+     * APIMethod: getRecord
+     * Returns the record at the given index or the current store index
+     *
+     * Parameters:
+     * index - the index from which to return the record. Optional. Defaults
+     * to the current store index
+     */
+    getRecord: function (index) {
+        if (!$defined(index)) {
+            index = this.index;
+        }
 
-});/*
-Script: Element.Measure.js
-	Extends the Element native object to include methods useful in measuring dimensions.
+        if (Jx.type(index) === 'number') {
+            if ($defined(this.data) && $defined(this.data[index])) {
+                return this.data[index];
+            }
+        } else {
+            //Not sure what the point of this part is. It compares the
+            //record to the index directly as if we passed in the record which
+            //means we already have the record... huh???
+            var r;
+            this.data.each(function(record){
+                if (record === index) {
+                    r = record;
+                }
+            },this);
+            return r;
+        }
+        return null;
+    },
+    /**
+     * APIMethod: replaceRecord
+     * Replaces the record at an existing index with a new record containing
+     * the passed in data.
+     *
+     * Parameters:
+     * data - the data to use in creating the new record
+     * index - the index at which to place the new record. Optional.
+     *          defaults to the current store index.
+     */
+    replace: function(data, index) {
+        if ($defined(data)) {
+            if (!$defined(index)) {
+                index = this.index;
+            }
+            var record = new this.record(this.options.columns,data),
+            oldRecord = this.data[index];
+            this.data[index] = record;
+            this.fireEvent('storeRecordReplaced', [oldRecord, record]);
+            return true;
+        }
+        return false;
+    },
+    /**
+     * APIMethod: deleteRecord
+     * Marks a record for deletion and removes it from the regular array of
+     * records. It adds it to a special holding array so it can be disposed
+     * of later.
+     *
+     * Parameters:
+     * index - the index at which to place the new record. Optional.
+     *          defaults to the current store index.
+     */
+    deleteRecord: function(index) {
+        if (!$defined(index)) {
+            index = this.index;
+        }
+        var record = this.data[index];
+        record.state = Jx.Record.DELETE;
+        // Set to Null or slice it out and compact the array???
+        //this.data[index] = null;
+        this.data.splice(index,1);
+        if (!$defined(this.deleted)) {
+            this.deleted = [];
+        }
+        this.deleted.push(record);
+        this.fireEvent('storeRecordDeleted', [this, record]);
+    },
+    /**
+     * APIMethod: insertRecord
+     * Shortcut to addRecord which facilitates marking a record as inserted.
+     *
+     * Parameters:
+     * data - the data to use in creating this inserted record. Should be in
+     *          whatever form the current implementation of Jx.Record needs
+     * position - where to place the record. Should be either 'top' or
+     *    'bottom'.
+     */
+    insertRecord: function (data, position) {
+        this.addRecord(data, position, true);
+    },
 
-	Element.measure / .expose methods by Daniel Steigerwald
-	License: MIT-style license.
-	Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz
+    /**
+     * APIMethod: getColumns
+     * Allows retrieving the columns array
+     */
+    getColumns: function () {
+        return this.options.columns;
+    },
 
-	License:
-		MIT-style license.
+    /**
+     * APIMethod: findByColumn
+     * Used to find a specific record by the value in a specific column. This
+     * is particularly useful for finding records by a unique id column. The
+     * search will stop on the first instance of the value
+     *
+     * Parameters:
+     * column - the name (or index) of the column to search by
+     * value - the value to look for
+     */
+    findByColumn: function (column, value) {
+        if (typeof StopIteration === "undefined") {
+            StopIteration = new Error("StopIteration");
+        }
 
-	Authors:
-		Aaron Newton
+        var index;
+        try {
+            this.data.each(function(record, idx){
+                if (record.equals(column, value)) {
+                    index = idx;
+                    throw StopIteration;
+                }
+            },this);
+        } catch (error) {
+            if (error !== StopIteration) {
+                throw error;
+            }
+            return index;
+        }
+        return null;
+    },
+    /**
+     * APIMethod: removeRecord
+     * removes (but does not mark for deletion) a record at the given index
+     * or the current store index if none is passed in.
+     *
+     * Parameters:
+     * index - Optional. The store index of the record to remove.
+     */
+    removeRecord: function (index) {
+        if (!$defined(index)) {
+            index = this.index;
+        }
+        this.data.splice(index,1);
+        this.fireEvent('storeRecordRemoved', [this, index])
+    },
+    /**
+     * APIMethod: removeRecords
+     * Used to remove multiple contiguous records from a store.
+     *
+     * Parameters:
+     * first - where to start removing records (zero-based)
+     * last - where to stop removing records (zero-based, inclusive)
+     */
+    removeRecords: function (first, last) {
+        for (var i = first; i <= last; i++) {
+            this.removeRecord(first);
+        }
+        this.fireEvent('storeMultipleRecordsRemoved', [this, first, last]);
+    },
 
-*/
+    /**
+   * APIMethod: parseTemplate
+   * parses the provided template to determine which store columns are
+   * required to complete it.
+   *
+   * Parameters:
+   * template - the template to parse
+   */
+  parseTemplate: function (template) {
+      //we parse the template based on the columns in the data store looking
+      //for the pattern {column-name}. If it's in there we add it to the
+      //array of ones to look fo
+      var arr = [],
+          s;
+      this.options.columns.each(function (col) {
+          s = '{' + col.name + '}';
+          if (template.contains(s)) {
+              arr.push(col.name);
+          }
+      }, this);
+      return arr;
+  },
 
-Element.implement({
+  /**
+   * APIMethod: fillTemplate
+   * Actually does the work of getting the data from the store
+   * and creating a single item based on the provided template
+   *
+   * Parameters:
+   * index - the index of the data in the store to use in populating the
+   *          template.
+   * template - the template to fill
+   * columnsNeeded - the array of columns needed by this template. should be
+   *      obtained by calling parseTemplate().
+     * obj - an object with some prefilled keys to use in substituting.
+     *      Ones that are also in the store will be overwritten.
+   */
+  fillTemplate: function (index, template, columnsNeeded, obj) {
+      var record = null,
+          itemObj;
+      if ($defined(index)) {
+          if (index instanceof Jx.Record) {
+              record = index;
+          } else {
+              record = this.getRecord(index);
+          }
+        } else {
+            record = this.getRecord(this.index);
+        }
 
-	measure: function(fn){
-		var vis = function(el) {
-			return !!(!el || el.offsetHeight || el.offsetWidth);
-		};
-		if (vis(this)) return fn.apply(this);
-		var parent = this.getParent(),
-			toMeasure = [], 
-			restorers = [];
-		while (!vis(parent) && parent != document.body) {
-			toMeasure.push(parent.expose());
-			parent = parent.getParent();
-		}
-		var restore = this.expose();
-		var result = fn.apply(this);
-		restore();
-		toMeasure.each(function(restore){
-			restore();
-		});
-		return result;
-	},
-
-	expose: function(){
-		if (this.getStyle('display') != 'none') return $empty;
-		var before = this.getStyles('display', 'position', 'visibility');
-		return this.setStyles({
-			display: 'block',
-			position: 'absolute',
-			visibility: 'hidden'
-		}).setStyles.pass(before, this);
-	},
-
-	getDimensions: function(options){
-		options = $merge({computeSize: false},options);
-		var dim = {};
-		var getSize = function(el, options){
-			return (options.computeSize)?el.getComputedSize(options):el.getSize();
-		};
-		if (this.getStyle('display') == 'none'){
-			dim = this.measure(function(){
-				return getSize(this, options);
-			});
-		} else {
-			try { //safari sometimes crashes here, so catch it
-				dim = getSize(this, options);
-			}catch(e){}
-		}
-		return $chk(dim.x) ? $extend(dim, {width: dim.x, height: dim.y}) : $extend(dim, {x: dim.width, y: dim.height});
-	},
-
-	getComputedSize: function(options){
-		options = $merge({
-			styles: ['padding','border'],
-			plains: {
-				height: ['top','bottom'],
-				width: ['left','right']
-			},
-			mode: 'both'
-		}, options);
-		var size = {width: 0,height: 0};
-		switch (options.mode){
-			case 'vertical':
-				delete size.width;
-				delete options.plains.width;
-				break;
-			case 'horizontal':
-				delete size.height;
-				delete options.plains.height;
-				break;
-		}
-		var getStyles = [];
-		//this function might be useful in other places; perhaps it should be outside this function?
-		$each(options.plains, function(plain, key){
-			plain.each(function(edge){
-				options.styles.each(function(style){
-					getStyles.push((style == 'border') ? style + '-' + edge + '-' + 'width' : style + '-' + edge);
-				});
-			});
-		});
-		var styles = {};
-		getStyles.each(function(style){ styles[style] = this.getComputedStyle(style); }, this);
-		var subtracted = [];
-		$each(options.plains, function(plain, key){ //keys: width, height, plains: ['left', 'right'], ['top','bottom']
-			var capitalized = key.capitalize();
-			size['total' + capitalized] = 0;
-			size['computed' + capitalized] = 0;
-			plain.each(function(edge){ //top, left, right, bottom
-				size['computed' + edge.capitalize()] = 0;
-				getStyles.each(function(style, i){ //padding, border, etc.
-					//'padding-left'.test('left') size['totalWidth'] = size['width'] + [padding-left]
-					if (style.test(edge)){
-						styles[style] = styles[style].toInt() || 0; //styles['padding-left'] = 5;
-						size['total' + capitalized] = size['total' + capitalized] + styles[style];
-						size['computed' + edge.capitalize()] = size['computed' + edge.capitalize()] + styles[style];
-					}
-					//if width != width (so, padding-left, for instance), then subtract that from the total
-					if (style.test(edge) && key != style &&
-						(style.test('border') || style.test('padding')) && !subtracted.contains(style)){
-						subtracted.push(style);
-						size['computed' + capitalized] = size['computed' + capitalized]-styles[style];
-					}
-				});
-			});
-		});
-
-		['Width', 'Height'].each(function(value){
-			var lower = value.toLowerCase();
-			if(!$chk(size[lower])) return;
-
-			size[lower] = size[lower] + this['offset' + value] + size['computed' + value];
-			size['total' + value] = size[lower] + size['total' + value];
-			delete size['computed' + value];
-		}, this);
-
-		return $extend(styles, size);
-	}
-
+      //create the item
+      itemObj = $defined(obj) ? obj : {};
+      columnsNeeded.each(function (col) {
+          itemObj[col] = record.get(col);
+      }, this);
+      return template.substitute(itemObj);
+  }
 });/*
-Script: Element.Pin.js
-	Extends the Element native object to include the pin method useful for fixed positioning for elements.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Compare
 
-	Authors:
-		Aaron Newton
-*/
+description: Class that provides functions for comparing various data types. Used by the Jx.Sort class and it's descendants
 
-(function(){
-	var supportsPositionFixed = false;
-	window.addEvent('domready', function(){
-		var test = new Element('div').setStyles({
-			position: 'fixed',
-			top: 0,
-			right: 0
-		}).inject(document.body);
-		supportsPositionFixed = (test.offsetTop === 0);
-		test.dispose();
-	});
+license: MIT-style license.
 
-	Element.implement({
+requires:
+ - Jx.Object
+ - More/Date.Extras
 
-		pin: function(enable){
-			if (this.getStyle('display') == 'none') return null;
-			
-			var p;
-			if (enable !== false){
-				p = this.getPosition();
-				if (!this.retrieve('pinned')){
-					var pos = {
-						top: p.y - window.getScroll().y,
-						left: p.x - window.getScroll().x
-					};
-					if (supportsPositionFixed){
-						this.setStyle('position', 'fixed').setStyles(pos);
-					} else {
-						this.store('pinnedByJS', true);
-						this.setStyles({
-							position: 'absolute',
-							top: p.y,
-							left: p.x
-						});
-						this.store('scrollFixer', (function(){
-							if (this.retrieve('pinned'))
-								this.setStyles({
-									top: pos.top.toInt() + window.getScroll().y,
-									left: pos.left.toInt() + window.getScroll().x
-								});
-						}).bind(this));
-						window.addEvent('scroll', this.retrieve('scrollFixer'));
-					}
-					this.store('pinned', true);
-				}
-			} else {
-				var op;
-				if (!Browser.Engine.trident){
-					if (this.getParent().getComputedStyle('position') != 'static') op = this.getParent();
-					else op = this.getParent().getOffsetParent();
-				}
-				p = this.getPosition(op);
-				this.store('pinned', false);
-				var reposition;
-				if (supportsPositionFixed && !this.retrieve('pinnedByJS')){
-					reposition = {
-						top: p.y + window.getScroll().y,
-						left: p.x + window.getScroll().x
-					};
-				} else {
-					this.store('pinnedByJS', false);
-					window.removeEvent('scroll', this.retrieve('scrollFixer'));
-					reposition = {
-						top: p.y,
-						left: p.x
-					};
-				}
-				this.setStyles($merge(reposition, {position: 'absolute'}));
-			}
-			return this.addClass('isPinned');
-		},
+provides: [Jx.Compare]
 
-		unpin: function(){
-			return this.pin(false).removeClass('isPinned');
-		},
+...
+ */
+// $Id: compare.js 977 2010-09-02 18:57:42Z pagameba $
+/**
+ * Class: Jx.Compare
+ *
+ * Extends: <Jx.Object>
+ *
+ * Class that holds functions for doing comparison operations.
+ * This class requires the mootools-more Date() extensions.
+ *
+ * notes:
+ * Each function that does a comparison returns
+ *
+ * 0 - if equal.
+ * 1 - if the first value is greater that the second.
+ * -1 - if the first value is less than the second.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
 
-		togglepin: function(){
-			this.pin(!this.retrieve('pinned'));
-		}
+Jx.Compare = new Class({
+    Family: 'Jx.Compare',
+    Extends: Jx.Object,
 
-	});
+    options: { separator: '.' },
 
-})();/*
-Script: Element.Position.js
-	Extends the Element native object to include methods useful positioning elements relative to others.
+    /**
+     * APIMethod: alphanumeric
+     * Compare alphanumeric variables. This is case sensitive
+     *
+     * Parameters:
+     * a - a value
+     * b - another value
+     */
+    alphanumeric: function (a, b) {
+        return (a === b) ? 0 :(a < b) ? -1 : 1;
+    },
 
-	License:
-		MIT-style license.
+    /**
+     * APIMethod: numeric
+     * Compares numbers
+     *
+     * Parameters:
+     * a - a number
+     * b - another number
+     */
+    numeric: function (a, b) {
+        return this.alphanumeric(this.convert(a), this.convert(b));
+    },
 
-	Authors:
-		Aaron Newton
-*/
+    /**
+     * Method: _convert
+     * Normalizes numbers relative to the separator.
+     *
+     * Parameters:
+     * val - the number to normalize
+     *
+     * Returns:
+     * the normalized value
+     */
+    convert: function (val) {
+        if (Jx.type(val) === 'string') {
+            var neg = false;
+            if (val.substr(0,1) == '-') {
+                neg = true;
+            }
+            val = parseFloat(val.replace(/^[^\d\.]*([\d., ]+).*/g, "$1").replace(new RegExp("[^\\\d" + this.options.separator + "]", "g"), '').replace(/,/, '.')) || 0;
+            if (neg) {
+                val = val * -1;
+            }
+        }
+        return val || 0;
+    },
 
-(function(){
+    /**
+     * APIMethod: ignorecase
+     * Compares to alphanumeric strings without regard to case.
+     *
+     * Parameters:
+     * a - a value
+     * b - another value
+     */
+    ignorecase: function (a, b) {
+        return this.alphanumeric(("" + a).toLowerCase(), ("" + b).toLowerCase());
+    },
 
-var original = Element.prototype.position;
+    /**
+     * APIMethod: currency
+     * Compares to currency values.
+     *
+     * Parameters:
+     * a - a currency value without the $
+     * b - another currency value without the $
+     */
+    currency: function (a, b) {
+        return this.numeric(a, b);
+    },
 
-Element.implement({
+    /**
+     * APIMethod: date
+     * Compares 2 date values (either a string or an object)
+     *
+     * Parameters:
+     * a - a date value
+     * b - another date value
+     */
+    date: function (a, b) {
+        var x = new Date().parse(a),
+            y = new Date().parse(b);
+        return (x < y) ? -1 : (x > y) ? 1 : 0;
+    },
+    /**
+     * APIMethod: boolean
+     * Compares 2 bolean values
+     *
+     * Parameters:
+     * a - a boolean value
+     * b - another boolean value
+     */
+    'boolean': function (a, b) {
+        return (a === true && b === false) ? -1 : (a === b) ? 0 : 1;
+    }
 
-	position: function(options){
-		//call original position if the options are x/y values
-		if (options && ($defined(options.x) || $defined(options.y))) return original ? original.apply(this, arguments) : this;
-		$each(options||{}, function(v, k){ if (!$defined(v)) delete options[k]; });
-		options = $merge({
-			relativeTo: document.body,
-			position: {
-				x: 'center', //left, center, right
-				y: 'center' //top, center, bottom
-			},
-			edge: false,
-			offset: {x: 0, y: 0},
-			returnPos: false,
-			relFixedPosition: false,
-			ignoreMargins: false,
-			allowNegative: false
-		}, options);
-		//compute the offset of the parent positioned element if this element is in one
-		var parentOffset = {x: 0, y: 0};
-		var parentPositioned = false;
-		/* dollar around getOffsetParent should not be necessary, but as it does not return
-		 * a mootools extended element in IE, an error occurs on the call to expose. See:
-		 * http://mootools.lighthouseapp.com/projects/2706/tickets/333-element-getoffsetparent-inconsistency-between-ie-and-other-browsers */
-		var offsetParent = this.measure(function(){
-			return $(this.getOffsetParent());
-		});
-		if (offsetParent && offsetParent != this.getDocument().body){
-			parentOffset = offsetParent.measure(function(){
-				return this.getPosition();
-			});
-			parentPositioned = true;
-			options.offset.x = options.offset.x - parentOffset.x;
-			options.offset.y = options.offset.y - parentOffset.y;
-		}
-		//upperRight, bottomRight, centerRight, upperLeft, bottomLeft, centerLeft
-		//topRight, topLeft, centerTop, centerBottom, center
-		var fixValue = function(option){
-			if ($type(option) != 'string') return option;
-			option = option.toLowerCase();
-			var val = {};
-			if (option.test('left')) val.x = 'left';
-			else if (option.test('right')) val.x = 'right';
-			else val.x = 'center';
-			if (option.test('upper') || option.test('top')) val.y = 'top';
-			else if (option.test('bottom')) val.y = 'bottom';
-			else val.y = 'center';
-			return val;
-		};
-		options.edge = fixValue(options.edge);
-		options.position = fixValue(options.position);
-		if (!options.edge){
-			if (options.position.x == 'center' && options.position.y == 'center') options.edge = {x:'center', y:'center'};
-			else options.edge = {x:'left', y:'top'};
-		}
-
-		this.setStyle('position', 'absolute');
-		var rel = $(options.relativeTo) || document.body;
-		var calc = rel == document.body ? window.getScroll() : rel.getPosition();
-		var top = calc.y;
-		var left = calc.x;
-
-		if (Browser.Engine.trident){
-			var scrolls = rel.getScrolls();
-			top += scrolls.y;
-			left += scrolls.x;
-		}
-
-		var dim = this.getDimensions({computeSize: true, styles:['padding', 'border','margin']});
-		if (options.ignoreMargins){
-			options.offset.x = options.offset.x - dim['margin-left'];
-			options.offset.y = options.offset.y - dim['margin-top'];
-		}
-		var pos = {};
-		var prefY = options.offset.y;
-		var prefX = options.offset.x;
-		var winSize = window.getSize();
-		switch(options.position.x){
-			case 'left':
-				pos.x = left + prefX;
-				break;
-			case 'right':
-				pos.x = left + prefX + rel.offsetWidth;
-				break;
-			default: //center
-				pos.x = left + ((rel == document.body ? winSize.x : rel.offsetWidth)/2) + prefX;
-				break;
-		}
-		switch(options.position.y){
-			case 'top':
-				pos.y = top + prefY;
-				break;
-			case 'bottom':
-				pos.y = top + prefY + rel.offsetHeight;
-				break;
-			default: //center
-				pos.y = top + ((rel == document.body ? winSize.y : rel.offsetHeight)/2) + prefY;
-				break;
-		}
-
-		if (options.edge){
-			var edgeOffset = {};
-
-			switch(options.edge.x){
-				case 'left':
-					edgeOffset.x = 0;
-					break;
-				case 'right':
-					edgeOffset.x = -dim.x-dim.computedRight-dim.computedLeft;
-					break;
-				default: //center
-					edgeOffset.x = -(dim.x/2);
-					break;
-			}
-			switch(options.edge.y){
-				case 'top':
-					edgeOffset.y = 0;
-					break;
-				case 'bottom':
-					edgeOffset.y = -dim.y-dim.computedTop-dim.computedBottom;
-					break;
-				default: //center
-					edgeOffset.y = -(dim.y/2);
-					break;
-			}
-			pos.x = pos.x + edgeOffset.x;
-			pos.y = pos.y + edgeOffset.y;
-		}
-		pos = {
-			left: ((pos.x >= 0 || parentPositioned || options.allowNegative) ? pos.x : 0).toInt(),
-			top: ((pos.y >= 0 || parentPositioned || options.allowNegative) ? pos.y : 0).toInt()
-		};
-		if (rel.getStyle('position') == 'fixed' || options.relFixedPosition){
-			var winScroll = window.getScroll();
-			pos.top = pos.top.toInt() + winScroll.y;
-			pos.left = pos.left.toInt() + winScroll.x;
-		}
-
-		if (options.returnPos) return pos;
-		else this.setStyles(pos);
-		return this;
-	}
-
-});
-
-})();/*
-Script: Element.Shortcuts.js
-	Extends the Element native object to include some shortcut methods.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-
-*/
-
-Element.implement({
-
-	isDisplayed: function(){
-		return this.getStyle('display') != 'none';
-	},
-
-	toggle: function(){
-		return this[this.isDisplayed() ? 'hide' : 'show']();
-	},
-
-	hide: function(){
-		var d;
-		try {
-			//IE fails here if the element is not in the dom
-			if ('none' != this.getStyle('display')) d = this.getStyle('display');
-		} catch(e){}
-
-		return this.store('originalDisplay', d || 'block').setStyle('display', 'none');
-	},
-
-	show: function(display){
-		return this.setStyle('display', display || this.retrieve('originalDisplay') || 'block');
-	},
-
-	swapClass: function(remove, add){
-		return this.removeClass(remove).addClass(add);
-	}
-
-});
-/*
-Script: FormValidator.js
-	A css-class based form validation system.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-*/
-var InputValidator = new Class({
-
-	Implements: [Options],
-
-	options: {
-		errorMsg: 'Validation failed.',
-		test: function(field){return true;}
-	},
-
-	initialize: function(className, options){
-		this.setOptions(options);
-		this.className = className;
-	},
-
-	test: function(field, props){
-		if ($(field)) return this.options.test($(field), props||this.getProps(field));
-		else return false;
-	},
-
-	getError: function(field, props){
-		var err = this.options.errorMsg;
-		if ($type(err) == 'function') err = err($(field), props||this.getProps(field));
-		return err;
-	},
-
-	getProps: function(field){
-		if (!$(field)) return {};
-		return field.get('validatorProps');
-	}
-
-});
-
-Element.Properties.validatorProps = {
-
-	set: function(props){
-		return this.eliminate('validatorProps').store('validatorProps', props);
-	},
-
-	get: function(props){
-		if (props) this.set(props);
-		if (this.retrieve('validatorProps')) return this.retrieve('validatorProps');
-		if (this.getProperty('validatorProps')){
-			try {
-				this.store('validatorProps', JSON.decode(this.getProperty('validatorProps')));
-			}catch(e){
-				return {};
-			}
-		} else {
-			var vals = this.get('class').split(' ').filter(function(cls){
-				return cls.test(':');
-			});
-			if (!vals.length){
-				this.store('validatorProps', {});
-			} else {
-				props = {};
-				vals.each(function(cls){
-					var split = cls.split(':');
-					if (split[1]) {
-						try {
-							props[split[0]] = JSON.decode(split[1]);
-						} catch(e) {}
-					}
-				});
-				this.store('validatorProps', props);
-			}
-		}
-		return this.retrieve('validatorProps');
-	}
-
-};
-
-var FormValidator = new Class({
-
-	Implements:[Options, Events],
-
-	Binds: ['onSubmit'],
-
-	options: {/*
-		onFormValidate: $empty(isValid, form, event),
-		onElementValidate: $empty(isValid, field, className, warn),
-		onElementPass: $empty(field),
-		onElementFail: $empty(field, validatorsFailed) */
-		fieldSelectors: 'input, select, textarea',
-		ignoreHidden: true,
-		useTitles: false,
-		evaluateOnSubmit: true,
-		evaluateFieldsOnBlur: true,
-		evaluateFieldsOnChange: true,
-		serial: true,
-		stopOnFailure: true,
-		warningPrefix: function(){
-			return FormValidator.getMsg('warningPrefix') || 'Warning: ';
-		},
-		errorPrefix: function(){
-			return FormValidator.getMsg('errorPrefix') || 'Error: ';
-		}
-	},
-
-	initialize: function(form, options){
-		this.setOptions(options);
-		this.element = $(form);
-		this.element.store('validator', this);
-		this.warningPrefix = $lambda(this.options.warningPrefix)();
-		this.errorPrefix = $lambda(this.options.errorPrefix)();
-		if (this.options.evaluateOnSubmit) this.element.addEvent('submit', this.onSubmit);
-		if (this.options.evaluateFieldsOnBlur) this.watchFields(this.getFields());
-	},
-
-	toElement: function(){
-		return this.element;
-	},
-
-	getFields: function(){
-		return (this.fields = this.element.getElements(this.options.fieldSelectors));
-	},
-
-	watchFields: function(fields){
-		fields.each(function(el){
-				el.addEvent('blur', this.validateField.pass([el, false], this));
-			if (this.options.evaluateFieldsOnChange)
-				el.addEvent('change', this.validateField.pass([el, true], this));
-		}, this);
-	},
-
-	onSubmit: function(event){
-		if (!this.validate(event) && event) event.preventDefault();
-		else this.reset();
-	},
-
-	reset: function(){
-		this.getFields().each(this.resetField, this);
-		return this;
-	},
-
-	validate: function(event){
-		var result = this.getFields().map(function(field){
-			return this.validateField(field, true);
-		}, this).every(function(v){ return v;});
-		this.fireEvent('formValidate', [result, this.element, event]);
-		if (this.options.stopOnFailure && !result && event) event.preventDefault();
-		return result;
-	},
-
-	validateField: function(field, force){
-		if (this.paused) return true;
-		field = $(field);
-		var passed = !field.hasClass('validation-failed');
-		var failed, warned;
-		if (this.options.serial && !force){
-			failed = this.element.getElement('.validation-failed');
-			warned = this.element.getElement('.warning');
-		}
-		if (field && (!failed || force || field.hasClass('validation-failed') || (failed && !this.options.serial))){
-			var validators = field.className.split(' ').some(function(cn){
-				return this.getValidator(cn);
-			}, this);
-			var validatorsFailed = [];
-			field.className.split(' ').each(function(className){
-				if (className && !this.test(className, field)) validatorsFailed.include(className);
-			}, this);
-			passed = validatorsFailed.length === 0;
-			if (validators && !field.hasClass('warnOnly')){
-				if (passed){
-					field.addClass('validation-passed').removeClass('validation-failed');
-					this.fireEvent('elementPass', field);
-				} else {
-					field.addClass('validation-failed').removeClass('validation-passed');
-					this.fireEvent('elementFail', [field, validatorsFailed]);
-				}
-			}
-			if (!warned){
-				var warnings = field.className.split(' ').some(function(cn){
-					if (cn.test('^warn-') || field.hasClass('warnOnly'))
-						return this.getValidator(cn.replace(/^warn-/,''));
-					else return null;
-				}, this);
-				field.removeClass('warning');
-				var warnResult = field.className.split(' ').map(function(cn){
-					if (cn.test('^warn-') || field.hasClass('warnOnly'))
-						return this.test(cn.replace(/^warn-/,''), field, true);
-					else return null;
-				}, this);
-			}
-		}
-		return passed;
-	},
-
-	test: function(className, field, warn){
-		var validator = this.getValidator(className);
-		field = $(field);
-		if (field.hasClass('ignoreValidation')) return true;
-		warn = $pick(warn, false);
-		if (field.hasClass('warnOnly')) warn = true;
-		var isValid = validator ? validator.test(field) : true;
-		if (validator && this.isVisible(field)) this.fireEvent('elementValidate', [isValid, field, className, warn]);
-		if (warn) return true;
-		return isValid;
-	},
-
-	isVisible : function(field){
-		if (!this.options.ignoreHidden) return true;
-		while(field != document.body){
-			if ($(field).getStyle('display') == 'none') return false;
-			field = field.getParent();
-		}
-		return true;
-	},
-
-	resetField: function(field){
-		field = $(field);
-		if (field){
-			field.className.split(' ').each(function(className){
-				if (className.test('^warn-')) className = className.replace(/^warn-/, '');
-				field.removeClass('validation-failed');
-				field.removeClass('warning');
-				field.removeClass('validation-passed');
-			}, this);
-		}
-		return this;
-	},
-
-	stop: function(){
-		this.paused = true;
-		return this;
-	},
-
-	start: function(){
-		this.paused = false;
-		return this;
-	},
-
-	ignoreField: function(field, warn){
-		field = $(field);
-		if (field){
-			this.enforceField(field);
-			if (warn) field.addClass('warnOnly');
-			else field.addClass('ignoreValidation');
-		}
-		return this;
-	},
-
-	enforceField: function(field){
-		field = $(field);
-		if (field) field.removeClass('warnOnly').removeClass('ignoreValidation');
-		return this;
-	}
-
-});
-
-FormValidator.getMsg = function(key){
-	return MooTools.lang.get('FormValidator', key);
-};
-
-FormValidator.adders = {
-
-	validators:{},
-
-	add : function(className, options){
-		this.validators[className] = new InputValidator(className, options);
-		//if this is a class (this method is used by instances of FormValidator and the FormValidator namespace)
-		//extend these validators into it
-		//this allows validators to be global and/or per instance
-		if (!this.initialize){
-			this.implement({
-				validators: this.validators
-			});
-		}
-	},
-
-	addAllThese : function(validators){
-		$A(validators).each(function(validator){
-			this.add(validator[0], validator[1]);
-		}, this);
-	},
-
-	getValidator: function(className){
-		return this.validators[className.split(':')[0]];
-	}
-
-};
-
-$extend(FormValidator, FormValidator.adders);
-
-FormValidator.implement(FormValidator.adders);
-
-FormValidator.add('IsEmpty', {
-
-	errorMsg: false,
-	test: function(element){
-		if (element.type == 'select-one' || element.type == 'select')
-			return !(element.selectedIndex >= 0 && element.options[element.selectedIndex].value != '');
-		else
-			return ((element.get('value') == null) || (element.get('value').length == 0));
-	}
-
-});
-
-FormValidator.addAllThese([
-
-	['required', {
-		errorMsg: function(){
-			return FormValidator.getMsg('required');
-		},
-		test: function(element){
-			return !FormValidator.getValidator('IsEmpty').test(element);
-		}
-	}],
-
-	['minLength', {
-		errorMsg: function(element, props){
-			if ($type(props.minLength))
-				return FormValidator.getMsg('minLength').substitute({minLength:props.minLength,length:element.get('value').length });
-			else return '';
-		},
-		test: function(element, props){
-			if ($type(props.minLength)) return (element.get('value').length >= $pick(props.minLength, 0));
-			else return true;
-		}
-	}],
-
-	['maxLength', {
-		errorMsg: function(element, props){
-			//props is {maxLength:10}
-			if ($type(props.maxLength))
-				return FormValidator.getMsg('maxLength').substitute({maxLength:props.maxLength,length:element.get('value').length });
-			else return '';
-		},
-		test: function(element, props){
-			//if the value is <= than the maxLength value, element passes test
-			return (element.get('value').length <= $pick(props.maxLength, 10000));
-		}
-	}],
-
-	['validate-integer', {
-		errorMsg: FormValidator.getMsg.pass('integer'),
-		test: function(element){
-			return FormValidator.getValidator('IsEmpty').test(element) || (/^-?[1-9]\d*$/).test(element.get('value'));
-		}
-	}],
-
-	['validate-numeric', {
-		errorMsg: FormValidator.getMsg.pass('numeric'),
-		test: function(element){
-			return FormValidator.getValidator('IsEmpty').test(element) ||
-				(/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(element.get('value'));
-		}
-	}],
-
-	['validate-digits', {
-		errorMsg: FormValidator.getMsg.pass('digits'),
-		test: function(element){
-			return FormValidator.getValidator('IsEmpty').test(element) || (/^[\d() .:\-\+#]+$/.test(element.get('value')));
-		}
-	}],
-
-	['validate-alpha', {
-		errorMsg: FormValidator.getMsg.pass('alpha'),
-		test: function(element){
-			return FormValidator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z]+$/).test(element.get('value'));
-		}
-	}],
-
-	['validate-alphanum', {
-		errorMsg: FormValidator.getMsg.pass('alphanum'),
-		test: function(element){
-			return FormValidator.getValidator('IsEmpty').test(element) || !(/\W/).test(element.get('value'));
-		}
-	}],
-
-	['validate-date', {
-		errorMsg: function(element, props){
-			if (Date.parse){
-				var format = props.dateFormat || '%x';
-				return FormValidator.getMsg('dateSuchAs').substitute({date: new Date().format(format)});
-			} else {
-				return FormValidator.getMsg('dateInFormatMDY');
-			}
-		},
-		test: function(element, props){
-			if (FormValidator.getValidator('IsEmpty').test(element)) return true;
-			var d;
-			if (Date.parse){
-				var format = props.dateFormat || '%x';
-				d = Date.parse(element.get('value'));
-				var formatted = d.format(format);
-				if (formatted != 'invalid date') element.set('value', formatted);
-				return !isNaN(d);
-			} else {
-				var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
-				if (!regex.test(element.get('value'))) return false;
-				d = new Date(element.get('value').replace(regex, '$1/$2/$3'));
-				return (parseInt(RegExp.$1, 10) == (1 + d.getMonth())) &&
-					(parseInt(RegExp.$2, 10) == d.getDate()) &&
-					(parseInt(RegExp.$3, 10) == d.getFullYear());
-			}
-		}
-	}],
-
-	['validate-email', {
-		errorMsg: FormValidator.getMsg.pass('email'),
-		test: function(element){
-			return FormValidator.getValidator('IsEmpty').test(element) || (/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i).test(element.get('value'));
-		}
-	}],
-
-	['validate-url', {
-		errorMsg: FormValidator.getMsg.pass('url'),
-		test: function(element){
-			return FormValidator.getValidator('IsEmpty').test(element) || (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(element.get('value'));
-		}
-	}],
-
-	['validate-currency-dollar', {
-		errorMsg: FormValidator.getMsg.pass('currencyDollar'),
-		test: function(element){
-			// [$]1[##][,###]+[.##]
-			// [$]1###+[.##]
-			// [$]0.##
-			// [$].##
-			return FormValidator.getValidator('IsEmpty').test(element) ||  (/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value'));
-		}
-	}],
-
-	['validate-one-required', {
-		errorMsg: FormValidator.getMsg.pass('oneRequired'),
-		test: function(element, props){
-			var p = $(props['validate-one-required']) || element.parentNode;
-			return p.getElements('input').some(function(el){
-				if (['checkbox', 'radio'].contains(el.get('type'))) return el.get('checked');
-				return el.get('value');
-			});
-		}
-	}]
-
-]);
-
-Element.Properties.validator = {
-
-	set: function(options){
-		var validator = this.retrieve('validator');
-		if (validator) validator.setOptions(options);
-		return this.store('validator:options');
-	},
-
-	get: function(options){
-		if (options || !this.retrieve('validator')){
-			if (options || !this.retrieve('validator:options')) this.set('validator', options);
-			this.store('validator', new FormValidator(this, this.retrieve('validator:options')));
-		}
-		return this.retrieve('validator');
-	}
-
-};
-
-Element.implement({
-
-	validate: function(options){
-		this.set('validator', options);
-		return this.get('validator', options).validate();
-	}
-
 });/*
-Script: FormValidator.Inline.js
-	Extends FormValidator to add inline messages.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Sort
 
-	Authors:
-		Aaron Newton
-*/
+description: Base class for the sort algorithm implementations
 
-FormValidator.Inline = new Class({
+license: MIT-style license.
 
-	Extends: FormValidator,
+requires:
+ - Jx.Object
+ - Jx.Compare
 
-	options: {
-		scrollToErrorsOnSubmit: true,
-		scrollFxOptions: {
-			offset: {
-				y: -20
-			}
-		}
-	},
+provides: [Jx.Sort]
 
-	initialize: function(form, options){
-		this.parent(form, options);
-		this.addEvent('onElementValidate', function(isValid, field, className, warn){
-			var validator = this.getValidator(className);
-			if (!isValid && validator.getError(field)){
-				if (warn) field.addClass('warning');
-				var advice = this.makeAdvice(className, field, validator.getError(field), warn);
-				this.insertAdvice(advice, field);
-				this.showAdvice(className, field);
-			} else {
-				this.hideAdvice(className, field);
-			}
-		});
-	},
+...
+ */
+// $Id: sort.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Sort
+ * Base class for all of the sorting algorithm classes.
+ *
+ * Extends: <Jx.Object>
+ *
+ * Events:
+ * onStart() - called when the sort starts
+ * onEnd() - called when the sort stops
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort = new Class({
 
-	makeAdvice: function(className, field, error, warn){
-		var errorMsg = (warn)?this.warningPrefix:this.errorPrefix;
-				errorMsg += (this.options.useTitles) ? field.title || error:error;
-		var cssClass = (warn) ? 'warning-advice' : 'validation-advice';
-		var advice = this.getAdvice(className, field);
-		if(advice) {
-			advice = advice.clone(true).set('html', errorMsg).replaces(advice);
-		} else {
-			advice = new Element('div', {
-				html: errorMsg,
-				styles: { display: 'none' },
-				id: 'advice-' + className + '-' + this.getFieldId(field)
-			}).addClass(cssClass);
-		}
-		field.store('advice-' + className, advice);
-		return advice;
-	},
+    Family : 'Jx.Sort',
 
-	getFieldId : function(field){
-		return field.id ? field.id : field.id = 'input_' + field.name;
-	},
+    Extends : Jx.Object,
 
-	showAdvice: function(className, field){
-		var advice = this.getAdvice(className, field);
-		if (advice && !field.retrieve(this.getPropName(className))
-				&& (advice.getStyle('display') == 'none'
-				|| advice.getStyle('visiblity') == 'hidden'
-				|| advice.getStyle('opacity') == 0)){
-			field.store(this.getPropName(className), true);
-			if (advice.reveal) advice.reveal();
-			else advice.setStyle('display', 'block');
-		}
-	},
+    options : {
+        /**
+         * Option: timeIt
+         * whether to time the sort
+         */
+        timeIt : false,
+        /**
+         * Event: onStart
+         */
+        onStart : $empty,
+        /**
+         * Event: onEnd
+         */
+        onEnd : $empty
+    },
 
-	hideAdvice: function(className, field){
-		var advice = this.getAdvice(className, field);
-		if (advice && field.retrieve(this.getPropName(className))){
-			field.store(this.getPropName(className), false);
-			//if Fx.Reveal.js is present, transition the advice out
-			if (advice.dissolve) advice.dissolve();
-			else advice.setStyle('display', 'none');
-		}
-	},
+    /**
+     * Property: timer
+     * holds the timer instance
+     */
+    timer : null,
+    /**
+     * Property: data
+     * The data to sort
+     */
+    data : null,
+    /**
+     * Property: Comparator
+     * The comparator to use in sorting
+     */
+    comparator : $empty,
+    /**
+     * Property: col
+     * The column to sort by
+     */
+    col : null,
 
-	getPropName: function(className){
-		return 'advice' + className;
-	},
+    parameters: ['data','fn','col','options'],
 
-	resetField: function(field){
-		field = $(field);
-		if (!field) return this;
-		this.parent(field);
-		field.className.split(' ').each(function(className){
-			this.hideAdvice(className, field);
-		}, this);
-		return this;
-	},
+    /**
+     * APIMethod: init
+     */
+    init : function () {
+        this.parent();
+        if (this.options.timeIt) {
+            this.addEvent('start', this.startTimer.bind(this));
+            this.addEvent('stop', this.stopTimer.bind(this));
+        }
+        this.data = this.options.data;
+        this.comparator = this.options.fn;
+        this.col = this.options.col;
+    },
 
-	getAllAdviceMessages: function(field, force){
-		var advice = [];
-		if (field.hasClass('ignoreValidation') && !force) return advice;
-		var validators = field.className.split(' ').some(function(cn){
-			var warner = cn.test('^warn-') || field.hasClass('warnOnly');
-			if (warner) cn = cn.replace(/^warn-/, '');
-			var validator = this.getValidator(cn);
-			if (!validator) return;
-			advice.push({
-				message: validator.getError(field),
-				warnOnly: warner,
-				passed: validator.test(),
-				validator: validator
-			});
-		}, this);
-		return advice;
-	},
+    /**
+     * APIMethod: sort
+     * Actually does the sorting. Must be overridden by subclasses.
+     */
+    sort : $empty,
 
-	getAdvice: function(className, field){
-		return field.retrieve('advice-' + className);
-	},
+    /**
+     * Method: startTimer
+     * Saves the starting time of the sort
+     */
+    startTimer : function () {
+        this.timer = new Date();
+    },
 
-	insertAdvice: function(advice, field){
-		//Check for error position prop
-		var props = field.get('validatorProps');
-		//Build advice
-		if (!props.msgPos || !$(props.msgPos)){
-			if(field.type.toLowerCase() == 'radio') field.getParent().adopt(advice);
-			else advice.inject($(field), 'after');
-		} else {
-			$(props.msgPos).grab(advice);
-		}
-	},
+    /**
+     * Method: stopTimer
+     * Determines the time the sort took.
+     */
+    stopTimer : function () {
+        this.end = new Date();
+        this.dif = this.timer.diff(this.end, 'ms');
+    },
 
-	validate: function(field, force){
-		var result = this.parent(field, force);
-		if (this.options.scrollToErrorsOnSubmit && !result){
-			var failed = $(this).getElement('.validation-failed');
-			var par = $(this).getParent();
-			var isScrolled = function(p){
-				return p.getScrollSize().y != p.getSize().y;
-			};
-			var scrolls;
-			while (par != document.body && !isScrolled(par)){
-				par = par.getParent();
-			}
-			var fx = par.retrieve('fvScroller');
-			if (!fx && window.Fx && Fx.Scroll){
-				fx = new Fx.Scroll(par, {
-					transition: 'quad:out',
-					offset: {
-						y: -20
-					}
-				});
-				par.store('fvScroller', fx);
-			}
-			if (failed){
-				if (fx) fx.toElement(failed);
-				else par.scrollTo(par.getScroll().x, failed.getPosition(par).y - 20);
-			}
-		}
-	}
+    /**
+     * APIMethod: setData
+     * sets the data to sort
+     *
+     * Parameters:
+     * data - the data to sort
+     */
+    setData : function (data) {
+        if ($defined(data)) {
+            this.data = data;
+        }
+    },
 
-});
-/*
-Script: FormValidator.Extras.js
-	Additional validators for the FormValidator class.
+    /**
+     * APIMethod: setColumn
+     * Sets the column to sort by
+     *
+     * Parameters:
+     * col - the column to sort by
+     */
+    setColumn : function (col) {
+        if ($defined(col)) {
+            this.col = col;
+        }
+    },
 
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-*/
-FormValidator.addAllThese([
-
-	['validate-enforce-oncheck', {
-		test: function(element, props){
-			if (element.checked){
-				var fv = element.getParent('form').retrieve('validator');
-				if (!fv) return true;
-				(props.toEnforce || $(props.enforceChildrenOf).getElements('input, select, textarea')).map(function(item){
-					fv.enforceField(item);
-				});
-			}
-			return true;
-		}
-	}],
-
-	['validate-ignore-oncheck', {
-		test: function(element, props){
-			if (element.checked){
-				var fv = element.getParent('form').retrieve('validator');
-				if (!fv) return true;
-				(props.toIgnore || $(props.ignoreChildrenOf).getElements('input, select, textarea')).each(function(item){
-					fv.ignoreField(item);
-					fv.resetField(item);
-				});
-			}
-			return true;
-		}
-	}],
-
-	['validate-nospace', {
-		errorMsg: function(){
-			return FormValidator.getMsg('noSpace');
-		},
-		test: function(element, props){
-			return !element.get('value').test(/\s/);
-		}
-	}],
-
-	['validate-toggle-oncheck', {
-		test: function(element, props){
-			var fv = element.getParent('form').retrieve('validator');
-			if (!fv) return true;
-			var eleArr = props.toToggle || $(props.toToggleChildrenOf).getElements('input, select, textarea');
-			if (!element.checked){
-				eleArr.each(function(item){
-					fv.ignoreField(item);
-					fv.resetField(item);
-				});
-			} else {
-				eleArr.each(function(item){
-					fv.enforceField(item);
-				});
-			}
-			return true;
-		}
-	}],
-
-	['validate-reqchk-bynode', {
-		errorMsg: function(){
-			return FormValidator.getMsg('reqChkByNode');
-		},
-		test: function(element, props){
-			return ($(props.nodeId).getElements(props.selector || 'input[type=checkbox], input[type=radio]')).some(function(item){
-				return item.checked;
-			});
-		}
-	}],
-
-	['validate-required-check', {
-		errorMsg: function(element, props){
-			return props.useTitle ? element.get('title') : FormValidator.getMsg('requiredChk');
-		},
-		test: function(element, props){
-			return !!element.checked;
-		}
-	}],
-
-	['validate-reqchk-byname', {
-		errorMsg: function(element, props){
-			return FormValidator.getMsg('reqChkByName').substitute({label: props.label || element.get('type')});
-		},
-		test: function(element, props){
-			var grpName = props.groupName || element.get('name');
-			var oneCheckedItem = $$(document.getElementsByName(grpName)).some(function(item, index){
-				return item.checked;
-			});
-			var fv = element.getParent('form').retrieve('validator');
-			if (oneCheckedItem && fv) fv.resetField(element);
-			return oneCheckedItem;
-		}
-	}],
-
-	['validate-match', {
-		errorMsg: function(element, props){
-			return FormValidator.getMsg('match').substitute({matchName: props.matchName || $(props.matchInput).get('name')});
-		},
-		test: function(element, props){
-			var eleVal = element.get('value');
-			var matchVal = $(props.matchInput) && $(props.matchInput).get('value');
-			return eleVal && matchVal ? eleVal == matchVal : true;
-		}
-	}],
-
-	['validate-after-date', {
-		errorMsg: function(element, props){
-			return FormValidator.getMsg('afterDate').substitute({
-				label: props.afterLabel || (props.afterElement ? FormValidator.getMsg('startDate') : FormValidator.getMsg('currentDate'))
-			});
-		},
-		test: function(element, props){
-			var start = $(props.afterElement) ? Date.parse($(props.afterElement).get('value')) : new Date();
-			var end = Date.parse(element.get('value'));
-			return end && start ? end >= start : true;
-		}
-	}],
-
-	['validate-before-date', {
-		errorMsg: function(element, props){
-			return FormValidator.getMsg('beforeDate').substitute({
-				label: props.beforeLabel || (props.beforeElement ? FormValidator.getMsg('endDate') : FormValidator.getMsg('currentDate'))
-			});
-		},
-		test: function(element, props){
-			var start = Date.parse(element.get('value'));
-			var end = $(props.beforeElement) ? Date.parse($(props.beforeElement).get('value')) : new Date();
-			return end && start ? end >= start : true;
-		}
-	}],
-
-	['validate-custom-required', {
-		errorMsg: function(){
-			return FormValidator.getMsg('required');
-		},
-		test: function(element, props){
-			return element.get('value') != props.emptyValue;
-		}
-	}],
-
-	['validate-same-month', {
-		errorMsg: function(element, props){
-			var startMo = $(props.sameMonthAs) && $(props.sameMonthAs).get('value');
-			var eleVal = element.get('value');
-			if (eleVal != '') return FormValidator.getMsg(startMo ? 'sameMonth' : 'startMonth');
-		},
-		test: function(element, props){
-			var d1 = Date.parse(element.get('value'));
-			var d2 = Date.parse($(props.sameMonthAs) && $(props.sameMonthAs).get('value'));
-			return d1 && d2 ? d1.format('%B') == d2.format('%B') : true;
-		}
-	}]
-
-]);/*
-Script: OverText.js
-	Shows text over an input that disappears when the user clicks into it. The text remains hidden if the user adds a value.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-*/
-
-var OverText = new Class({
-
-	Implements: [Options, Events, Class.Occlude],
-
-	Binds: ['reposition', 'assert', 'focus'],
-
-	options: {/*
-		textOverride: null,
-		onFocus: $empty()
-		onTextHide: $empty(textEl, inputEl),
-		onTextShow: $empty(textEl, inputEl), */
-		positionOptions: {
-			position: 'upperLeft',
-			edge: 'upperLeft',
-			offset: {
-				x: 4,
-				y: 2
-			}
-		},
-		poll: false,
-		pollInterval: 250
-	},
-
-	property: 'OverText',
-
-	initialize: function(element, options){
-		this.element = $(element);
-		if (this.occlude()) return this.occluded;
-		this.setOptions(options);
-		this.attach(this.element);
-		OverText.instances.push(this);
-		if (this.options.poll) this.poll();
-		return this;
-	},
-
-	toElement: function(){
-		return this.element;
-	},
-
-	attach: function(){
-		var val = this.options.textOverride || this.element.get('alt') || this.element.get('title');
-		if (!val) return;
-		this.text = new Element('div', {
-			'class': 'overTxtDiv',
-			styles: {
-				lineHeight: 'normal',
-				position: 'absolute'
-			},
-			html: val,
-			events: {
-				click: this.hide.pass(true, this)
-			}
-		}).inject(this.element, 'after');
-		this.element.addEvents({
-			focus: this.focus,
-			blur: this.assert,
-			change: this.assert
-		}).store('OverTextDiv', this.text);
-		window.addEvent('resize', this.reposition.bind(this));
-		this.assert();
-		this.reposition();
-	},
-
-	startPolling: function(){
-		this.pollingPaused = false;
-		return this.poll();
-	},
-
-	poll: function(stop){
-		//start immediately
-		//pause on focus
-		//resumeon blur
-		if (this.poller && !stop) return this;
-		var test = function(){
-			if (!this.pollingPaused) this.assert();
-		}.bind(this);
-		if (stop) $clear(this.poller);
-		else this.poller = test.periodical(this.options.pollInterval, this);
-		return this;
-	},
-
-	stopPolling: function(){
-		this.pollingPaused = true;
-		return this.poll(true);
-	},
-
-	focus: function(){
-		if (!this.text.isDisplayed() || this.element.get('disabled')) return;
-		this.hide();
-	},
-
-	hide: function(){
-		if (this.text.isDisplayed() && !this.element.get('disabled')){
-			this.text.hide();
-			this.fireEvent('textHide', [this.text, this.element]);
-			this.pollingPaused = true;
-			try {
-				this.element.fireEvent('focus').focus();
-			} catch(e){} //IE barfs if you call focus on hidden elements
-		}
-		return this;
-	},
-
-	show: function(){
-		if (!this.text.isDisplayed()){
-			this.text.show();
-			this.reposition();
-			this.fireEvent('textShow', [this.text, this.element]);
-			this.pollingPaused = false;
-		}
-		return this;
-	},
-
-	assert: function(){
-		this[this.test() ? 'show' : 'hide']();
-	},
-
-	test: function(){
-		var v = this.element.get('value');
-		return !v;
-	},
-
-	reposition: function(){
-		try {
-			this.assert();
-			if (!this.element.getParent() || !this.element.offsetHeight) return this.hide();
-			if (this.test()) this.text.position($merge(this.options.positionOptions, {relativeTo: this.element}));
-		} catch(e){	}
-		return this;
-	}
-
+    /**
+     * APIMethod: setComparator
+     * Sets the comparator to use in sorting
+     *
+     * Parameters:
+     * fn - the function to use as the comparator
+     */
+    setComparator : function (fn) {
+        this.comparator = fn;
+    }
 });
-
-OverText.instances = [];
-
-OverText.update = function(){
-
-	return OverText.instances.map(function(ot){
-		if (ot.element && ot.text) return ot.reposition();
-		return null; //the input or the text was destroyed
-	});
-
-};
-
-if (window.Fx && Fx.Reveal) {
-	Fx.Reveal.implement({
-		hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed, .overTxtDiv' : false
-	});
-}/*
-Script: Fx.Elements.js
-	Effect to change any number of CSS properties of any number of Elements.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Valerio Proietti
-*/
-
-Fx.Elements = new Class({
-
-	Extends: Fx.CSS,
-
-	initialize: function(elements, options){
-		this.elements = this.subject = $$(elements);
-		this.parent(options);
-	},
-
-	compute: function(from, to, delta){
-		var now = {};
-		for (var i in from){
-			var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
-			for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
-		}
-		return now;
-	},
-
-	set: function(now){
-		for (var i in now){
-			var iNow = now[i];
-			for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
-		}
-		return this;
-	},
-
-	start: function(obj){
-		if (!this.check(obj)) return this;
-		var from = {}, to = {};
-		for (var i in obj){
-			var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
-			for (var p in iProps){
-				var parsed = this.prepare(this.elements[i], p, iProps[p]);
-				iFrom[p] = parsed.from;
-				iTo[p] = parsed.to;
-			}
-		}
-		return this.parent(from, to);
-	}
-
-});/*
-Script: Fx.Accordion.js
-	An Fx.Elements extension which allows you to easily create accordion type controls.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Valerio Proietti
-*/
-
-var Accordion = Fx.Accordion = new Class({
-
-	Extends: Fx.Elements,
-
-	options: {/*
-		onActive: $empty(toggler, section),
-		onBackground: $empty(toggler, section),*/
-		display: 0,
-		show: false,
-		height: true,
-		width: false,
-		opacity: true,
-		fixedHeight: false,
-		fixedWidth: false,
-		wait: false,
-		alwaysHide: false,
-		trigger: 'click',
-		initialDisplayFx: true
-	},
-
-	initialize: function(){
-		var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
-		this.parent(params.elements, params.options);
-		this.togglers = $$(params.togglers);
-		this.container = $(params.container);
-		this.previous = -1;
-		if (this.options.alwaysHide) this.options.wait = true;
-		if ($chk(this.options.show)){
-			this.options.display = false;
-			this.previous = this.options.show;
-		}
-		if (this.options.start){
-			this.options.display = false;
-			this.options.show = false;
-		}
-		this.effects = {};
-		if (this.options.opacity) this.effects.opacity = 'fullOpacity';
-		if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
-		if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
-		for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
-		this.elements.each(function(el, i){
-			if (this.options.show === i){
-				this.fireEvent('active', [this.togglers[i], el]);
-			} else {
-				for (var fx in this.effects) el.setStyle(fx, 0);
-			}
-		}, this);
-		if ($chk(this.options.display)) this.display(this.options.display, this.options.initialDisplayFx);
-	},
-
-	addSection: function(toggler, element){
-		toggler = $(toggler);
-		element = $(element);
-		var test = this.togglers.contains(toggler);
-		this.togglers.include(toggler);
-		this.elements.include(element);
-		var idx = this.togglers.indexOf(toggler);
-		toggler.addEvent(this.options.trigger, this.display.bind(this, idx));
-		if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
-		if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
-		element.fullOpacity = 1;
-		if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
-		if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
-		element.setStyle('overflow', 'hidden');
-		if (!test){
-			for (var fx in this.effects) element.setStyle(fx, 0);
-		}
-		return this;
-	},
-
-	display: function(index, useFx){
-		useFx = $pick(useFx, true);
-		index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
-		if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
-		this.previous = index;
-		var obj = {};
-		this.elements.each(function(el, i){
-			obj[i] = {};
-			var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
-			this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
-			for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
-		}, this);
-		return useFx ? this.start(obj) : this.set(obj);
-	}
-
-});/*
-Script: Fx.Move.js
-	Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-
-*/
-
-Fx.Move = new Class({
-
-	Extends: Fx.Morph,
-
-	options: {
-		relativeTo: document.body,
-		position: 'center',
-		edge: false,
-		offset: {x: 0, y: 0}
-	},
-
-	start: function(destination){
-		return this.parent(this.element.position($merge(this.options, destination, {returnPos: true})));
-	}
-
-});
-
-Element.Properties.move = {
-
-	set: function(options){
-		var morph = this.retrieve('move');
-		if (morph) morph.cancel();
-		return this.eliminate('move').store('move:options', $extend({link: 'cancel'}, options));
-	},
-
-	get: function(options){
-		if (options || !this.retrieve('move')){
-			if (options || !this.retrieve('move:options')) this.set('move', options);
-			this.store('move', new Fx.Move(this, this.retrieve('move:options')));
-		}
-		return this.retrieve('move');
-	}
-
-};
-
-Element.implement({
-
-	move: function(options){
-		this.get('move').start(options);
-		return this;
-	}
-
-});
 /*
-Script: Fx.Reveal.js
-	Defines Fx.Reveal, a class that shows and hides elements with a transition.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Sort.Mergesort
 
-	Authors:
-		Aaron Newton
+description: An implementation of the merge sort algorithm
 
-*/
+license: MIT-style license.
 
-Fx.Reveal = new Class({
+requires:
+ - Jx.Sort
 
-	Extends: Fx.Morph,
+provides: [Jx.Sort.Mergesort]
 
-	options: {/*	  
-		onShow: $empty(thisElemeng),
-		onHide: $empty(thisElemeng),
-		onComplete: $empty(thisElemeng),
-		heightOverride: null,
-		widthOverride: null, */
-		styles: ['padding', 'border', 'margin'],
-		transitionOpacity: !Browser.Engine.trident4,
-		mode: 'vertical',
-		display: 'block',
-		hideInputs: Browser.Engine.trident ? 'select, input, textarea, object, embed' : false
-	},
+...
+ */
+// $Id: mergesort.js 977 2010-09-02 18:57:42Z pagameba $
+/**
+ * class: Jx.Sort.Mergesort
+ *
+ * Extends: <Jx.Sort>
+ *
+ * Implementation of a mergesort algorithm designed to
+ * work on <Jx.Store> data.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort.Mergesort = new Class({
+    Family: 'Jx.Sort.Mergesort',
+    Extends : Jx.Sort,
 
-	dissolve: function(){
-		try {
-			if (!this.hiding && !this.showing){
-				if (this.element.getStyle('display') != 'none'){
-					this.hiding = true;
-					this.showing = false;
-					this.hidden = true;
-					var startStyles = this.element.getComputedSize({
-						styles: this.options.styles,
-						mode: this.options.mode
-					});
-					var setToAuto = (this.element.style.height === ''||this.element.style.height == 'auto');
-					this.element.setStyle('display', 'block');
-					if (this.options.transitionOpacity) startStyles.opacity = 1;
-					var zero = {};
-					$each(startStyles, function(style, name){
-						zero[name] = [style, 0];
-					}, this);
-					var overflowBefore = this.element.getStyle('overflow');
-					this.element.setStyle('overflow', 'hidden');
-					var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
-					this.$chain.unshift(function(){
-						if (this.hidden){
-							this.hiding = false;
-							$each(startStyles, function(style, name){
-								startStyles[name] = style;
-							}, this);
-							this.element.setStyles($merge({display: 'none', overflow: overflowBefore}, startStyles));
-							if (setToAuto){
-								if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
-								if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
-							}
-							if (hideThese) hideThese.setStyle('visibility', 'visible');
-						}
-						this.fireEvent('hide', this.element);
-						this.callChain();
-					}.bind(this));
-					if (hideThese) hideThese.setStyle('visibility', 'hidden');
-					this.start(zero);
-				} else {
-					this.callChain.delay(10, this);
-					this.fireEvent('complete', this.element);
-					this.fireEvent('hide', this.element);
-				}
-			} else if (this.options.link == 'chain'){
-				this.chain(this.dissolve.bind(this));
-			} else if (this.options.link == 'cancel' && !this.hiding){
-				this.cancel();
-				this.dissolve();
-			}
-		} catch(e){
-			this.hiding = false;
-			this.element.setStyle('display', 'none');
-			this.callChain.delay(10, this);
-			this.fireEvent('complete', this.element);
-			this.fireEvent('hide', this.element);
-		}
-		return this;
-	},
+    name : 'mergesort',
 
-	reveal: function(){
-		try {
-			if (!this.showing && !this.hiding){
-				if (this.element.getStyle('display') == 'none' ||
-					 this.element.getStyle('visiblity') == 'hidden' ||
-					 this.element.getStyle('opacity') == 0){
-					this.showing = true;
-					this.hiding = false;
-					this.hidden = false;
-					var setToAuto, startStyles;
-					//toggle display, but hide it
-					this.element.measure(function(){
-						setToAuto = (this.element.style.height === '' || this.element.style.height == 'auto');
-						//create the styles for the opened/visible state
-						startStyles = this.element.getComputedSize({
-							styles: this.options.styles,
-							mode: this.options.mode
-						});
-					}.bind(this));
-					$each(startStyles, function(style, name){
-						startStyles[name] = style;
-					});
-					//if we're overridding height/width
-					if ($chk(this.options.heightOverride)) startStyles.height = this.options.heightOverride.toInt();
-					if ($chk(this.options.widthOverride)) startStyles.width = this.options.widthOverride.toInt();
-					if (this.options.transitionOpacity) {
-						this.element.setStyle('opacity', 0);
-						startStyles.opacity = 1;
-					}
-					//create the zero state for the beginning of the transition
-					var zero = {
-						height: 0,
-						display: this.options.display
-					};
-					$each(startStyles, function(style, name){ zero[name] = 0; });
-					var overflowBefore = this.element.getStyle('overflow');
-					//set to zero
-					this.element.setStyles($merge(zero, {overflow: 'hidden'}));
-					//hide inputs
-					var hideThese = this.options.hideInputs ? this.element.getElements(this.options.hideInputs) : null;
-					if (hideThese) hideThese.setStyle('visibility', 'hidden');
-					//start the effect
-					this.start(startStyles);
-					this.$chain.unshift(function(){
-						this.element.setStyle('overflow', overflowBefore);
-						if (!this.options.heightOverride && setToAuto){
-							if (['vertical', 'both'].contains(this.options.mode)) this.element.style.height = '';
-							if (['width', 'both'].contains(this.options.mode)) this.element.style.width = '';
-						}
-						if (!this.hidden) this.showing = false;
-						if (hideThese) hideThese.setStyle('visibility', 'visible');
-						this.callChain();
-						this.fireEvent('show', this.element);
-					}.bind(this));
-				} else {
-					this.callChain();
-					this.fireEvent('complete', this.element);
-					this.fireEvent('show', this.element);
-				}
-			} else if (this.options.link == 'chain'){
-				this.chain(this.reveal.bind(this));
-			} else if (this.options.link == 'cancel' && !this.showing){
-				this.cancel();
-				this.reveal();
-			}
-		} catch(e){
-			this.element.setStyles({
-				display: this.options.display,
-				visiblity: 'visible',
-				opacity: 1
-			});
-			this.showing = false;
-			this.callChain.delay(10, this);
-			this.fireEvent('complete', this.element);
-			this.fireEvent('show', this.element);
-		}
-		return this;
-	},
+    /**
+     * APIMethod: sort
+     * Actually runs the sort on the data
+     *
+     * returns: the sorted data
+     */
+    sort : function () {
+        this.fireEvent('start');
+        var d = this.mergeSort(this.data);
+        this.fireEvent('stop');
+        return d;
 
-	toggle: function(){
-		if (this.element.getStyle('display') == 'none' ||
-			 this.element.getStyle('visiblity') == 'hidden' ||
-			 this.element.getStyle('opacity') == 0){
-			this.reveal();
-		} else {
-			this.dissolve();
-		}
-		return this;
-	}
+    },
 
-});
+    /**
+     * Method: mergeSort
+     * Does the physical sorting. Called
+     * recursively.
+     *
+     * Parameters:
+     * arr - the array to sort
+     *
+     * returns: the sorted array
+     */
+    mergeSort : function (arr) {
+        if (arr.length <= 1) {
+            return arr;
+        }
 
-Element.Properties.reveal = {
+        var middle = (arr.length) / 2,
+            left = arr.slice(0, middle),
+            right = arr.slice(middle),
+            result;
+        left = this.mergeSort(left);
+        right = this.mergeSort(right);
+        result = this.merge(left, right);
+        return result;
+    },
 
-	set: function(options){
-		var reveal = this.retrieve('reveal');
-		if (reveal) reveal.cancel();
-		return this.eliminate('reveal').store('reveal:options', $extend({link: 'cancel'}, options));
-	},
+    /**
+     * Method: merge
+     * Does the work of merging to arrays in order.
+     *
+     * parameters:
+     * left - the left hand array
+     * right - the right hand array
+     *
+     * returns: the merged array
+     */
+    merge : function (left, right) {
+        var result = [];
 
-	get: function(options){
-		if (options || !this.retrieve('reveal')){
-			if (options || !this.retrieve('reveal:options')) this.set('reveal', options);
-			this.store('reveal', new Fx.Reveal(this, this.retrieve('reveal:options')));
-		}
-		return this.retrieve('reveal');
-	}
+        while (left.length > 0 && right.length > 0) {
+            if (this.comparator((left[0]).get(this.col), (right[0])
+                    .get(this.col)) <= 0) {
+                result.push(left[0]);
+                left = left.slice(1);
+            } else {
+                result.push(right[0]);
+                right = right.slice(1);
+            }
+        }
+        while (left.length > 0) {
+            result.push(left[0]);
+            left = left.slice(1);
+        }
+        while (right.length > 0) {
+            result.push(right[0]);
+            right = right.slice(1);
+        }
+        return result;
+    }
 
-};
-
-Element.Properties.dissolve = Element.Properties.reveal;
-
-Element.implement({
-
-	reveal: function(options){
-		this.get('reveal', options).reveal();
-		return this;
-	},
-
-	dissolve: function(options){
-		this.get('reveal', options).dissolve();
-		return this;
-	},
-
-	nix: function(){
-		var params = Array.link(arguments, {destroy: Boolean.type, options: Object.type});
-		this.get('reveal', params.options).dissolve().chain(function(){
-			this[params.destroy ? 'destroy' : 'dispose']();
-		}.bind(this));
-		return this;
-	},
-
-	wink: function(){
-		var params = Array.link(arguments, {duration: Number.type, options: Object.type});
-		var reveal = this.get('reveal', params.options);
-		reveal.reveal().chain(function(){
-			(function(){
-				reveal.dissolve();
-			}).delay(params.duration || 2000);
-		});
-	}
-
-
-});/*
-Script: Fx.Scroll.js
-	Effect to smoothly scroll any element, including the window.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Valerio Proietti
-*/
-
-Fx.Scroll = new Class({
-
-	Extends: Fx,
-
-	options: {
-		offset: {x: 0, y: 0},
-		wheelStops: true
-	},
-
-	initialize: function(element, options){
-		this.element = this.subject = $(element);
-		this.parent(options);
-		var cancel = this.cancel.bind(this, false);
-
-		if ($type(this.element) != 'element') this.element = $(this.element.getDocument().body);
-
-		var stopper = this.element;
-
-		if (this.options.wheelStops){
-			this.addEvent('start', function(){
-				stopper.addEvent('mousewheel', cancel);
-			}, true);
-			this.addEvent('complete', function(){
-				stopper.removeEvent('mousewheel', cancel);
-			}, true);
-		}
-	},
-
-	set: function(){
-		var now = Array.flatten(arguments);
-		this.element.scrollTo(now[0], now[1]);
-	},
-
-	compute: function(from, to, delta){
-		return [0, 1].map(function(i){
-			return Fx.compute(from[i], to[i], delta);
-		});
-	},
-
-	start: function(x, y){
-		if (!this.check(x, y)) return this;
-		var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize();
-		var scroll = this.element.getScroll(), values = {x: x, y: y};
-		for (var z in values){
-			var max = scrollSize[z] - offsetSize[z];
-			if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z].limit(0, max) : max;
-			else values[z] = scroll[z];
-			values[z] += this.options.offset[z];
-		}
-		return this.parent([scroll.x, scroll.y], [values.x, values.y]);
-	},
-
-	toTop: function(){
-		return this.start(false, 0);
-	},
-
-	toLeft: function(){
-		return this.start(0, false);
-	},
-
-	toRight: function(){
-		return this.start('right', false);
-	},
-
-	toBottom: function(){
-		return this.start(false, 'bottom');
-	},
-
-	toElement: function(el){
-		var position = $(el).getPosition(this.element);
-		return this.start(position.x, position.y);
-	}
-
 });
 /*
-Script: Fx.Slide.js
-	Effect to slide an element in and out of view.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Sort.Heapsort
 
-	Authors:
-		Valerio Proietti
-*/
+description: An implementation of the heap sort algorithm
 
-Fx.Slide = new Class({
+license: MIT-style license.
 
-	Extends: Fx,
+requires:
+ - Jx.Sort
 
-	options: {
-		mode: 'vertical'
-	},
+provides: [Jx.Sort.Heapsort]
 
-	initialize: function(element, options){
-		this.addEvent('complete', function(){
-			this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
-			if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
-		}, true);
-		this.element = this.subject = $(element);
-		this.parent(options);
-		var wrapper = this.element.retrieve('wrapper');
-		this.wrapper = wrapper || new Element('div', {
-			styles: $extend(this.element.getStyles('margin', 'position'), {overflow: 'hidden'})
-		}).wraps(this.element);
-		this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
-		this.now = [];
-		this.open = true;
-	},
+...
+ */
+// $Id: heapsort.js 977 2010-09-02 18:57:42Z pagameba $
+/**
+ * Class: Jx.Sort.Heapsort
+ *
+ * Extends: <Jx.Sort>
+ *
+ * Implementation of a heapsort algorithm designed to
+ * work on <Jx.Store> data.
+ *
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort.Heapsort = new Class({
+    Family: 'Jx.Sort.Heapsort',
+    Extends : Jx.Sort,
 
-	vertical: function(){
-		this.margin = 'margin-top';
-		this.layout = 'height';
-		this.offset = this.element.offsetHeight;
-	},
+    name : 'heapsort',
 
-	horizontal: function(){
-		this.margin = 'margin-left';
-		this.layout = 'width';
-		this.offset = this.element.offsetWidth;
-	},
+    /**
+     * APIMethod: sort
+     * Actually runs the sort on the data
+     *
+     * Returns: the sorted data
+     */
+    sort : function () {
+        this.fireEvent('start');
 
-	set: function(now){
-		this.element.setStyle(this.margin, now[0]);
-		this.wrapper.setStyle(this.layout, now[1]);
-		return this;
-	},
+        var count = this.data.length,
+            end;
 
-	compute: function(from, to, delta){
-		return [0, 1].map(function(i){
-			return Fx.compute(from[i], to[i], delta);
-		});
-	},
+        if (count === 1) {
+            return this.data;
+        }
 
-	start: function(how, mode){
-		if (!this.check(how, mode)) return this;
-		this[mode || this.options.mode]();
-		var margin = this.element.getStyle(this.margin).toInt();
-		var layout = this.wrapper.getStyle(this.layout).toInt();
-		var caseIn = [[margin, layout], [0, this.offset]];
-		var caseOut = [[margin, layout], [-this.offset, 0]];
-		var start;
-		switch (how){
-			case 'in': start = caseIn; break;
-			case 'out': start = caseOut; break;
-			case 'toggle': start = (layout == 0) ? caseIn : caseOut;
-		}
-		return this.parent(start[0], start[1]);
-	},
+        if (count > 2) {
+            this.heapify(count);
 
-	slideIn: function(mode){
-		return this.start('in', mode);
-	},
+            end = count - 1;
+            while (end > 1) {
+                this.data.swap(end, 0);
+                end = end - 1;
+                this.siftDown(0, end);
+            }
+        } else {
+            // check then order the two we have
+            if ((this.comparator((this.data[0]).get(this.col), (this.data[1])
+                    .get(this.col)) > 0)) {
+                this.data.swap(0, 1);
+            }
+        }
 
-	slideOut: function(mode){
-		return this.start('out', mode);
-	},
+        this.fireEvent('stop');
+        return this.data;
+    },
 
-	hide: function(mode){
-		this[mode || this.options.mode]();
-		this.open = false;
-		return this.set([-this.offset, 0]);
-	},
+    /**
+     * Method: heapify
+     * Puts the data in Max-heap order
+     *
+     * Parameters: count - the number of records we're sorting
+     */
+    heapify : function (count) {
+        var start = Math.round((count - 2) / 2);
 
-	show: function(mode){
-		this[mode || this.options.mode]();
-		this.open = true;
-		return this.set([0, this.offset]);
-	},
+        while (start >= 0) {
+            this.siftDown(start, count - 1);
+            start = start - 1;
+        }
+    },
 
-	toggle: function(mode){
-		return this.start('toggle', mode);
-	}
+    /**
+     * Method: siftDown
+     *
+     * Parameters: start - the beginning of the sort range end - the end of the
+     * sort range
+     */
+    siftDown : function (start, end) {
+        var root = start,
+            child;
 
-});
+        while (root * 2 <= end) {
+            child = root * 2;
+            if ((child + 1 < end) && (this.comparator((this.data[child]).get(this.col),
+                            (this.data[child + 1]).get(this.col)) < 0)) {
+                child = child + 1;
+            }
+            if ((this.comparator((this.data[root]).get(this.col),
+                    (this.data[child]).get(this.col)) < 0)) {
+                this.data.swap(root, child);
+                root = child;
+            } else {
+                return;
+            }
+        }
+    }
 
-Element.Properties.slide = {
-
-	set: function(options){
-		var slide = this.retrieve('slide');
-		if (slide) slide.cancel();
-		return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));
-	},
-
-	get: function(options){
-		if (options || !this.retrieve('slide')){
-			if (options || !this.retrieve('slide:options')) this.set('slide', options);
-			this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
-		}
-		return this.retrieve('slide');
-	}
-
-};
-
-Element.implement({
-
-	slide: function(how, mode){
-		how = how || 'toggle';
-		var slide = this.get('slide'), toggle;
-		switch (how){
-			case 'hide': slide.hide(mode); break;
-			case 'show': slide.show(mode); break;
-			case 'toggle':
-				var flag = this.retrieve('slide:flag', slide.open);
-				slide[flag ? 'slideOut' : 'slideIn'](mode);
-				this.store('slide:flag', !flag);
-				toggle = true;
-			break;
-			default: slide.start(how, mode);
-		}
-		if (!toggle) this.eliminate('slide:flag');
-		return this;
-	}
-
 });
 /*
-Script: Fx.SmoothScroll.js
-	Class for creating a smooth scrolling effect to all internal links on the page.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Sort.Quicksort
 
-	Authors:
-		Valerio Proietti
-*/
+description: An implementation of the quick sort algorithm.
 
-var SmoothScroll = Fx.SmoothScroll = new Class({
+license: MIT-style license.
 
-	Extends: Fx.Scroll,
+requires:
+ - Jx.Sort
 
-	initialize: function(options, context){
-		context = context || document;
-		this.doc = context.getDocument();
-		var win = context.getWindow();
-		this.parent(this.doc, options);
-		this.links = this.options.links ? $$(this.options.links) : $$(this.doc.links);
-		var location = win.location.href.match(/^[^#]*/)[0] + '#';
-		this.links.each(function(link){
-			if (link.href.indexOf(location) != 0) {return;}
-			var anchor = link.href.substr(location.length);
-			if (anchor) this.useLink(link, anchor);
-		}, this);
-		if (!Browser.Engine.webkit419) {
-			this.addEvent('complete', function(){
-				win.location.hash = this.anchor;
-			}, true);
-		}
-	},
+provides: [Jx.Sort.Quicksort]
 
-	useLink: function(link, anchor){
-		var el;
-		link.addEvent('click', function(event){
-			if (el !== false && !el) el = $(anchor) || this.doc.getElement('a[name=' + anchor + ']');
-			if (el) {
-				event.preventDefault();
-				this.anchor = anchor;
-				this.toElement(el);
-				link.blur();
-			}
-		}.bind(this));
-	}
+...
+ */
+// $Id: quicksort.js 977 2010-09-02 18:57:42Z pagameba $
+/**
+ * Class: Jx.Sort.Quicksort
+ *
+ * Extends: <Jx.Sort>
+ *
+ * Implementation of a quicksort algorithm designed to
+ * work on <Jx.Store> data.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort.Quicksort = new Class({
+    Family: 'Jx.Sort.Quicksort',
+    Extends : Jx.Sort,
 
-});/*
-Script: Fx.Sort.js
-	Defines Fx.Sort, a class that reorders lists with a transition.
+    name : 'quicksort',
 
-	License:
-		MIT-style license.
+    /**
+     * APIMethod: sort
+     * Actually runs the sort on the data
+     *
+     * returns: the sorted data
+     */
+    sort : function (left, right) {
+        this.fireEvent('start');
 
-	Authors:
-		Aaron Newton
+        if (!$defined(left)) {
+            left = 0;
+        }
+        if (!$defined(right)) {
+            right = this.data.length - 1;
+        }
 
-*/
+        this.quicksort(left, right);
 
-Fx.Sort = new Class({
+        this.fireEvent('stop');
 
-	Extends: Fx.Elements,
+        return this.data;
 
-	options: {
-		mode: 'vertical'
-	},
+    },
 
-	initialize: function(elements, options){
-		this.parent(elements, options);
-		this.elements.each(function(el){
-			if (el.getStyle('position') == 'static') el.setStyle('position', 'relative');
-		});
-		this.setDefaultOrder();
-	},
+    /**
+     * Method: quicksort
+     * Initiates the sorting. Is
+     * called recursively
+     *
+     * Parameters:
+     * left - the left hand, or lower, bound of the sort
+     * right - the right hand, or upper, bound of the sort
+     */
+    quicksort : function (left, right) {
+        if (left >= right) {
+            return;
+        }
 
-	setDefaultOrder: function(){
-		this.currentOrder = this.elements.map(function(el, index){
-			return index;
-		});
-	},
+        var index = this.partition(left, right);
+        this.quicksort(left, index - 1);
+        this.quicksort(index + 1, right);
+    },
 
-	sort: function(newOrder){
-		if ($type(newOrder) != 'array') return false;
-		var top = 0;
-		var left = 0;
-		var zero = {};
-		var vert = this.options.mode == 'vertical';
-		var current = this.elements.map(function(el, index){
-			var size = el.getComputedSize({styles: ['border', 'padding', 'margin']});
-			var val;
-			if (vert){
-				val = {
-					top: top,
-					margin: size['margin-top'],
-					height: size.totalHeight
-				};
-				top += val.height - size['margin-top'];
-			} else {
-				val = {
-					left: left,
-					margin: size['margin-left'],
-					width: size.totalWidth
-				};
-				left += val.width;
-			}
-			var plain = vert ? 'top' : 'left';
-			zero[index] = {};
-			var start = el.getStyle(plain).toInt();
-			zero[index][plain] = start || 0;
-			return val;
-		}, this);
-		this.set(zero);
-		newOrder = newOrder.map(function(i){ return i.toInt(); });
-		if (newOrder.length != this.elements.length){
-			this.currentOrder.each(function(index){
-				if (!newOrder.contains(index)) newOrder.push(index);
-			});
-			if (newOrder.length > this.elements.length)
-				newOrder.splice(this.elements.length-1, newOrder.length - this.elements.length);
-		}
-		top = 0;
-		left = 0;
-		var margin = 0;
-		var next = {};
-		newOrder.each(function(item, index){
-			var newPos = {};
-			if (vert){
-				newPos.top = top - current[item].top - margin;
-				top += current[item].height;
-			} else {
-				newPos.left = left - current[item].left;
-				left += current[item].width;
-			}
-			margin = margin + current[item].margin;
-			next[item]=newPos;
-		}, this);
-		var mapped = {};
-		$A(newOrder).sort().each(function(index){
-			mapped[index] = next[index];
-		});
-		this.start(mapped);
-		this.currentOrder = newOrder;
-		return this;
-	},
+    /**
+     * Method: partition
+     *
+     * Parameters:
+     * left - the left hand, or lower, bound of the sort
+     * right - the right hand, or upper, bound of the sort
+     */
+    partition : function (left, right) {
+        this.findMedianOfMedians(left, right);
+        var pivotIndex = left,
+            pivotValue = (this.data[pivotIndex]).get(this.col),
+            index = left,
+            i;
 
-	rearrangeDOM: function(newOrder){
-		newOrder = newOrder || this.currentOrder;
-		var parent = this.elements[0].getParent();
-		var rearranged = [];
-		this.elements.setStyle('opacity', 0);
-		//move each element and store the new default order
-		newOrder.each(function(index){
-			rearranged.push(this.elements[index].inject(parent).setStyles({
-				top: 0,
-				left: 0
-			}));
-		}, this);
-		this.elements.setStyle('opacity', 1);
-		this.elements = $$(rearranged);
-		this.setDefaultOrder();
-		return this;
-	},
+        this.data.swap(pivotIndex, right);
+        for (i = left; i < right; i++) {
+            if (this.comparator((this.data[i]).get(this.col),
+                    pivotValue) < 0) {
+                this.data.swap(i, index);
+                index = index + 1;
+            }
+        }
+        this.data.swap(right, index);
 
-	getDefaultOrder: function(){
-		return this.elements.map(function(el, index){
-			return index;
-		});
-	},
+        return index;
 
-	forward: function(){
-		return this.sort(this.getDefaultOrder());
-	},
+    },
 
-	backward: function(){
-		return this.sort(this.getDefaultOrder().reverse());
-	},
+    /**
+     * Method: findMedianOfMedians
+     *
+     * Parameters: l
+     * eft - the left hand, or lower, bound of the sort
+     * right - the right hand, or upper, bound of the sort
+     */
+    findMedianOfMedians : function (left, right) {
+        if (left === right) {
+            return this.data[left];
+        }
 
-	reverse: function(){
-		return this.sort(this.currentOrder.reverse());
-	},
+        var i,
+            shift = 1,
+            endIndex,
+            medianIndex;
+        while (shift <= (right - left)) {
+            for (i = left; i <= right; i += shift * 5) {
+                endIndex = (i + shift * 5 - 1 < right) ? i + shift * 5 - 1 : right;
+                medianIndex = this.findMedianIndex(i, endIndex,
+                        shift);
 
-	sortByElements: function(elements){
-		return this.sort(elements.map(function(el){
-			return this.elements.indexOf(el);
-		}, this));
-	},
+                this.data.swap(i, medianIndex);
+            }
+            shift *= 5;
+        }
 
-	swap: function(one, two){
-		if ($type(one) == 'element') one = this.elements.indexOf(one);
-		if ($type(two) == 'element') two = this.elements.indexOf(two);
-		
-		var newOrder = $A(this.currentOrder);
-		newOrder[this.currentOrder.indexOf(one)] = two;
-		newOrder[this.currentOrder.indexOf(two)] = one;
-		this.sort(newOrder);
-	}
+        return this.data[left];
+    },
 
-});/*
-Script: Drag.js
-	The base Drag Class. Can be used to drag and resize Elements using mouse events.
+    /**
+     * Method: findMedianIndex
+     *
+     * Parameters:
+     * left - the left hand, or lower, bound of the sort
+     * right - the right hand, or upper, bound of the sort
+     */
+    findMedianIndex : function (left, right, shift) {
+        var groups = Math.round((right - left) / shift + 1),
+            k = Math.round(left + groups / 2 * shift),
+            i,
+            minIndex,
+            v,
+            minValue,
+            j;
+        if (k > this.data.length - 1) {
+            k = this.data.length - 1;
+        }
+        for (i = left; i < k; i += shift) {
+            minIndex = i;
+            v = this.data[minIndex];
+            minValue = v.get(this.col);
 
-	License:
-		MIT-style license.
+            for (j = i; j <= right; j += shift) {
+                if (this.comparator((this.data[j]).get(this.col),
+                        minValue) < 0) {
+                    minIndex = j;
+                    minValue = (this.data[minIndex]).get(this.col);
+                }
+            }
+            this.data.swap(i, minIndex);
+        }
 
-	Authors:
-		Valerio Proietti
-		Tom Occhinno
-		Jan Kassens
-*/
-
-var Drag = new Class({
-
-	Implements: [Events, Options],
-
-	options: {/*
-		onBeforeStart: $empty(thisElement),
-		onStart: $empty(thisElement, event),
-		onSnap: $empty(thisElement)
-		onDrag: $empty(thisElement, event),
-		onCancel: $empty(thisElement),
-		onComplete: $empty(thisElement, event),*/
-		snap: 6,
-		unit: 'px',
-		grid: false,
-		style: true,
-		limit: false,
-		handle: false,
-		invert: false,
-		preventDefault: false,
-		modifiers: {x: 'left', y: 'top'}
-	},
-
-	initialize: function(){
-		var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
-		this.element = $(params.element);
-		this.document = this.element.getDocument();
-		this.setOptions(params.options || {});
-		var htype = $type(this.options.handle);
-		this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : $(this.options.handle)) || this.element;
-		this.mouse = {'now': {}, 'pos': {}};
-		this.value = {'start': {}, 'now': {}};
-
-		this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
-
-		this.bound = {
-			start: this.start.bind(this),
-			check: this.check.bind(this),
-			drag: this.drag.bind(this),
-			stop: this.stop.bind(this),
-			cancel: this.cancel.bind(this),
-			eventStop: $lambda(false)
-		};
-		this.attach();
-	},
-
-	attach: function(){
-		this.handles.addEvent('mousedown', this.bound.start);
-		return this;
-	},
-
-	detach: function(){
-		this.handles.removeEvent('mousedown', this.bound.start);
-		return this;
-	},
-
-	start: function(event){
-		if (this.options.preventDefault) event.preventDefault();
-		this.mouse.start = event.page;
-		this.fireEvent('beforeStart', this.element);
-		var limit = this.options.limit;
-		this.limit = {x: [], y: []};
-		for (var z in this.options.modifiers){
-			if (!this.options.modifiers[z]) continue;
-			if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
-			else this.value.now[z] = this.element[this.options.modifiers[z]];
-			if (this.options.invert) this.value.now[z] *= -1;
-			this.mouse.pos[z] = event.page[z] - this.value.now[z];
-			if (limit && limit[z]){
-				for (var i = 2; i--; i){
-					if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
-				}
-			}
-		}
-		if ($type(this.options.grid) == 'number') this.options.grid = {x: this.options.grid, y: this.options.grid};
-		this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
-		this.document.addEvent(this.selection, this.bound.eventStop);
-	},
-
-	check: function(event){
-		if (this.options.preventDefault) event.preventDefault();
-		var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
-		if (distance > this.options.snap){
-			this.cancel();
-			this.document.addEvents({
-				mousemove: this.bound.drag,
-				mouseup: this.bound.stop
-			});
-			this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
-		}
-	},
-
-	drag: function(event){
-		if (this.options.preventDefault) event.preventDefault();
-		this.mouse.now = event.page;
-		for (var z in this.options.modifiers){
-			if (!this.options.modifiers[z]) continue;
-			this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
-			if (this.options.invert) this.value.now[z] *= -1;
-			if (this.options.limit && this.limit[z]){
-				if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
-					this.value.now[z] = this.limit[z][1];
-				} else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
-					this.value.now[z] = this.limit[z][0];
-				}
-			}
-			if (this.options.grid[z]) this.value.now[z] -= ((this.value.now[z] - this.limit[z][0]) % this.options.grid[z]);
-			if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
-			else this.element[this.options.modifiers[z]] = this.value.now[z];
-		}
-		this.fireEvent('drag', [this.element, event]);
-	},
-
-	cancel: function(event){
-		this.document.removeEvent('mousemove', this.bound.check);
-		this.document.removeEvent('mouseup', this.bound.cancel);
-		if (event){
-			this.document.removeEvent(this.selection, this.bound.eventStop);
-			this.fireEvent('cancel', this.element);
-		}
-	},
-
-	stop: function(event){
-		this.document.removeEvent(this.selection, this.bound.eventStop);
-		this.document.removeEvent('mousemove', this.bound.drag);
-		this.document.removeEvent('mouseup', this.bound.stop);
-		if (event) this.fireEvent('complete', [this.element, event]);
-	}
-
+        return k;
+    }
 });
-
-Element.implement({
-
-	makeResizable: function(options){
-		var drag = new Drag(this, $merge({modifiers: {x: 'width', y: 'height'}}, options));
-		this.store('resizer', drag);
-		return drag.addEvent('drag', function(){
-			this.fireEvent('resize', drag);
-		}.bind(this));
-	}
-
-});
 /*
-Script: Drag.Move.js
-	A Drag extension that provides support for the constraining of draggables to containers and droppables.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Sort.Nativesort
 
-	Authors:
-		Valerio Proietti
-		Tom Occhinno
-		Jan Kassens*/
+description: An implementation of the Javascript native sorting with the Jx.Sort interface
 
-Drag.Move = new Class({
+license: MIT-style license.
 
-	Extends: Drag,
+requires:
+ - Jx.Sort
 
-	options: {/*
-		onEnter: $empty(thisElement, overed),
-		onLeave: $empty(thisElement, overed),
-		onDrop: $empty(thisElement, overed, event),*/
-		droppables: [],
-		container: false,
-		precalculate: false,
-		includeMargins: true,
-		checkDroppables: true
-	},
+provides: [Jx.Sort.Nativesort]
 
-	initialize: function(element, options){
-		this.parent(element, options);
-		this.droppables = $$(this.options.droppables);
-		this.container = $(this.options.container);
-		if (this.container && $type(this.container) != 'element') this.container = $(this.container.getDocument().body);
+...
+ */
+// $Id: nativesort.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Sort.Nativesort
+ *
+ * Extends: <Jx.Sort>
+ *
+ * Implementation of a native sort algorithm designed to work on <Jx.Store> data.
+ *
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Sort.Nativesort = new Class({
+    Family: 'Jx.Sort.Nativesort',
+    Extends : Jx.Sort,
 
-		var position = this.element.getStyle('position');
-		if (position=='static') position = 'absolute';
-		if ([this.element.getStyle('left'), this.element.getStyle('top')].contains('auto')) this.element.position(this.element.getPosition(this.element.offsetParent));
-		this.element.setStyle('position', position);
+    name : 'nativesort',
 
-		this.addEvent('start', this.checkDroppables, true);
+    /**
+     * Method: sort
+     * Actually runs the sort on the data
+     *
+     * Returns:
+     * the sorted data
+     */
+    sort : function () {
+        this.fireEvent('start');
 
-		this.overed = null;
-	},
+        var compare = function (a, b) {
+            return this.comparator((this.data[a]).get(this.col), (this.data[b])
+                    .get(this.col));
+        };
 
-	start: function(event){
-		if (this.container){
-			var ccoo = this.container.getCoordinates(this.element.getOffsetParent()), cbs = {}, ems = {};
+        this.data.sort(compare);
+        this.fireEvent('stop');
+        return this.data;
+    }
 
-			['top', 'right', 'bottom', 'left'].each(function(pad){
-				cbs[pad] = this.container.getStyle('border-' + pad).toInt();
-				ems[pad] = this.element.getStyle('margin-' + pad).toInt();
-			}, this);
-
-			var width = this.element.offsetWidth + ems.left + ems.right;
-			var height = this.element.offsetHeight + ems.top + ems.bottom;
-
-			if (this.options.includeMargins) {
-				$each(ems, function(value, key) {
-					ems[key] = 0;
-				});
-			}
-			if (this.container == this.element.getOffsetParent()) {
-				this.options.limit = {
-					x: [0 - ems.left, ccoo.right - cbs.left - cbs.right - width + ems.right],
-					y: [0 - ems.top, ccoo.bottom - cbs.top - cbs.bottom - height + ems.bottom]
-				};
-			} else {
-				this.options.limit = {
-					x: [ccoo.left + cbs.left - ems.left, ccoo.right - cbs.right - width + ems.right],
-					y: [ccoo.top + cbs.top - ems.top, ccoo.bottom - cbs.bottom - height + ems.bottom]
-				};
-			}
-
-		}
-		if (this.options.precalculate){
-			this.positions = this.droppables.map(function(el) {
-				return el.getCoordinates();
-			});
-		}
-		this.parent(event);
-	},
-
-	checkAgainst: function(el, i){
-		el = (this.positions) ? this.positions[i] : el.getCoordinates();
-		var now = this.mouse.now;
-		return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
-	},
-
-	checkDroppables: function(){
-		var overed = this.droppables.filter(this.checkAgainst, this).getLast();
-		if (this.overed != overed){
-			if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
-			if (overed) this.fireEvent('enter', [this.element, overed]);
-			this.overed = overed;
-		}
-	},
-
-	drag: function(event){
-		this.parent(event);
-		if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
-	},
-
-	stop: function(event){
-		this.checkDroppables();
-		this.fireEvent('drop', [this.element, this.overed, event]);
-		this.overed = null;
-		return this.parent(event);
-	}
-
 });
-
-Element.implement({
-
-	makeDraggable: function(options){
-		var drag = new Drag.Move(this, options);
-		this.store('dragger', drag);
-		return drag;
-	}
-
-});
 /*
-Script: Slider.js
-	Class for creating horizontal and vertical slider controls.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Store.Response
 
-	Authors:
-		Valerio Proietti
-*/
+description: The object used to return response information to strategies.
 
-var Slider = new Class({
+license: MIT-style license.
 
-	Implements: [Events, Options],
+requires:
+ - Jx.Store
 
-	Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'],
+provides: [Jx.Store.Response]
 
-	options: {/*
-		onTick: $empty(intPosition),
-		onChange: $empty(intStep),
-		onComplete: $empty(strStep),*/
-		onTick: function(position){
-			if (this.options.snap) position = this.toPosition(this.step);
-			this.knob.setStyle(this.property, position);
-		},
-		snap: false,
-		offset: 0,
-		range: false,
-		wheel: false,
-		steps: 100,
-		mode: 'horizontal'
-	},
+...
+ */
+// $Id: response.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Store.Response
+ * 
+ * Extends: <Jx.Object>
+ * 
+ * This class is used by the protocol to send information back to the calling 
+ * strategy (or other caller).
+ *
+ * License: 
+ * Copyright (c) 2009, Jon Bomgardner.
+ * 
+ * This file is licensed under an MIT style license
+ */
+Jx.Store.Response = new Class({
 
-	initialize: function(element, knob, options){
-		this.setOptions(options);
-		this.element = $(element);
-		this.knob = $(knob);
-		this.previousChange = this.previousEnd = this.step = -1;
-		var offset, limit = {}, modifiers = {'x': false, 'y': false};
-		switch (this.options.mode){
-			case 'vertical':
-				this.axis = 'y';
-				this.property = 'top';
-				offset = 'offsetHeight';
-				break;
-			case 'horizontal':
-				this.axis = 'x';
-				this.property = 'left';
-				offset = 'offsetWidth';
-		}
-		this.half = this.knob[offset] / 2;
-		this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
-		this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
-		this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
-		this.range = this.max - this.min;
-		this.steps = this.options.steps || this.full;
-		this.stepSize = Math.abs(this.range) / this.steps;
-		this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
+    Family: 'Jx.Store.Response',
+    Extends: Jx.Object,
 
-		this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
-		modifiers[this.axis] = this.property;
-		limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
-
-		this.bound = {
-			clickedElement: this.clickedElement.bind(this),
-			scrolledElement: this.scrolledElement.bindWithEvent(this),
-			draggedKnob: this.draggedKnob.bind(this)
-		};
-
-		var dragOptions = {
-			snap: 0,
-			limit: limit,
-			modifiers: modifiers,
-			onDrag: this.bound.draggedKnob,
-			onStart: this.bound.draggedKnob,
-			onBeforeStart: (function(){
-				this.isDragging = true;
-			}).bind(this),
-			onComplete: function(){
-				this.isDragging = false;
-				this.draggedKnob();
-				this.end();
-			}.bind(this)
-		};
-		if (this.options.snap){
-			dragOptions.grid = Math.ceil(this.stepWidth);
-			dragOptions.limit[this.axis][1] = this.full;
-		}
-
-		this.drag = new Drag(this.knob, dragOptions);
-		this.attach();
-	},
-
-	attach: function(){
-		this.element.addEvent('mousedown', this.bound.clickedElement);
-		if (this.options.wheel) this.element.addEvent('mousewheel', this.bound.scrolledElement);
-		this.drag.attach();
-		return this;
-	},
-
-	detach: function(){
-		this.element.removeEvent('mousedown', this.bound.clickedElement);
-		this.element.removeEvent('mousewheel', this.bound.scrolledElement);
-		this.drag.detach();
-		return this;
-	},
-
-	set: function(step){
-		if (!((this.range > 0) ^ (step < this.min))) step = this.min;
-		if (!((this.range > 0) ^ (step > this.max))) step = this.max;
-
-		this.step = Math.round(step);
-		this.checkStep();
-		this.fireEvent('tick', this.toPosition(this.step));
-		this.end();
-		return this;
-	},
-
-	clickedElement: function(event){
-		if (this.isDragging || event.target == this.knob) return;
-
-		var dir = this.range < 0 ? -1 : 1;
-		var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
-		position = position.limit(-this.options.offset, this.full -this.options.offset);
-
-		this.step = Math.round(this.min + dir * this.toStep(position));
-		this.checkStep();
-		this.fireEvent('tick', position);
-		this.end();
-	},
-
-	scrolledElement: function(event){
-		var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
-		this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
-		event.stop();
-	},
-
-	draggedKnob: function(){
-		var dir = this.range < 0 ? -1 : 1;
-		var position = this.drag.value.now[this.axis];
-		position = position.limit(-this.options.offset, this.full -this.options.offset);
-		this.step = Math.round(this.min + dir * this.toStep(position));
-		this.checkStep();
-	},
-
-	checkStep: function(){
-		if (this.previousChange != this.step){
-			this.previousChange = this.step;
-			this.fireEvent('change', this.step);
-		}
-	},
-
-	end: function(){
-		if (this.previousEnd !== this.step){
-			this.previousEnd = this.step;
-			this.fireEvent('complete', this.step + '');
-		}
-	},
-
-	toStep: function(position){
-		var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
-		return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
-	},
-
-	toPosition: function(step){
-		return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
-	}
-
-});/*
-Script: Sortables.js
-	Class for creating a drag and drop sorting interface for lists of items.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Tom Occhino
-*/
-
-var Sortables = new Class({
-
-	Implements: [Events, Options],
-
-	options: {/*
-		onSort: $empty(element, clone),
-		onStart: $empty(element, clone),
-		onComplete: $empty(element),*/
-		snap: 4,
-		opacity: 1,
-		clone: false,
-		revert: false,
-		handle: false,
-		constrain: false
-	},
-
-	initialize: function(lists, options){
-		this.setOptions(options);
-		this.elements = [];
-		this.lists = [];
-		this.idle = true;
-
-		this.addLists($$($(lists) || lists));
-		if (!this.options.clone) this.options.revert = false;
-		if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
-	},
-
-	attach: function(){
-		this.addLists(this.lists);
-		return this;
-	},
-
-	detach: function(){
-		this.lists = this.removeLists(this.lists);
-		return this;
-	},
-
-	addItems: function(){
-		Array.flatten(arguments).each(function(element){
-			this.elements.push(element);
-			var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element));
-			(this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
-		}, this);
-		return this;
-	},
-
-	addLists: function(){
-		Array.flatten(arguments).each(function(list){
-			this.lists.push(list);
-			this.addItems(list.getChildren());
-		}, this);
-		return this;
-	},
-
-	removeItems: function(){
-		return $$(Array.flatten(arguments).map(function(element){
-			this.elements.erase(element);
-			var start = element.retrieve('sortables:start');
-			(this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
-			
-			return element;
-		}, this));
-	},
-
-	removeLists: function(){
-		return $$(Array.flatten(arguments).map(function(list){
-			this.lists.erase(list);
-			this.removeItems(list.getChildren());
-			
-			return list;
-		}, this));
-	},
-
-	getClone: function(event, element){
-		if (!this.options.clone) return new Element('div').inject(document.body);
-		if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
-		return element.clone(true).setStyles({
-			margin: '0px',
-			position: 'absolute',
-			visibility: 'hidden',
-			'width': element.getStyle('width')
-		}).inject(this.list).position(element.getPosition(element.getOffsetParent()));
-	},
-
-	getDroppables: function(){
-		var droppables = this.list.getChildren();
-		if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list);
-		return droppables.erase(this.clone).erase(this.element);
-	},
-
-	insert: function(dragging, element){
-		var where = 'inside';
-		if (this.lists.contains(element)){
-			this.list = element;
-			this.drag.droppables = this.getDroppables();
-		} else {
-			where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
-		}
-		this.element.inject(element, where);
-		this.fireEvent('sort', [this.element, this.clone]);
-	},
-
-	start: function(event, element){
-		if (!this.idle) return;
-		this.idle = false;
-		this.element = element;
-		this.opacity = element.get('opacity');
-		this.list = element.getParent();
-		this.clone = this.getClone(event, element);
-
-		this.drag = new Drag.Move(this.clone, {
-			snap: this.options.snap,
-			container: this.options.constrain && this.element.getParent(),
-			droppables: this.getDroppables(),
-			onSnap: function(){
-				event.stop();
-				this.clone.setStyle('visibility', 'visible');
-				this.element.set('opacity', this.options.opacity || 0);
-				this.fireEvent('start', [this.element, this.clone]);
-			}.bind(this),
-			onEnter: this.insert.bind(this),
-			onCancel: this.reset.bind(this),
-			onComplete: this.end.bind(this)
-		});
-
-		this.clone.inject(this.element, 'before');
-		this.drag.start(event);
-	},
-
-	end: function(){
-		this.drag.detach();
-		this.element.set('opacity', this.opacity);
-		if (this.effect){
-			var dim = this.element.getStyles('width', 'height');
-			var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
-			this.effect.element = this.clone;
-			this.effect.start({
-				top: pos.top,
-				left: pos.left,
-				width: dim.width,
-				height: dim.height,
-				opacity: 0.25
-			}).chain(this.reset.bind(this));
-		} else {
-			this.reset();
-		}
-	},
-
-	reset: function(){
-		this.idle = true;
-		this.clone.destroy();
-		this.fireEvent('complete', this.element);
-	},
-
-	serialize: function(){
-		var params = Array.link(arguments, {modifier: Function.type, index: $defined});
-		var serial = this.lists.map(function(list){
-			return list.getChildren().map(params.modifier || function(element){
-				return element.get('id');
-			}, this);
-		}, this);
-
-		var index = params.index;
-		if (this.lists.length == 1) index = 0;
-		return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
-	}
-
+    /**
+     * Property: code
+     * This is the success/failure code
+     */
+    code: null,
+    /**
+     * Property: data
+     * The data passed received by the protocol.
+     */
+    data: null,
+    /**
+     * Property: meta
+     * The metadata received by the protocol
+     */
+    meta: null,
+    /**
+     * Property: requestType
+     * one of 'read', 'insert', 'delete', or 'update'
+     */
+    requestType: null,
+    /**
+     * Property: requestParams
+     * The parameters passed to the method that created this response
+     */
+    requestParams: null,
+    /**
+     * Property: request
+     * the mootools Request object used in this operation (if one is actually
+     * used)
+     */
+    request: null,
+    /**
+     * Property: error
+     * the error data received from the called page if any.
+     */
+    error: null,
+    /**
+     * APIMethod: success
+     * determines if this response represents a successful response
+     */
+    success: function () {
+        return this.code > 0;
+    }
 });
-/*
-Script: Request.JSONP.js
-	Defines Request.JSONP, a class for cross domain javascript via script injection.
 
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-		Guillermo Rauch
-*/
-
-Request.JSONP = new Class({
-
-	Implements: [Chain, Events, Options, Log],
-
-	options: {/*
-		onRetry: $empty(intRetries),
-		onRequest: $empty(scriptElement),
-		onComplete: $empty(data),
-		onSuccess: $empty(data),
-		onCancel: $empty(),*/
-		url: '',
-		data: {},
-		retries: 0,
-		timeout: 0,
-		link: 'ignore',
-		callbackKey: 'callback',
-		injectScript: document.head
-	},
-
-	initialize: function(options){
-		this.setOptions(options);
-		this.running = false;
-		this.requests = 0;
-		this.triesRemaining = [];
-	},
-
-	check: function(){
-		if (!this.running) return true;
-		switch (this.options.link){
-			case 'cancel': this.cancel(); return true;
-			case 'chain': this.chain(this.caller.bind(this, arguments)); return false;
-		}
-		return false;
-	},
-
-	send: function(options){
-		if (!$chk(arguments[1]) && !this.check(options)) return this;
-
-		var type = $type(options), old = this.options, index = $chk(arguments[1]) ? arguments[1] : this.requests++;
-		if (type == 'string' || type == 'element') options = {data: options};
-
-		options = $extend({data: old.data, url: old.url}, options);
-
-		if (!$chk(this.triesRemaining[index])) this.triesRemaining[index] = this.options.retries;
-		var remaining = this.triesRemaining[index];
-
-		(function(){
-			var script = this.getScript(options);
-			this.log('JSONP retrieving script with url: ' + script.get('src'));
-			this.fireEvent('request', script);
-			this.running = true;
-
-			(function(){
-				if (remaining){
-					this.triesRemaining[index] = remaining - 1;
-					if (script){
-						script.destroy();
-						this.request(options, index);
-						this.fireEvent('retry', this.triesRemaining[index]);
-					}
-				} else if(script && this.options.timeout){
-					script.destroy();
-					this.cancel();
-					this.fireEvent('failure');
-				}
-			}).delay(this.options.timeout, this);
-		}).delay(Browser.Engine.trident ? 50 : 0, this);
-		return this;
-	},
-
-	cancel: function(){
-		if (!this.running) return this;
-		this.running = false;
-		this.fireEvent('cancel');
-		return this;
-	},
-
-	getScript: function(options){
-		var index = Request.JSONP.counter, data;
-		Request.JSONP.counter++;
-
-		switch ($type(options.data)){
-			case 'element': data = $(options.data).toQueryString(); break;
-			case 'object': case 'hash': data = Hash.toQueryString(options.data);
-		}
-
-		var src = options.url + 
-			 (options.url.test('\\?') ? '&' :'?') + 
-			 (options.callbackKey || this.options.callbackKey) + 
-			 '=Request.JSONP.request_map.request_'+ index + 
-			 (data ? '&' + data : '');
-		if (src.length > 2083) this.log('JSONP '+ src +' will fail in Internet Explorer, which enforces a 2083 bytes length limit on URIs');
-
-		var script = new Element('script', {type: 'text/javascript', src: src});
-		Request.JSONP.request_map['request_' + index] = function(data){ this.success(data, script); }.bind(this);
-		return script.inject(this.options.injectScript);
-	},
-
-	success: function(data, script){
-		if (script) script.destroy();
-		this.running = false;
-		this.log('JSONP successfully retrieved: ', data);
-		this.fireEvent('complete', [data]).fireEvent('success', [data]).callChain();
-	}
-
-});
-
-Request.JSONP.counter = 0;
-Request.JSONP.request_map = {};/*
-Script: Request.Queue.js
-	Controls several instances of Request and its variants to run only one request at a time.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Aaron Newton
-*/
-
-Request.Queue = new Class({
-
-	Implements: [Options, Events],
-
-	Binds: ['attach', 'request', 'complete', 'cancel', 'success', 'failure', 'exception'],
-
-	options: {/*
-		onRequest: $empty(argsPassedToOnRequest),
-		onSuccess: $empty(argsPassedToOnSuccess),
-		onComplete: $empty(argsPassedToOnComplete),
-		onCancel: $empty(argsPassedToOnCancel),
-		onException: $empty(argsPassedToOnException),
-		onFailure: $empty(argsPassedToOnFailure),*/
-		stopOnFailure: true,
-		autoAdvance: true,
-		concurrent: 1,
-		requests: {}
-	},
-
-	initialize: function(options){
-		this.setOptions(options);
-		this.requests = new Hash;
-		this.addRequests(this.options.requests);
-		this.queue = [];
-		this.reqBinders = {};
-	},
-
-	addRequest: function(name, request){
-		this.requests.set(name, request);
-		this.attach(name, request);
-		return this;
-	},
-
-	addRequests: function(obj){
-		$each(obj, this.addRequest, this);
-		return this;
-	},
-
-	getName: function(req){
-		return this.requests.keyOf(req);
-	},
-
-	attach: function(name, req){
-		if (req._groupSend) return this;
-		['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
-			if(!this.reqBinders[name]) this.reqBinders[name] = {};
-			this.reqBinders[name][evt] = function(){
-				this['on' + evt.capitalize()].apply(this, [name, req].extend(arguments));
-			}.bind(this);
-			req.addEvent(evt, this.reqBinders[name][evt]);
-		}, this);
-		req._groupSend = req.send;
-		req.send = function(options){
-			this.send(name, options);
-			return req;
-		}.bind(this);
-		return this;
-	},
-
-	removeRequest: function(req){
-		var name = $type(req) == 'object' ? this.getName(req) : req;
-		if (!name && $type(name) != 'string') return this;
-		req = this.requests.get(name);
-		if (!req) return this;
-		['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){
-			req.removeEvent(evt, this.reqBinders[name][evt]);
-		}, this);
-		req.send = req._groupSend;
-		delete req._groupSend;
-		return this;
-	},
-
-	getRunning: function(){
-		return this.requests.filter(function(r){ return r.running; });
-	},
-
-	isRunning: function(){
-		return !!this.getRunning().getKeys().length;
-	},
-
-	send: function(name, options){
-		var q = function(){
-			this.requests.get(name)._groupSend(options);
-			this.queue.erase(q);
-		}.bind(this);
-		q.name = name;
-		if (this.getRunning().getKeys().length >= this.options.concurrent || (this.error && this.options.stopOnFailure)) this.queue.push(q);
-		else q();
-		return this;
-	},
-
-	hasNext: function(name){
-		return (!name) ? !!this.queue.length : !!this.queue.filter(function(q){ return q.name == name; }).length;
-	},
-
-	resume: function(){
-		this.error = false;
-		(this.options.concurrent - this.getRunning().getKeys().length).times(this.runNext, this);
-		return this;
-	},
-
-	runNext: function(name){
-		if (!this.queue.length) return this;
-		if (!name){
-			this.queue[0]();
-		} else {
-			var found;
-			this.queue.each(function(q){
-				if (!found && q.name == name){
-					found = true;
-					q();
-				}
-			});
-		}
-		return this;
-	},
-
-	runAll: function() {
-		this.queue.each(function(q) {
-			q();
-		});
-		return this;
-	},
-
-	clear: function(name){
-		if (!name){
-			this.queue.empty();
-		} else {
-			this.queue = this.queue.map(function(q){
-				if (q.name != name) return q;
-				else return false;
-			}).filter(function(q){ return q; });
-		}
-		return this;
-	},
-
-	cancel: function(name){
-		this.requests.get(name).cancel();
-		return this;
-	},
-
-	onRequest: function(){
-		this.fireEvent('request', arguments);
-	},
-
-	onComplete: function(){
-		this.fireEvent('complete', arguments);
-	},
-
-	onCancel: function(){
-		if (this.options.autoAdvance && !this.error) this.runNext();
-		this.fireEvent('cancel', arguments);
-	},
-
-	onSuccess: function(){
-		if (this.options.autoAdvance && !this.error) this.runNext();
-		this.fireEvent('success', arguments);
-	},
-
-	onFailure: function(){
-		this.error = true;
-		if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
-		this.fireEvent('failure', arguments);
-	},
-
-	onException: function(){
-		this.error = true;
-		if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext();
-		this.fireEvent('exception', arguments);
-	}
-
-});
+Jx.Store.Response.WAITING = 2;
+Jx.Store.Response.SUCCESS = 1;
+Jx.Store.Response.FAILURE = 0;
 /*
-Script: Request.Periodical.js
-	Requests the same url at a time interval that increases when no data is returned from the requested server
+---
 
-	License:
-		MIT-style license.
+name: Jx.Store.Protocol
 
-	Authors:
-		Christoph Pojer
+description: Base class for all store protocols.
 
-*/
+license: MIT-style license.
 
-Request.implement({
+requires:
+ - Jx.Store.Response
 
-	options: {
-		initialDelay: 5000,
-		delay: 5000,
-		limit: 60000
-	},
+provides: [Jx.Store.Protocol]
 
-	startTimer: function(data){
-		var fn = (function(){
-			if (!this.running) this.send({data: data});
-		});
-		this.timer = fn.delay(this.options.initialDelay, this);
-		this.lastDelay = this.options.initialDelay;
-		this.completeCheck = function(j){
-			$clear(this.timer);
-			if (j) this.lastDelay = this.options.delay;
-			else this.lastDelay = (this.lastDelay+this.options.delay).min(this.options.limit);
-			this.timer = fn.delay(this.lastDelay, this);
-		};
-		this.addEvent('complete', this.completeCheck);
-		return this;
-	},
+...
+ */
+// $Id: protocol.js 977 2010-09-02 18:57:42Z pagameba $
+/**
+ * Class: Jx.Store.Protocol
+ *
+ * Extends: <Jx.Object>
+ *
+ * Base class for all protocols. Protocols are used for communication, primarily,
+ * in Jx.Store. It may be possible to adapt them to be used in other places but
+ * that is not their intended function.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Store.Protocol = new Class({
 
-	stopTimer: function(){
-		$clear(this.timer);
-		this.removeEvent('complete', this.completeCheck);
-		return this;
-	}
+    Extends: Jx.Object,
+    Family: 'Jx.Store.Protocol',
 
-});/*
-Script: Assets.js
-	Provides methods to dynamically load JavaScript, CSS, and Image files into the document.
+    parser: null,
 
-	License:
-		MIT-style license.
+    options: {},
 
-	Authors:
-		Valerio Proietti
-*/
+    init: function () {
+        this.parent();
 
-var Asset = {
+        if ($defined(this.options.parser)) {
+            this.parser = this.options.parser;
+        }
+    },
 
-	javascript: function(source, properties){
-		properties = $extend({
-			onload: $empty,
-			document: document,
-			check: $lambda(true)
-		}, properties);
+    cleanup: function () {
+        this.parser = null;
+        this.parent();
+    },
 
-		var script = new Element('script', {src: source, type: 'text/javascript'});
-
-		var load = properties.onload.bind(script), check = properties.check, doc = properties.document;
-		delete properties.onload; delete properties.check; delete properties.document;
-
-		script.addEvents({
-			load: load,
-			readystatechange: function(){
-				if (['loaded', 'complete'].contains(this.readyState)) load();
-			}
-		}).set(properties);
-
-		if (Browser.Engine.webkit419) var checker = (function(){
-			if (!$try(check)) return;
-			$clear(checker);
-			load();
-		}).periodical(50);
-
-		return script.inject(doc.head);
-	},
-
-	css: function(source, properties){
-		return new Element('link', $merge({
-			rel: 'stylesheet', media: 'screen', type: 'text/css', href: source
-		}, properties)).inject(document.head);
-	},
-
-	image: function(source, properties){
-		properties = $merge({
-			onload: $empty,
-			onabort: $empty,
-			onerror: $empty
-		}, properties);
-		var image = new Image();
-		var element = $(image) || new Element('img');
-		['load', 'abort', 'error'].each(function(name){
-			var type = 'on' + name;
-			var event = properties[type];
-			delete properties[type];
-			image[type] = function(){
-				if (!image) return;
-				if (!element.parentNode){
-					element.width = image.width;
-					element.height = image.height;
-				}
-				image = image.onload = image.onabort = image.onerror = null;
-				event.delay(1, element, element);
-				element.fireEvent(name, element, 1);
-			};
-		});
-		image.src = element.src = source;
-		if (image && image.complete) image.onload.delay(1);
-		return element.set(properties);
-	},
-
-	images: function(sources, options){
-		options = $merge({
-			onComplete: $empty,
-			onProgress: $empty
-		}, options);
-		sources = $splat(sources);
-		var images = [];
-		var counter = 0;
-		return new Elements(sources.map(function(source){
-			return Asset.image(source, {
-				onload: function(){
-					options.onProgress.call(this, counter, sources.indexOf(source));
-					counter++;
-					if (counter == sources.length) options.onComplete();
-				}
-			});
-		}));
-	}
-
-};/*
-Script: Color.js
-	Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Valerio Proietti
-*/
-
-var Color = new Native({
-
-	initialize: function(color, type){
-		if (arguments.length >= 3){
-			type = 'rgb'; color = Array.slice(arguments, 0, 3);
-		} else if (typeof color == 'string'){
-			if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
-			else if (color.match(/hsb/)) color = color.hsbToRgb();
-			else color = color.hexToRgb(true);
-		}
-		type = type || 'rgb';
-		switch (type){
-			case 'hsb':
-				var old = color;
-				color = color.hsbToRgb();
-				color.hsb = old;
-			break;
-			case 'hex': color = color.hexToRgb(true); break;
-		}
-		color.rgb = color.slice(0, 3);
-		color.hsb = color.hsb || color.rgbToHsb();
-		color.hex = color.rgbToHex();
-		return $extend(color, this);
-	}
-
-});
-
-Color.implement({
-
-	mix: function(){
-		var colors = Array.slice(arguments);
-		var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50;
-		var rgb = this.slice();
-		colors.each(function(color){
-			color = new Color(color);
-			for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
-		});
-		return new Color(rgb, 'rgb');
-	},
-
-	invert: function(){
-		return new Color(this.map(function(value){
-			return 255 - value;
-		}));
-	},
-
-	setHue: function(value){
-		return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
-	},
-
-	setSaturation: function(percent){
-		return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
-	},
-
-	setBrightness: function(percent){
-		return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
-	}
-
-});
-
-var $RGB = function(r, g, b){
-	return new Color([r, g, b], 'rgb');
-};
-
-var $HSB = function(h, s, b){
-	return new Color([h, s, b], 'hsb');
-};
-
-var $HEX = function(hex){
-	return new Color(hex, 'hex');
-};
-
-Array.implement({
-
-	rgbToHsb: function(){
-		var red = this[0], green = this[1], blue = this[2];
-		var hue, saturation, brightness;
-		var max = Math.max(red, green, blue), min = Math.min(red, green, blue);
-		var delta = max - min;
-		brightness = max / 255;
-		saturation = (max != 0) ? delta / max : 0;
-		if (saturation == 0){
-			hue = 0;
-		} else {
-			var rr = (max - red) / delta;
-			var gr = (max - green) / delta;
-			var br = (max - blue) / delta;
-			if (red == max) hue = br - gr;
-			else if (green == max) hue = 2 + rr - br;
-			else hue = 4 + gr - rr;
-			hue /= 6;
-			if (hue < 0) hue++;
-		}
-		return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
-	},
-
-	hsbToRgb: function(){
-		var br = Math.round(this[2] / 100 * 255);
-		if (this[1] == 0){
-			return [br, br, br];
-		} else {
-			var hue = this[0] % 360;
-			var f = hue % 60;
-			var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
-			var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
-			var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
-			switch (Math.floor(hue / 60)){
-				case 0: return [br, t, p];
-				case 1: return [q, br, p];
-				case 2: return [p, br, t];
-				case 3: return [p, q, br];
-				case 4: return [t, p, br];
-				case 5: return [br, p, q];
-			}
-		}
-		return false;
-	}
-
-});
-
-String.implement({
-
-	rgbToHsb: function(){
-		var rgb = this.match(/\d{1,3}/g);
-		return (rgb) ? rgb.rgbToHsb() : null;
-	},
-
-	hsbToRgb: function(){
-		var hsb = this.match(/\d{1,3}/g);
-		return (hsb) ? hsb.hsbToRgb() : null;
-	}
-
-});
-/*
-Script: Group.js
-	Class for monitoring collections of events
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Valerio Proietti
-*/
-
-var Group = new Class({
-
-	initialize: function(){
-		this.instances = Array.flatten(arguments);
-		this.events = {};
-		this.checker = {};
-	},
-
-	addEvent: function(type, fn){
-		this.checker[type] = this.checker[type] || {};
-		this.events[type] = this.events[type] || [];
-		if (this.events[type].contains(fn)) return false;
-		else this.events[type].push(fn);
-		this.instances.each(function(instance, i){
-			instance.addEvent(type, this.check.bind(this, [type, instance, i]));
-		}, this);
-		return this;
-	},
-
-	check: function(type, instance, i){
-		this.checker[type][i] = true;
-		var every = this.instances.every(function(current, j){
-			return this.checker[type][j] || false;
-		}, this);
-		if (!every) return;
-		this.checker[type] = {};
-		this.events[type].each(function(event){
-			event.call(this, this.instances, instance);
-		}, this);
-	}
-
-});
-/*
-Script: Hash.Cookie.js
-	Class for creating, reading, and deleting Cookies in JSON format.
-
-	License:
-		MIT-style license.
-
-	Authors:
-		Valerio Proietti
-		Aaron Newton
-*/
-
-Hash.Cookie = new Class({
-
-	Extends: Cookie,
-
-	options: {
-		autoSave: true
-	},
-
-	initialize: function(name, options){
-		this.parent(name, options);
-		this.load();
-	},
-
-	save: function(){
-		var value = JSON.encode(this.hash);
-		if (!value || value.length > 4096) return false; //cookie would be truncated!
-		if (value == '{}') this.dispose();
-		else this.write(value);
-		return true;
-	},
-
-	load: function(){
-		this.hash = new Hash(JSON.decode(this.read(), true));
-		return this;
-	}
-
-});
-
-Hash.each(Hash.prototype, function(method, name){
-	if (typeof method == 'function') Hash.Cookie.implement(name, function(){
-		var value = method.apply(this.hash, arguments);
-		if (this.options.autoSave) this.save();
-		return value;
-	});
+    /**
+     * APIMethod: read
+     * Supports reading data from a location. Abstract method that subclasses
+     * should implement.
+     *
+     * Parameters:
+     * options - optional options for configuring the request
+     */
+    read: $empty,
+    /**
+     * APIMethod: insert
+     * Supports inserting data from a location. Abstract method that subclasses
+     * should implement.
+     *
+     * Parameters:
+     * data - the data to use in creating the record in the form of one or more
+     *        Jx.Store.Record instances
+     * options - optional options for configuring the request
+     */
+    insert: $empty,
+    /**
+     * APIMethod: update
+     * Supports updating data at a location. Abstract method that subclasses
+     * should implement.
+     *
+     * Parameters:
+     * data - the data to update (one or more Jx.Store.Record objects)
+     * options - optional options for configuring the request
+     */
+    update: $empty,
+    /**
+     * APIMethod: delete
+     * Supports deleting data from a location. Abstract method that subclasses
+     * should implement.
+     *
+     * Parameters:
+     * data - the data to update (one or more Jx.Store.Record objects)
+     * options - optional options for configuring the request
+     */
+    "delete": $empty,
+    /**
+     * APIMethod: abort
+     * used to abort any of the above methods (where practical). Abstract method
+     * that subclasses should implement.
+     */
+    abort: $empty
 });/*
-Script: IframeShim.js
-	Defines IframeShim, a class for obscuring select lists and flash objects in IE.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Store.Protocol.Local
 
-	Authors:
-		Aaron Newton
-*/
+description: Store protocol used to load data that is already present in a page as an object.
 
-var IframeShim = new Class({
+license: MIT-style license.
 
-	Implements: [Options, Events, Class.Occlude],
+requires:
+ - Jx.Store.Protocol
 
-	options: {
-		className: 'iframeShim',
-		display: false,
-		zIndex: null,
-		margin: 0,
-		offset: {x: 0, y: 0},
-		browsers: true || (Browser.Engine.trident4 || (Browser.Engine.gecko && !Browser.Engine.gecko19 && Browser.Platform.mac))
-	},
+provides: [Jx.Store.Protocol.Local]
 
-	property: 'IframeShim',
+...
+ */
+// $Id: protocol.local.js 977 2010-09-02 18:57:42Z pagameba $
+/**
+ * Class: Jx.Store.Protocol.Local
+ * 
+ * Extends: Jx.Store.Protocol
+ * 
+ * Based on the Protocol base class, the local protocol uses data that it is
+ * handed upon instantiation to process requests.
+ * 
+ * Constructor Parameters:
+ * data - The data to use 
+ * options - any options for the base protocol class
+ * 
+ * License: 
+ * Copyright (c) 2009, Jon Bomgardner.
+ * inspired by the openlayers.org implementation of a similar system
+ * 
+ * This file is licensed under an MIT style license
+ */
+Jx.Store.Protocol.Local = new Class({
+    
+    Extends: Jx.Store.Protocol,
+    
+    parameters: ['data', 'options'],
+    /**
+     * Property: data
+     * The data passed to the protocol
+     */
+    data: null,
+    
+    init: function () {
+        this.parent();
+        
+        if ($defined(this.options.data)) {
+            this.data = this.parser.parse(this.options.data);
+        }
+    },
+    /**
+     * APIMethod: read
+     * process requests for data and sends the appropriate response via the
+     * dataLoaded event.
+     * 
+     * Parameters: 
+     * options - options to use in processing the request.
+     */
+    read: function (options) {
+        var resp = new Jx.Store.Response(),
+            page = options.data.page,
+            itemsPerPage = options.data.itemsPerPage,
+            start,
+            end,
+            data = this.data;
 
-	initialize: function(element, options){
-		this.element = $(element);
-		if (this.occlude()) return this.occluded;
-		this.setOptions(options);
-		this.makeShim();
-		return this;
-	},
-
-	makeShim: function(){
-		if(this.options.browsers){
-			var zIndex = this.element.getStyle('zIndex').toInt();
-
-			if (!zIndex){
-				var pos = this.element.getStyle('position');
-				if (pos == 'static' || !pos) this.element.setStyle('position', 'relative');
-				this.element.setStyle('zIndex', zIndex || 1);
-			}
-			zIndex = ($chk(this.options.zIndex) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1;
-			if (zIndex < 0) zIndex = 1;
-			this.shim = new Element('iframe', {
-				src: (window.location.protocol == 'https') ? '://0' : 'javascript:void(0)',
-				scrolling: 'no',
-				frameborder: 0,
-				styles: {
-					zIndex: zIndex,
-					position: 'absolute',
-					border: 'none',
-					filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)'
-				},
-				'class': this.options.className
-			}).store('IframeShim', this);
-			var inject = (function(){
-				this.shim.inject(this.element, 'after');
-				this[this.options.display ? 'show' : 'hide']();
-				this.fireEvent('inject');
-			}).bind(this);
-			if (Browser.Engine.trident && !IframeShim.ready) window.addEvent('load', inject);
-			else inject();
-		} else {
-			this.position = this.hide = this.show = this.dispose = $lambda(this);
-		}
-	},
-
-	position: function(){
-		if (!IframeShim.ready) return this;
-		var size = this.element.measure(function(){ return this.getSize(); });
-		if ($type(this.options.margin)){
-			size.x = size.x - (this.options.margin * 2);
-			size.y = size.y - (this.options.margin * 2);
-			this.options.offset.x += this.options.margin;
-			this.options.offset.y += this.options.margin;
-		}
-		if (this.shim) {
-			this.shim.set({width: size.x, height: size.y}).position({
-				relativeTo: this.element,
-				offset: this.options.offset
-			});
-		}
-		return this;
-	},
-
-	hide: function(){
-		if (this.shim) this.shim.setStyle('display', 'none');
-		return this;
-	},
-
-	show: function(){
-		if (this.shim) this.shim.setStyle('display', 'block');
-		return this.position();
-	},
-
-	dispose: function(){
-		if (this.shim) this.shim.dispose();
-		return this;
-	},
-
-	destroy: function(){
-		if (this.shim) this.shim.destroy();
-		return this;
-	}
-
-});
-
-window.addEvent('load', function(){
-	IframeShim.ready = true;
+        resp.requestType = 'read';
+        resp.requestParams = arguments;
+        
+        
+        if ($defined(data)) {
+            if (page <= 1 && itemsPerPage === -1) {
+                //send them all
+                resp.data = data;
+                resp.meta = { count: data.length };
+            } else {
+                start = (page - 1) * itemsPerPage;
+                end = start + itemsPerPage;
+                resp.data = data.slice(start, end);
+                resp.meta = { 
+                    page: page, 
+                    itemsPerPage: itemsPerPage,
+                    totalItems: data.length,
+                    totalPages: Math.ceil(data.length/itemsPerPage)
+                };
+            }
+            resp.code = Jx.Store.Response.SUCCESS;
+            this.fireEvent('dataLoaded', resp);
+        } else {
+            resp.code = Jx.Store.Response.SUCCESS;
+            this.fireEvent('dataLoaded', resp);
+        }                        
+    }
+    
+    /**
+     * The following methods are not implemented as they make no sense for a
+     * local protocol:
+     * - create
+     * - update 
+     * - delete
+     * - commit
+     * - abort
+     */
 });/*
-Script: Scroller.js
-	Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Store.Protocol.Ajax
 
-	Authors:
-		Valerio Proietti
-*/
+description: Store protocol used to load data from a remote data source via Ajax.
 
-var Scroller = new Class({
+license: MIT-style license.
 
-	Implements: [Events, Options],
+requires:
+ - Jx.Store.Protocol
 
-	options: {
-		area: 20,
-		velocity: 1,
-		onChange: function(x, y){
-			this.element.scrollTo(x, y);
-		},
-		fps: 50
-	},
+provides: [Jx.Store.Protocol.Ajax]
 
-	initialize: function(element, options){
-		this.setOptions(options);
-		this.element = $(element);
-		this.listener = ($type(this.element) != 'element') ? $(this.element.getDocument().body) : this.element;
-		this.timer = null;
-		this.bound = {
-			attach: this.attach.bind(this),
-			detach: this.detach.bind(this),
-			getCoords: this.getCoords.bind(this)
-		};
-	},
+...
+ */
+// $Id: protocol.ajax.js 977 2010-09-02 18:57:42Z pagameba $
+/**
+ * Class: Jx.Store.Protocol.Ajax
+ *
+ * Extends: <Jx.Store.Protocol>
+ *
+ * This protocol is used to send and receive data via AJAX. It also has the
+ * capability to use a REST-style API.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Store.Protocol.Ajax = new Class({
 
-	start: function(){
-		this.listener.addEvents({
-			mouseenter: this.bound.attach,
-			mouseleave: this.bound.detach
-		});
-	},
+    Extends: Jx.Store.Protocol,
 
-	stop: function(){
-		this.listener.removeEvents({
-			mouseenter: this.bound.attach,
-			mouseleave: this.bound.detach
-		});
-		this.timer = $clear(this.timer);
-	},
+    options: {
+        /**
+         * Option: requestOptions
+         * Options to pass to the mootools Request class
+         */
+        requestOptions: {
+            method: 'get'
+        },
+        /**
+         * Option: rest
+         * Flag indicating whether this protocol is operating against a RESTful
+         * web service
+         */
+        rest: false,
+        /**
+         * Option: urls
+         * This is a hash of the urls to use for each method. If the rest
+         * option is set to true the only one needed will be the urls.rest.
+         * These can be overridden if needed by passing an options object into
+         * the various methods with the appropriate urls.
+         */
+        urls: {
+            rest: null,
+            insert: null,
+            read: null,
+            update: null,
+            'delete': null
+        }
+    },
 
-	attach: function(){
-		this.listener.addEvent('mousemove', this.bound.getCoords);
-	},
+    init: function() {
+        this.parent();
+    },
+    /**
+     * APIMethod: read
+     * Send a read request via AJAX
+     *
+     * Parameters:
+     * options - the options to pass to the request.
+     */
+    read: function (options) {
+        var resp = new Jx.Store.Response(),
+            temp = {},
+            opts,
+            req;
+        resp.requestType = 'read';
+        resp.requestParams = arguments;
 
-	detach: function(){
-		this.listener.removeEvent('mousemove', this.bound.getCoords);
-		this.timer = $clear(this.timer);
-	},
 
-	getCoords: function(event){
-		this.page = (this.listener.get('tag') == 'body') ? event.client : event.page;
-		if (!this.timer) this.timer = this.scroll.periodical(Math.round(1000 / this.options.fps), this);
-	},
+        // set up options
+        if (this.options.rest) {
+            temp.url = this.options.urls.rest;
+        } else {
+            temp.url = this.options.urls.read;
+        }
 
-	scroll: function(){
-		var size = this.element.getSize(), 
-			scroll = this.element.getScroll(), 
-			pos = this.element.getOffsets(), 
-			scrollSize = this.element.getScrollSize(), 
-			change = {x: 0, y: 0};
-		for (var z in this.page){
-			if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0)
-				change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity;
-			else if (this.page[z] + this.options.area > (size[z] + pos[z]) && scroll[z] + size[z] != scrollSize[z])
-				change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity;
-		}
-		if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]);
-	}
+        opts = $merge(this.options.requestOptions, temp, options);
+        opts.onSuccess = this.handleResponse.bind(this,resp);
 
-});/*
-Script: Tips.js
-	Class for creating nice tips that follow the mouse cursor when hovering an element.
+        req = new Request(opts);
+        resp.request = req;
+        req.send();
 
-	License:
-		MIT-style license.
+        resp.code = Jx.Store.Response.WAITING;
 
-	Authors:
-		Valerio Proietti
-		Christoph Pojer
-*/
+        return resp;
 
-var Tips = new Class({
+    },
+    /**
+     * Method: handleResponse
+     * Called as an event handler for a returning request. Parses the request's
+     * response into the actual response object.
+     *
+     * Parameters:
+     * response - the response related to teh returning request.
+     */
+    handleResponse: function (response) {
+        var req = response.request,
+            str = req.xhr.responseText,
+            data = this.parser.parse(str);
+        if ($defined(data)) {
+            if ($defined(data.success) && data.success) {
+                if ($defined(data.data)) {
+                    response.data = data.data;
+                }
+                if ($defined(data.meta)) {
+                    response.meta = data.meta;
+                }
+                response.code = Jx.Store.Response.SUCCESS;
+            } else {
+                response.code = Jx.Store.Response.FAILURE;
+                response.error = $defined(data.error) ? data.error : null;
+            }
+        } else {
+            response.code = Jx.Store.Response.FAILURE;
+        }
+        this.fireEvent('dataLoaded', response);
+    },
+    /**
+     * APIMethod: insert
+     * Takes a Jx.Record instance and saves it
+     *
+     * Parameters:
+     * record - a Jx.Store.Record or array of them
+     * options - options to pass to the request
+     */
+    insert: function (record, options) {
+        if (this.options.rest) {
+            options = $merge({url: this.options.urls.rest},options);
+        } else {
+            options = $merge({url: this.options.urls.insert},options);
+        }
+        this.options.requestOptions.method = 'POST';
+        return this.run(record, options, "insert");
+    },
+    /**
+     * APIMethod: update
+     * Takes a Jx.Record and updates it via AJAX
+     *
+     * Parameters:
+     * record - a Jx.Record instance
+     * options - Options to pass to the request
+     */
+    update: function (record, options) {
+        if (this.options.rest) {
+            options = $merge({url: this.options.urls.rest},options);
+            this.options.requestOptions.method = 'PUT';
+        } else {
+            options = $merge({url: this.options.urls.update},options);
+            this.options.requestOptions.method = 'POST';
+        }
+        return this.run(record, options, "update");
+    },
+    /**
+     * APIMethod: delete
+     * Takes a Jx.Record and deletes it via AJAX
+     *
+     * Parameters:
+     * record - a Jx.Record instance
+     * options - Options to pass to the request
+     */
+    "delete": function (record, options) {
+        if (this.options.rest) {
+            options = $merge({url: this.options.urls.rest},options);
+            this.options.requestOptions.method = 'DELETE';
+        } else {
+            options = $merge({url: this.options.urls['delete']},options);
+            this.options.requestOptions.method = 'POST';
+        }
+        return this.run(record, options, "delete");
+    },
+    /**
+     * APIMethod: abort
+     * aborts the request related to the passed in response.
+     *
+     * Parameters:
+     * response - the response with the request to abort
+     */
+    abort: function (response) {
+        response.request.cancel();
 
-	Implements: [Events, Options],
+    },
+    /**
+     * Method: run
+     * called by update, delete, and insert methods that actually does the work
+     * of kicking off the request.
+     *
+     * Parameters:
+     * record - The Jx.Record to work with
+     * options - Options to pass to the request
+     * method - The name of the method calling this function
+     */
+    run: function (record, options, method) {
+        var resp = new Jx.Store.Response(),
+            opts,
+            req;
+        
+        this.options.requestOptions.data = $merge(this.options.requestOptions.data, {
+          data: this.parser.encode(record)
+        });
 
-	options: {
-		onShow: function(tip){
-			tip.setStyle('visibility', 'visible');
-		},
-		onHide: function(tip){
-			tip.setStyle('visibility', 'hidden');
-		},
-		title: 'title',
-		text: function(el){
-			return el.get('rel') || el.get('href');
-		},
-		showDelay: 100,
-		hideDelay: 100,
-		className: null,
-		offset: {x: 16, y: 16},
-		fixed: false
-	},
+        resp.requestType = method;
+        resp.requestParams = [record, options, method];
 
-	initialize: function(){
-		var params = Array.link(arguments, {options: Object.type, elements: $defined});
-		if (params.options && params.options.offsets) params.options.offset = params.options.offsets;
-		this.setOptions(params.options);
-		this.container = new Element('div', {'class': 'tip'});
-		this.tip = this.getTip();
-		
-		if (params.elements) this.attach(params.elements);
-	},
+        //set up options
+        opts = $merge(this.options.requestOptions, options);
+        opts.onSuccess = this.handleResponse.bind(this,resp);
 
-	getTip: function(){
-		return new Element('div', {
-			'class': this.options.className,
-			styles: {
-				visibility: 'hidden',
-				display: 'none',
-				position: 'absolute',
-				top: 0,
-				left: 0
-			}
-		}).adopt(
-			new Element('div', {'class': 'tip-top'}),
-			this.container,
-			new Element('div', {'class': 'tip-bottom'})
-		).inject(document.body);
-	},
+        req = new Request(opts);
+        resp.request = req;
+        req.send();
 
-	attach: function(elements){
-		var read = function(option, element){
-			if (option == null) return '';
-			return $type(option) == 'function' ? option(element) : element.get(option);
-		};
-		$$(elements).each(function(element){
-			var title = read(this.options.title, element);
-			element.erase('title').store('tip:native', title).retrieve('tip:title', title);
-			element.retrieve('tip:text', read(this.options.text, element));
-			
-			var events = ['enter', 'leave'];
-			if (!this.options.fixed) events.push('move');
-			
-			events.each(function(value){
-				element.addEvent('mouse' + value, element.retrieve('tip:' + value, this['element' + value.capitalize()].bindWithEvent(this, element)));
-			}, this);
-		}, this);
-		
-		return this;
-	},
+        resp.code = Jx.Store.Response.WAITING;
 
-	detach: function(elements){
-		$$(elements).each(function(element){
-			['enter', 'leave', 'move'].each(function(value){
-				element.removeEvent('mouse' + value, element.retrieve('tip:' + value) || $empty);
-			});
-			
-			element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
-			
-			if ($type(this.options.title) == 'string' && this.options.title == 'title'){
-				var original = element.retrieve('tip:native');
-				if (original) element.set('title', original);
-			}
-		}, this);
-		
-		return this;
-	},
+        return resp;
+    }
 
-	elementEnter: function(event, element){
-		$A(this.container.childNodes).each(Element.dispose);
-		
-		['title', 'text'].each(function(value){
-			var content = element.retrieve('tip:' + value);
-			if (!content) return;
-			
-			this[value + 'Element'] = new Element('div', {'class': 'tip-' + value}).inject(this.container);
-			this.fill(this[value + 'Element'], content);
-		}, this);
-		
-		this.timer = $clear(this.timer);
-		this.timer = this.show.delay(this.options.showDelay, this, element);
-		this.tip.setStyle('display', 'block');
-		this.position((!this.options.fixed) ? event : {page: element.getPosition()});
-	},
-
-	elementLeave: function(event, element){
-		$clear(this.timer);
-		this.tip.setStyle('display', 'none');
-		this.timer = this.hide.delay(this.options.hideDelay, this, element);
-	},
-
-	elementMove: function(event){
-		this.position(event);
-	},
-
-	position: function(event){
-		var size = window.getSize(), scroll = window.getScroll(),
-			tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight},
-			props = {x: 'left', y: 'top'},
-			obj = {};
-		
-		for (var z in props){
-			obj[props[z]] = event.page[z] + this.options.offset[z];
-			if ((obj[props[z]] + tip[z] - scroll[z]) > size[z]) obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z];
-		}
-		
-		this.tip.setStyles(obj);
-	},
-
-	fill: function(element, contents){
-		if(typeof contents == 'string') element.set('html', contents);
-		else element.adopt(contents);
-	},
-
-	show: function(el){
-		this.fireEvent('show', [this.tip, el]);
-	},
-
-	hide: function(el){
-		this.fireEvent('hide', [this.tip, el]);
-	}
-
 });/*
-Script: Date.English.US.js
-	Date messages for US English.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Store.Strategy
 
-	Authors:
-		Aaron Newton
+description: Base class for all store strategies.
 
-*/
+license: MIT-style license.
 
-MooTools.lang.set('en-US', 'Date', {
+requires:
+ - Jx.Store
 
-	months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
-	days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
-	//culture's date order: MM/DD/YYYY
-	dateOrder: ['month', 'date', 'year'],
-	shortDate: '%m/%d/%Y',
-	shortTime: '%I:%M%p',
-	AM: 'AM',
-	PM: 'PM',
+provides: [Jx.Store.Strategy]
 
-	/* Date.Extras */
-	ordinal: function(dayOfMonth){
-		//1st, 2nd, 3rd, etc.
-		return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)];
-	},
 
-	lessThanMinuteAgo: 'less than a minute ago',
-	minuteAgo: 'about a minute ago',
-	minutesAgo: '{delta} minutes ago',
-	hourAgo: 'about an hour ago',
-	hoursAgo: 'about {delta} hours ago',
-	dayAgo: '1 day ago',
-	daysAgo: '{delta} days ago',
-	lessThanMinuteUntil: 'less than a minute from now',
-	minuteUntil: 'about a minute from now',
-	minutesUntil: '{delta} minutes from now',
-	hourUntil: 'about an hour from now',
-	hoursUntil: 'about {delta} hours from now',
-	dayUntil: '1 day from now',
-	daysUntil: '{delta} days from now'
-
+...
+ */
+// $Id: strategy.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Store.Strategy
+ * 
+ * Extends: <Jx.Object>
+ * 
+ * Base class for all Jx.Store strategies
+ *
+ * License: 
+ * Copyright (c) 2009, Jon Bomgardner.
+ * 
+ * This file is licensed under an MIT style license
+ */
+Jx.Store.Strategy = new Class({
+    
+    Extends: Jx.Object,
+    Family: 'Jx.Store.Strategy',
+    /**
+     * APIProperty: store
+     * The store this strategy is associated with
+     */
+    store: null,
+    /**
+     * APIProperty: active
+     * whether this strategy has been activated or not.
+     */
+    active: null,
+    
+    /**
+     * Method: init
+     * initialize the strategy, should be called by subclasses
+     */
+    init: function () {
+        this.parent();
+        this.active = false;
+    },
+    /**
+     * APIMethod: setStore
+     * Associates this strategy with a particular store.
+     */
+    setStore: function (store) {
+        if (store instanceof Jx.Store) {
+            this.store = store;
+            return true;
+        }
+        return false;
+    },
+    
+    /**
+     * APIMethod: activate
+     * activates the strategy if it isn't already active.
+     */
+    activate: function () {
+        if (!this.active) {
+            this.active = true;
+            return true;
+        }
+        return false;
+    },
+    /**
+     * APIMethod: deactivate
+     * deactivates the strategy if it is already active.
+     */
+    deactivate: function () {
+        if (this.active) {
+            this.active = false;
+            return true;
+        }
+        return false;
+    }
 });/*
-Script: FormValidator.English.js
-	Date messages for English.
+---
 
-	License:
-		MIT-style license.
+name: Jx.Store.Strategy.Full
 
-	Authors:
-		Aaron Newton
+description: Strategy for loading the full data set from a source.
 
-*/
+license: MIT-style license.
 
-MooTools.lang.set('en-US', 'FormValidator', {
+requires:
+ - Jx.Store.Strategy
 
-	required:'This field is required.',
-	minLength:'Please enter at least {minLength} characters (you entered {length} characters).',
-	maxLength:'Please enter no more than {maxLength} characters (you entered {length} characters).',
-	integer:'Please enter an integer in this field. Numbers with decimals (e.g. 1.25) are not permitted.',
-	numeric:'Please enter only numeric values in this field (i.e. "1" or "1.1" or "-1" or "-1.1").',
-	digits:'Please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).',
-	alpha:'Please use letters only (a-z) with in this field. No spaces or other characters are allowed.',
-	alphanum:'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.',
-	dateSuchAs:'Please enter a valid date such as {date}',
-	dateInFormatMDY:'Please enter a valid date such as MM/DD/YYYY (i.e. "12/31/1999")',
-	email:'Please enter a valid email address. For example "fred at domain.com".',
-	url:'Please enter a valid URL such as http://www.google.com.',
-	currencyDollar:'Please enter a valid $ amount. For example $100.00 .',
-	oneRequired:'Please enter something for at least one of these inputs.',
-	errorPrefix: 'Error: ',
-	warningPrefix: 'Warning: ',
+provides: [Jx.Store.Strategy.Full]
 
-	//FormValidator.Extras
-
-	noSpace: 'There can be no spaces in this input.',
-	reqChkByNode: 'No items are selected.',
-	requiredChk: 'This field is required.',
-	reqChkByName: 'Please select a {label}.',
-	match: 'This field needs to match the {matchName} field',
-	startDate: 'the start date',
-	endDate: 'the end date',
-	currendDate: 'the current date',
-	afterDate: 'The date should be the same or after {label}.',
-	beforeDate: 'The date should be the same or before {label}.',
-	startMonth: 'Please select a start month',
-	sameMonth: 'These two dates must be in the same month - you must change one or the other.'
-
-});// $Id: common.js 478 2009-07-10 14:09:58Z pagameba $
+...
+ */
+// $Id: strategy.full.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx
- * Jx is a global singleton object that contains the entire Jx library
- * within it.  All Jx functions, attributes and classes are accessed
- * through the global Jx object.  Jx should not create any other
- * global variables, if you discover that it does then please report
- * it as a bug
+ * Class: Jx.Store.Strategy.Full
+ * 
+ * Extends: <Jx.Store.Strategy>
+ * 
+ * This is a strategy for loading all of the data from a source at one time.
  *
  * License: 
- * Copyright (c) 2008, DM Solutions Group Inc.
+ * Copyright (c) 2009, Jon Bomgardner.
  * 
  * This file is licensed under an MIT style license
  */
- 
-/* firebug console supressor for IE/Safari/Opera */
-window.addEvent('load', function() {
-    if (!("console" in window) || !("firebug" in window.console)) {
-        var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
-        "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
 
-        window.console = {};
-        for (var i = 0; i < names.length; ++i) {
-            window.console[names[i]] = function() {};
+Jx.Store.Strategy.Full = new Class({
+    
+    Extends: Jx.Store.Strategy,
+    
+    name: 'full',
+    
+    options:{},
+    /**
+     * Method: init
+     * initialize this strategy
+     */
+    init: function () {
+        this.parent();
+        this.bound.load = this.load.bind(this);
+        this.bound.loadStore = this.loadStore.bind(this);
+    },
+    
+    /**
+     * APIMethod: activate
+     * activates the strategy if it isn't already active.
+     */
+    activate: function () {
+        this.parent();
+        this.store.addEvent('storeLoad', this.bound.load);
+        
+    },
+    
+    /**
+     * APIMethod: deactivate
+     * deactivates the strategy if it is already active.
+     */
+    deactivate: function () {
+        this.parent();
+        this.store.removeEvent('storeLoad', this.bound.load);
+        
+    },
+    /**
+     * APIMethod: load
+     * Called as the eventhandler for the store load method. Can also
+     * be called independently to load data into the current store.
+     * 
+     * Parameters:
+     * params - a hash of parameters to use in loading the data.
+     */
+    load: function (params) {
+        this.store.fireEvent('storeBeginDataLoad', this.store);
+        this.store.protocol.addEvent('dataLoaded', this.bound.loadStore);
+        var opts = {}
+        if ($defined(params)) {
+            opts.data = params;
+        } else {
+            opts.data = {};
         }
-    }
-});
-/* inspired by extjs, apparently removes css image flicker and related problems in IE 6 */
-/* This is already done in mootools Source/Core/Browser.js  KASI*/
-/*
-(function() {
-    var ua = navigator.userAgent.toLowerCase();
-    var isIE = ua.indexOf("msie") > -1,
-        isIE7 = ua.indexOf("msie 7") > -1;
-    if(isIE && !isIE7) {
-        try {
-            document.execCommand("BackgroundImageCache", false, true);
-        } catch(e) {}
-    }    
-})();
-*/
-Class.Mutators.Family = function(self,name) {
-    if ($defined(name)){
-        self.$family = {'name': name};
-        $[name] = $.object;
-        return self;
-    }
-    else {
-        this.implement('$family',{'name':self});
-    }
-};
-
-/* Setup global namespace
- * If jxcore is loaded by jx.js, then the namespace and baseURL are
- * already established
- */
-if (typeof Jx == 'undefined') {
-    var Jx = {};
-    (function() {
-        var aScripts = document.getElementsByTagName('SCRIPT');
-        for (var i=0; i<aScripts.length; i++) {
-            var s = aScripts[i].src;
-            var matches = /(.*[jx|js|lib])\/jxlib(.*)/.exec(s);
-            if (matches && matches[0]) {
-                /**
-                 * Property: {String} baseURL
-                 * This is the URL that Jx was loaded from, it is 
-                 * automatically calculated from the script tag
-                 * src property that included Jx.
-                 *
-                 * Note that this assumes that you are loading Jx
-                 * from a js/ or lib/ folder in parallel to the
-                 * images/ folder that contains the various images
-                 * needed by Jx components.  If you have a different
-                 * folder structure, you can define Jx's base
-                 * by including the following before including
-                 * the jxlib javascript file:
-                 *
-                 * (code)
-                 * Jx = {
-                 *    baseURL: 'some/path'
-                 * }
-                 * (end)
-                 */ 
-                 Jx.aPixel = document.createElement('img', {alt:'',title:''});
-                 Jx.aPixel.src = matches[1]+'/a_pixel.png';
-                 Jx.baseURL = Jx.aPixel.src.substring(0,
-                     Jx.aPixel.src.indexOf('a_pixel.png'));
-                
+        opts.data.page = 0;
+        opts.data.itemsPerPage = -1;
+        this.store.protocol.read(opts);
+    },
+    
+    /**
+     * Method: loadStore
+     * Called as the event handler for the protocol's dataLoaded event. Checks
+     * the response for success and loads the data into the store if needed.
+     * 
+     * Parameters:
+     * resp - the response from the protocol
+     */
+    loadStore: function (resp) {
+        this.store.protocol.removeEvent('dataLoaded', this.bound.loadStore);
+        if (resp.success()) {
+            this.store.empty();
+            if ($defined(resp.meta)) {
+                this.parseMetaData(resp.meta);
             }
-        }
-       /**
-        * Determine if we're running in Adobe AIR. If so, determine which sandbox we're in
-        */
-        var src = aScripts[0].src;
-        if (src.contains('app:')){
-            Jx.isAir = true;
+            this.store.addRecords(resp.data);
+            this.store.loaded = true;
+            this.store.fireEvent('storeDataLoaded',this.store);
         } else {
-            Jx.isAir = false;
+            this.store.loaded = false;
+            this.store.fireEvent('storeDataLoadFailed', [this.store, resp]);
         }
-    })();
-} 
+    },
+    /**
+     * Method: parseMetaData
+     * Takes the meta property of the response object and puts the data 
+     * where it belongs.
+     * 
+     * Parameters:
+     * meta - the meta data object from the response.
+     */
+    parseMetaData: function (meta) {
+        if ($defined(meta.columns)) {
+            this.store.options.columns = meta.columns;
+        }
+        if ($defined(meta.primaryKey)) {
+            this.store.options.recordOptions.primaryKey = meta.primaryKey;
+        }
+    }
+});/*
+---
 
-/**
- * Method: applyPNGFilter
- *
- * Static method that applies the PNG Filter Hack for IE browsers
- * when showing 24bit PNG's.  Used automatically for img tags with
- * a class of png24.
- *
- * The filter is applied using a nifty feature of IE that allows javascript to
- * be executed as part of a CSS style rule - this ensures that the hack only
- * gets applied on IE browsers.
- *
- * The CSS that triggers this hack is only in the ie6.css files of the various
- * themes.
- *
- * Parameters:
- * object {Object} the object (img) to which the filter needs to be applied.
- */
-Jx.applyPNGFilter = function(o)  {
-   var t=Jx.aPixel.src;
-   if( o.src != t ) {
-       var s=o.src;
-       o.src = t;
-       o.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+s+"',sizingMethod='scale')";
-   }
-};
+name: Jx.Store.Strategy.Paginate
 
-Jx.imgQueue = [];   //The queue of images to be loaded
-Jx.imgLoaded = {};  //a hash table of images that have been loaded and cached
-Jx.imagesLoading = 0; //counter for number of concurrent image loads 
+description: Strategy for loading data in pages and moving between them. This strategy makes sure the store only contains the current page's data.
 
-/**
- * Method: addToImgQueue
- * Request that an image be set to a DOM IMG element src attribute.  This puts 
- * the image into a queue and there are private methods to manage that queue
- * and limit image loading to 2 at a time.
- *
- * Parameters:
- * obj - {Object} an object containing an element and src
- * property, where element is the element to update and src
- * is the url to the image.
- */
-Jx.addToImgQueue = function(obj) {
-    if (Jx.imgLoaded[obj.src]) {
-        //if this image was already requested (i.e. it's in cache) just set it directly
-        obj.element.src = obj.src;
-    } else {
-        //otherwise stick it in the queue
-        Jx.imgQueue.push(obj);
-        Jx.imgLoaded[obj.src] = true;
-    }
-    //start the queue management process
-    Jx.checkImgQueue();
-};
+license: MIT-style license.
 
-/**
- * Method: checkImgQueue
- *
- * An internal method that ensures no more than 2 images are loading at a time.
- */
-Jx.checkImgQueue = function() {
-    while (Jx.imagesLoading < 2 && Jx.imgQueue.length > 0) {
-        Jx.loadNextImg();
-    }
-};
+requires:
+ - Jx.Store.Strategy
 
-/**
- * Method: loadNextImg
- *
- * An internal method actually populate the DOM element with the image source.
- */
-Jx.loadNextImg = function() {
-    var obj = Jx.imgQueue.shift();
-    if (obj) {
-        ++Jx.imagesLoading;
-        obj.element.onload = function(){--Jx.imagesLoading; Jx.checkImgQueue();};
-        obj.element.onerror = function(){--Jx.imagesLoading; Jx.checkImgQueue();};
-        obj.element.src = obj.src;
-    }
-};
+provides: [Jx.Store.Strategy.Paginate]
 
-/**
- * Method: createIframeShim
- * Creates a new iframe element that is intended to fill a container
- * to mask out other operating system controls (scrollbars, inputs, 
- * buttons, etc) when HTML elements are supposed to be above them.
- *
- * Returns:
- * an HTML iframe element that can be inserted into the DOM.
- */
-Jx.createIframeShim = function() {
-    return new Element('iframe', {
-        'class':'jxIframeShim',
-        'scrolling':'no',
-        'frameborder':0
-    });
-};
-/**
- * Method: getNumber
- * safely parse a number and return its integer value.  A NaN value 
- * returns 0.  CSS size values are also parsed correctly.
- *
- * Parameters: 
- * n - {Mixed} the string or object to parse.
- *
- * Returns:
- * {Integer} the integer value that the parameter represents
- */
-Jx.getNumber = function(n, def) {
-  var result = n===null||isNaN(parseInt(n,10))?(def||0):parseInt(n,10);
-  return result;
-};
 
-/**
- * Method: getPageDimensions
- * return the dimensions of the browser client area.
- *
- * Returns:
- * {Object} an object containing a width and height property 
- * that represent the width and height of the browser client area.
+...
  */
-Jx.getPageDimensions = function() {
-    return {width: window.getWidth(), height: window.getHeight()};
-};
-
+// $Id: strategy.paginate.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Element
+ * Class: Jx.Store.Strategy.Paginate
+ * 
+ * Extends: <Jx.Store.Strategy>
+ * 
+ * Store strategy for paginating results in a store.
  *
- * Element is a global object provided by the mootools library.  The
- * functions documented here are extensions to the Element object provided
- * by Jx to make cross-browser compatibility easier to achieve.  Most of the
- * methods are measurement related.
- *
- * While the code in these methods has been converted to use MooTools methods,
- * there may be better MooTools methods to use to accomplish these things.
- * Ultimately, it would be nice to eliminate most or all of these and find the
- * MooTools equivalent or convince MooTools to add them.
+ * License: 
+ * Copyright (c) 2009, Jon Bomgardner.
+ * 
+ * This file is licensed under an MIT style license
  */
-Element.implement({
+Jx.Store.Strategy.Paginate = new Class({
+    
+    Extends: Jx.Store.Strategy,
+    
+    name: 'paginate',
+    
+    options: {
+        /**
+         * Option: getPaginationParams
+         * a function that returns an object that holds the parameters
+         * necessary for getting paginated data from a protocol.
+         */
+        getPaginationParams: function () {
+            return {
+                page: this.page,
+                itemsPerPage: this.itemsPerPage
+            };
+        },
+        /**
+         * Option: startingItemsPerPage
+         * Used to set the intial itemsPerPage for the strategy. the pageSize 
+         * can be changed using the setPageSize() method.
+         */
+        startingItemsPerPage: 25,
+        /**
+         * Option: startingPage
+         * The page to start on. Defaults to 1 but can be set to any other 
+         * page.
+         */
+        startingPage: 1,
+        /**
+         * Option: expirationInterval
+         * The interval, in milliseconds (1000 = 1 sec), to hold a page of
+         * data before it expires. If the page is expired, the next time the
+         * page is accessed it must be retrieved again. Default is 5 minutes
+         * (1000 * 60 * 5)
+         */
+        expirationInterval: (1000 * 60 * 5),
+        /**
+         * Option: ignoreExpiration
+         * Set to TRUE to ignore the expirationInterval setting and never
+         * expire pages.
+         */
+        ignoreExpiration: false
+    },
     /**
-     * Method: getBoxSizing
-     * return the box sizing of an element, one of 'content-box' or 
-     *'border-box'.
-     *
-     * Parameters: 
-     * elem - {Object} the element to get the box sizing of.
-     *
-     * Returns:
-     * {String} the box sizing of the element.
+     * Property: data
+     * holds the pages of data keyed by page number.
      */
-    getBoxSizing : function() {
-      var result = 'content-box';
-      if (Browser.Engine.trident || Browser.Engine.presto) { 
-          var cm = document["compatMode"];
-          if (cm == "BackCompat" || cm == "QuirksMode") { 
-              result = 'border-box'; 
-          } else {
-              result = 'content-box'; 
-        }
-      } else {
-          if (arguments.length === 0) {
-              node = document.documentElement; 
-          }
-          var sizing = this.getStyle("-moz-box-sizing");
-          if (!sizing) { 
-              sizing = this.getStyle("box-sizing"); 
-          }
-          result = (sizing ? sizing : 'content-box');
-      }
-      return result;
-    },
+    data: null,
     /**
-     * Method: getContentBoxSize
-     * return the size of the content area of an element.  This is the size of
-     * the element less margins, padding, and borders.
-     *
-     * Parameters: 
-     * elem - {Object} the element to get the content size of.
-     *
-     * Returns:
-     * {Object} an object with two properties, width and height, that
-     * are the size of the content area of the measured element.
+     * property: cacheTimer
+     * holds one or more cache timer ids - one per page. Each page is set to 
+     * expire after a certain amount of time.
      */
-    getContentBoxSize : function() {
-      var w = this.offsetWidth;
-      var h = this.offsetHeight;
-      var padding = this.getPaddingSize();
-      var border = this.getBorderSize();
-      w = w - padding.left - padding.right - border.left - border.right;
-      h = h - padding.bottom - padding.top - border.bottom - border.top;
-      return {width: w, height: h};
-    },
+    cacheTimer: null,
     /**
-     * Method: getBorderBoxSize
-     * return the size of the border area of an element.  This is the size of
-     * the element less margins.
-     *
-     * Parameters: 
-     * elem - {Object} the element to get the border sizing of.
-     *
-     * Returns:
-     * {Object} an object with two properties, width and height, that
-     * are the size of the border area of the measured element.
+     * Property: page
+     * Tracks the page the store currently holds.
      */
-    getBorderBoxSize: function() {
-      var w = this.offsetWidth;
-      var h = this.offsetHeight;
-      return {width: w, height: h}; 
+    page: null,
+    /**
+     * Property: itemsPerPage
+     * The number of items on each page
+     */
+    itemsPerPage: null,
+    
+    /**
+     * Method: init
+     * initialize this strategy
+     */
+    init: function () {
+        this.parent();
+        this.data = new Hash();
+        this.cacheTimer = new Hash();
+        //set up bindings that we need here
+        this.bound.load = this.load.bind(this);
+        this.bound.loadStore = this.loadStore.bind(this);
+        this.itemsPerPage = this.options.startingItemsPerPage;
+        this.page = this.options.startingPage;
     },
     
     /**
-     * Method: getMarginBoxSize
-     * return the size of the margin area of an element.  This is the size of
-     * the element plus margins.
-     *
-     * Parameters: 
-     * elem - {Object} the element to get the margin sizing of.
-     *
-     * Returns:
-     * {Object} an object with two properties, width and height, that
-     * are the size of the margin area of the measured element.
+     * APIMethod: activate
+     * activates the strategy if it isn't already active.
      */
-    getMarginBoxSize: function() {
-        var margins = this.getMarginSize();
-        var w = this.offsetWidth + margins.left + margins.right;
-        var h = this.offsetHeight + margins.top + margins.bottom;
-        return {width: w, height: h};
+    activate: function () {
+        this.parent();
+        this.store.addEvent('storeLoad', this.bound.load);
     },
     
     /**
-     * Method: setContentBoxSize
-     * set either or both of the width and height of an element to
-     * the provided size.  This function ensures that the content
-     * area of the element is the requested size and the resulting
-     * size of the element may be larger depending on padding and
-     * borders.
-     *
-     * Parameters: 
-     * elem - {Object} the element to set the content area of.
-     * size - {Object} an object with a width and/or height property that is the size to set
-     * the content area of the element to.
+     * APIMethod: deactivate
+     * deactivates the strategy if it is already active.
      */
-    setContentBoxSize : function(size) {
-        if (this.getBoxSizing() == 'border-box') {
-            var padding = this.getPaddingSize();
-            var border = this.getBorderSize();
-            if (typeof size.width != 'undefined') {
-                var width = (size.width + padding.left + padding.right + border.left + border.right);
-                if (width < 0) {
-                    width = 0;
-                }
-                this.style.width = width + 'px';
+    deactivate: function () {
+        this.parent();
+        this.store.removeEvent('storeLoad', this.bound.load);
+    },
+    /**
+     * APIMethod: load
+     * Called to load data into the store
+     * 
+     * Parameters:
+     * params - a Hash of parameters to use in getting data from the protocol.
+     */
+    load: function (params) {
+        this.store.fireEvent('storeBeginDataLoad', this.store);
+        this.store.protocol.addEvent('dataLoaded', this.bound.loadStore);
+        this.params = params;
+        var opts = {
+            data: $merge(params, this.options.getPaginationParams.apply(this))
+        };
+        this.store.protocol.read(opts);
+    },
+    /**
+     * Method: loadStore
+     * Used to assist in the loading of data into the store. This is 
+     * called as a response to the protocol finishing.
+     * 
+     *  Parameters:
+     *  resp - the response object
+     */
+    loadStore: function (resp) {
+        this.store.protocol.removeEvent('dataLoaded', this.bound.loadStore);
+        if (resp.success()) {
+            if ($defined(resp.meta)) {
+                this.parseMetaData(resp.meta);
             }
-            if (typeof size.height != 'undefined') {
-                var height = (size.height + padding.top + padding.bottom + border.top + border.bottom);
-                if (height < 0) {
-                    height = 0;
-                }
-                this.style.height = height + 'px';
-            }
+            this.data.set(this.page,resp.data);
+            this.loadData(resp.data);
         } else {
-            if (typeof size.width != 'undefined') {
-                this.style.width = size.width + 'px';
-            }
-            if (typeof size.height != 'undefined') {
-                this.style.height = size.height + 'px';
-            }
+            this.store.fireEvent('storeDataLoadFailed', this.store);
         }
     },
     /**
-     * Method: setBorderBoxSize
-     * set either or both of the width and height of an element to
-     * the provided size.  This function ensures that the border
-     * size of the element is the requested size and the resulting
-     * content areaof the element may be larger depending on padding and
-     * borders.
-     *
-     * Parameters: 
-     * elem - {Object} the element to set the border size of.
-     * size - {Object} an object with a width and/or height property that is the size to set
-     * the content area of the element to.
+     * Method: loadData
+     * This method does the actual work of loading data to the store. It is
+     * called when either the protocol finishes or setPage() has the data and
+     * it's not expired.
+     * 
+     * Parameters:
+     * data - the data to load into the store.
      */
-    setBorderBoxSize : function(size) {
-      if (this.getBoxSizing() == 'content-box') {
-        var padding = this.getPaddingSize();
-        var border = this.getBorderSize();
-        var margin = this.getMarginSize();
-        if (typeof size.width != 'undefined') {
-          var width = (size.width - padding.left - padding.right - border.left - border.right - margin.left - margin.right);
-          if (width < 0) {
-            width = 0;
-          }
-          this.style.width = width + 'px';
+    loadData: function (data) {
+        this.store.empty();
+        this.store.loaded = false;
+        if (!this.options.ignoreExpiration) {
+            var id = this.expirePage.delay(this.options.expirationInterval, this, this.page);
+            this.cacheTimer.set(this.page,id);
         }
-        if (typeof size.height != 'undefined') {
-          var height = (size.height - padding.top - padding.bottom - border.top - border.bottom - margin.top - margin.bottom);
-          if (height < 0) {
-            height = 0;
-          }
-          this.style.height = height + 'px';
+        this.store.addRecords(data);
+        this.store.loaded = true;
+        this.store.fireEvent('storeDataLoaded',this.store);
+    },
+    /**
+     * Method: parseMetaData
+     * Takes the metadata returned from the protocol and places it in the
+     * appropriate Vplaces.
+     * 
+     * Parameters:
+     * meta - the meta data object returned from the protocol.
+     */
+    parseMetaData: function (meta) {
+        if ($defined(meta.columns)) {
+            this.store.options.columns = meta.columns;
         }
-      } else {
-        if (typeof size.width != 'undefined' && size.width >= 0) {
-          this.style.width = size.width + 'px';
+        if ($defined(meta.totalItems)) {
+            this.totalItems = meta.totalItems;
         }
-        if (typeof size.height != 'undefined' && size.height >= 0) {
-          this.style.height = size.height + 'px';
+        if ($defined(meta.totalPages)) {
+            this.totalPages = meta.totalPages;
         }
-      }
+        if ($defined(meta.primaryKey)) {
+            this.store.options.recordOptions.primaryKey = meta.primaryKey;
+        }
+            
     },
     /**
-     * Method: getPaddingSize
-     * returns the padding for each edge of an element
-     *
-     * Parameters: 
-     * elem - {Object} The element to get the padding for.
-     *
-     * Returns:
-     * {Object} an object with properties left, top, right and bottom
-     * that contain the associated padding values.
+     * Method: expirePage
+     * Is called when a pages cache timer expires. Will expire the page by 
+     * erasing the page and timer. This will force a reload of the data the 
+     * next time the page is accessed.
+     * 
+     * Parameters:
+     * page - the page number to expire.
      */
-    getPaddingSize : function () {
-      var l = Jx.getNumber(this.getStyle('padding-left'));
-      var t = Jx.getNumber(this.getStyle('padding-top'));
-      var r = Jx.getNumber(this.getStyle('padding-right'));
-      var b = Jx.getNumber(this.getStyle('padding-bottom'));
-      return {left:l, top:t, right: r, bottom: b};
+    expirePage: function (page) {
+        this.data.erase(page);
+        this.cacheTimer.erase(page);
     },
     /**
-     * Method: getBorderSize
-     * returns the border size for each edge of an element
-     *
-     * Parameters: 
-     * elem - {Object} The element to get the borders for.
-     *
-     * Returns:
-     * {Object} an object with properties left, top, right and bottom
-     * that contain the associated border values.
+     * APIMethod: setPage
+     * Allows a caller (i.e. a paging toolbar) to move to a specific page.
+     * 
+     * Parameters:
+     * page - the page to move to. Can be any absolute page number, any number
+     *        prefaced with '-' or '+' (i.e. '-1', '+3'), 'first', 'last', 
+     *        'next', or 'previous'
      */
-    getBorderSize : function() {
-      var l = Jx.getNumber(this.getStyle('border-left-width'));
-      var t = Jx.getNumber(this.getStyle('border-top-width'));
-      var r = Jx.getNumber(this.getStyle('border-right-width'));
-      var b = Jx.getNumber(this.getStyle('border-bottom-width'));
-      return {left:l, top:t, right: r, bottom: b};
+    setPage: function (page) {
+        if (Jx.type(page) === 'string') {
+            switch (page) {
+                case 'first':
+                    this.page = 1;
+                    break;
+                case 'last':
+                    this.page = this.totalPages;
+                    break;
+                case 'next':
+                    this.page++;
+                    break;
+                case 'previous':
+                    this.page--;
+                    break;
+                default:
+                    this.page = this.page + Jx.getNumber(page);
+                    break;
+            }
+        } else {
+            this.page = page;
+        }
+        if (this.cacheTimer.has(this.page)) {
+            $clear(this.cacheTimer.get(this.page));
+            this.cacheTimer.erase(this.page);
+        }
+        if (this.data.has(this.page)){
+            this.loadData(this.data.get(this.page));
+        } else {
+            this.load(this.params);
+        }
     },
     /**
-     * Method: getMarginSize
-     * returns the margin size for each edge of an element
-     *
-     * Parameters: 
-     * elem - {Object} The element to get the margins for.
-     *
-     * Returns:
-     *: {Object} an object with properties left, top, right and bottom
-     * that contain the associated margin values.
+     * APIMethod: getPage
+     * returns the current page
      */
-    getMarginSize : function() {
-      var l = Jx.getNumber(this.getStyle('margin-left'));
-      var t = Jx.getNumber(this.getStyle('margin-top'));
-      var r = Jx.getNumber(this.getStyle('margin-right'));
-      var b = Jx.getNumber(this.getStyle('margin-bottom'));
-      return {left:l, top:t, right: r, bottom: b};
+    getPage: function () {
+        return this.page;
     },
-    
     /**
-     * Method: descendantOf
-     * determines if the element is a descendent of the reference node.
-     *
-     * Parameters:
-     * node - {HTMLElement} the reference node
-     *
-     * Returns:
-     * {Boolean} true if the element is a descendent, false otherwise.
+     * APIMethod: getNumberOfPages
+     * returns the total number of pages.
      */
-    descendantOf: function(node) {
-        var parent = $(this.parentNode);
-        while (parent != node && parent && parent.parentNode && parent.parentNode != parent) {
-            parent = $(parent.parentNode);
-        }
-        return parent == node;
+    getNumberOfPages: function () {
+        return this.totalPages;
     },
-    
     /**
-     * Method: findElement
-     * search the parentage of the element to find an element of the given
-     * tag name.
-     *
-     * Parameters:
-     * type - {String} the tag name of the element type to search for
-     *
-     * Returns:
-     * {HTMLElement} the first node (this one or first parent) with the
-     * requested tag name or false if none are found.
+     * APIMethod: setPageSize
+     * sets the current size of the pages. Calling this will expire every page 
+     * and force the current one to reload with the new size.
      */
-    findElement: function(type) {
-        var o = this;
-        var tagName = o.tagName;
-        while (o.tagName != type && o && o.parentNode && o.parentNode != o) {
-            o = $(o.parentNode);
-        }
-        return o.tagName == type ? o : false;
+    setPageSize: function (size) {
+        //set the page size 
+        this.itemsPerPage = size;
+        //invalidate all pages cached and reload the current one only
+        this.cacheTimer.each(function(val){
+            $clear(val);
+        },this);
+        this.cacheTimer.empty();
+        this.data.empty();
+        this.load();
+    },
+    /**
+     * APIMethod: getPageSize
+     * returns the current page size
+     */
+    getPageSize: function () {
+        return this.itemsPerPage;
+    },
+    /**
+     * APIMethod: getTotalCount
+     * returns the total number of items as received from the protocol.
+     */
+    getTotalCount: function () {
+        return this.totalItems;
     }
-} );
+});/*
+---
 
+name: Jx.Store.Strategy.Progressive
+
+description: Strategy based on Strategy.Paginate but loads data progressively without removing old or curent data from the store.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Store.Strategy.Paginate
+
+provides: [Jx.Store.Strategy.Progressive]
+
+...
+ */
 /**
- * Class: Jx.ContentLoader
- * 
- * ContentLoader is a mix-in class that provides a consistent
- * mechanism for other Jx controls to load content in one of
- * four different ways:
+ * Class: Jx.Store.Strategy.Progressive
  *
- * o using an existing element, by id
+ * Extends: <Jx.Store.Strategy.Paginate>
  *
- * o using an existing element, by object reference
+ * Store strategy for progressively obtaining results in a store. You can
+ * continually call nextPage() to get the next page and the store will retain
+ * all current data. You can set a maximum number of records the store should
+ * hold and whether it should dropRecords when that max is hit.
  *
- * o using an HTML string
+ * License:
+ * Copyright (c) 2010, Jon Bomgardner.
  *
- * o using a URL to get the content remotely
- *
- * Use the Implements syntax in your Class to add Jx.ContentLoader
- * to your class.
- *
- * Option: content
- * content may be an HTML element reference, the id of an HTML element
- * already in the DOM, or an HTML string that becomes the inner HTML of
- * the element.
- *
- * Option: contentURL
- * the URL to load content from
+ * This file is licensed under an MIT style license
  */
-Jx.ContentLoader = new Class ({
+Jx.Store.Strategy.Progressive = new Class({
+    
+    Extends: Jx.Store.Strategy.Paginate,
+    
+    name: 'progressive',
+    
+    options: {
+        /**
+         * Option: maxRecords
+         * The maximum number of records we want in the store at any one time.
+         */
+        maxRecords: 1000,
+        /**
+         * Option: dropRecords
+         * Whether the strategy should drop records when the maxRecords limit 
+         * is reached. if this is false then maxRecords is ignored and data is
+         * always added to the bottom of the store. 
+         */
+        dropRecords: true
+    },
     /**
-     * Property: contentIsLoaded
-     *
-     * tracks the load state of the content, specifically useful
-     * in the case of remote content.
-     */ 
-    contentIsLoaded: false,
+     * Property: startingPage
+     */
+    startingPage: 0,
     /**
-     * Method: loadContent
-     *
-     * triggers loading of content based on options set for the current
-     * object.
-     *
-     * Parameters: 
-     * element - {Object} the element to insert the content into
-     *
-     * Events:
-     *
-     * ContentLoader adds the following events to an object.  You can
-     * register for these events using the addEvent method or by providing
-     * callback functions via the on{EventName} properties in the options 
-     * object
-     *
-     * contentLoaded - called when the content has been loaded.  If the
-     *     content is not asynchronous then this is called before loadContent
-     *     returns.
-     * contentLoadFailed - called if the content fails to load, primarily
-     *     useful when using the contentURL method of loading content.
-     */     
-    loadContent: function(element) {
-        element = $(element);
-        if (this.options.content) {
-            var c;
-            if (this.options.content.domObj) {
-                c = $(this.options.content.domObj);
-            } else {
-                c = $(this.options.content);
+     * Property: maxPages
+     */
+    maxPages: null,
+    /**
+     * Property: loadedPages
+     */
+    loadedPages: 0,
+    /**
+     * Property: loadAt
+     * Options are 'top' or 'bottom'. Defaults to 'bottom'.
+     */
+    loadAt: 'bottom',
+    
+    /**
+     * Method: init
+     * initialize this strategy
+     */
+    init: function () {
+        this.parent();
+        if (this.options.dropPages) {
+            this.maxPages = Math.ceil(this.options.maxRecords/this.itemsPerPage);
+        }
+    },
+    
+    /**
+     * Method: loadStore
+     * Used to assist in the loading of data into the store. This is 
+     * called as a response to the protocol finishing.
+     * 
+     *  Parameters:
+     *  resp - the response object
+     */
+    loadStore: function (resp) {
+        this.store.protocol.removeEvent('dataLoaded', this.bound.loadStore);
+        if (resp.success()) {
+            if ($defined(resp.meta)) {
+                this.parseMetaData(resp.meta);
             }
-            if (c) {
-                if (this.options.content.addTo) {
-                    this.options.content.addTo(element);
-                } else {
-                    element.appendChild(c);                    
-                }
-                this.contentIsLoaded = true;                
-            } else {
-                element.innerHTML = this.options.content;
-                this.contentIsLoaded = true;
-            }
-        } else if (this.options.contentURL) {
-            this.contentIsLoaded = false;
-            this.req = new Request({
-                url: this.options.contentURL, 
-                method:'get',
-                evalScripts:true,
-                onSuccess:(function(html) {
-                    element.innerHTML = html;
-                    this.contentIsLoaded = true;
-                    if (Jx.isAir){
-                        $clear(this.reqTimeout);
-                    }
-                    this.fireEvent('contentLoaded', this);
-                }).bind(this), 
-                onFailure: (function(){
-                    this.contentIsLoaded = true;
-                    this.fireEvent('contentLoadFailed', this);
-                }).bind(this),
-                headers: {'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT'}
-            });
-            this.req.send();
-            if (Jx.isAir) {
-                var timeout = $defined(this.options.timeout) ? this.options.timeout : 10000;
-                this.reqTimeout = this.checkRequest.delay(timeout, this);
-            }
+            this.loadData(resp.data);
         } else {
-            this.contentIsLoaded = true;
+            this.store.fireEvent('storeDataLoadFailed', this.store);
         }
-        if (this.options.contentId) {
-            element.id = this.options.contentId;
+    },
+    
+    /**
+     * Method: loadData
+     * This method does the actual work of loading data to the store. It is
+     * called when either the protocol finishes or setPage() has the data and
+     * it's not expired.
+     * 
+     * Parameters:
+     * data - the data to load into the store.
+     */
+    loadData: function (data) {
+        this.store.loaded = false;
+        this.store.addRecords(data, this.loadAt);
+        this.store.loaded = true;
+        this.loadedPages++;
+        this.store.fireEvent('storeDataLoaded',this.store);
+    },
+    
+    /**
+     * APIMethod: nextPage
+     * Allows a caller (i.e. a paging toolbar) to load more data to the end of 
+     * the store
+     * 
+     * Parameters:
+     * params - a hash of parameters to pass to the request if needed.
+     */
+    nextPage: function (params) {
+        if (!$defined(params)) {
+            params = {};
         }
-        if (this.contentIsLoaded) {
-            this.fireEvent('contentLoaded', this);
+        if (this.options.dropRecords && this.totalPages > this.startingPage + this.loadedPages) {
+            this.loadAt = 'bottom';
+            if (this.loadedPages >= this.maxPages) {
+                //drop records before getting more
+                this.startingPage++;
+                this.store.removeRecords(0,this.itemsPerPage - 1);
+                this.loadedPages--;
+            }
         }
+        this.page = this.startingPage + this.loadedPages + 1;
+        this.load($merge(this.params, params));
     },
-    
-    processContent: function(element) {
-        $A(element.childNodes).each(function(node){
-            if (node.tagName == 'INPUT' || node.tagName == 'SELECT' || node.tagName == 'TEXTAREA') {
-                if (node.type == 'button') {
-                    node.addEvent('click', function(){
-                        this.fireEvent('click', this, node);
-                    });
-                } else {
-                    node.addEvent('change', function(){
-                        this.fireEvent('change',node);
-                    });
-                }
-            } else {
-                if (node.childNodes) {
-                    this.processContent(node);
-                }
+    /**
+     * APIMethod: previousPage
+     * Allows a caller to move back to the previous page.
+     *
+     * Parameters:
+     * params - a hash of parameters to pass to the request if needed.
+     */
+    previousPage: function (params) {
+        //if we're not dropping pages there's nothing to do
+        if (!this.options.dropRecords) {
+            return;
+        }
+        
+        if (!$defined(params)) {
+            params = {};
+        }
+        if (this.startingPage > 0) {
+            this.loadAt = 'top';
+            if (this.loadedPages >= this.maxPages) {
+                //drop off end before loading previous pages
+                this.startingPage--;
+                this.store.removeRecords(this.options.maxRecords - this.itemsPerPage, this.options.maxRecords);
+                this.loadedPages--;
             }
-        }, this);
+            this.page = this.startingPage;
+            this.load($merge(this.params, params));
+        }
     }
-});
+});/*
+---
 
+name: Jx.Store.Strategy.Save
 
+description: Strategy used for saving data back to a source. Can be called manually or setup to automatically save on every change.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Store.Strategy
+
+provides: [Jx.Store.Strategy.Save]
+
+...
+ */
+// $Id: strategy.save.js 977 2010-09-02 18:57:42Z pagameba $
 /**
- * It seems AIR never returns an XHR that "fails" by not finding the 
- * appropriate file when run in the application sandbox and retrieving a local
- * file. This affects Jx.ContentLoader in that a "failed" event is never fired. 
+ * Class: Jx.Store.Strategy.Save 
  * 
- * To fix this, I've added a timeout that waits about 10 seconds or so in the code above
- * for the XHR to return, if it hasn't returned at the end of the timeout, we cancel the
- * XHR and fire the failure event.
+ * Extends: <Jx.Store.Strategy>
+ * 
+ * A Store strategy class for saving data via protocols
  *
- * This code only gets added if we're in AIR.
+ * License: 
+ * Copyright (c) 2009, Jon Bomgardner.
+ * 
+ * This file is licensed under an MIT style license
  */
-if (Jx.isAir){
-    Jx.ContentLoader.implement({
+Jx.Store.Strategy.Save = new Class({
+    
+    Extends: Jx.Store.Strategy,
+    
+    name: 'save',
+    
+    options: {
         /**
-         * Method: checkRequest()
-         * Is fired after a delay to check the request to make sure it's not
-         * failing in AIR.
+         * Option: autoSave
+         * Whether the strategy should be watching the store to save changes
+         * automatically. Set to True to watch events, set it to a number of 
+         * milliseconds to have the strategy save every so many seconds
          */
-        checkRequest: function(){
-            if (this.req.xhr.readyState === 1) {
-                //we still haven't gotten the file. Cancel and fire the
-                //failure
-                $clear(this.reqTimeout);
-                this.req.cancel();
-                this.contentIsLoaded = true;
-                this.fireEvent('contentLoadFailed', this);
-            }
+        autoSave: false
+    },
+    /**
+     * Property: failedChanges
+     * an array holding all failed requests
+     */
+    failedChanges: [],
+    /**
+     * Property: successfulChanges
+     * an array holding all successful requests
+     */
+    successfulChanges: [],
+    /**
+     * Property: totalChanges
+     * The total number of changes being processed. Used to determine
+     * when to fire off the storeChangesCompleted event on the store
+     */
+    totalChanges: 0,
+    
+    /**
+     * Method: init
+     * initialize this strategy
+     */
+    init: function () {
+        this.bound.save = this.saveRecord.bind(this);
+        this.bound.update = this.updateRecord.bind(this);
+        this.bound.completed = this.onComplete.bind(this);
+        this.parent();
+    },
+    
+    /**
+     * APIMethod: activate
+     * activates the strategy if it isn't already active.
+     */
+    activate: function () {
+        this.parent();
+        if (Jx.type(this.options.autoSave) === 'number') {
+            this.periodicalId = this.save.periodical(this.options.autoSave, this);
+        } else if (this.options.autoSave) {
+            this.store.addEvent('storeRecordAdded', this.bound.save);
+            this.store.addEvent('storeColumnChanged', this.bound.update);
+            this.store.addEvent('storeRecordDeleted', this.bound.save);
         }
-    });
-}
-
-/**
- * Class: Jx.AutoPosition
- * Mix-in class that provides a method for positioning
- * elements relative to other elements.
- */
-Jx.AutoPosition = new Class({
+        
+    },
+    
     /**
-     * Method: position
-     * positions an element relative to another element
-     * based on the provided options.  Positioning rules are
-     * a string with two space-separated values.  The first value
-     * references the parent element and the second value references
-     * the thing being positioned.  In general, multiple rules can be
-     * considered by passing an array of rules to the horizontal and
-     * vertical options.  The position method will attempt to position
-     * the element in relation to the relative element using the rules
-     * specified in the options.  If the element does not fit in the
-     * viewport using the rule, then the next rule is attempted.  If
-     * all rules fail, the last rule is used and element may extend
-     * outside the viewport.  Horizontal and vertical rules are
-     * processed independently.
+     * APIMethod: deactivate
+     * deactivates the strategy if it is already active.
+     */
+    deactivate: function () {
+        this.parent();
+        if ($defined(this.periodicalId)) {
+            $clear(this.periodicalId);
+        } else if (this.options.autoSave) {
+            this.store.removeEvent('storeRecordAdded', this.bound.save);
+            this.store.removeEvent('storeColumnChanged', this.bound.update);
+            this.store.removeEvent('storeRecordDeleted', this.bound.save);
+        }
+        
+    },
+    
+    /**
+     * APIMethod: updateRecord
+     * called by event handlers when store data is updated
      *
-     * Horizontal Positioning:
-     * Horizontal values are 'left', 'center', 'right', and numeric values.
-     * Some common rules are:
-     * o 'left left' is interpreted as aligning the left
-     * edge of the element to be positioned with the left edge of the
-     * reference element.  
-     * o 'right right' aligns the two right edges.  
-     * o 'right left' aligns the left edge of the element to the right of
-     * the reference element.  
-     * o 'left right' aligns the right edge of the element to the left
-     * edge of the reference element.
-     *
-     * Vertical Positioning:
-     * Vertical values are 'top', 'center', 'bottom', and numeric values.
-     * Some common rules are:
-     * o 'top top' is interpreted as aligning the top
-     * edge of the element to be positioned with the top edge of the
-     * reference element.  
-     * o 'bottom bottom' aligns the two bottom edges.  
-     * o 'bottom top' aligns the top edge of the element to the bottom of
-     * the reference element.  
-     * o 'top bottom' aligns the bottom edge of the element to the top
-     * edge of the reference element.
+     * Parameters:
+     * index - {Integer} the row that was affected
+     * column - {String} the column that was affected
+     * oldValue - {Mixed} the previous value
+     * newValue - {Mixed} the new value
+     */
+    updateRecord: function(index, column, oldValue, newValue) {
+      var resp = this.saveRecord(this.store, this.store.getRecord(index));
+      // no response if updating or record state not set
+      if (resp) {
+        resp.index = index;
+      }
+    },
+    /**
+     * APIMethod: saveRecord
+     * Called by event handlers when a store record is added, or deleted. 
+     * If deleted, the record will be removed from the deleted array.
      * 
      * Parameters:
-     * element - the element to position
-     * relative - the element to position relative to
-     * options - the positioning options, see list below.
-     *
-     * Options:
-     * horizontal - the horizontal positioning rule to use to position the 
-     *    element.  Valid values are 'left', 'center', 'right', and a numeric
-     *    value.  The default value is 'center center'.
-     * vertical - the vertical positioning rule to use to position the 
-     *    element.  Valid values are 'top', 'center', 'bottom', and a numeric
-     *    value.  The default value is 'center center'.
-     * offsets - an object containing numeric pixel offset values for the object
-     *    being positioned as top, right, bottom and left properties.
+     * record - The Jx.Record instance that was changed
+     * store - The instance of the store
      */
-    position: function(element, relative, options) {
-        element = $(element);
-        relative = $(relative);
-        var hor = $splat(options.horizontal || ['center center']);
-        var ver = $splat(options.vertical || ['center center']);
-        var offsets = $merge({top:0,right:0,bottom:0,left:0}, options.offsets || {});
-        
-        var coords = relative.getCoordinates(); //top, left, width, height
-        var page;
-        var scroll;
-        if (!$(element.parentNode) || element.parentNode ==  document.body) {
-            page = Jx.getPageDimensions();
-            scroll = $(document.body).getScroll();
-        } else {
-            page = $(element.parentNode).getContentBoxSize(); //width, height
-            scroll = $(element.parentNode).getScroll();
-        }
-        if (relative == document.body) {
-            // adjust coords for the scroll offsets to make the object
-            // appear in the right part of the page.
-            coords.left += scroll.x;
-            coords.top += scroll.y;            
-        } else if (element.parentNode == relative) {
-            // if the element is opening *inside* its relative, we want
-            // it to position correctly within it so top/left becomes
-            // the reference system.
-            coords.left = 0;
-            coords.top = 0;
-        }
-        var size = element.getMarginBoxSize(); //width, height
-        var left;
-        var right;
-        var top;
-        var bottom;
-        var n;
-        if (!hor.some(function(opt) {
-            var parts = opt.split(' ');
-            if (parts.length != 2) {
-                return false;
+    saveRecord: function (store, record) {
+        //determine the status and route based on that
+        if (!this.updating && $defined(record.state)) {
+            if (this.totalChanges === 0) {
+                this.store.protocol.addEvent('dataLoaded', this.bound.completed);
             }
-            if (!isNaN(parseInt(parts[0],10))) {
-                n = parseInt(parts[0],10);
-                if (n>=0) {
-                    left = n;                    
-                } else {
-                    left = coords.left + coords.width + n;
-                }
-            } else {
-                switch(parts[0]) {
-                    case 'right':
-                        left = coords.left + coords.width;
-                        break;
-                    case 'center':
-                        left = coords.left + Math.round(coords.width/2);
-                        break;
-                    case 'left':
-                    default:
-                        left = coords.left;
-                        break;
-                }                
+            this.totalChanges++;
+            var ret;
+            switch (record.state) {
+                case Jx.Record.UPDATE:
+                    ret = this.store.protocol.update(record);
+                    break;
+                case Jx.Record.DELETE:
+                    ret = this.store.protocol['delete'](record);
+                    break;
+                case Jx.Record.INSERT:
+                    ret = this.store.protocol.insert(record);
+                    break;
             }
-            if (!isNaN(parseInt(parts[1],10))) {
-                n = parseInt(parts[1],10);
-                if (n<0) {
-                    right = left + n;
-                    left = right - size.width;
-                } else {
-                    left += n;
-                    right = left + size.width;
+            return ret;
+        }
+    },
+    /**
+     * APIMethod: save
+     * Called manually when the developer wants to save all data changes 
+     * in one shot. It will empty the deleted array and reset all other status 
+     * flags
+     */
+    save: function () {
+        //go through all of the data and figure out what needs to be acted on
+        if (this.store.loaded) {
+            var records = [];
+            records[Jx.Record.UPDATE] = [];
+            records[Jx.Record.INSERT] = [];
+            
+            this.store.data.each(function (record) {
+                if ($defined(record) && $defined(record.state)) {
+                    records[record.state].push(record);
                 }
-                right = coords.left + coords.width + parseInt(parts[1],10);
-                left = right - size.width;
-            } else {
-                switch(parts[1]) {
-                    case 'left':
-                        left -= offsets.left;
-                        right = left + size.width;
-                        break;
-                    case 'right':
-                        left += offsets.right;
-                        right = left;
-                        left = left - size.width;
-                        break;
-                    case 'center':
-                    default:
-                        left = left - Math.round(size.width/2);
-                        right = left + size.width;
-                        break;
-                }                
-            }
-            return (left >= scroll.x && right <= scroll.x + page.width);
-        })) {
-            // all failed, snap the last position onto the page as best
-            // we can - can't do anything if the element is wider than the
-            // space available.
-            if (right > page.width) {
-                left = scroll.x + page.width - size.width;
-            }
-            if (left < 0) {
-                left = 0;
-            }
+            }, this);
+            records[Jx.Record.DELETE] = this.store.deleted;
+            
+            records.flatten().each(function (record) {
+                this.saveRecord(null, record);
+            }, this);
         }
-        element.setStyle('left', left);
         
-        if (!ver.some(function(opt) {
-                var parts = opt.split(' ');
-                if (parts.length != 2) {
-                    return false;
-                }
-                if (!isNaN(parseInt(parts[0],10))) {
-                    top = parseInt(parts[0],10);
-                } else {
-                    switch(parts[0]) {
-                        case 'bottom':
-                            top = coords.top + coords.height;
-                            break;
-                        case 'center':
-                            top = coords.top + Math.round(coords.height/2);
-                            break;
-                        case 'top':
-                        default:
-                            top = coords.top;
-                            break;
+    },
+    /**
+     * Method: onComplete
+     * Handles processing of the response(s) from the protocol. Each 
+     * update/insert/delete will have an individual response. If any responses 
+     * come back failed we will hold that response and send it to the caller
+     * via the fired event. This method is responsible for updating the status
+     * of each record as it returns and on inserts, it updates the primary key
+     * of the record. If it was a delete it will remove it permanently from
+     * the store's deleted array (provided it returns successful - based on
+     * the success attribute of the meta object). When all changes have been 
+     * accounted for the method fires a finished event and passes all of the 
+     * failed responses to the caller so they can be handled appropriately.
+     * 
+     * Parameters:
+     * response - the response returned from the protocol
+     */
+    onComplete: function (response) {
+        if (!response.success() || ($defined(response.meta) && !response.meta.success)) {
+            this.failedChanges.push(response);
+        } else {
+            //process the response
+            var record = response.requestParams[0];
+            if (response.requestType === 'delete') {
+                this.store.deleted.erase(record);
+            } else { 
+                if (response.requestType === 'insert' || response.requestType == 'update') {
+                    if ($defined(response.data)) {
+                        this.updating = true;
+                        $H(response.data).each(function (val, key) {
+                            var d = record.set(key, val);
+                            if (d[1] != val && $defined(response.index)) {
+                              d.unshift(response.index);
+                              record.store.fireEvent('storeColumnChanged', d);
+                            }
+                        });
+                        this.updating = false;
                     }
                 }
-                if (!isNaN(parseInt(parts[1],10))) {
-                    var n = parseInt(parts[1],10);
-                    if (n>=0) {
-                        top += n;
-                        bottom = top + size.height;
-                    } else {
-                        bottom = top + n;
-                        top = bottom - size.height; 
-                    }
-                } else {
-                    switch(parts[1]) {
-                        case 'top':
-                            top -= offsets.top;
-                            bottom = top + size.height;
-                            break;
-                        case 'bottom':
-                            top += offsets.bottom;
-                            bottom = top;
-                            top = top - size.height;
-                            break;
-                        case 'center':
-                        default:
-                            top = top - Math.round(size.height/2);
-                            bottom = top + size.height;
-                            break;
-                    }                    
-                }
-                return (top >= scroll.y && bottom <= scroll.y + page.height);
-            })) {
-                // all failed, snap the last position onto the page as best
-                // we can - can't do anything if the element is higher than the
-                // space available.
-                if (bottom > page.height) {
-                    top = scroll.y + page.height - size.height;
-                }
-                if (top < 0) {
-                    top = 0;
-                }
-            }
-            element.setStyle('top', top);
-            
-            /* update the jx layout if necessary */
-            var jxl = element.retrieve('jxLayout');
-            if (jxl) {
-                jxl.options.left = left;
-                jxl.options.top = top;
-            }
+                record.state = null;
+            } 
+            this.successfulChanges.push(response);
         }
-});
+        this.totalChanges--;
+        if (this.totalChanges === 0) {
+            this.store.protocol.removeEvent('dataLoaded', this.bound.completed);
+            this.store.fireEvent('storeChangesCompleted', {
+                successful: this.successfulChanges,
+                failed: this.failedChanges
+            });
+        }
+    }
+});/*
+---
 
+name: Jx.Store.Strategy.Sort
+
+description: Strategy used for sorting results in a store after they are loaded.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Store.Strategy
+ - Jx.Sort.Mergesort
+ - Jx.Compare
+
+provides: [Jx.Store.Strategy.Sort]
+...
+ */
+// $Id: strategy.sort.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.Chrome
- * A mix-in class that provides chrome helper functions.  Chrome is the
- * extraneous visual element that provides the look and feel to some elements
- * i.e. dialogs.  Chrome is added inside the element specified but may
- * bleed outside the element to provide drop shadows etc.  This is done by
- * absolutely positioning the chrome objects in the container based on
- * calculations using the margins, borders, and padding of the jxChrome
- * class and the element it is added to.
+ * Class: Jx.Store.Strategy.Sort
+ * 
+ * Extends: <Jx.Store.Strategy>
+ * 
+ * Strategy used for sorting stores. It can either be called manually or it
+ * can listen for specific events from the store.
  *
- * Chrome can consist of either pure CSS border and background colors, or
- * a background-image on the jxChrome class.  Using a background-image on
- * the jxChrome class creates four images inside the chrome container that
- * are positioned in the top-left, top-right, bottom-left and bottom-right
- * corners of the chrome container and are sized to fill 50% of the width
- * and height.  The images are positioned and clipped such that the 
- * appropriate corners of the chrome image are displayed in those locations.
+ * License: 
+ * Copyright (c) 2009, Jon Bomgardner.
+ * 
+ * This file is licensed under an MIT style license
  */
-Jx.Chrome = new Class({
+Jx.Store.Strategy.Sort = new Class({
+    
+    Extends: Jx.Store.Strategy,
+    
+    name: 'sort',
+    
+    options: {
+        /**
+         * Option: sortOnStoreEvents
+         * an array of events this strategy should listen for on the store and
+         * sort when it sees them.
+         */
+        sortOnStoreEvents: ['storeColumnChanged','storeDataLoaded'],
+        /**
+         * Option: defaultSort
+         * The default sorting type, currently set to merge but can be any of
+         * the sorters available
+         */
+        defaultSort : 'merge',
+        /**
+         * Option: separator
+         * The separator to pass to the comparator
+         * constructor (<Jx.Compare>) - defaults to '.'
+         */
+        separator : '.',
+        /**
+         * Option: sortCols
+         * An array of columns to sort by arranged in the order you want 
+         * them sorted.
+         */
+        sortCols : []
+    },
+    
     /**
-     * Property: chrome
-     * the DOM element that contains the chrome
+     * Property: sorters
+     * an object listing the different sorters available
      */
-    chrome: null,
+    sorters : {
+        quick : "Quicksort",
+        merge : "Mergesort",
+        heap : "Heapsort",
+        'native' : "Nativesort"
+    },
     
     /**
-     * Method: makeChrome
-     * create chrome on an element.
-     *
-     * Parameters:
-     * element - {HTMLElement} the element to put the chrome on.
+     * Method: init
+     * initialize this strategy
      */
-    makeChrome: function(element) {
-        var c = new Element('div', {
-            'class':'jxChrome',
-            events: {
-                contextmenu: function(e) { e.stop(); }
-            }      
-        });
+    init: function () {
+        this.parent();
+        this.bound.sort = this.sort.bind(this);
+    },
+    
+    /**
+     * APIMethod: activate
+     * activates the strategy if it isn't already active.
+     */
+    activate: function () {
+        if ($defined(this.options.sortOnStoreEvents)) {
+            this.options.sortOnStoreEvents.each(function (ev) {
+                this.store.addEvent(ev, this.bound.sort);
+            },this);
+        }
+    },
+    
+    /**
+     * APIMethod: deactivate
+     * deactivates the strategy if it is already active.
+     */
+    deactivate: function () {
+        if ($defined(this.options.sortOnStoreEvents)) {
+            this.options.sortOnStoreEvents.each(function (ev) {
+                this.store.removeEvent(ev, this.bound.sort);
+            },this);
+        }
+    },
+    
+    /**
+     * APIMethod: sort 
+     * Runs the sorting and grouping
+     * 
+     * Parameters: 
+     * cols - Optional. An array of columns to sort/group by 
+     * sort - the sort type (quick,heap,merge,native),defaults to
+     *     options.defaultSort
+     * dir - the direction to sort. Set to "desc" for descending,
+     * anything else implies ascending (even null). 
+     */
+    sort : function (cols, sort, dir) {
+        if (this.store.count()) {
+            this.store.fireEvent('sortStart', this);
+            var c;
+            if ($defined(cols) && Jx.type(cols) === 'array') {
+                c = this.options.sortCols = cols;
+            } else if ($defined(cols) && Jx.type(cols) === 'string') {
+                this.options.sortCols = [];
+                this.options.sortCols.push(cols);
+                c = this.options.sortCols;
+            } else if ($defined(this.options.sortCols)) {
+                c = this.options.sortCols;
+            } else {
+                return null;
+            }
+            
+            this.sortType = sort;
+            // first sort on the first array item
+            this.store.data = this.doSort(c[0], sort, this.store.data, true);
         
-        /* add to element so we can get the background image style */
-        element.adopt(c);
+            if (c.length > 1) {
+                this.store.data = this.subSort(this.store.data, 0, 1);
+            }
         
-        /* pick up any offset because of chrome, set
-         * through padding on the chrome object.  Other code can then
-         * make use of these offset values to fix positioning.
+            if ($defined(dir) && dir === 'desc') {
+                this.store.data.reverse();
+            }
+        
+            this.store.fireEvent('storeSortFinished', this);
+        }
+    },
+    
+    /**
+     * Method: subSort 
+     * Does the actual group sorting.
+     * 
+     * Parameters: 
+     * data - what to sort 
+     * groupByCol - the column that determines the groups 
+     * sortCol - the column to sort by
+     * 
+     * returns: the result of the grouping/sorting
+     */
+    subSort : function (data, groupByCol, sortByCol) {
+        
+        if (sortByCol >= this.options.sortCols.length) {
+            return data;
+        }
+        /**
+         *  loop through the data array and create another array with just the
+         *  items for each group. Sort that sub-array and then concat it 
+         *  to the result.
          */
-        this.chromeOffsets = c.getPaddingSize();
-        c.setStyle('padding', 0);
+        var result = [];
+        var sub = [];
         
-        /* get the chrome image from the background image of the element */
-        /* the app: protocol check is for adobe air support */
-        var src = c.getStyle('backgroundImage');
-        if(src != null)
-        { 
-        	if (!(src.contains('http://') || src.contains('https://') || src.contains('file://') || src.contains('app:/'))) 
-            {
-                src = null;
-            } 
-            else 
-            {
-                src = src.slice(4,-1);
-                /* this only seems to be IE and Opera, but they add quotes
-                 * around the url - yuck
-                 */
-                if (src.charAt(0) == '"') {
-                    src = src.slice(1,-1);
+        var groupCol = this.options.sortCols[groupByCol];
+        var sortCol = this.options.sortCols[sortByCol];
+    
+        var group = data[0].get(groupCol);
+        this.sorter.setColumn(sortCol);
+        for (var i = 0; i < data.length; i++) {
+            if (group === (data[i]).get(groupCol)) {
+                sub.push(data[i]);
+            } else {
+                // sort
+    
+                if (sub.length > 1) {
+                    result = result.concat(this.subSort(this.doSort(sortCol, this.sortType, sub, true), groupByCol + 1, sortByCol + 1));
+                } else {
+                    result = result.concat(sub);
                 }
-
-                /* and remove the background image */
-                c.setStyle('backgroundImage', 'none');
-
-                /* make chrome */
-                ['TR','TL','BL','BR'].each(function(s){
-                    c.adopt(
-                        new Element('div',{
-                            'class':'jxChrome'+s
-                        }).adopt(
-                        new Element('img',{
-                            'class':'png24',
-                            src:src,
-                            alt: '',
-                            title: ''
-                        })));
-                }, this);
+            
+                // change group
+                group = (data[i]).get(groupCol);
+                // clear sub
+                sub.empty();
+                // add to sub
+                sub.push(data[i]);
             }
         }
-        if (!window.opera) {
-            c.adopt(Jx.createIframeShim());
+        
+        if (sub.length > 1) {
+            this.sorter.setData(sub);
+            result = result.concat(this.subSort(this.doSort(sortCol, this.sortType, sub, true), groupByCol + 1, sortByCol + 1));
+        } else {
+            result = result.concat(sub);
         }
         
-        /* remove from DOM so the other resizing logic works as expected */
-        c.dispose();    
-        this.chrome = c;
+        //this.data = result;
+        
+        return result;
     },
+    
     /**
-     * Method: showChrome
-     * show the chrome on an element.  This creates the chrome if necessary.
-     * If the chrome has been previously created and not removed, you can
-     * call this without an element and it will just resize the chrome within
-     * its existing element.  You can also pass in a different element from
-     * which the chrome was previously attached to and it will move the chrome
-     * to the new element.
-     *
-     * Parameters:
-     * element - {HTMLElement} the element to show the chrome on.
+     * Method: doSort 
+     * Called to change the sorting of the data
+     * 
+     * Parameters: 
+     * col - the column to sort by 
+     * sort - the kind of sort to use (see list above) 
+     * data - the data to sort (leave blank or pass null to sort data
+     * existing in the store) 
+     * ret - flag that tells the function whether to pass
+     * back the sorted data or store it in the store 
+     * options - any options needed to pass to the sorter upon creation
+     * 
+     * returns: nothing or the data depending on the value of ret parameter.
      */
-    showChrome: function(element) {
-        element = $(element);
-        if (!this.chrome) {
-            this.makeChrome(element);
+    doSort : function (col, sort, data, ret, options) {
+        options = {} || options;
+        
+        sort = (sort) ? this.sorters[sort] : this.sorters[this.options.defaultSort];
+        data = data ? data : this.data;
+        ret = ret ? true : false;
+        
+        if (!$defined(this.comparator)) {
+            this.comparator = new Jx.Compare({
+                separator : this.options.separator
+            });
         }
-        this.resizeChrome(element);
-        if (element && this.chrome.parentNode !== element) {
-            element.adopt(this.chrome);
+        
+        this.col = col = this.resolveCol(col);
+        
+        var fn = this.comparator[col.type].bind(this.comparator);
+        if (!$defined(this.sorter)) {
+            this.sorter = new Jx.Sort[sort](data, fn, col.name, options);
+        } else {
+            this.sorter.setComparator(fn);
+            this.sorter.setColumn(col.name);
+            this.sorter.setData(data);
         }
+        var d = this.sorter.sort();
+        
+        if (ret) {
+            return d;
+        } else {
+            this.data = d;
+        }
     },
     /**
-     * Method: hideChrome
-     * removes the chrome from the DOM.  If you do this, you can't
-     * call showChrome with no arguments.
+     * Method: resolveCol
+     * resolves the given column identifier and resolves it to the 
+     * actual column object in the store.
+     * 
+     * Parameters:
+     * col - the name or index of the required column.
      */
-    hideChrome: function() {
-        if (this.chrome) {
-            this.chrome.dispose();
+    resolveCol: function (col) {
+        var t = Jx.type(col);
+        if (t === 'number') {
+            col = this.store.options.columns[col];
+        } else if (t === 'string') {
+            this.store.options.columns.each(function (column) {
+                if (column.name === col) {
+                    col = column;
+                }
+            }, this);
         }
-    },
-    resizeChrome: function(o) {
-        if (this.chrome && Browser.Engine.trident) {
-            this.chrome.setContentBoxSize($(o).getBorderBoxSize());
-        }
+        return col;   
     }
-});
+});/*
+---
 
+name: Jx.Store.Parser
+
+description: Base class for all data parsers. Parsers are used by protocols to get data received or sent in the proper formats.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Store
+
+provides: [Jx.Store.Parser]
+
+...
+ */
+// $Id: parser.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.Addable
- * A mix-in class that provides a helper function that allows an object
- * to be added to an existing element on the page.
+ * Class: Jx.Store.Parser
+ * 
+ * Extends: <Jx.Object>
+ * 
+ * Base class for all parsers
+ *
+ * License: 
+ * Copyright (c) 2009, Jon Bomgardner.
+ * 
+ * This file is licensed under an MIT style license
  */
-Jx.Addable = new Class({
-    addable: null,
+
+Jx.Store.Parser = new Class({
+    
+    Extends: Jx.Object,
+    Family: 'Jx.Store.Parser',
+    
     /**
-     * Method: addTo
-     * adds the object to the DOM relative to another element.  If you use
-     * 'top' or 'bottom' then the element is added to the relative
-     * element (becomes a child node).  If you use 'before' or 'after'
-     * then the element is inserted adjacent to the reference node. 
+     * APIMethod: parse
+     * Reads data passed to it by a protocol and parses it into a specific
+     * format needed by the store/record.
+     * 
+     * Parameters:
+     * data - string of data to parse
+     */
+    parse: $empty,
+    /**
+     * APIMethod: encode
+     * Takes an Jx.Record object and encodes it into a format that can be transmitted 
+     * by a protocol.
+     * 
+     * Parameters:
+     * object - an object to encode
+     */
+    encode: $empty
+});/*
+---
+
+name: Jx.Store.Parser.JSON
+
+description: Parser for reading and writting JSON formatted data.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Store.Parser
+ - Core/JSON
+
+provides: [Jx.Store.Parser.JSON]
+
+...
+ */
+// $Id: parser.json.js 977 2010-09-02 18:57:42Z pagameba $
+/**
+ * Class: Jx.Store.Parser.JSON
+ *
+ * Extends: <Jx.Store.Parser>
+ *
+ * A Parser that handles encoding and decoding JSON strings
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Store.Parser.JSON = new Class({
+
+    Extends: Jx.Store.Parser,
+
+    options: {
+        /**
+         * Option: secure
+         * Whether to use secure decoding. When using secure decoding the
+         * parser will return null if any invalid JSON characters are in the
+         * passed in string. Defaults to false.
+         */
+        secure: false
+    },
+    /**
+     * APIMethod: parse
+     * Turns a string into a JSON object if possible.
      *
      * Parameters:
-     * reference - {Object} the DOM element or id of a DOM element
-     * to append the object relative to
-     * where - {String} where to append the element in relation to the
-     * reference node.  Can be 'top', 'bottom', 'before' or 'after'.
-     * The default is 'bottom'.
+     * data - the string representation of the data we're parsing
+     */
+    parse: function (data) {
+        var type = Jx.type(data);
+
+        if (type === 'string') {
+            return JSON.decode(data, this.options.secure);
+        }
+        //otherwise just return the data object
+        return data;
+    },
+
+    /**
+     * APIMethod: encode
+     * Takes an object and turns it into JSON.
      *
-     * Returns:
-     * the object itself, which is useful for chaining calls together
+     * Parameters:
+     * object - the object to encode
      */
-    addTo: function(reference, where) {
-        $(this.addable || this.domObj).inject(reference,where);
-        this.fireEvent('addTo',this);
-        return this;
-    },
-    
-    toElement: function() {
-        return this.addable || this.domObj;
+    encode: function (object) {
+        var data;
+        if (object instanceof Jx.Record) {
+            data = object.asHash();
+        } else {
+            data = object;
+        }
+
+        return JSON.encode(data);
     }
-});// $Id: button.js 424 2009-05-12 12:51:44Z pagameba $
+});/*
+---
+
+name: Jx.Button
+
+description: Jx.Button creates a clickable element that can be added to a web page.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+
+optional:
+ - Core/Drag
+
+provides: [Jx.Button]
+
+css:
+ - button
+
+images:
+ - button.png
+
+...
+ */
+// $Id: button.js 977 2010-09-02 18:57:42Z pagameba $
 /**
  * Class: Jx.Button
  *
- * Extends: Object
+ * Extends: <Jx.Widget>
  *
- * Implements: Options, Events, <Jx.Addable>
- *
  * Jx.Button creates a clickable element that can be added to a web page.
  * When the button is clicked, it fires a 'click' event.
  *
- * The CSS styling for a button is controlled by several classes related
- * to the various objects in the button's HTML structure:
- *
- * (code)
- * <div class="jxButtonContainer">
- *  <a class="jxButton">
- *   <span class="jxButtonContent">
- *    <img class="jxButtonIcon" src="image_url">
- *    <span class="jxButtonLabel">button label</span>
- *   </span>
- *  </a>
- * </div>
- * (end)
- *
- * The CSS classes will change depending on the type option passed to the
- * constructor of the button.  The default type is Button.  Passing another
- * value such as Tab will cause all the CSS classes to change from jxButton
- * to jxTab.  For example:
- *
- * (code)
- * <div class="jxTabContainer">
- *  <a class="jxTab">
- *   <span class="jxTabContent">
- *    <img class="jxTabIcon" src="image_url">
- *    <span class="jxTabLabel">tab label</span>
- *   </span>
- *  </a>
- * </div>
- * (end)
- *
  * When you construct a new instance of Jx.Button, the button does not
  * automatically get inserted into the web page.  Typically a button
  * is used as part of building another capability such as a Jx.Toolbar.
  * However, if you want to manually insert the button into your application,
- * you may use the addTo method to append or insert the button into the 
- * page.  
+ * you may use the <Jx.Button::addTo> method to append or insert the button into the
+ * page.
  *
  * There are two modes for a button, normal and toggle.  A toggle button
  * has an active state analogous to a checkbox.  A toggle button generates
@@ -10410,7 +21630,7 @@
  * 'click' event.  You can pass a function in the 'onClick' option when
  * constructing a button or you can call the addEvent('click', myFunction)
  * method.  The addEvent method can be called several times, allowing more
- * than one function to be called when a button is clicked.  You can use the 
+ * than one function to be called when a button is clicked.  You can use the
  * removeEvent('click', myFunction) method to stop receiving click events.
  *
  * Example:
@@ -10443,34 +21663,16 @@
  * down - the button is down (only if type is 'toggle')
  * up - the button is up (only if the type is 'toggle').
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Button = new Class({
     Family: 'Jx.Button',
-    Implements: [Options,Events,Jx.Addable],
-    
-    /**
-     * the HTML element that is inserted into the DOM for this button.  You
-     * may reference this object to append it to the DOM or remove it from
-     * the DOM if necessary.
-     */
-    domObj: null,
-    
+    Extends: Jx.Widget,
+
     options: {
-        /* Option: id
-         * optional.  A string value to use as the ID of the button
-         * container.
-         */
-        id: '',
-        /* Option: type
-         * optional.  A string value that indicates what type of button this
-         * is.  The default value is Button.  The type is used to form the CSS
-         * class names used for various HTML elements within the button.
-         */
-        type: 'Button',
         /* Option: image
          * optional.  A string value that is the url to load the image to
          * display in this button.  The default styles size this image to 16 x
@@ -10486,7 +21688,8 @@
         tooltip: '',
         /* Option: label
          * optional, default is no label.  A string value that is used as a
-         * label on the button.
+         * label on the button. - use an object for localization: { set: 'Examples', key: 'lanKey', value: 'langValue' }
+         * see widget.js for details
          */
         label: '',
         /* Option: toggle
@@ -10494,20 +21697,21 @@
          */
         toggle: false,
         /* Option: toggleClass
-         * defaults to Toggle, this is class is added to buttons with the
-         * option toggle: true
+         * A class to apply to the button if it is a toggle button,
+         * 'jxButtonToggle' by default.
          */
-        toggleClass: 'Toggle',
-        /* Option: halign
-         * horizontal alignment of the button label, 'center' by default. 
-         * Other values are 'left' and 'right'.
+        toggleClass: 'jxButtonToggle',
+        /* Option: pressedClass
+         * A class to apply to the button when it is pressed,
+         * 'jxButtonPressed' by default.
          */
-        halign: 'center',
-        /* Option: valign
-         * {String} vertical alignment of the button label, 'middle' by
-         * default.  Other values are 'top' and 'bottom'.
+        pressedClass: 'jxButtonPressed',
+        /* Option: activeClass
+         * A class to apply to the buttonwhen it is active,
+         * 'jxButtonActive' by default.
          */
-        valign: 'middle',
+        activeClass: 'jxButtonActive',
+
         /* Option: active
          * optional, default false.  Controls the initial state of toggle
          * buttons.
@@ -10517,136 +21721,142 @@
          * whether the button is enabled or not.
          */
         enabled: true,
-        /* Option: container
-         * the tag name of the HTML element that should be created to contain
-         * the button, by default this is 'div'.
+        /* Option: href
+         * set an href on the button's action object, typically an <a> tag.
+         * Default is javascript:void(0) and use onClick.
          */
-        container: 'div'
+        href: 'javascript:void(0);',
+        /* Option: target
+         * for buttons that have an href, allow setting the target
+         */
+        target: '',
+        /* Option: template
+         * the HTML structure of the button.  As a minimum, there must be a
+         * containing element with a class of jxButtonContainer and an
+         * internal element with a class of jxButton.  jxButtonIcon and
+         * jxButtonLabel are used if present to put the image and label into
+         * the button.
+         */
+        template: '<span class="jxButtonContainer"><a class="jxButton"><span class="jxButtonContent"><img class="jxButtonIcon" src="'+Jx.aPixel.src+'"><span class="jxButtonLabel"></span></span></a></span>'
     },
+
     /**
-     * Constructor: Jx.Button
+     * Property: classes
+     * used to auto-populate this object with element references when
+     * processing templates
+     */
+    classes: new Hash({
+        domObj: 'jxButtonContainer',
+        domA: 'jxButton',
+        domImg: 'jxButtonIcon',
+        domLabel: 'jxButtonLabel'
+    }),
+
+    /**
+     * Method: render
      * create a new button.
-     *
-     * Parameters:
-     * options - {Object} an object containing optional properties for this
-     * button as below.
      */
-    initialize : function( options ) {
-        this.setOptions(options);
-        
-        // the main container for the button
-        var d = new Element(this.options.container, {'class': 'jx'+this.options.type+'Container'});
-        if (this.options.toggle && this.options.toggleClass) {
-            d.addClass('jx'+this.options.type+this.options.toggleClass);
+    render: function() {
+        this.parent();
+        var options = this.options,
+            hasFocus,
+            mouseDown;
+        /* is the button toggle-able? */
+        if (options.toggle) {
+            this.domObj.addClass(options.toggleClass);
         }
+
         // the clickable part of the button
-        var hasFocus;
-        var mouseDown;
-        var a = new Element('a', {
-            'class': 'jx'+this.options.type, 
-            href: 'javascript:void(0)', 
-            title: this.options.tooltip, 
-            alt: this.options.tooltip,
-            events: {
+        if (this.domA) {
+            this.domA.set({
+                target: options.target,
+                href: options.href,
+                title: this.getText(options.tooltip),
+                alt: this.getText(options.tooltip)
+            });
+            this.domA.addEvents({
                 click: this.clicked.bindWithEvent(this),
                 drag: (function(e) {e.stop();}).bindWithEvent(this),
                 mousedown: (function(e) {
-                    this.domA.addClass('jx'+this.options.type+'Pressed');
+                    this.domA.addClass(options.pressedClass);
                     hasFocus = true;
                     mouseDown = true;
                     this.focus();
                 }).bindWithEvent(this),
                 mouseup: (function(e) {
-                    this.domA.removeClass('jx'+this.options.type+'Pressed');
+                    this.domA.removeClass(options.pressedClass);
                     mouseDown = false;
                 }).bindWithEvent(this),
                 mouseleave: (function(e) {
-                    this.domA.removeClass('jx'+this.options.type+'Pressed');
+                    this.domA.removeClass(options.pressedClass);
                 }).bindWithEvent(this),
                 mouseenter: (function(e) {
                     if (hasFocus && mouseDown) {
-                        this.domA.addClass('jx'+this.options.type+'Pressed');
+                        this.domA.addClass(options.pressedClass);
                     }
                 }).bindWithEvent(this),
                 keydown: (function(e) {
                     if (e.key == 'enter') {
-                        this.domA.addClass('jx'+this.options.type+'Pressed');
+                        this.domA.addClass(options.pressedClass);
                     }
                 }).bindWithEvent(this),
                 keyup: (function(e) {
                     if (e.key == 'enter') {
-                        this.domA.removeClass('jx'+this.options.type+'Pressed');
+                        this.domA.removeClass(options.pressedClass);
                     }
                 }).bindWithEvent(this),
                 blur: function() { hasFocus = false; }
+            });
+
+            if (typeof Drag != 'undefined') {
+                new Drag(this.domA, {
+                    onStart: function() {this.stop();}
+                });
             }
-        });
-        d.adopt(a);
-        
-        if (typeof Drag != 'undefined') {
-            new Drag(a, {
-                modifiers: {x: null, y: null},
-                onStart: function() {this.stop();}
-            });
         }
-        
-        var s = new Element('span', {'class': 'jx'+this.options.type+'Content'});
-        a.adopt(s);
-        
-        if (this.options.image || !this.options.label) {
-            var i = new Element('img', {
-                'class':'jx'+this.options.type+'Icon',
-                'src': Jx.aPixel.src,
-                title: this.options.tooltip, 
-                alt: this.options.tooltip
-            });
-            //if image is not a_pixel, set the background image of the image
-            //otherwise let the default css take over.
-            if (this.options.image && this.options.image.indexOf('a_pixel.png') == -1) {
-                i.setStyle('backgroundImage',"url("+this.options.image+")");
+
+        if (this.domImg) {
+            if (options.image || !options.label) {
+                this.domImg.set({
+                    title: this.getText(options.tooltip),
+                    alt: this.getText(options.tooltip)
+                });
+                if (options.image && options.image.indexOf(Jx.aPixel.src) == -1) {
+                    this.domImg.setStyle('backgroundImage',"url("+options.image+")");
+                }
+                if (options.imageClass) {
+                    this.domImg.addClass(options.imageClass);
+                }
+            } else {
+                //remove the image if we don't need it
+                this.domImg.setStyle('display','none');
             }
-            s.appendChild(i);
-            if (this.options.imageClass) {
-                i.addClass(this.options.imageClass);
+        }
+
+        if (this.domLabel) {
+            if (options.label || this.domA.hasClass('jxDiscloser')) {
+                this.setLabel(options.label);
+            } else {
+                //this.domLabel.removeClass('jx'+this.type+'Label');
+                this.domLabel.setStyle('display','none');
             }
-            this.domImg = i;
         }
-        
-        var l = new Element('span', {
-            html: this.options.label
-        });
-        if (this.options.label) {
-            l.addClass('jx'+this.options.type+'Label');
-        }
-        s.appendChild(l);
-        
-        if (this.options.id) {
-            d.id = this.options.id;
-        }
-        if (this.options.halign == 'left') {
-            d.addClass('jx'+this.options.type+'ContentLeft');                
-        }
 
-        if (this.options.valign == 'top') {
-            d.addClass('jx'+this.options.type+'ContentTop');
+        if (options.id) {
+            this.domObj.set('id', options.id);
         }
-        
-        this.domA = a;
-        this.domLabel = l;
-        this.domObj = d;        
 
         //update the enabled state
-        this.setEnabled(this.options.enabled);
-        
+        this.setEnabled(options.enabled);
+
         //update the active state if necessary
-        if (this.options.active) {
-            this.options.active = false;
+        if (options.active) {
+            options.active = false;
             this.setActive(true);
         }
-        
     },
     /**
-     * Method: clicked
+     * APIMethod: clicked
      * triggered when the user clicks the button, processes the
      * actionPerformed event
      *
@@ -10654,9 +21864,10 @@
      * evt - {Event} the user click event
      */
     clicked : function(evt) {
-        if (this.options.enabled) {
-            if (this.options.toggle) {
-                this.setActive(!this.options.active);
+        var options = this.options;
+        if (options.enabled && !this.isBusy()) {
+            if (options.toggle) {
+                this.setActive(!options.active);
             } else {
                 this.fireEvent('click', {obj: this, event: evt});
             }
@@ -10664,18 +21875,18 @@
         //return false;
     },
     /**
-     * Method: isEnabled
+     * APIMethod: isEnabled
      * This returns true if the button is enabled, false otherwise
      *
      * Returns:
      * {Boolean} whether the button is enabled or not
      */
-    isEnabled: function() { 
-        return this.options.enabled; 
+    isEnabled: function() {
+        return this.options.enabled;
     },
-    
+
     /**
-     * Method: setEnabled
+     * APIMethod: setEnabled
      * enable or disable the button.
      *
      * Parameters:
@@ -10683,45 +21894,49 @@
      */
     setEnabled: function(enabled) {
         this.options.enabled = enabled;
-        if (this.options.enabled) {
+        if (enabled) {
             this.domObj.removeClass('jxDisabled');
         } else {
             this.domObj.addClass('jxDisabled');
         }
     },
     /**
-     * Method: isActive
+     * APIMethod: isActive
      * For toggle buttons, this returns true if the toggle button is
      * currently active and false otherwise.
      *
      * Returns:
      * {Boolean} the active state of a toggle button
      */
-    isActive: function() { 
-        return this.options.active; 
+    isActive: function() {
+        return this.options.active;
     },
     /**
-     * Method: setActive
+     * APIMethod: setActive
      * Set the active state of the button
      *
      * Parameters:
      * active - {Boolean} the new active state of the button
      */
     setActive: function(active) {
-        if (this.options.active == active) {
-            return;
+        var options = this.options;
+        if (options.enabled && !this.isBusy()) {
+          if (options.active == active) {
+              return;
+          }
+          options.active = active;
+          if (this.domA) {
+              if (options.active) {
+                  this.domA.addClass(options.activeClass);
+              } else {
+                  this.domA.removeClass(options.activeClass);
+              }
+          }
+          this.fireEvent(active ? 'down':'up', this);
         }
-        this.options.active = active;
-        if (this.options.active) {
-            this.domA.addClass('jx'+this.options.type+'Active');
-            this.fireEvent('down', this);
-        } else {
-            this.domA.removeClass('jx'+this.options.type+'Active');
-            this.fireEvent('up', this);
-        }
     },
     /**
-     * Method: setImage
+     * APIMethod: setImage
      * set the image of this button to a new image URL
      *
      * Parameters:
@@ -10729,98 +21944,125 @@
      */
     setImage: function(path) {
         this.options.image = path;
-        if (path) {
-            if (!this.domImg) {
-                var i = new Element('img', {
-                    'class':'jx'+this.options.type+'Icon',
-                    'src': Jx.aPixel.src,
-                    alt: '',
-                    title: ''
-                });
-                if (this.options.imageClass) {
-                    i.addClass(this.options.imageClass);
-                }
-                this.domA.firstChild.grab(i, 'top');
-                this.domImg = i;
-            }
-            this.domImg.setStyle('backgroundImage',"url("+this.options.image+")");                        
-        } else if (this.domImg){
-            this.domImg.dispose();
-            this.domImg = null;
+        if (this.domImg) {
+            this.domImg.setStyle('backgroundImage',
+                                 "url("+path+")");
+            this.domImg.setStyle('display', path ? null : 'none');
         }
     },
     /**
-     * Method: setLabel
-     * 
-     * sets the text of the button.  Only works if a label was supplied
-     * when the button was constructed
+     * APIMethod: setLabel
+     * sets the text of the button.
      *
-     * Parameters: 
-     *
+     * Parameters:
      * label - {String} the new label for the button
      */
     setLabel: function(label) {
-        this.domLabel.set('html', label);
-        if (!label && this.domLabel.hasClass('jxButtonLabel')) {
-            this.domLabel.removeClass('jxButtonLabel');
-        } else if (label && !this.domLabel.hasClass('jxButtonLabel')) {
-            this.domLabel.addClass('jxButtonLabel');
+        this.options.label = label;
+        if (this.domLabel) {
+            this.domLabel.set('html', this.getText(label));
+            this.domLabel.setStyle('display', label || this.domA.hasClass('jxDiscloser') ? null : 'none');
         }
     },
     /**
-     * Method: getLabel
-     * 
+     * APIMethod: getLabel
      * returns the text of the button.
      */
     getLabel: function() {
-        return this.domLabel ? this.domLabel.innerHTML : '';
+        return this.options.label;
     },
     /**
-     * Method: setTooltip
+     * APIMethod: setTooltip
      * sets the tooltip displayed by the button
      *
-     * Parameters: 
+     * Parameters:
      * tooltip - {String} the new tooltip
      */
     setTooltip: function(tooltip) {
         if (this.domA) {
             this.domA.set({
-                'title':tooltip,
-                'alt':tooltip
+                'title':this.getText(tooltip),
+                'alt':this.getText(tooltip)
             });
         }
+        //need to account for the tooltip on the image as well
+        if (this.domImg) {
+            //check if title and alt are set...
+            var t = this.domImg.get('title');
+            if ($defined(t)) {
+                //change it...
+                this.domImg.set({
+                    'title':this.getText(tooltip),
+                    'alt':this.getText(tooltip)
+                });
+            }
+        }
     },
     /**
-     * Method: focus
+     * APIMethod: focus
      * capture the keyboard focus on this button
      */
     focus: function() {
-        this.domA.focus();
+        if (this.domA) {
+            this.domA.focus();
+        }
     },
     /**
-     * Method: blur
+     * APIMethod: blur
      * remove the keyboard focus from this button
      */
     blur: function() {
-        this.domA.blur();
+        if (this.domA) {
+            this.domA.blur();
+        }
+    },
+
+    /**
+     * APIMethod: changeText
+     *
+     * updates the label of the button on langChange Event for
+     * Internationalization
+     */
+    changeText : function(lang) {
+        this.parent();
+        this.setLabel(this.options.label);
+        this.setTooltip(this.options.tooltip);
     }
 });
-// $Id: flyout.js 424 2009-05-12 12:51:44Z pagameba $
+/*
+---
+
+name: Jx.Button.Flyout
+
+description: Flyout buttons expose a panel when the user clicks the button.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Button
+
+provides: [Jx.Button.Flyout]
+
+images:
+ - flyout_chrome.png
+ - emblems.png
+
+...
+ */
+// $Id: flyout.js 977 2010-09-02 18:57:42Z pagameba $
 /**
  * Class: Jx.Button.Flyout
  *
  * Extends: <Jx.Button>
  *
- * Implements: <Jx.ContentLoader>, <Jx.AutoPosition>, <Jx.Chrome>
- *
  * Flyout buttons expose a panel when the user clicks the button.  The
- * panel can have arbitrary content.  You must provide any necessary 
+ * panel can have arbitrary content.  You must provide any necessary
  * code to hook up elements in the panel to your application.
  *
  * When the panel is opened, the 'open' event is fired.  When the panel is
  * closed, the 'close' event is fired.  You can register functions to handle
  * these events in the options passed to the constructor (onOpen, onClose).
- * 
+ *
  * The user can close the flyout panel by clicking the button again, by
  * clicking anywhere outside the panel and other buttons, or by pressing the
  * 'esc' key.
@@ -10858,711 +22100,271 @@
  * open - this event is triggered when the flyout is opened.
  * close - this event is triggered when the flyout is closed.
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Button.Flyout = new Class({
     Family: 'Jx.Button.Flyout',
     Extends: Jx.Button,
-    Implements: [Jx.ContentLoader, Jx.AutoPosition, Jx.Chrome],
-    
+    Binds: ['keypressHandler', 'clickHandler'],
+    options: {
+        /* Option: template
+         * the HTML structure of the flyout button
+         */
+        template: '<span class="jxButtonContainer"><a class="jxButton jxButtonFlyout jxDiscloser"><span class="jxButtonContent"><img class="jxButtonIcon" src="'+Jx.aPixel.src+'"><span class="jxButtonLabel "></span></a></span>',
+        /* Option: contentTemplate
+         * the HTML structure of the flyout content area
+         */
+        contentTemplate: '<div class="jxFlyout"><div class="jxFlyoutContent"></div></div>',
+        /* Option: position
+         * where to position the flyout, see Jx.Widget::position
+         * for details on how to specify this option
+         */
+        position: {
+          horizontal: ['left left', 'right right'],
+          vertical: ['bottom top', 'top bottom']
+        },
+        /* Option: positionElement
+         * the element to position the flyout relative to, by default
+         * it is the domObj of this button and should only be changed
+         * if you really know what you are doing
+         */
+        positionElement: null
+    },
+
     /**
+     * Property: contentClasses
+     * the classes array for processing the contentTemplate
+     */
+    contentClasses: new Hash({
+        contentContainer: 'jxFlyout',
+        content: 'jxFlyoutContent'
+    }),
+
+    /**
      * Property: content
      * the HTML element that contains the flyout content
      */
     content: null,
     /**
-     * Constructor: initialize
-     * construct a new instance of a flyout button.  The single options
-     * argument takes a combination of options that apply to <Jx.Button>,
-     * <Jx.ContentLoader>, and <Jx.AutoPosition>.
-     *
-     * Parameters: 
-     * options - an options object used to initialize the button, see 
-     * <Jx.Button.Options>, <Jx.ContentLoader.Options>, and
-     * <Jx.AutoPosition.Options> for details.
+     * Method: render
+     * construct a new instance of a flyout button.
      */
-    initialize: function(options) {
+    render: function() {
+        var options = this.options;
         if (!Jx.Button.Flyout.Stack) {
             Jx.Button.Flyout.Stack = [];
         }
-        this.parent(options);
-        this.domA.addClass('jx'+this.options.type+'Flyout');
-        
-        this.contentContainer = new Element('div',{
-            'class':'jxFlyout'
-        });
-        
-        this.content = new Element('div', {
-            'class': 'jxFlyoutContent'
-        });
-        if (this.options.contentClass) {
-            this.content.addClass(this.options.contentClass);
+        this.parent();
+        this.processElements(options.contentTemplate, this.contentClasses);
+
+        if (options.contentClass) {
+            this.content.addClass(options.contentClass);
         }
-        this.contentContainer.adopt(this.content);
-        
+
         this.content.store('jxFlyout', this);
-        this.loadContent(this.content);
-        this.keypressWatcher = this.keypressHandler.bindWithEvent(this);
-        this.hideWatcher = this.clickHandler.bindWithEvent(this);
+        if(!options.loadOnDemand || options.active) {
+          this.loadContent(this.content);
+        }else{
+          this.addEvent('contentLoaded', function(ev) {
+            this.show(ev);
+          }.bind(this));
+        }
     },
+    cleanup: function() {
+      this.content.eliminate('jxFlyout');
+      this.content.destroy();
+      this.contentClasses.each(function(v,k){
+        this[k] = null;
+      }, this);
+      this.parent();
+    },
     /**
-     * Method: clicked
+     * APIMethod: clicked
      * Override <Jx.Button::clicked> to hide/show the content area of the
      * flyout.
      *
      * Parameters:
      * e - {Event} the user event
-     */ 
+     */
     clicked: function(e) {
-        if (!this.options.enabled) {
+        var options = this.options;
+        if (!options.enabled) {
             return;
         }
-        /* find out what we are contained by if we don't already know */
-        if (!this.owner) {
-            this.owner = document.body;
-            var node = $(this.domObj.parentNode);
-            while (node != document.body && this.owner == document.body) {
+        if (this.contentIsLoaded && options.cacheContent) {
+          this.show(e);
+        // load on demand or reload content if caching is disabled
+        } else if (options.loadOnDemand || !options.cacheContent) {
+          this.loadContent(this.content);
+        } else {
+          this.show(e);
+        }
+    },
+   /**
+    * Private Method: show
+    * Shows the Flyout after the content is loaded asynchronously
+    *
+    * Parameters:
+    * e - {Event} - the user or contentLoaded event
+    */
+    show: function(e) {
+        var node,
+            flyout,
+            owner = this.owner,
+            stack = Jx.Button.Flyout.Stack,
+            options = this.options;
+       /* find out what we are contained by if we don't already know */
+        if (!owner) {
+            this.owner = owner = document.body;
+            var node = document.id(this.domObj.parentNode);
+            while (node != document.body && owner == document.body) {
                 var flyout = node.retrieve('jxFlyout');
                 if (flyout) {
-                    this.owner = flyout;
+                    this.owner = owner = flyout;
                     break;
                 } else {
-                    node = $(node.parentNode);
+                    node = document.id(node.parentNode);
                 }
             }
         }
-        if (Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1] == this) {
+        if (stack[stack.length - 1] == this) {
             this.hide();
             return;
-        } else if (this.owner != document.body) {
+        } else if (owner != document.body) {
             /* if we are part of another flyout, close any open flyouts
              * inside the parent and register this as the current flyout
              */
-            if (this.owner.currentFlyout == this) {
+            if (owner.currentFlyout == this) {
                 /* if the flyout to close is this flyout,
                  * hide this and return */
                 this.hide();
                 return;
-            } else if (this.owner.currentFlyout) {
-                this.owner.currentFlyout.hide();
+            } else if (owner.currentFlyout) {
+                owner.currentFlyout.hide();
             }
-            this.owner.currentFlyout = this;                
+            owner.currentFlyout = this;
         } else {
             /* if we are at the top level, close the entire stack before
              * we open
              */
-            while (Jx.Button.Flyout.Stack.length) {
-                Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1].hide();
+            while (stack.length) {
+                stack[stack.length - 1].hide();
             }
         }
         // now we go on the stack.
-        Jx.Button.Flyout.Stack.push(this);
+        stack.push(this);
+        this.fireEvent('beforeOpen');
 
-        this.options.active = true;
-        this.domA.addClass('jx'+this.options.type+'Active');
+        options.active = true;
+        this.domA.addClass(options.activeClass);
         this.contentContainer.setStyle('visibility','hidden');
-        $(document.body).adopt(this.contentContainer);
+        document.id(document.body).adopt(this.contentContainer);
         this.content.getChildren().each(function(child) {
-            if (child.resize) { 
-                child.resize(); 
+            if (child.resize) {
+                child.resize();
             }
         });
         this.showChrome(this.contentContainer);
-        
-        this.position(this.contentContainer, this.domObj, {
-            horizontal: ['left left', 'right right'],
-            vertical: ['bottom top', 'top bottom'],
-            offsets: this.chromeOffsets
+
+        var rel = options.positionElement || this.domObj;
+        var pos = $merge(options.position, {
+          offsets: this.chromeOffsets
         });
-        
+        this.position(this.contentContainer, rel, pos);
+
         /* we have to size the container for IE to render the chrome correctly
          * there is some horrible peekaboo bug in IE 6
          */
-        this.contentContainer.setContentBoxSize($(this.content).getMarginBoxSize());
-        
+        this.contentContainer.setContentBoxSize(document.id(this.content).getMarginBoxSize());
+
+        this.stack(this.contentContainer);
         this.contentContainer.setStyle('visibility','');
 
-        document.addEvent('keydown', this.keypressWatcher);
-        document.addEvent('click', this.hideWatcher);
+        document.addEvent('keydown', this.keypressHandler);
+        document.addEvent('click', this.clickHandler);
         this.fireEvent('open', this);
     },
+
     /**
-     * Method: hide
+     * APIMethod: hide
      * Closes the flyout if open
      */
     hide: function() {
         if (this.owner != document.body) {
-            this.owner.currentFlyout = null;            
+            this.owner.currentFlyout = null;
         }
         Jx.Button.Flyout.Stack.pop();
         this.setActive(false);
         this.contentContainer.dispose();
-        document.removeEvent('keydown', this.keypressWatcher);    
-        document.removeEvent('click', this.hideWatcher);
+        this.unstack(this.contentContainer);
+        document.removeEvent('keydown', this.keypressHandler);
+        document.removeEvent('click', this.clickHandler);
         this.fireEvent('close', this);
     },
-    /* hide flyout if the user clicks outside of the flyout */
+    /**
+     * Method: clickHandler
+     * hide flyout if the user clicks outside of the flyout
+     */
     clickHandler: function(e) {
         e = new Event(e);
-        var elm = $(e.target);
-        var flyout = Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1];
+        var elm = document.id(e.target),
+            flyout = Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1];
         if (!elm.descendantOf(flyout.content) &&
             !elm.descendantOf(flyout.domObj)) {
             flyout.hide();
         }
     },
-    /* hide flyout if the user presses the ESC key */
+    /**
+     * Method: keypressHandler
+     * hide flyout if the user presses the ESC key
+     */
     keypressHandler: function(e) {
         e = new Event(e);
         if (e.key == 'esc') {
             Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1].hide();
         }
     }
-});// $Id: layout.js 424 2009-05-12 12:51:44Z pagameba $
-/**
- * Class: Jx.Layout
- *
- * Extends: Object
- * 
- * Implements: Options, Events
- *
- * Jx.Layout is used to provide more flexible layout options for applications
- *
- * Jx.Layout wraps an existing DOM element (typically a div) and provides
- * extra functionality for sizing that element within its parent and sizing
- * elements contained within it that have a 'resize' function attached to them.
- *
- * To create a Jx.Layout, pass the element or id plus an options object to
- * the constructor.
- *
- * Example:
- * (code)
- * var myContainer = new Jx.Layout('myDiv', options);
- * (end)
- *
- * Events:
- * sizeChange - fired when the size of the container changes
- *
- * License: 
- * Copyright (c) 2008, DM Solutions Group Inc.
- * 
- * This file is licensed under an MIT style license
- */
- 
-Jx.Layout = new Class({
-    Family: 'Jx.Layout',
-    Implements: [Options,Events],
-    
-    options: {
-        /* Option: propagate
-         * boolean, controls propogation of resize to child nodes.
-         * True by default. If set to false, changes in size will not be
-         * propogated to child nodes.
-         */
-        propagate: true,
-        /* Option: position
-         * how to position the element, either 'absolute' or 'relative'.
-         * The default (if not passed) is 'absolute'.  When using
-         * 'absolute' positioning, both the width and height are
-         * controlled by Jx.Layout.  If 'relative' positioning is used
-         * then only the width is controlled, allowing the height to
-         * be controlled by its content.
-         */
-        position: 'absolute',
-        /* Option: left
-         * the distance (in pixels) to maintain the left edge of the element
-         * from its parent element.  The default value is 0.  If this is set
-         * to 'null', then the left edge can be any distance from its parent
-         * based on other parameters.
-         */
-        left: 0,
-        /* Option: right
-         * the distance (in pixels) to maintain the right edge of the element
-         * from its parent element.  The default value is 0.  If this is set
-         * to 'null', then the right edge can be any distance from its parent
-         * based on other parameters.
-         */
-        right: 0,
-        /* Option: top
-         * the distance (in pixels) to maintain the top edge of the element
-         * from its parent element.  The default value is 0.  If this is set
-         * to 'null', then the top edge can be any distance from its parent
-         * based on other parameters.
-         */
-        top: 0,
-        /* Option: bottom
-         * the distance (in pixels) to maintain the bottom edge of the element
-         * from its parent element.  The default value is 0.  If this is set
-         * to 'null', then the bottom edge can be any distance from its parent
-         * based on other parameters.
-         */
-        bottom: 0,
-        /* Option: width
-         * the width (in pixels) of the element.  The default value is null.
-         * If this is set to 'null', then the width can be any value based on
-         * other parameters.
-         */
-        width: null,
-        /* Option: height
-         * the height (in pixels) of the element.  The default value is null.
-         * If this is set to 'null', then the height can be any value based on
-         * other parameters.
-         */
-        height: null,
-        /* Option: minWidth
-         * the minimum width that the element can be sized to.  The default
-         * value is 0.
-         */
-        minWidth: 0,
-        /* Option: minHeight
-         * the minimum height that the element can be sized to.  The
-         * default value is 0.
-         */
-        minHeight: 0,
-        /* Option: maxWidth
-         * the maximum width that the element can be sized to.  The default
-         * value is -1, which means no maximum.
-         */
-        maxWidth: -1,
-        /* Option: maxHeight
-         * the maximum height that the element can be sized to.  The
-         * default value is -1, which means no maximum.
-         */
-        maxHeight: -1
-    },
-    /**
-     * Constructor: Jx.Layout
-     * Create a new instance of Jx.Layout.
-     *
-     * Parameters:
-     * domObj - {HTMLElement} element or id to apply the layout to
-     * options - <Jx.Layout.Options>
-     */
-    initialize: function(domObj, options) {
-        this.setOptions(options);
-        this.domObj = $(domObj);
-        this.domObj.resize = this.resize.bind(this);
-        this.domObj.setStyle('position', this.options.position);
-        this.domObj.store('jxLayout', this);
+});/*
+---
 
-        if (document.body == this.domObj.parentNode) {
-            window.addEvent('resize', this.windowResize.bindWithEvent(this));
-            window.addEvent('load', this.windowResize.bind(this));                
-        }
-        //this.resize();
-    },
-    
-    /**
-     * Method: windowResize
-     * when the window is resized, any Jx.Layout controlled elements that are
-     * direct children of the BODY element are resized
-     */
-     windowResize: function() {
-         this.resize();
-         if (this.resizeTimer) {
-             $clear(this.resizeTimer);
-             this.resizeTimer = null;
-         }
-         this.resizeTimer = this.resize.delay(50, this);
-    },
-    
-    /**
-     * Method: resize
-     * resize the element controlled by this Jx.Layout object.
-     *
-     * Parameters:
-     * options - new options to apply, see <Jx.Layout.Options>
-     */
-    resize: function(options) {
-         /* this looks like a really big function but actually not
-          * much code gets executed in the two big if statements
-          */
-        this.resizeTimer = null;
-        var needsResize = false;
-        if (options) {
-            for (var i in options) {
-                //prevent forceResize: false from causing a resize
-                if (i == 'forceResize') {
-                    continue;
-                }
-                if (this.options[i] != options[i]) {
-                    needsResize = true;
-                    this.options[i] = options[i];
-                }
-            }
-            if (options.forceResize) {
-                needsResize = true;
-            }
-        }
-        if (!$(this.domObj.parentNode)) {
-            return;
-        }
-        
-        var parentSize;
-        if (this.domObj.parentNode.tagName == 'BODY') {
-            parentSize = Jx.getPageDimensions();
-        } else {
-            parentSize = $(this.domObj.parentNode).getContentBoxSize();
-        }
-    
-        if (this.lastParentSize && !needsResize) {
-            needsResize = (this.lastParentSize.width != parentSize.width || 
-                          this.lastParentSize.height != parentSize.height);
-        } else {
-            needsResize = true;
-        }
-        this.lastParentSize = parentSize;            
-        
-        if (!needsResize) {
-            return;
-        }
-        
-        var l, t, w, h;
-        
-        /* calculate left and width */
-        if (this.options.left != null) {
-            /* fixed left */
-            l = this.options.left;
-            if (this.options.right == null) {
-                /* variable right */
-                if (this.options.width == null) {
-                    /* variable right and width
-                     * set right to min, stretch width */
-                    w = parentSize.width - l;
-                    if (w < this.options.minWidth ) {
-                        w = this.options.minWidth;
-                    }
-                    if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
-                        w = this.options.maxWidth;
-                    }
-                } else {
-                    /* variable right, fixed width
-                     * use width
-                     */
-                    w = this.options.width;
-                }
-            } else {
-                /* fixed right */
-                if (this.options.width == null) {
-                    /* fixed right, variable width
-                     * stretch width
-                     */
-                    w = parentSize.width - l - this.options.right;
-                    if (w < this.options.minWidth) {
-                        w = this.options.minWidth;
-                    }
-                    if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
-                        w = this.options.maxWidth;
-                    }
-                } else {
-                    /* fixed right, fixed width
-                     * respect left and width, allow right to stretch
-                     */
-                    w = this.options.width;
-                }
-            }
-            
-        } else {
-            if (this.options.right == null) {
-                if (this.options.width == null) {
-                    /* variable left, width and right
-                     * set left, right to min, stretch width
-                     */
-                     l = 0;
-                     w = parentSize.width;
-                     if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
-                         l = l + parseInt(w - this.options.maxWidth)/2;
-                         w = this.options.maxWidth;
-                     }
-                } else {
-                    /* variable left, fixed width, variable right
-                     * distribute space between left and right
-                     */
-                    w = this.options.width;
-                    l = parseInt((parentSize.width - w)/2);
-                    if (l < 0) {
-                        l = 0;
-                    }
-                }
-            } else {
-                if (this.options.width != null) {
-                    /* variable left, fixed width, fixed right
-                     * left is calculated directly
-                     */
-                    w = this.options.width;
-                    l = parentSize.width - w - this.options.right;
-                    if (l < 0) {
-                        l = 0;
-                    }
-                } else {
-                    /* variable left and width, fixed right
-                     * set left to min value and stretch width
-                     */
-                    l = 0;
-                    w = parentSize.width - this.options.right;
-                    if (w < this.options.minWidth) {
-                        w = this.options.minWidth;
-                    }
-                    if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
-                        l = w - this.options.maxWidth - this.options.right;
-                        w = this.options.maxWidth;                        
-                    }
-                }
-            }
-        }
-        
-        /* calculate the top and height */
-        if (this.options.top != null) {
-            /* fixed top */
-            t = this.options.top;
-            if (this.options.bottom == null) {
-                /* variable bottom */
-                if (this.options.height == null) {
-                    /* variable bottom and height
-                     * set bottom to min, stretch height */
-                    h = parentSize.height - t;
-                    if (h < this.options.minHeight) {
-                        h = this.options.minHeight;
-                    }
-                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
-                        h = this.options.maxHeight;
-                    }
-                } else {
-                    /* variable bottom, fixed height
-                     * stretch height
-                     */
-                    h = this.options.height;
-                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
-                        t = h - this.options.maxHeight;
-                        h = this.options.maxHeight;
-                    }
-                }
-            } else {
-                /* fixed bottom */
-                if (this.options.height == null) {
-                    /* fixed bottom, variable height
-                     * stretch height
-                     */
-                    h = parentSize.height - t - this.options.bottom;
-                    if (h < this.options.minHeight) {
-                        h = this.options.minHeight;
-                    }
-                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
-                        h = this.options.maxHeight;
-                    }                
-                } else {
-                    /* fixed bottom, fixed height
-                     * respect top and height, allow bottom to stretch
-                     */
-                    h = this.options.height;
-                }
-            }
-        } else {
-            if (this.options.bottom == null) {
-                if (this.options.height == null) {
-                    /* variable top, height and bottom
-                     * set top, bottom to min, stretch height
-                     */
-                     t = 0;
-                     h = parentSize.height;
-                     if (h < this.options.minHeight) {
-                         h = this.options.minHeight;
-                     }
-                     if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
-                         t = parseInt((parentSize.height - this.options.maxHeight)/2);
-                         h = this.options.maxHeight;
-                     }
-                } else {
-                    /* variable top, fixed height, variable bottom
-                     * distribute space between top and bottom
-                     */
-                    h = this.options.height;
-                    t = parseInt((parentSize.height - h)/2);
-                    if (t < 0) {
-                        t = 0;
-                    }
-                }
-            } else {
-                if (this.options.height != null) {
-                    /* variable top, fixed height, fixed bottom
-                     * top is calculated directly
-                     */
-                    h = this.options.height;
-                    t = parentSize.height - h - this.options.bottom;
-                    if (t < 0) {
-                        t = 0;
-                    }
-                } else {
-                    /* variable top and height, fixed bottom
-                     * set top to min value and stretch height
-                     */
-                    t = 0;
-                    h = parentSize.height - this.options.bottom;
-                    if (h < this.options.minHeight) {
-                        h = this.options.minHeight;
-                    }
-                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
-                        t = parentSize.height - this.options.maxHeight - this.options.bottom;
-                        h = this.options.maxHeight;
-                    }
-                }
-            }
-        }
-        
-        //TODO: check left, top, width, height against current styles
-        // and only apply changes if they are not the same.
-        
-        /* apply the new sizes */
-        var sizeOpts = {width: w};
-        if (this.options.position == 'absolute') {
-            var padding = $(this.domObj.parentNode).getPaddingSize();
-            this.domObj.setStyles({
-                position: this.options.position,
-                left: l+padding.left,
-                top: t+padding.top
-            });
-            sizeOpts.height = h;
-        } else {
-            if (this.options.height) {
-                sizeOpts.height = this.options.height;
-            }
-        }
-        this.domObj.setBorderBoxSize(sizeOpts);
-        
-        if (this.options.propagate) {
-            // propogate changes to children
-            var o = {forceResize: options ? options.forceResize : false};
-            $A(this.domObj.childNodes).each(function(child){
-                if (child.resize && child.getStyle('display') != 'none') {
-                    child.resize.delay(0,child,o);                
-                }
-            });
-        }
+name: Jx.ColorPalette
 
-        this.fireEvent('sizeChange',this);
-    }
-});// $Id: tab.js 424 2009-05-12 12:51:44Z pagameba $
-/**
- * Class: Jx.Button.Tab
- *
- * Extends: <Jx.Button>
- *
- * Implements: <Jx.ContentLoader>
- *
- * A single tab in a tab set.  A tab has a label (displayed in the tab) and a
- * content area that is displayed when the tab is active.  A tab has to be
- * added to both a <Jx.TabSet> (for the content) and <Jx.Toolbar> (for the
- * actual tab itself) in order to be useful.  Alternately, you can use
- * a <Jx.TabBox> which combines both into a single control at the cost of
- * some flexibility in layout options.
- *
- * A tab is a <Jx.ContentLoader> and you can specify the initial content of
- * the tab using any of the methods supported by 
- * <Jx.ContentLoader::loadContent>.  You can acccess the actual DOM element
- * that contains the content (if you want to dynamically insert content
- * for instance) via the <Jx.Tab::content> property.
- *
- * A tab is a button of type *toggle* which means that it emits the *up*
- * and *down* events.
- *
- * Example:
- * (code)
- * var tab1 = new Jx.Button.Tab({
- *     label: 'tab 1', 
- *     content: 'content1',
- *     onDown: function(tab) {
- *         console.log('tab became active');
- *     },
- *     onUp: function(tab) {
- *         console.log('tab became inactive');
- *     }
- * });
- * (end)
- *
- * 
- *
- * License: 
- * Copyright (c) 2008, DM Solutions Group Inc.
- * 
- * This file is licensed under an MIT style license
+description: A Jx.ColorPalette presents a user interface for selecting colors.  This is typically combined with a Jx.Button.Color which embeds the color palette in a flyout.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+
+provides: [Jx.ColorPalette]
+
+css:
+ - color
+
+images:
+ - grid.png
+
+...
  */
-Jx.Button.Tab = new Class({
-    Family: 'Jx.Button.Tab',
-    Extends: Jx.Button,
-    Implements: [Jx.ContentLoader],
-    /**
-     * Property: content
-     * {HTMLElement} The content area that is displayed when the tab is active.
-     */
-    content: null,
-    /**
-     * Constructor: Jx.Button.Tab
-     * Create a new instance of Jx.Button.Tab.  Any layout options passed are used
-     * to create a <Jx.Layout> for the tab content area.
-     *
-     * Parameters:
-     * options - {Object} an object containing options that are used
-     * to control the appearance of the tab.  See <Jx.Button>,
-     * <Jx.ContentLoader::loadContent> and <Jx.Layout::Jx.Layout> for
-     * valid options.
-     */
-    initialize : function( options) {
-        this.parent($merge(options, {type:'Tab', toggle:true}));
-        this.content = new Element('div', {'class':'tabContent'});
-        new Jx.Layout(this.content, options);
-        this.loadContent(this.content);
-        var that = this;
-        this.addEvent('down', function(){that.content.addClass('tabContentActive');});
-        this.addEvent('up', function(){that.content.removeClass('tabContentActive');});
-        
-        if (this.options.close) {
-            this.domObj.addClass('jxTabClose');
-            var a = new Element('a', {
-                'class': 'jxTabClose',
-                events: {
-                    'click': (function(){
-                        this.fireEvent('close');                        
-                    }).bind(this)
-                } 
-            });
-            a.adopt(new Element('img', {
-                src: Jx.aPixel.src,
-                alt: '',
-                title: ''
-            }));
-            this.domObj.adopt(a);
-        }
-    },
-    /**
-     * Method: clicked
-     * triggered when the user clicks the button, processes the
-     * actionPerformed event
-     */
-    clicked : function(evt) {
-        if (this.options.enabled) {
-            this.setActive(true);            
-        }
-    }
-});// $Id: colorpalette.js 473 2009-07-08 16:46:21Z pagameba $
+// $Id: colorpalette.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.ColorPalette
  *
- * Extends: Object
+ * Extends: <Jx.Widget>
  *
- * Implements: Options, Events, <Jx.Addable>
- *
  * A Jx.ColorPalette presents a user interface for selecting colors.
  * Currently, the user can either enter a HEX colour value or select from a
  * palette of web-safe colours.  The user can also enter an opacity value.
  *
  * A Jx.ColorPalette can be embedded anywhere in a web page using its addTo
- * method.  However, a <Jx.Button> subclass is provided (<Jx.Button.Color>)
- * that embeds a colour panel inside a button for easy use in toolbars.
+ * method.  However, a <Jx.Button> suJx.Tooltipbclass is provided
+ * (<Jx.Button.Color>) that embeds a colour panel inside a button for easy use
+ * in toolbars.
  *
- * Colour changes are propogated via a change event.  To be notified 
+ * Colour changes are propogated via a change event.  To be notified
  * of changes in a Jx.ColorPalette, use the addEvent method.
  *
  * Example:
@@ -11573,14 +22375,18 @@
  * change - triggered when the color changes.
  * click - the user clicked on a color swatch (emitted after a change event)
  *
- * License: 
+ * MooTools.lang keys:
+ * - colorpalette.alphaLabel
+ * 
+ * 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.ColorPalette = new Class({
     Family: 'Jx.ColorPalette',
-    Implements: [Options, Events, Jx.Addable],
+    Extends: Jx.Widget,
     /**
      * Property: {HTMLElement} domObj
      * the HTML element representing the color panel
@@ -11603,22 +22409,13 @@
          * an array of hex colors for creating the palette, defaults to a
          * set of web safe colors.
          */
-        hexColors: ['00', '33', '66', '99', 'CC', 'FF'],
-        /* Option: alphaLabel
-         * the text to display next to the alpha input for i18n.
-         */
-        alphaLabel: 'alpha (%)'
+        hexColors: ['00', '33', '66', '99', 'CC', 'FF']
     },
     /**
-     * Constructor: Jx.ColorPalette
+     * Method: render
      * initialize a new instance of Jx.ColorPalette
-     *
-     * Parameters:
-     * options - <Jx.ColorPalette.Options>
      */
-    initialize: function(options) {
-        this.setOptions(options);
-
+    render: function() {
         this.domObj = new Element('div', {
             id: this.options.id,
             'class':'jxColorPalette'
@@ -11631,10 +22428,13 @@
         this.previewSwatch = new Element('div', {'class':'jxColorHover'});
         d.adopt(this.selectedSwatch);
         d.adopt(this.previewSwatch);
-        
+
         top.adopt(d);
 
-        this.colorInputLabel = new Element('label', {'class':'jxColorLabel', html:'#'});
+        this.colorInputLabel = new Element('label', {
+          'class':'jxColorLabel', 
+          html:'#'
+        });
         top.adopt(this.colorInputLabel);
 
         var cc = this.changed.bind(this);
@@ -11651,7 +22451,7 @@
 
         top.adopt(this.colorInput);
 
-        this.alphaLabel = new Element('label', {'class':'jxAlphaLabel', 'html':this.options.alphaLabel});
+        this.alphaLabel = new Element('label', {'class':'jxAlphaLabel', 'html':this.getText({set:'Jx',key:'colorpalette',value:'alphaLabel'}) });
         top.adopt(this.alphaLabel);
 
         this.alphaInput = new Element('input', {
@@ -11709,11 +22509,13 @@
                     /* remainder of the columns are built
                      * based on the current row/column
                      */
-                    r = parseInt(i/6)*3 + parseInt(j/6);
+                    r = parseInt(i/6,10)*3 + parseInt(j/6,10);
                     g = j%6;
                     b = i%6;
                 }
-                var bgColor = '#'+this.options.hexColors[r]+this.options.hexColors[g]+this.options.hexColors[b];
+                var bgColor = '#'+this.options.hexColors[r]+
+                                  this.options.hexColors[g]+
+                                  this.options.hexColors[b];
 
                 var td = new Element('td');
                 if (!bSkip) {
@@ -11732,8 +22534,7 @@
                     a.store('swatchColor', bgColor);
                     td.adopt(a);
                 } else {
-                    td.addClass('emptyCell');
-                    var span = new Element('span');
+                    var span = new Element('span', {'class':'emptyCell'});
                     td.adopt(span);
                 }
                 tr.adopt(td);
@@ -11806,7 +22607,7 @@
     },
 
     /**
-     * Method: setColor
+     * APIMethod: setColor
      * set the colour represented by this colour panel
      *
      * Parameters:
@@ -11818,7 +22619,7 @@
     },
 
     /**
-     * Method: setAlpha
+     * APIMethod: setAlpha
      * set the alpha represented by this colour panel
      *
      * Parameters:
@@ -11839,21 +22640,60 @@
 
         this.colorInput.value = this.options.color.substring(1);
 
-        this.alphaInput.value = parseInt(this.options.alpha*100);
+        this.alphaInput.value = parseInt(this.options.alpha*100,10);
         if (this.options.alpha < 1) {
             styles.opacity = this.options.alpha;
             styles.filter = 'Alpha(opacity='+(this.options.alpha*100)+')';
+            
         } else {
-            styles.opacity = '';
-            styles.filter = '';
+            styles.opacity = 1;
+            //not sure what the proper way to remove the filter would be since
+            // I don't have IE to test against.
+            styles.filter = '';  
         }
         this.selectedSwatch.setStyles(styles);
         this.previewSwatch.setStyles(styles);
+        
         this.fireEvent('change', this);
+    },
+    
+    /**
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the
+     * widget.
+     * 
+     * Parameters:
+     * lang - the language being changed to or that had it's data set of 
+     *    translations changed.
+     */
+    changeText: function (lang) {
+    	this.parent();
+    	
+    	if ($defined(this.alphaLabel)) {
+    		this.alphaLabel.set('html', this.getText({set:'Jx',key:'colorpalette',value:'alphaLabel'}));
+    	}
     }
 });
 
-// $Id: color.js 424 2009-05-12 12:51:44Z pagameba $ 
+/*
+---
+
+name: Jx.Button.Color
+
+description:
+
+license: MIT-style license.
+
+requires:
+ - Jx.Button.Flyout
+ - Jx.ColorPalette
+
+provides: [Jx.Button.Color]
+
+...
+ */
+// $Id: color.js 977 2010-09-02 18:57:42Z pagameba $
 /**
  * Class: Jx.Button.Color
  *
@@ -11882,17 +22722,22 @@
  * Events:
  * change - fired when the color is changed.
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Button.Color = new Class({
     Family: 'Jx.Button.Color',
     Extends: Jx.Button.Flyout,
-    
+
+    /**
+     * Property: swatch
+     * the color swatch element used to portray the currently selected
+     * color
+     */
     swatch: null,
-     
+
     options: {
         /**
          * Option: color
@@ -11906,78 +22751,93 @@
          *  (opaque) if not specified.
          *
          */
-        alpha: 100
+        alpha: 100,
+        /*
+         * Option: template
+         * the HTML template for the color button
+         */
+        template: '<span class="jxButtonContainer"><a class="jxButton jxButtonFlyout jxDiscloser"><span class="jxButtonContent"><span class="jxButtonSwatch"><span class="jxButtonSwatchColor"></span></span><span class="jxButtonLabel"></span></span></a></span>'
     },
 
     /**
-     * Constructor: Jx.Button.Color
-     * initialize a new color button.
-     *
-     * Parameters:
-     * options - <Jx.Button.Color.Options> initialize instance options.
-     *
+     * Property: classes
+     * {<Hash>} a hash of object properties to CSS class names used to
+     * automatically extract references to important DOM elements when
+     * processing a widget template.  This allows developers to provide custom
+     * HTML structures without affecting the functionality of widgets.
      */
-    initialize: function(options) {
+    classes: new Hash({
+        domObj: 'jxButtonContainer',
+        domA: 'jxButton',
+        swatch: 'jxButtonSwatchColor',
+        domLabel: 'jxButtonLabel'
+    }),
+
+    /**
+     * Method: render
+     * creates a new color button.
+     */
+    render: function() {
         if (!Jx.Button.Color.ColorPalette) {
             Jx.Button.Color.ColorPalette = new Jx.ColorPalette(this.options);
         }
-        var d = new Element('span', {'class':'jxButtonSwatch'});
 
-        this.selectedSwatch = new Element('span');
-        d.appendChild(this.selectedSwatch);
-
-        this.colorChangeFn = this.changed.bind(this);
-        this.hideFn = this.hide.bind(this);
-        /* we need to have an image to replace, but if a label is 
+        /* we need to have an image to replace, but if a label is
            requested, there wouldn't normally be an image. */
-        options.image = Jx.aPixel.src;
+        this.options.image = Jx.aPixel.src;
 
         /* now we can safely initialize */
-        this.parent(options);
-        
-        // now replace the image with our swatch
-        d.replaces(this.domImg);
+        this.parent();
         this.updateSwatch();
+
+        this.bound.changed = this.changed.bind(this);
+        this.bound.hide = this.hide.bind(this);
     },
-
+    cleanup: function() {
+      this.bound.changed = false;
+      this.bound.hide = false;
+      this.parent();
+    },
     /**
-     * Method: clicked
+     * APIMethod: clicked
      * override <Jx.Button.Flyout> to use a singleton color palette.
      */
     clicked: function() {
-        if (Jx.Button.Color.ColorPalette.currentButton) {
-            Jx.Button.Color.ColorPalette.currentButton.hide();
+        var cp = Jx.Button.Color.ColorPalette;
+        if (cp.currentButton) {
+            cp.currentButton.hide();
         }
-        Jx.Button.Color.ColorPalette.currentButton = this;
-        Jx.Button.Color.ColorPalette.addEvent('change', this.colorChangeFn);
-        Jx.Button.Color.ColorPalette.addEvent('click', this.hideFn);
-        this.content.appendChild(Jx.Button.Color.ColorPalette.domObj);
-        Jx.Button.Color.ColorPalette.domObj.setStyle('display', 'block');
+        cp.currentButton = this;
+        cp.addEvent('change', this.bound.changed);
+        cp.addEvent('click', this.bound.hide);
+        this.content.appendChild(cp.domObj);
+        cp.domObj.setStyle('display', 'block');
         Jx.Button.Flyout.prototype.clicked.apply(this, arguments);
         /* setting these before causes an update problem when clicking on
          * a second color button when another one is open - the color
          * wasn't updating properly
          */
-         
-        Jx.Button.Color.ColorPalette.options.color = this.options.color;
-        Jx.Button.Color.ColorPalette.options.alpha = this.options.alpha/100;
-        Jx.Button.Color.ColorPalette.updateSelected();
+
+        cp.options.color = this.options.color;
+        cp.options.alpha = this.options.alpha/100;
+        cp.updateSelected();
 },
 
     /**
-     * Method: hide
+     * APIMethod: hide
      * hide the color panel
      */
     hide: function() {
+        var cp = Jx.Button.Color.ColorPalette;
         this.setActive(false);
-        Jx.Button.Color.ColorPalette.removeEvent('change', this.colorChangeFn);
-        Jx.Button.Color.ColorPalette.removeEvent('click', this.hideFn);
+        cp.removeEvent('change', this.bound.changed);
+        cp.removeEvent('click', this.bound.hide);
         Jx.Button.Flyout.prototype.hide.apply(this, arguments);
-        Jx.Button.Color.ColorPalette.currentButton = null;
+        cp.currentButton = null;
     },
 
     /**
-     * Method: setColor
+     * APIMethod: setColor
      * set the color represented by this color panel
      *
      * Parameters:
@@ -11989,7 +22849,7 @@
     },
 
     /**
-     * Method: setAlpha
+     * APIMethod: setAlpha
      * set the alpha represented by this color panel
      *
      * Parameters:
@@ -12033,22 +22893,43 @@
         if (this.options.alpha < 100) {
             styles.filter = 'Alpha(opacity='+(this.options.alpha)+')';
             styles.opacity = this.options.alpha / 100;
-            
+
         } else {
-            styles.opacity = '';
+            styles.opacity = 1;
             styles.filter = '';
         }
-        this.selectedSwatch.setStyles(styles);
+        this.swatch.setStyles(styles);
     }
 });
-// $Id: menu.js 424 2009-05-12 12:51:44Z pagameba $
+/*
+---
+
+name: Jx.Menu
+
+description: A main menu as opposed to a sub menu that lives inside the menu.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Button
+ - Jx.List
+
+provides: [Jx.Menu]
+
+css:
+ - menu
+
+images:
+ - flyout_chrome.png
+ - emblems.png
+...
+ */
+// $Id: menu.js 966 2010-07-20 13:31:16Z pagameba $
 /**
  * Class: Jx.Menu
  *
- * Extends: Object
+ * Extends: <Jx.Widget>
  *
- * Implements: Options, Events, <Jx.AutoPosition>, <Jx.Chrome>, <Jx.Addable>
- *
  * A main menu as opposed to a sub menu that lives inside the menu.
  *
  * TODO: Jx.Menu
@@ -12059,28 +22940,16 @@
  * (code)
  * (end)
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Menu = new Class({
     Family: 'Jx.Menu',
+    Extends: Jx.Widget,
+    // Binds: ['onMouseEnter','onMouseLeave','hide','keypressHandler'],
     /**
-     * Implements:
-     * * Options
-     * * Events
-     * * <Jx.AutoPosition>
-     * * <Jx.Chrome>
-     * * <Jx.Addable>
-     */
-    Implements: [Options, Events, Jx.AutoPosition, Jx.Chrome, Jx.Addable],
-    /**
-     * Property: domObj
-     * {HTMLElement} The HTML element containing the menu.
-     */
-    domObj : null,
-    /**
      * Property: button
      * {<Jx.Button>} The button that represents this menu in a toolbar and
      * opens the menu.
@@ -12093,77 +22962,183 @@
      */
     subDomObj : null,
     /**
-     * Property: items
-     * {Array} the items in this menu
+     * Property: list
+     * {<Jx.List>} the list of items in the menu
      */
-    items : null,
+    list: null,
+
+    parameters: ['buttonOptions', 'options'],
+
+    options: {
+        /**
+         * Option: exposeOnHover
+         * {Boolean} default false, if set to true the menu will show
+         * when the mouse hovers over it rather than when it is clicked.
+         */
+        exposeOnHover: false,
+        /**
+         * Option: hideDelay
+         * {Integer} default 0, if greater than 0, this is the number of
+         * milliseconds to delay before hiding a menu when the mouse leaves
+         * the menu button or list.
+         */
+        hideDelay: 0,
+        template: "<div class='jxMenuContainer'><ul class='jxMenu'></ul></div>",
+        buttonTemplate: '<span class="jxButtonContainer"><a class="jxButton jxButtonMenu jxDiscloser"><span class="jxButtonContent"><img class="jxButtonIcon" src="'+Jx.aPixel.src+'"><span class="jxButtonLabel"></span></span></a></span>',
+        position: {
+            horizontal: ['left left'],
+            vertical: ['bottom top', 'top bottom']
+        }
+    },
+
+    classes: new Hash({
+        contentContainer: 'jxMenuContainer',
+        subDomObj: 'jxMenu'
+    }),
+    
+    init: function() {
+        this.bound.stop = function(e){e.stop();};
+        this.bound.remove = function(item) {item.setOwner(null);};
+        this.bound.show = this.show.bind(this);
+        this.bound.mouseenter = this.onMouseEnter.bind(this);
+        this.bound.mouseleave = this.onMouseLeave.bind(this);
+        this.bound.keypress = this.keypressHandler.bind(this);
+        this.bound.hide = this.hide.bind(this);
+        this.parent();
+    },
+
     /**
-     * Constructor: Jx.Menu
+     * APIMethod: render
      * Create a new instance of Jx.Menu.
-     *
-     * Parameters:
-     * options - see <Jx.Button.Options>.  If no options are provided then
-     * no button is created.
      */
-    initialize : function(options) {
-        this.setOptions(options);
+    render : function() {
+        this.parent();
         if (!Jx.Menu.Menus) {
             Jx.Menu.Menus = [];
         }
-        /* stores menu items and sub menus */
-        this.items = [];
-        
-        this.contentContainer = new Element('div',{
-            'class':'jxMenuContainer',
-            events: {
-                contextmenu: function(e){e.stop();}
-            }
+
+        this.contentClone = this.contentContainer.clone();
+        this.list = new Jx.List(this.subDomObj, {
+            onRemove: this.bound.remove
         });
-        
-        /* the DOM element that holds the actual menu */
-        this.subDomObj = new Element('ul',{
-            'class':'jxMenu'
-        });
-        
-        this.contentContainer.adopt(this.subDomObj);
-        
+
         /* if options are passed, make a button inside an LI so the
            menu can be embedded inside a toolbar */
-        if (options) {
-            this.button = new Jx.Button($merge(options,{
-                onClick:this.show.bind(this)
+        if (this.options.buttonOptions) {
+            this.button = new Jx.Button($merge(this.options.buttonOptions,{
+                template: this.options.buttonTemplate,
+                onClick:this.bound.show
             }));
-            this.button.domA.addClass('jxButtonMenu');
-            this.button.domA.addEvent('mouseover', this.onMouseOver.bindWithEvent(this));
-            
+
+            this.button.domA.addEvent('mouseenter', this.bound.mouseenter);
+            this.button.domA.addEvent('mouseleave', this.bound.mouseleave);
+
             this.domObj = this.button.domObj;
+            this.domObj.store('jxMenu', this);
         }
         
-        /* pre-bind the hide function for efficiency */
-        this.hideWatcher = this.hide.bindWithEvent(this);
-        this.keypressWatcher = this.keypressHandler.bindWithEvent(this);
-        
+        this.subDomObj.addEvent('mouseenter', this.bound.mouseenter);
+        this.subDomObj.addEvent('mouseleave', this.bound.mouseleave);
+
         if (this.options.parent) {
             this.addTo(this.options.parent);
         }
     },
+    cleanup: function() {
+      if (this.hideTimer) {
+        window.clearTimeout(this.hideTimer);
+      }
+      this.list.removeEvent('remove', this.bound.remove);
+      this.list.destroy();
+      this.list = null;
+      if (this.button) {
+        this.domObj.eliminate('jxMenu');
+        this.domObj = null;
+        this.button.removeEvent('click', this.bound.show);
+        this.button.domA.removeEvents({
+          mouseenter: this.bound.mouseenter,
+          mouseleave: this.bound.mouseleave
+        });
+        
+        this.button.destroy();
+        this.button = null;
+      }
+      this.subDomObj.removeEvents({
+        mouseenter: this.bound.mouseenter,
+        mouseleave: this.bound.mouseleave
+      });
+      this.subDomObj.removeEvents();
+      this.contentContainer.removeEvent('contextmenu', this.bound.stop);
+      this.subDomObj.destroy();
+      this.contentContainer.destroy();
+      this.contentClone.destroy();
+      this.bound.remove = null;
+      this.bound.show = null;
+      this.bound.stop = null;
+      this.bound.mouseenter = null;
+      this.bound.mouseleave = null;
+      this.bound.keypress = null;
+      this.bound.hide = null;
+      this.parent();
+    },
     /**
-     * Method: add
+     * APIMethod: add
      * Add menu items to the sub menu.
      *
      * Parameters:
      * item - {<Jx.MenuItem>} the menu item to add.  Multiple menu items
-     * can be added by passing multiple arguments to this function.
+     *     can be added by passing an array of menu items.
+     * position - the index to add the item at, defaults to the end of the
+     *     menu
      */
-    add : function() {
-        $A(arguments).flatten().each(function(item){
-            this.items.push(item);
-            item.setOwner(this);
-            this.subDomObj.adopt(item.domObj);
-        }, this);
+    add: function(item, position, owner) {
+        if (Jx.type(item) == 'array') {
+            item.each(function(i){
+                i.setOwner(owner||this);
+            }, this);
+        } else {
+            item.setOwner(owner||this);
+        }
+        this.list.add(item, position);
         return this;
     },
     /**
+     * APIMethod: remove
+     * Remove a menu item from the menu
+     *
+     * Parameters:
+     * item - {<Jx.MenuItem>} the menu item to remove
+     */
+    remove: function(item) {
+        this.list.remove(item);
+        return this;
+    },
+    /**
+     * APIMethod: replace
+     * Replace a menu item with another menu item
+     *
+     * Parameters:
+     * what - {<Jx.MenuItem>} the menu item to replace
+     * withWhat - {<Jx.MenuItem>} the menu item to replace it with
+     */
+    replace: function(item, withItem) {
+        this.list.replace(item, withItem);
+        return this;
+    },
+    /**
+     * APIMethod: empty
+     * Empty the menu of items
+     */
+    empty: function() {
+      this.list.each(function(item){
+        if (item.empty) {
+          item.empty();
+        }
+        item.setOwner(null);
+      }, this);
+      this.list.empty();
+    },
+    /**
      * Method: deactivate
      * Deactivate the menu by hiding it.
      */
@@ -12176,11 +23151,35 @@
      * Parameters:
      * e - {Event} the mouse event
      */
-    onMouseOver: function(e) {
-        if (Jx.Menu.Menus[0] && Jx.Menu.Menus[0] != this) {
-            this.show({event:e});
+    onMouseEnter: function(e) {
+      if (this.hideTimer) {
+        window.clearTimeout(this.hideTimer);
+        this.hideTimer = null;
+      }
+      if (Jx.Menu.Menus[0] && Jx.Menu.Menus[0] != this) {
+          this.show.delay(1,this);
+      } else if (this.options.exposeOnHover) {
+        if (Jx.Menu.Menus[0] && Jx.Menu.Menus[0] == this) {
+          Jx.Menu.Menus[0] = null;
         }
+        this.show.delay(1,this);
+      }
     },
+    /**
+     * Method: onMouseLeave
+     * Handle the user moving the mouse off this button or menu by
+     * starting the hide process if so configured.
+     *
+     * Parameters:
+     * e - {Event} the mouse event
+     */
+    onMouseLeave: function(e) {
+      if (this.options.hideDelay > 0) {
+        this.hideTimer = (function(){
+          this.deactivate();
+        }).delay(this.options.hideDelay, this);
+      }
+    },
     
     /**
      * Method: eventInMenu
@@ -12195,7 +23194,7 @@
      * a sub menu of this menu, false otherwise
      */
     eventInMenu: function(e) {
-        var target = $(e.target);
+        var target = document.id(e.target);
         if (!target) {
             return false;
         }
@@ -12219,9 +23218,9 @@
             return false;
         }
     },
-    
+
     /**
-     * Method: hide
+     * APIMethod: hide
      * Hide the menu.
      *
      * Parameters:
@@ -12241,76 +23240,86 @@
             Jx.Menu.Menus[0] = null;
         }
         if (this.button && this.button.domA) {
-            this.button.domA.removeClass('jx'+this.button.options.type+'Active');            
+            this.button.domA.removeClass(this.button.options.activeClass);
         }
-        this.items.each(function(item){item.hide(e);});
-        document.removeEvent('mousedown', this.hideWatcher);
-        document.removeEvent('keydown', this.keypressWatcher);
-        this.contentContainer.setStyle('display','none');
-        this.fireEvent('hide', this); 
+        if (this.hideTimer) {
+          window.clearTimeout(this.hideTimer);
+        }
+        this.list.each(function(item){item.retrieve('jxMenuItem').hide(e);});
+        document.removeEvent('mousedown', this.bound.hide);
+        document.removeEvent('keydown', this.bound.keypress);
+        this.unstack(this.contentContainer);
+        this.contentContainer.dispose();
+        this.visibleItem = null;
+        this.fireEvent('hide', this);
     },
     /**
-     * Method: show
+     * APIMethod: show
      * Show the menu
-     *
-     * Parameters:
-     * e - {Event} the mouse event
      */
-    show : function(o) {
-        var e = o.event;
-        if (Jx.Menu.Menus[0]) {
-            if (Jx.Menu.Menus[0] != this) {
-                Jx.Menu.Menus[0].button.blur();
-                Jx.Menu.Menus[0].hide(e);
-            } else {
-                this.hide();
+    show : function() {
+        if (this.button) {
+            if (Jx.Menu.Menus[0]) {
+                if (Jx.Menu.Menus[0] != this) {
+                    Jx.Menu.Menus[0].button.blur();
+                    Jx.Menu.Menus[0].hide();
+                } else {
+                    this.hide();
+                    return;
+                }
+            }
+            Jx.Menu.Menus[0] = this;
+            this.button.focus();
+            if (this.list.count() == 0) {
                 return;
-            }  
-        } 
-        if (this.items.length === 0) {
-            return;
+            }
         }
-        Jx.Menu.Menus[0] = this;
-        this.button.focus();
-        this.contentContainer.setStyle('visibility','hidden');
-        this.contentContainer.setStyle('display','block');
-        $(document.body).adopt(this.contentContainer);            
-        /* we have to size the container for IE to render the chrome correctly
-         * but just in the menu/sub menu case - there is some horrible peekaboo
-         * bug in IE related to ULs that we just couldn't figure out
-         */
+        if (this.hideTimer) {
+          window.clearTimeout(this.hideTimer);
+        }
+
+        this.subDomObj.dispose();
+        this.contentContainer.destroy();
+        this.contentContainer = this.contentClone.clone();
+        this.contentContainer.empty().adopt(this.subDomObj);
+        this.contentContainer.addEvent('contextmenu', this.bound.stop);
+        this.contentContainer.setStyle('display','none');
+        document.id(document.body).adopt(this.contentContainer);
+        this.contentContainer.setStyles({
+            visibility: 'hidden',
+            display: 'block'
+        });
         this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
         this.showChrome(this.contentContainer);
-        
-        this.position(this.contentContainer, this.button.domObj, {
-            horizontal: ['left left'],
-            vertical: ['bottom top', 'top bottom'],
+
+        this.position(this.contentContainer, this.domObj, $merge({
             offsets: this.chromeOffsets
-        });
+        }, this.options.position));
+        this.stack(this.contentContainer);
+        this.contentContainer.setStyle('visibility','visible');
 
-        this.contentContainer.setStyle('visibility','');
-        
         if (this.button && this.button.domA) {
-            this.button.domA.addClass('jx'+this.button.options.type+'Active');            
+            this.button.domA.addClass(this.button.options.activeClass);
         }
-        if (e) {
-            //why were we doing this? it is affecting the closing of
-            //other elements like flyouts (issue 13)
-            //e.stop();
-        }
-        /* fix bug in IE that closes the menu as it opens because of bubbling */
-        document.addEvent('mousedown', this.hideWatcher);
-        document.addEvent('keydown', this.keypressWatcher);
-        this.fireEvent('show', this); 
+
+        /* fix bug in IE that closes the menu as it opens 
+         * because of bubbling (I think)
+         */
+        document.addEvent('mousedown', this.bound.hide);
+        document.addEvent('keydown', this.bound.keypress);
+        this.fireEvent('show', this);
     },
     /**
-     * Method: setVisibleItem
+     * APIMethod: setVisibleItem
      * Set the sub menu that is currently open
      *
      * Parameters:
      * obj- {<Jx.SubMenu>} the sub menu that just became visible
      */
     setVisibleItem: function(obj) {
+        if (this.hideTimer) {
+          window.clearTimeout(this.hideTimer);
+        }
         if (this.visibleItem != obj) {
             if (this.visibleItem && this.visibleItem.hide) {
                 this.visibleItem.hide();
@@ -12326,87 +23335,380 @@
         if (e.key == 'esc') {
             this.hide();
         }
+    },
+    /**
+     * APIMethod: isEnabled
+     * This returns true if the menu is enabled, false otherwise
+     *
+     * Returns:
+     * {Boolean} whether the menu is enabled or not
+     */
+    isEnabled: function() {
+        return this.button ? this.button.isEnabled() : this.options.enabled ;
+    },
+
+    /**
+     * APIMethod: setEnabled
+     * enable or disable the menu.
+     *
+     * Parameters:
+     * enabled - {Boolean} the new enabled state of the menu
+     */
+    setEnabled: function(enabled) {
+        return this.button ? this.button.setEnabled(enabled) : this.options.enable;
+    },
+    /**
+     * APIMethod: isActive
+     * returns true if the menu is open.
+     *
+     * Returns:
+     * {Boolean} the active state of the menu
+     */
+    isActive: function() {
+        return this.button ? this.button.isActive() : this.options.active;
+    },
+    /**
+     * APIMethod: setActive
+     * Set the active state of the menu
+     *
+     * Parameters:
+     * active - {Boolean} the new active state of the menu
+     */
+    setActive: function(active) {
+        if (this.button) {
+          this.button.setActive(active);
+        }
+    },
+    /**
+     * APIMethod: setImage
+     * set the image of this menu to a new image URL
+     *
+     * Parameters:
+     * path - {String} the new url to use as the image for this menu
+     */
+    setImage: function(path) {
+        if (this.button) {
+          this.button.setImage(path);
+        }
+    },
+    /**
+     * APIMethod: setLabel
+     *
+     * sets the text of the menu.
+     *
+     * Parameters:
+     *
+     * label - {String} the new label for the menu
+     */
+    setLabel: function(label) {
+        if (this.button) {
+          this.button.setLabel(label);
+        }
+    },
+    /**
+     * APIMethod: getLabel
+     *
+     * returns the text of the menu.
+     */
+    getLabel: function() {
+        return this.button ? this.button.getLabel() : '';
+    },
+    /**
+     * APIMethod: setTooltip
+     * sets the tooltip displayed by the menu
+     *
+     * Parameters:
+     * tooltip - {String} the new tooltip
+     */
+    setTooltip: function(tooltip) {
+        if (this.button) {
+          this.button.setTooltip(tooltip);
+        }
+    },
+    /**
+     * APIMethod: focus
+     * capture the keyboard focus on this menu
+     */
+    focus: function() {
+        if (this.button) {
+          this.button.focus();
+        }
+    },
+    /**
+     * APIMethod: blur
+     * remove the keyboard focus from this menu
+     */
+    blur: function() {
+        if (this.button) {
+          this.button.blur();
+        }
     }
 });
 
-// $Id: set.js 424 2009-05-12 12:51:44Z pagameba $
+/*
+---
+
+name: Jx.Menu.Item
+
+description: A menu item is a single entry in a menu.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Menu
+
+provides: [Jx.Menu.Item]
+
+images:
+ - menuitem.png
+...
+ */
+// $Id: menu.item.js 967 2010-07-20 13:31:44Z pagameba $
 /**
- * Class: Jx.ButtonSet
+ * Class: Jx.Menu.Item
  *
- * Extends: Object
+ * Extends: <Jx.Button>
  *
- * Implements: Options, Events
+ * A menu item is a single entry in a menu.  It is typically composed of
+ * a label and an optional icon.  Selecting the menu item emits an event.
  *
- * A ButtonSet manages a set of <Jx.Button> instances by ensuring that only one
- * of the buttons is active.  All the buttons need to have been created with
- * the toggle option set to true for this to work.
+ * Jx.Menu.Item is represented by a <Jx.Button> with type MenuItem and the
+ * associated CSS changes noted in <Jx.Button>.  The container of a MenuItem
+ * is an 'li' element.
  *
  * Example:
  * (code)
+ * (end)
+ *
+ * Events:
+ * click - fired when the menu item is clicked.
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Menu.Item = new Class({
+    Family: 'Jx.Menu.Item',
+    Extends: Jx.Button,
+    // Binds: ['onMouseOver'],
+    /**
+     * Property: owner
+     * {<Jx.SubMenu> or <Jx.Menu>} the menu that contains the menu item.
+     */
+    owner: null,
+    options: {
+        //image: null,
+        label: '&nbsp;',
+        toggleClass: 'jxMenuItemToggle',
+        pressedClass: 'jxMenuItemPressed',
+        activeClass: 'jxMenuItemActive',
+        /* Option: template
+         * the HTML structure of the button.  As a minimum, there must be a
+         * containing element with a class of jxMenuItemContainer and an
+         * internal element with a class of jxMenuItem.  jxMenuItemIcon and
+         * jxMenuItemLabel are used if present to put the image and label into
+         * the button.
+         */
+        template: '<li class="jxMenuItemContainer"><a class="jxMenuItem"><span class="jxMenuItemContent"><img class="jxMenuItemIcon" src="'+Jx.aPixel.src+'"><span class="jxMenuItemLabel"></span></span></a></li>'
+    },
+    classes: new Hash({
+        domObj:'jxMenuItemContainer',
+        domA: 'jxMenuItem',
+        domImg: 'jxMenuItemIcon',
+        domLabel: 'jxMenuItemLabel'
+    }),
+    init: function() {
+      this.bound.mouseover = this.onMouseOver.bind(this);
+      this.parent();
+    },
+    /**
+     * APIMethod: render
+     * Create a new instance of Jx.Menu.Item
+     */
+    render: function() {
+        if (!this.options.image) {
+            this.options.image = Jx.aPixel.src;
+        }
+        this.parent();
+        if (this.options.image && this.options.image != Jx.aPixel.src) {
+            this.domObj.removeClass(this.options.toggleClass);
+        }
+        if (this.options.target) {
+          this.domA.set('target', this.options.target);
+        }
+        this.domObj.addEvent('mouseover', this.bound.mouseover);
+        this.domObj.store('jxMenuItem', this);
+    },
+    cleanup: function() {
+      this.domObj.eliminate('jxMenuItem');
+      this.domObj.removeEvent('mouseover', this.bound.mouseover);
+      this.bound.mouseover = null;
+      this.owner = null;
+      this.parent();
+    },
+    /**
+     * Method: setOwner
+     * Set the owner of this menu item
+     *
+     * Parameters:
+     * obj - {Object} the new owner
+     */
+    setOwner: function(obj) {
+        this.owner = obj;
+    },
+    /**
+     * Method: hide
+     * Hide the menu item.
+     */
+    hide: function() {this.blur.delay(1,this);},
+    /**
+     * Method: show
+     * Show the menu item
+     */
+    show: $empty,
+    /**
+     * Method: clicked
+     * Handle the user clicking on the menu item, overriding the <Jx.Button::clicked>
+     * method to facilitate menu tracking
+     *
+     * Parameters:
+     * obj - {Object} an object containing an event property that was the user
+     * event.
+     */
+    clicked: function(obj) {
+        var href = this.options.href && this.options.href.indexOf('javascript:') != 0;
+        if (this.options.enabled) {
+          if (!href) {
+            if (this.options.toggle) {
+                this.setActive.delay(1,this,!this.options.active);
+            }
+            this.fireEvent.delay(1, this, ['click', {obj: this}]);
+            this.blur();
+          }
+          if (this.owner && this.owner.deactivate) {
+              this.owner.deactivate.delay(1, this.owner, obj.event);
+          }
+        }
+        return href ? true : false;
+    },
+    /**
+     * Method: onmouseover
+     * handle the mouse moving over the menu item
+     */
+    onMouseOver: function(e) {
+        e.stop();
+        if (this.owner && this.owner.setVisibleItem) {
+            this.owner.setVisibleItem(this);
+        }
+        return false;
+    },
+    
+    /**
+     * APIMethod: changeText
+     *
+     * updates the label of the menu item on langChange Event for
+     * Internationalization
+     */
+    changeText: function(lang) {
+        this.parent();
+        if (this.owner && this.owner.deactivate) {
+            this.owner.deactivate();
+        }
+    }
+});
+
+/*
+---
+
+name: Jx.ButtonSet
+
+description: A ButtonSet manages a set of Jx.Button instances by ensuring that only one of the buttons is active.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Object
+
+provides: [Jx.ButtonSet]
+
+
+...
+ */
+// $Id: set.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.ButtonSet
+ *
+ * Extends: <Jx.Object>
+ *
+ * A ButtonSet manages a set of <Jx.Button> instances by ensuring that only
+ * one of the buttons is active.  All the buttons need to have been created
+ * with the toggle option set to true for this to work.
+ *
+ * Example:
+ * (code)
  * var toolbar = new Jx.Toolbar('bar');
  * var buttonSet = new Jx.ButtonSet();
- * 
- * var tab1 = new Jx.Button({label: 'b1', toggle:true, contentID: 'content1'});
- * var tab2 = new Jx.Button({label: 'b2', toggle:true, contentID: 'content2'});
- * var tab3 = new Jx.Button({label: 'b3', toggle:true, contentID: 'content3'});
- * var tab4 = new Jx.Button({label: 'b4', toggle:true, contentURL: 'test_content.html'});
- * 
+ *
+ * var b1 = new Jx.Button({label: 'b1', toggle:true, contentID: 'content1'});
+ * var b2 = new Jx.Button({label: 'b2', toggle:true, contentID: 'content2'});
+ * var b3 = new Jx.Button({label: 'b3', toggle:true, contentID: 'content3'});
+ * var b4 = new Jx.Button({label: 'b4', toggle:true, contentID: 'content4'});
+ *
  * buttonSet.add(b1,b2,b3,b4);
  * (end)
  *
  * Events:
  * change - the current button has changed
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.ButtonSet = new Class({
     Family: 'Jx.ButtonSet',
-    Implements: [Options,Events],
+    Extends: Jx.Object,
+    Binds: ['buttonChanged'],
     /**
      * Property: buttons
      * {Array} array of buttons that are managed by this button set
      */
-    buttons: null,
-    /**
-     * Constructor: Jx.ButtonSet
-     * Create a new instance of <Jx.ButtonSet>
-     *
-     * Parameters:
-     * options - an options object, only event handlers are supported
-     * as options at this time.
-     */
-    initialize : function(options) {
-        this.setOptions(options);
-        this.buttons = [];
-        this.buttonChangedHandler = this.buttonChanged.bind(this);
+    buttons: [],
+    
+    cleanup: function() {
+      this.buttons.each(function(b){
+        b.removeEvent('down', this.buttonChanged);
+        b.setActive = null;
+      },this);
+      this.activeButton = null;
+      this.buttons = null;
+      this.parent();
     },
-    
+
     /**
-     * Method: add
+     * APIMethod: add
      * Add one or more <Jx.Button>s to the ButtonSet.
      *
      * Parameters:
-     * button - {<Jx.Button>} an instance of <Jx.Button> to add to the button set.  More
-     * than one button can be added by passing extra parameters to this method.
+     * button - {<Jx.Button>} an instance of <Jx.Button> to add to the button
+     * set.  More than one button can be added by passing extra parameters to
+     * this method.
      */
     add : function() {
         $A(arguments).each(function(button) {
-            if (button.domObj.hasClass('jx'+button.options.type+'Toggle')) {
-                button.domObj.removeClass('jx'+button.options.type+'Toggle');
-                button.domObj.addClass('jx'+button.options.type+'Set');                
+            if (button.domObj.hasClass(button.options.toggleClass)) {
+                button.domObj.removeClass(button.options.toggleClass);
+                button.domObj.addClass(button.options.toggleClass+'Set');
             }
-            button.addEvent('down',this.buttonChangedHandler);
-            var that = this;
+            button.addEvent('down',this.buttonChanged);
             button.setActive = function(active) {
-                if (this.options.active && that.activeButton == this) {
+                if (button.options.active && this.activeButton == button) {
                     return;
                 } else {
-                    Jx.Button.prototype.setActive.apply(this, [active]);
+                    Jx.Button.prototype.setActive.apply(button, [active]);
                 }
-            };
+            }.bind(this);
             if (!this.activeButton || button.options.active) {
                 button.options.active = false;
                 button.setActive(true);
@@ -12416,7 +23718,7 @@
         return this;
     },
     /**
-     * Method: remove
+     * APIMethod: remove
      * Remove a button from this Button.
      *
      * Parameters:
@@ -12428,12 +23730,20 @@
             if (this.buttons.length) {
                 this.buttons[0].setActive(true);
             }
-            button.removeEvent('down',this.buttonChangedHandler);
+            button.removeEvent('down',this.buttonChanged);
             button.setActive = Jx.Button.prototype.setActive;
         }
     },
     /**
-     * Method: setActiveButton
+     * APIMethod: empty
+     * empty the button set and clear the active button
+     */
+    empty: function() {
+      this.buttons = [];
+      this.activeButton = null;
+    },
+    /**
+     * APIMethod: setActiveButton
      * Set the active button to the one passed to this method
      *
      * Parameters:
@@ -12447,7 +23757,7 @@
         }
     },
     /**
-     * Method: selectionChanged
+     * Method: buttonChanged
      * Handle selection changing on the buttons themselves and activate the
      * appropriate button in response.
      *
@@ -12458,21 +23768,39 @@
         this.setActiveButton(button);
         this.fireEvent('change', this);
     }
-});
+});/*
+---
 
+name: Jx.Button.Multi
 
+description: Multi buttons are used to contain multiple buttons in a drop down list where only one button is actually visible and clickable in the interface.
 
-// $Id: multi.js 424 2009-05-12 12:51:44Z pagameba $
+license: MIT-style license.
+
+requires:
+ - Jx.Button
+ - Jx.Menu
+ - Jx.ButtonSet
+
+provides: [Jx.Button.Multi]
+
+images:
+ - button_multi.png
+ - button_multi_disclose.png
+
+...
+ */
+// $Id: multi.js 977 2010-09-02 18:57:42Z pagameba $
 /**
  * Class: Jx.Button.Multi
  *
  * Extends: <Jx.Button>
  *
- * Implements: 
+ * Implements:
  *
  * Multi buttons are used to contain multiple buttons in a drop down list
  * where only one button is actually visible and clickable in the interface.
- * 
+ *
  * When the user clicks the active button, it performs its normal action.
  * The user may also click a drop-down arrow to the right of the button and
  * access the full list of buttons.  Clicking a button in the list causes
@@ -12483,7 +23811,7 @@
  *
  * This is not really a button, but rather a container for buttons.  The
  * button structure is a div containing two buttons, a normal button and
- * a flyout button.  The flyout contains a toolbar into which all the 
+ * a flyout button.  The flyout contains a toolbar into which all the
  * added buttons are placed.  The main button content is cloned from the
  * last button clicked (or first button added).
  *
@@ -12514,174 +23842,250 @@
  * multiButton.add(b1, b2, b3);
  * (end)
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Button.Multi = new Class({
     Family: 'Jx.Button.Multi',
+    Extends: Jx.Button,
 
-    Extends: Jx.Button,
     /**
      * Property: {<Jx.Button>} activeButton
      * the currently selected button
      */
     activeButton: null,
+
     /**
      * Property: buttons
      * {Array} the buttons added to this multi button
      */
     buttons: null,
+
+    options: {
+        /* Option: template
+         * the button template for a multi button
+         */
+        template: '<span class="jxButtonContainer"><a class="jxButton jxButtonMulti jxDiscloser"><span class="jxButtonContent"><img src="'+Jx.aPixel.src+'" class="jxButtonIcon"><span class="jxButtonLabel"></span></span></a><a class="jxButtonDisclose" href="javascript:void(0)"><img src="'+Jx.aPixel.src+'"></a></span>',
+        menuOptions: {}
+    },
+
     /**
-     * Constructor: Jx.Button.Multi
+     * Property: classes
+     * {<Hash>} a hash of object properties to CSS class names used to
+     * automatically extract references to important DOM elements when
+     * processing a widget template.  This allows developers to provide custom
+     * HTML structures without affecting the functionality of widgets.
+     */
+    classes: new Hash({
+        domObj: 'jxButtonContainer',
+        domA: 'jxButton',
+        domImg: 'jxButtonIcon',
+        domLabel: 'jxButtonLabel',
+        domDisclose: 'jxButtonDisclose'
+    }),
+
+    /**
+     * Method: render
      * construct a new instance of Jx.Button.Multi.
      */
-    initialize: function(opts) {
-        this.parent(opts);
-
+    render: function() {
+        this.parent();
         this.buttons = [];
 
-        this.domA.addClass('jxButtonMulti');
-        
-        this.menu = new Jx.Menu();
+        this.menu = new Jx.Menu({}, this.options.menuOptions);
         this.menu.button = this;
         this.buttonSet = new Jx.ButtonSet();
-        
-        this.clickHandler = this.clicked.bind(this);
-        
-        var a = new Element('a', {
-            'class': 'jxButtonDisclose',
-            'href': 'javascript:void(0)'
-        });
-        var button = this;
-        var hasFocus;
-        
-        a.addEvents({
-            'click': (function(e) {
-                if (this.items.length === 0) {
-                    return;
-                }
-                if (!button.options.enabled) {
-                    return;
-                }
-                this.contentContainer.setStyle('visibility','hidden');
-                this.contentContainer.setStyle('display','block');
-                $(document.body).adopt(this.contentContainer);            
-                /* we have to size the container for IE to render the chrome correctly
-                 * but just in the menu/sub menu case - there is some horrible peekaboo
-                 * bug in IE related to ULs that we just couldn't figure out
-                 */
-                this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
 
-                this.showChrome(this.contentContainer);
+        this.bound.click = this.clicked.bind(this);
 
-                this.position(this.contentContainer, this.button.domObj, {
-                    horizontal: ['right right'],
-                    vertical: ['bottom top', 'top bottom'],
-                    offsets: this.chromeOffsets
-                });
+        if (this.domDisclose) {
+            var button = this;
+            var hasFocus;
 
-                this.contentContainer.setStyle('visibility','');
+            this.bound.disclose = {
+              click: function(e) {
+                  if (this.list.count() === 0) {
+                      return;
+                  }
+                  if (!button.options.enabled) {
+                      return;
+                  }
+                  this.contentContainer.setStyle('visibility','hidden');
+                  this.contentContainer.setStyle('display','block');
+                  document.id(document.body).adopt(this.contentContainer);
+                  /* we have to size the container for IE to render the chrome
+                   * correctly but just in the menu/sub menu case - there is
+                   * some horrible peekaboo bug in IE related to ULs that we
+                   * just couldn't figure out
+                   */
+                  this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
 
-                document.addEvent('mousedown', this.hideWatcher);
-                document.addEvent('keyup', this.keypressWatcher);
-                
-                /* why were we doing this? It was affecting issue 13
-                if (e.$extended) {
-                    e.stop();                                        
-                } else if (e.event && e.event.$extended) {
-                    e.event.stop();
-                }
-                */
-                this.fireEvent('show', this);
-            }).bindWithEvent(this.menu),
-            'mouseenter':(function(){
-                $(this.domObj.firstChild).addClass('jxButtonHover');
-                if (hasFocus) {
-                    a.addClass('jx'+this.options.type+'Pressed');
-                }
-            }).bind(this),
-            'mouseleave':(function(){
-                $(this.domObj.firstChild).removeClass('jxButtonHover');
-                a.removeClass('jx'+this.options.type+'Pressed');
-            }).bind(this),
-            mousedown: (function(e) {
-                a.addClass('jx'+this.options.type+'Pressed');
-                hasFocus = true;
-                this.focus();
-            }).bindWithEvent(this),
-            mouseup: (function(e) {
-                a.removeClass('jx'+this.options.type+'Pressed');                  
-            }).bindWithEvent(this),
-            keydown: (function(e) {
-                if (e.key == 'enter') {
-                    a.addClass('jx'+this.options.type+'Pressed');
-                }
-            }).bindWithEvent(this),
-            keyup: (function(e) {
-                if (e.key == 'enter') {
-                    a.removeClass('jx'+this.options.type+'Pressed');
-                }
-            }).bindWithEvent(this),
-            blur: function() { hasFocus = false; }
-            
-        });
-        if (typeof Drag != 'undefined') {
-            new Drag(a, {
-                onStart: function() {this.stop();}
+                  this.showChrome(this.contentContainer);
+
+                  this.position(this.contentContainer, this.button.domObj, {
+                      horizontal: ['right right'],
+                      vertical: ['bottom top', 'top bottom'],
+                      offsets: this.chromeOffsets
+                  });
+
+                  this.contentContainer.setStyle('visibility','');
+
+                  document.addEvent('mousedown', this.bound.hide);
+                  document.addEvent('keyup', this.bound.keypress);
+
+                  this.fireEvent('show', this);
+              }.bindWithEvent(this.menu),
+              mouseenter:function(){
+                  document.id(this.domObj.firstChild).addClass('jxButtonHover');
+                  if (hasFocus) {
+                      this.domDisclose.addClass(this.options.pressedClass);
+                  }
+              }.bind(this),
+              mouseleave:function(){
+                  document.id(this.domObj.firstChild).removeClass('jxButtonHover');
+                  this.domDisclose.removeClass(this.options.pressedClass);
+              }.bind(this),
+              mousedown: function(e) {
+                  this.domDisclose.addClass(this.options.pressedClass);
+                  hasFocus = true;
+                  this.focus();
+              }.bindWithEvent(this),
+              mouseup: function(e) {
+                  this.domDisclose.removeClass(this.options.pressedClass);
+              }.bindWithEvent(this),
+              keydown: function(e) {
+                  if (e.key == 'enter') {
+                      this.domDisclose.addClass(this.options.pressedClass);
+                  }
+              }.bindWithEvent(this),
+              keyup: function(e) {
+                  if (e.key == 'enter') {
+                      this.domDisclose.removeClass(this.options.pressedClass);
+                  }
+              }.bindWithEvent(this),
+              blur: function() { hasFocus = false; }
+            };
+
+            this.domDisclose.addEvents({
+              click: this.bound.disclose.click,
+              mouseenter: this.bound.disclose.mouseenter,
+              mouseleave: this.bound.disclose.mouseleave,
+              mousedown: this.bound.disclose.mousedown,
+              mouseup: this.bound.disclose.mouseup,
+              keydown: this.bound.disclose.keydown,
+              keyup: this.bound.disclose.keyup,
+              blur: this.bound.disclose.blur
             });
+            if (typeof Drag != 'undefined') {
+                new Drag(this.domDisclose, {
+                    onStart: function() {this.stop();}
+                });
+            }
         }
-        
+        this.bound.show = function() {
+            this.domA.addClass(this.options.activeClass);
+        }.bind(this);
+        this.bound.hide = function() {
+            if (this.options.active) {
+                this.domA.addClass(this.options.activeClass);
+            }
+        }.bind(this);
+
         this.menu.addEvents({
-            'show': (function() {
-                this.domA.addClass('jxButtonActive');                    
-            }).bind(this),
-            'hide': (function() {
-                if (this.options.active) {
-                    this.domA.addClass('jxButtonActive');                    
-                }
-            }).bind(this)
+            'show': this.bound.show,
+            'hide': this.bound.hide
         });
-        a.adopt(new Element('img', {
-            src: Jx.aPixel.src,
-            alt: '',
-            title: ''
-        }));
-        this.domObj.adopt(a);
-        this.discloser = a;
         if (this.options.items) {
             this.add(this.options.items);
         }
     },
+    cleanup: function() {
+      var self = this,
+          bound = this.bound;
+      // clean up the discloser
+      if (self.domDisclose) {
+        self.domDisclose.removeEvents({
+          click: bound.disclose.click,
+          mouseenter: bound.disclose.mouseenter,
+          mouseleave: bound.disclose.mouseleave,
+          mousedown: bound.disclose.mousedown,
+          mouseup: bound.disclose.mouseup,
+          keydown: bound.disclose.keydown,
+          keyup: bound.disclose.keyup,
+          blur: bound.disclose.blur
+        });
+      }
+
+      // clean up the button set
+      self.buttonSet.destroy();
+      self.buttonSet = null;
+
+      // clean up the buttons array
+      self.buttons.each(function(b){
+        b.removeEvents();
+        self.menu.remove(b.multiButton);
+        b.multiButton.destroy();
+        b.multiButton = null;
+        b.destroy();
+      });
+      self.buttons.empty();
+      self.buttons = null;
+
+      // clean up the menu object
+      self.menu.removeEvents({
+        'show': bound.show,
+        'hide': bound.hide
+      });
+      // unset the menu button because it references this object
+      self.menu.button = null;
+      self.menu.destroy();
+      self.menu = null;
+
+      // clean up binds and call parent to finish
+      self.bound.show = null;
+      self.bound.hide = null;
+      self.bound.clicked = null;
+      self.bound.disclose = null;
+      self.activeButton = null;
+      self.parent();
+    },
     /**
-     * Method: add
+     * APIMethod: add
      * adds one or more buttons to the Multi button.  The first button
-     * added becomes the active button initialize.  This function 
+     * added becomes the active button initialize.  This function
      * takes a variable number of arguments, each of which is expected
      * to be an instance of <Jx.Button>.
      *
-     * Parameters: 
+     * Parameters:
      * button - {<Jx.Button>} a <Jx.Button> instance, may be repeated in the parameter list
      */
     add: function() {
         $A(arguments).flatten().each(function(theButton){
+          var f,
+              opts,
+              button;
             if (!theButton instanceof Jx.Button) {
                 return;
             }
+            theButton.domA.addClass('jxDiscloser');
+            theButton.setLabel(theButton.options.label);
             this.buttons.push(theButton);
-            var f = this.setButton.bind(this, theButton);
-            var opts = $merge(
-                theButton.options,
-                { toggle: true, onClick: f}
-            );
-            if (!opts.label) {
-                opts.label = '&nbsp;';
-            }
+            f = this.setButton.bind(this, theButton);
+            opts = {
+                image: theButton.options.image,
+                imageClass: theButton.options.imageClass,
+                label: theButton.options.label || '&nbsp;',
+                enabled: theButton.options.enabled,
+                tooltip: theButton.options.tooltip,
+                toggle: true,
+                onClick: f
+            };
             if (!opts.image || opts.image.indexOf('a_pixel') != -1) {
                 delete opts.image;
             }
-            var button = new Jx.Menu.Item(opts);
+            button = new Jx.Menu.Item(opts);
             this.buttonSet.add(button);
             this.menu.add(button);
             theButton.multiButton = button;
@@ -12693,7 +24097,7 @@
         }, this);
     },
     /**
-     * Method: remove
+     * APIMethod: remove
      * remove a button from a multi button
      *
      * Parameters:
@@ -12726,25 +24130,32 @@
         }
     },
     /**
-     * Method: setActiveButton
+     * APIMethod: empty
+     * remove all buttons from the multi button
+     */
+    empty: function() {
+      this.buttons.each(function(b){this.remove(b);}, this);
+    },
+    /**
+     * APIMethod: setActiveButton
      * update the menu item to be the requested button.
      *
-     * Parameters: 
+     * Parameters:
      * button - {<Jx.Button>} a <Jx.Button> instance that was added to this multi button.
      */
     setActiveButton: function(button) {
         if (this.activeButton) {
             this.activeButton.domA.dispose();
-            this.activeButton.domA.removeEvent(this.clickHandler);
+            this.activeButton.domA.removeEvent('click', this.bound.click);
         }
         if (button && button.domA) {
             this.domObj.grab(button.domA, 'top');
             this.domA = button.domA;
-            this.domA.addEvent('click', this.clickHandler);
+            this.domA.addEvent('click', this.bound.click);
             if (this.options.toggle) {
                 this.options.active = false;
-                this.setActive(true); 
-            }         
+                this.setActive(true);
+            }
         }
         this.activeButton = button;
     },
@@ -12753,378 +24164,1411 @@
      * update the active button in the menu item, trigger the button's action
      * and hide the flyout that contains the buttons.
      *
-     * Parameters: 
+     * Parameters:
      * button - {<Jx.Button>} The button to set as the active button
      */
     setButton: function(button) {
         this.setActiveButton(button);
         button.clicked();
     }
-});// $Id: menu.item.js 424 2009-05-12 12:51:44Z pagameba $
+});/*
+---
+
+name: Jx.Layout
+
+description: Jx.Layout is used to provide more flexible layout options for applications
+
+license: MIT-style license.
+
+requires:
+ - Jx.Object
+
+
+provides: [Jx.Layout]
+
+css:
+ - layout
+
+...
+ */
+// $Id: layout.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.Menu.Item
+ * Class: Jx.Layout
  *
- * Extends: <Jx.Button>
+ * Extends: <Jx.Object>
  *
- * A menu item is a single entry in a menu.  It is typically composed of
- * a label and an optional icon.  Selecting the menu item emits an event.
+ * Jx.Layout is used to provide more flexible layout options for applications
  *
- * Jx.Menu.Item is represented by a <Jx.Button> with type MenuItem and the
- * associated CSS changes noted in <Jx.Button>.  The container of a MenuItem
- * is an 'li' element.
+ * Jx.Layout wraps an existing DOM element (typically a div) and provides
+ * extra functionality for sizing that element within its parent and sizing
+ * elements contained within it that have a 'resize' function attached to them.
  *
+ * To create a Jx.Layout, pass the element or id plus an options object to
+ * the constructor.
+ *
  * Example:
  * (code)
+ * var myContainer = new Jx.Layout('myDiv', options);
  * (end)
  *
  * Events:
- * click - fired when the menu item is clicked.
+ * sizeChange - fired when the size of the container changes
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
-Jx.Menu.Item = new Class({
-    Family: 'Jx.Menu.Item',
-    Extends: Jx.Button,
+
+Jx.Layout = new Class({
+    Family: 'Jx.Layout',
+    Extends: Jx.Object,
+
+    options: {
+        /* Option: resizeWithWindow
+         * boolean, automatically resize this layout when the window size
+         * changes, even if the element is not a direct descendant of the
+         * BODY.  False by default.
+         */
+        resizeWithWindow: false,
+        /* Option: propagate
+         * boolean, controls propogation of resize to child nodes.
+         * True by default. If set to false, changes in size will not be
+         * propogated to child nodes.
+         */
+        propagate: true,
+        /* Option: position
+         * how to position the element, either 'absolute' or 'relative'.
+         * The default (if not passed) is 'absolute'.  When using
+         * 'absolute' positioning, both the width and height are
+         * controlled by Jx.Layout.  If 'relative' positioning is used
+         * then only the width is controlled, allowing the height to
+         * be controlled by its content.
+         */
+        position: 'absolute',
+        /* Option: left
+         * the distance (in pixels) to maintain the left edge of the element
+         * from its parent element.  The default value is 0.  If this is set
+         * to 'null', then the left edge can be any distance from its parent
+         * based on other parameters.
+         */
+        left: 0,
+        /* Option: right
+         * the distance (in pixels) to maintain the right edge of the element
+         * from its parent element.  The default value is 0.  If this is set
+         * to 'null', then the right edge can be any distance from its parent
+         * based on other parameters.
+         */
+        right: 0,
+        /* Option: top
+         * the distance (in pixels) to maintain the top edge of the element
+         * from its parent element.  The default value is 0.  If this is set
+         * to 'null', then the top edge can be any distance from its parent
+         * based on other parameters.
+         */
+        top: 0,
+        /* Option: bottom
+         * the distance (in pixels) to maintain the bottom edge of the element
+         * from its parent element.  The default value is 0.  If this is set
+         * to 'null', then the bottom edge can be any distance from its parent
+         * based on other parameters.
+         */
+        bottom: 0,
+        /* Option: width
+         * the width (in pixels) of the element.  The default value is null.
+         * If this is set to 'null', then the width can be any value based on
+         * other parameters.
+         */
+        width: null,
+        /* Option: height
+         * the height (in pixels) of the element.  The default value is null.
+         * If this is set to 'null', then the height can be any value based on
+         * other parameters.
+         */
+        height: null,
+        /* Option: minWidth
+         * the minimum width that the element can be sized to.  The default
+         * value is 0.
+         */
+        minWidth: 0,
+        /* Option: minHeight
+         * the minimum height that the element can be sized to.  The
+         * default value is 0.
+         */
+        minHeight: 0,
+        /* Option: maxWidth
+         * the maximum width that the element can be sized to.  The default
+         * value is -1, which means no maximum.
+         */
+        maxWidth: -1,
+        /* Option: maxHeight
+         * the maximum height that the element can be sized to.  The
+         * default value is -1, which means no maximum.
+         */
+        maxHeight: -1
+    },
+
     /**
-     * Property: owner
-     * {<Jx.SubMenu> or <Jx.Menu>} the menu that contains the menu item.
+     * Parameters:
+     * domObj - {HTMLElement} element or id to apply the layout to
+     * options - <Jx.Layout.Options>
      */
-    owner: null,
+    parameters: ['domObj','options'],
+
+    /**
+     * APIMethod: init
+     * Create a new instance of Jx.Layout.
+     */
+    init: function() {
+        this.domObj = document.id(this.options.domObj);
+        this.domObj.resize = this.resize.bind(this);
+        this.domObj.setStyle('position', this.options.position);
+        this.domObj.store('jxLayout', this);
+
+        if (this.options.resizeWithWindow || document.body == this.domObj.parentNode) {
+            window.addEvent('resize', this.windowResize.bindWithEvent(this));
+            window.addEvent('load', this.windowResize.bind(this));
+        }
+        //this.resize();
+    },
+
+    /**
+     * Method: windowResize
+     * when the window is resized, any Jx.Layout controlled elements that are
+     * direct children of the BODY element are resized
+     */
+     windowResize: function() {
+         this.resize();
+         if (this.resizeTimer) {
+             $clear(this.resizeTimer);
+             this.resizeTimer = null;
+         }
+         this.resizeTimer = this.resize.delay(50, this);
+    },
+
+    /**
+     * Method: resize
+     * resize the element controlled by this Jx.Layout object.
+     *
+     * Parameters:
+     * options - new options to apply, see <Jx.Layout.Options>
+     */
+    resize: function(options) {
+         /* this looks like a really big function but actually not
+          * much code gets executed in the two big if statements
+          */
+        this.resizeTimer = null;
+        var needsResize = false;
+        if (options) {
+            for (var i in options) {
+                //prevent forceResize: false from causing a resize
+                if (i == 'forceResize') {
+                    continue;
+                }
+                if (this.options[i] != options[i]) {
+                    needsResize = true;
+                    this.options[i] = options[i];
+                }
+            }
+            if (options.forceResize) {
+                needsResize = true;
+            }
+        }
+        if (!document.id(this.domObj.parentNode)) {
+            return;
+        }
+
+        var parentSize;
+        if (this.domObj.parentNode.tagName == 'BODY') {
+            parentSize = Jx.getPageDimensions();
+        } else {
+            parentSize = document.id(this.domObj.parentNode).getContentBoxSize();
+        }
+
+        if (this.lastParentSize && !needsResize) {
+            needsResize = (this.lastParentSize.width != parentSize.width ||
+                          this.lastParentSize.height != parentSize.height);
+        } else {
+            needsResize = true;
+        }
+        this.lastParentSize = parentSize;
+
+        if (!needsResize) {
+            return;
+        }
+
+        var l, t, w, h;
+
+        /* calculate left and width */
+        if (this.options.left != null) {
+            /* fixed left */
+            l = this.options.left;
+            if (this.options.right == null) {
+                /* variable right */
+                if (this.options.width == null) {
+                    /* variable right and width
+                     * set right to min, stretch width */
+                    w = parentSize.width - l;
+                    if (w < this.options.minWidth ) {
+                        w = this.options.minWidth;
+                    }
+                    if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
+                        w = this.options.maxWidth;
+                    }
+                } else {
+                    /* variable right, fixed width
+                     * use width
+                     */
+                    w = this.options.width;
+                }
+            } else {
+                /* fixed right */
+                if (this.options.width == null) {
+                    /* fixed right, variable width
+                     * stretch width
+                     */
+                    w = parentSize.width - l - this.options.right;
+                    if (w < this.options.minWidth) {
+                        w = this.options.minWidth;
+                    }
+                    if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
+                        w = this.options.maxWidth;
+                    }
+                } else {
+                    /* fixed right, fixed width
+                     * respect left and width, allow right to stretch
+                     */
+                    w = this.options.width;
+                }
+            }
+
+        } else {
+            if (this.options.right == null) {
+                if (this.options.width == null) {
+                    /* variable left, width and right
+                     * set left, right to min, stretch width
+                     */
+                     l = 0;
+                     w = parentSize.width;
+                     if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
+                         l = l + parseInt(w - this.options.maxWidth,10)/2;
+                         w = this.options.maxWidth;
+                     }
+                } else {
+                    /* variable left, fixed width, variable right
+                     * distribute space between left and right
+                     */
+                    w = this.options.width;
+                    l = parseInt((parentSize.width - w)/2,10);
+                    if (l < 0) {
+                        l = 0;
+                    }
+                }
+            } else {
+                if (this.options.width != null) {
+                    /* variable left, fixed width, fixed right
+                     * left is calculated directly
+                     */
+                    w = this.options.width;
+                    l = parentSize.width - w - this.options.right;
+                    if (l < 0) {
+                        l = 0;
+                    }
+                } else {
+                    /* variable left and width, fixed right
+                     * set left to min value and stretch width
+                     */
+                    l = 0;
+                    w = parentSize.width - this.options.right;
+                    if (w < this.options.minWidth) {
+                        w = this.options.minWidth;
+                    }
+                    if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
+                        l = w - this.options.maxWidth - this.options.right;
+                        w = this.options.maxWidth;
+                    }
+                }
+            }
+        }
+
+        /* calculate the top and height */
+        if (this.options.top != null) {
+            /* fixed top */
+            t = this.options.top;
+            if (this.options.bottom == null) {
+                /* variable bottom */
+                if (this.options.height == null) {
+                    /* variable bottom and height
+                     * set bottom to min, stretch height */
+                    h = parentSize.height - t;
+                    if (h < this.options.minHeight) {
+                        h = this.options.minHeight;
+                    }
+                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
+                        h = this.options.maxHeight;
+                    }
+                } else {
+                    /* variable bottom, fixed height
+                     * stretch height
+                     */
+                    h = this.options.height;
+                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
+                        t = h - this.options.maxHeight;
+                        h = this.options.maxHeight;
+                    }
+                }
+            } else {
+                /* fixed bottom */
+                if (this.options.height == null) {
+                    /* fixed bottom, variable height
+                     * stretch height
+                     */
+                    h = parentSize.height - t - this.options.bottom;
+                    if (h < this.options.minHeight) {
+                        h = this.options.minHeight;
+                    }
+                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
+                        h = this.options.maxHeight;
+                    }
+                } else {
+                    /* fixed bottom, fixed height
+                     * respect top and height, allow bottom to stretch
+                     */
+                    h = this.options.height;
+                }
+            }
+        } else {
+            if (this.options.bottom == null) {
+                if (this.options.height == null) {
+                    /* variable top, height and bottom
+                     * set top, bottom to min, stretch height
+                     */
+                     t = 0;
+                     h = parentSize.height;
+                     if (h < this.options.minHeight) {
+                         h = this.options.minHeight;
+                     }
+                     if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
+                         t = parseInt((parentSize.height - this.options.maxHeight)/2,10);
+                         h = this.options.maxHeight;
+                     }
+                } else {
+                    /* variable top, fixed height, variable bottom
+                     * distribute space between top and bottom
+                     */
+                    h = this.options.height;
+                    t = parseInt((parentSize.height - h)/2,10);
+                    if (t < 0) {
+                        t = 0;
+                    }
+                }
+            } else {
+                if (this.options.height != null) {
+                    /* variable top, fixed height, fixed bottom
+                     * top is calculated directly
+                     */
+                    h = this.options.height;
+                    t = parentSize.height - h - this.options.bottom;
+                    if (t < 0) {
+                        t = 0;
+                    }
+                } else {
+                    /* variable top and height, fixed bottom
+                     * set top to min value and stretch height
+                     */
+                    t = 0;
+                    h = parentSize.height - this.options.bottom;
+                    if (h < this.options.minHeight) {
+                        h = this.options.minHeight;
+                    }
+                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
+                        t = parentSize.height - this.options.maxHeight - this.options.bottom;
+                        h = this.options.maxHeight;
+                    }
+                }
+            }
+        }
+
+        //TODO: check left, top, width, height against current styles
+        // and only apply changes if they are not the same.
+
+        /* apply the new sizes */
+        var sizeOpts = {width: w};
+        if (this.options.position == 'absolute') {
+            var m = document.id(this.domObj.parentNode).measure(function(){
+                return this.getSizes(['padding'],['left','top']).padding;
+            });
+            this.domObj.setStyles({
+                position: this.options.position,
+                left: l+m.left,
+                top: t+m.top
+            });
+            sizeOpts.height = h;
+        } else {
+            if (this.options.height) {
+                sizeOpts.height = this.options.height;
+            }
+        }
+        this.domObj.setBorderBoxSize(sizeOpts);
+
+        if (this.options.propagate) {
+            // propogate changes to children
+            var o = {forceResize: options ? options.forceResize : false};
+            $A(this.domObj.childNodes).each(function(child){
+                if (child.resize && child.getStyle('display') != 'none') {
+                    child.resize.delay(0,child,o);
+                }
+            });
+        }
+
+        this.fireEvent('sizeChange',this);
+    }
+});/*
+---
+
+name: Jx.Toolbar
+
+description: A toolbar is a container object that contains other objects such as buttons.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+ - Jx.List
+
+provides: [Jx.Toolbar]
+
+css:
+ - toolbar
+
+images:
+ - toolbar.png
+...
+ */
+// $Id: toolbar.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Toolbar
+ *
+ * Extends: <Jx.Widget>
+ *
+ * A toolbar is a container object that contains other objects such as
+ * buttons.  The toolbar organizes the objects it contains automatically,
+ * wrapping them as necessary.  Multiple toolbars may be placed within
+ * the same containing object.
+ *
+ * Jx.Toolbar includes CSS classes for styling the appearance of a
+ * toolbar to be similar to traditional desktop application toolbars.
+ *
+ * There is one special object, Jx.ToolbarSeparator, that provides
+ * a visual separation between objects in a toolbar.
+ *
+ * While a toolbar is generally a *dumb* container, it serves a special
+ * purpose for menus by providing some infrastructure so that menus can behave
+ * properly.
+ *
+ * In general, almost anything can be placed in a Toolbar, and mixed with
+ * anything else.
+ *
+ * Example:
+ * The following example shows how to create a Jx.Toolbar instance and place
+ * two objects in it.
+ *
+ * (code)
+ * //myToolbarContainer is the id of a <div> in the HTML page.
+ * function myFunction() {}
+ * var myToolbar = new Jx.Toolbar('myToolbarContainer');
+ *
+ * var myButton = new Jx.Button(buttonOptions);
+ *
+ * var myElement = document.createElement('select');
+ *
+ * myToolbar.add(myButton, new Jx.ToolbarSeparator(), myElement);
+ * (end)
+ *
+ * Events:
+ * add - fired when one or more buttons are added to a toolbar
+ * remove - fired when on eor more buttons are removed from a toolbar
+ *
+ * Implements:
+ * Options
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Toolbar = new Class({
+    Family: 'Jx.Toolbar',
+    Extends: Jx.Widget,
+    /**
+     * Property: list
+     * {<Jx.List>} the list that holds the items in this toolbar
+     */
+    list : null,
+    /**
+     * Property: domObj
+     * {HTMLElement} the HTML element that the toolbar lives in
+     */
+    domObj : null,
+    /**
+     * Property: isActive
+     * When a toolbar contains <Jx.Menu> instances, they want to know
+     * if any menu in the toolbar is active and this is how they
+     * find out.
+     */
+    isActive : false,
     options: {
-        enabled: true,
-        image: null,
-        label: '&nbsp;',
-        toggleClass: 'Toggle'
+        /* Option: position
+         * the position of this toolbar in the container.  The position
+         * affects some items in the toolbar, such as menus and flyouts, which
+         * need to open in a manner sensitive to the position.  May be one of
+         * 'top', 'right', 'bottom' or 'left'.  Default is 'top'.
+         */
+        position: 'top',
+        /* Option: parent
+         * a DOM element to add this toolbar to
+         */
+        parent: null,
+        /* Option: autoSize
+         * if true, the toolbar will attempt to set its size based on the
+         * things it contains.  Default is false.
+         */
+        autoSize: false,
+        /**
+         * Option: align
+         * Determines whether the toolbar is aligned left, center, or right.
+         * Mutually exclusive with the scroll option. If scroll is set to true
+         * this option does nothing. Default: 'left', valid values: 'left',
+         * 'center', or 'right'
+         */
+        align: 'left',
+        /* Option: scroll
+         * if true, the toolbar may scroll if the contents are wider than
+         * the size of the toolbar
+         */
+        scroll: true,
+        template: '<ul class="jxToolbar"></ul>'
     },
+    classes: new Hash({
+        domObj: 'jxToolbar'
+    }),
     /**
-     * Constructor: Jx.Menu.Item
-     * Create a new instance of Jx.Menu.Item
+     * APIMethod: render
+     * Create a new instance of Jx.Toolbar.
+     */
+    render: function() {
+        this.parent();
+        this.domObj.store('jxToolbar', this);
+        if ($defined(this.options.id)) {
+            this.domObj.id = this.options.id;
+        }
+
+        this.list = new Jx.List(this.domObj, {
+            onAdd: function(item) {
+                this.fireEvent('add', this);
+            }.bind(this),
+            onRemove: function(item) {
+                this.fireEvent('remove', this);
+            }.bind(this)
+        });
+
+        if (this.options.parent) {
+            this.addTo(this.options.parent);
+        }
+        this.deactivateWatcher = this.deactivate.bindWithEvent(this);
+        if (this.options.items) {
+            this.add(this.options.items);
+        }
+    },
+
+    /**
+     * Method: addTo
+     * add this toolbar to a DOM element automatically creating a toolbar
+     * container if necessary
      *
      * Parameters:
-     * options - See <Jx.Button.Options>
+     * parent - the DOM element or toolbar container to add this toolbar to.
      */
-    initialize: function(options) {
-        this.parent($merge({
-                image: Jx.aPixel.src
-            },
-            options, {
-                container:'li',
-                type:'MenuItem',
-                toggleClass: (options.image ? null : this.options.toggleClass)
+    addTo: function(parent) {
+        var tbc = document.id(parent).retrieve('jxBarContainer');
+        if (!tbc) {
+            tbc = new Jx.Toolbar.Container({
+                parent: parent,
+                position: this.options.position,
+                autoSize: this.options.autoSize,
+                align: this.options.align,
+                scroll: this.options.scroll
+            });
+        }
+        tbc.add(this);
+        return this;
+    },
+
+    /**
+     * Method: add
+     * Add an item to the toolbar.  If the item being added is a Jx component
+     * with a domObj property, the domObj is added.  If the item being added
+     * is an LI element, then it is given a CSS class of *jxToolItem*.
+     * Otherwise, the thing is wrapped in a <Jx.ToolbarItem>.
+     *
+     * Parameters:
+     * thing - {Object} the thing to add.  More than one thing can be added
+     * by passing multiple arguments.
+     */
+    add: function( ) {
+        $A(arguments).flatten().each(function(thing) {
+            var item = thing;
+            if (item.domObj) {
+                item = item.domObj;
             }
-        ));
-        this.domObj.addEvent('mouseover', this.onMouseOver.bindWithEvent(this));
+
+            if (item.tagName == 'LI') {
+                if (!item.hasClass('jxToolItem')) {
+                    item.addClass('jxToolItem');
+                }
+            } else {
+                item = new Jx.Toolbar.Item(thing);
+            }
+            this.list.add(item);
+        }, this);
+        
+        //Update the size of the toolbar container.
+        this.update();
+        
+        return this;
     },
     /**
-     * Method: setOwner
-     * Set the owner of this menu item
+     * Method: remove
+     * remove an item from a toolbar.  If the item is not in this toolbar
+     * nothing happens
      *
      * Parameters:
-     * obj - {Object} the new owner
+     * item - {Object} the object to remove
+     *
+     * Returns:
+     * {Object} the item that was removed, or null if the item was not
+     * removed.
      */
-    setOwner: function(obj) {
-        this.owner = obj;
+    remove: function(item) {
+        if (item.domObj) {
+            item = item.domObj;
+        }
+        var li = item.findElement('LI');
+        this.list.remove(li);
+        this.update();
+        return this;
     },
     /**
-     * Method: hide
-     * Hide the menu item.
+     * APIMethod: empty
+     * remove all items from the toolbar
      */
-    hide: function() {this.blur();},
+    empty: function() {
+      this.list.each(function(item){this.remove(item);},this);
+    },
     /**
-     * Method: show
-     * Show the menu item
+     * Method: deactivate
+     * Deactivate the Toolbar (when it is acting as a menu bar).
      */
-    show: $empty,
+    deactivate: function() {
+        this.list.each(function(item){
+            if (item.retrieve('jxMenu')) {
+                item.retrieve('jxMenu').hide();
+            }
+        });
+        this.setActive(false);
+    },
     /**
-     * Method: clicked
-     * Handle the user clicking on the menu item, overriding the <Jx.Button::clicked>
-     * method to facilitate menu tracking
+     * Method: isActive
+     * Indicate if the toolbar is currently active (as a menu bar)
      *
+     * Returns:
+     * {Boolean}
+     */
+    isActive: function() {
+        return this.isActive;
+    },
+    /**
+     * Method: setActive
+     * Set the active state of the toolbar (for menus)
+     *
      * Parameters:
-     * obj - {Object} an object containing an event property that was the user
-     * event.
+     * b - {Boolean} the new state
      */
-    clicked: function(obj) {
-        if (this.options.enabled) {
-            if (this.options.toggle) {
-                this.setActive(!this.options.active);
-            }
-            this.fireEvent('click', this);
-            if (this.owner && this.owner.deactivate) {
-                this.owner.deactivate(obj.event);
-            }
+    setActive: function(b) {
+        this.isActive = b;
+        if (this.isActive) {
+            document.addEvent('click', this.deactivateWatcher);
+        } else {
+            document.removeEvent('click', this.deactivateWatcher);
         }
     },
     /**
-     * Method: onmouseover
-     * handle the mouse moving over the menu item
+     * Method: setVisibleItem
+     * For menus, they want to know which menu is currently open.
      *
      * Parameters:
-     * e - {Event} the mousemove event
+     * obj - {<Jx.Menu>} the menu that just opened.
      */
-    onMouseOver: function(e) {
-        if (this.owner && this.owner.setVisibleItem) {
-            this.owner.setVisibleItem(this);
+    setVisibleItem: function(obj) {
+        if (this.visibleItem && this.visibleItem.hide && this.visibleItem != obj) {
+            this.visibleItem.hide();
         }
-        this.show(e);
+        this.visibleItem = obj;
+        if (this.isActive()) {
+            this.visibleItem.show();
+        }
+    },
+    
+    showItem: function(item) {
+        this.fireEvent('show', item);
+    },
+    /**
+     * Method: update
+     * Updates the size of the UL so that the size is always consistently the 
+     * exact size of the size of the sum of the buttons. This will keep all of 
+     * the buttons on one line.
+     */
+    update: function () {
+        // if (['top','bottom'].contains(this.options.position)) {
+        //     (function(){
+        //         var s = 0;
+        //         var children = this.domObj.getChildren();
+        //         children.each(function(button){
+        //             var size = button.getMarginBoxSize();
+        //             s += size.width +0.5;
+        //         },this);
+        //         if (s !== 0) {
+        //             this.domObj.setStyle('width', Math.round(s));
+        //         } else {
+        //             this.domObj.setStyle('width','auto');
+        //         }
+        //     }).delay(1,this);
+        // }
+        this.fireEvent('update');
+    },
+    changeText : function(lang) {
+      this.update();
     }
 });
+/*
+---
 
-// $Id: combo.js 424 2009-05-12 12:51:44Z pagameba $
+name: Jx.Toolbar.Container
+
+description: A toolbar container contains toolbars.  This has an optional dependency on Fx.Tween that, if included, will allow toolbars that contain more elements than can be displayed to be smoothly scrolled left and right.  Without this optional dependency, the toolbar will jump in fixed increments rather than smoothly scrolling.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Toolbar
+ - Jx.Button
+
+optional:
+ - Core/Fx.Tween
+
+provides: [Jx.Toolbar.Container]
+
+images:
+ - emblems.png
+
+...
+ */
+// $Id: container.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.Button.Combo
+ * Class: Jx.Toolbar.Container
  *
- * Extends: <Jx.Button.Multi>
+ * Extends: <Jx.Widget>
  *
- * A drop down list of selectable items.  Items can be either a string, an image or both.
+ * A toolbar container contains toolbars.  A single toolbar container fills
+ * the available space horizontally.  Toolbars placed in a toolbar container
+ * do not wrap when they exceed the available space.
  *
- * Example:
- * (code)
- * new Jx.Button.Combo({
- *     label: 'Choose a symbol',
- *     items: [
- *         {label: 'Star', image: 'images/swatches.png', imageClass: 'comboStar'},
- *         {label: 'Square', image: 'images/swatches.png', imageClass: 'comboSquare'},
- *         {label: 'Triangle', image: 'images/swatches.png', imageClass: 'comboTriangle'},
- *         {label: 'Circle', image: 'images/swatches.png', imageClass: 'comboCircle'},
- *         {label: 'Plus', image: 'images/swatches.png', imageClass: 'comboPlus'},
- *         {label: 'Cross', image: 'images/swatches.png', imageClass: 'comboCross'}
- *     ],
- *     onChange: function(combo) { alert('you selected ' + combo.getValue()) }
- * })
- * (end)
- *
  * Events:
- * change - triggered when the user selects a new item from the list
+ * add - fired when one or more toolbars are added to a container
+ * remove - fired when one or more toolbars are removed from a container
  *
- * License: 
+ * Implements:
+ * Options
+ * Events
+ * {<Jx.Addable>}
+ *
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
-Jx.Button.Combo = new Class({
-    Family: 'Jx.Button.Combo',
-    Extends: Jx.Button.Multi,
-    domObj : null,
-    ul : null,
+
+Jx.Toolbar.Container = new Class({
+
+    Family: 'Jx.Toolbar.Container',
+    Extends: Jx.Widget,
+    Binds: ['update'],
+    pluginNamespace: 'ToolbarContainer',
     /**
-     * Property: currentSelection
-     * {Object} current selection in the list 
+     * Property: domObj
+     * {HTMLElement} the HTML element that the container lives in
      */
-    currentSelection : null,
-    
+    domObj: null,
     options: {
-        /* Option: editable
-         * boolean, default false.  Can the value be edited by the user?
+        /* Option: parent
+         * a DOM element to add this to
          */
-        editable: false,
-        /* Option: label
-         * string, default ''.  The label to display next to the combo.
+        parent: null,
+        /* Option: position
+         * the position of the toolbar container in its parent, one of 'top',
+         * 'right', 'bottom', or 'left'.  Default is 'top'
          */
-        label: ''
+        position: 'top',
+        /* Option: autoSize
+         * automatically size the toolbar container to fill its container.
+         * Default is false
+         */
+        autoSize: false,
+        /* Option: scroll
+         * Control whether the user can scroll of the content of the
+         * container if the content exceeds the size of the container.
+         * Default is true.
+         */
+        scroll: true,
+        /**
+         * Option: align
+         * Determines whether the toolbar is aligned left, center, or right.
+         * Mutually exclusive with the scroll option. This option overrides
+         * scroll if set to something other than the default. Default: 'left',
+         * valid values are 'left','center', or 'right'
+         */
+        align: 'left',
+        template: "<div class='jxBarContainer'><div class='jxBarControls'></div></div>",
+        scrollerTemplate: "<div class='jxBarScroller'><div class='jxBarWrapper'></div></div>"
     },
-    
-    /** 
-     * Constructor: Jx.Combo
-     * create a new instance of Jx.Combo
-     *
-     * Parameters:
-     * options - <Jx.button.Combo.Options>
+    classes: new Hash({
+        domObj: 'jxBarContainer',
+        scroller: 'jxBarScroller',
+        //used to hide the overflow of the wrapper
+        wrapper: 'jxBarWrapper',
+        controls: 'jxBarControls'
+        //used to allow multiple toolbars to float next to each other
+    }),
+
+    updating: false,
+
+    /**
+     * APIMethod: render
+     * Create a new instance of Jx.Toolbar.Container
      */
-    initialize: function(options) {
-        this.parent(); //we don't want to pass options to parent
-        this.setOptions(options);
-        this.domA.removeClass('jxButtonMulti');
-        if (this.options.editable) {
-            // remove the button's normal A tag and replace it with a span
-            // so the input ends up not being inside an A tag - this was
-            // causing all kinds of problems for selecting text inside it
-            // due to some user-select: none classes that were introduced
-            // to make buttons not selectable in the first place.
-            //
-            // Ultimately I think we want to fix this so that the discloser
-            // in Jx.Button.Multi is a separate beast and we can use it here
-            // without inheriting from multi buttons
-            var s = new Element('span', {'class':'jxButton'});
-            s.adopt(this.domA.firstChild);
-            this.domA = s.replaces(this.domA);
-            this.domA.addClass('jxButtonComboDefault');
-            this.domA.addClass('jxButtonEditCombo');
-            this.domInput = new Element('input',{
-                type:'text',
-                events:{
-                    change: this.valueChanged.bindWithEvent(this),
-                    keydown: this.onKeyPress.bindWithEvent(this),
-                    focus: (function() {
-                        if (this.domA.hasClass('jxButtonComboDefault')) {
-                            this.domInput.value = '';
-                            this.domA.removeClass('jxButtonComboDefault');
-                        }
-                    }).bind(this)
-                },
-                value: this.options.label
+    render: function() {
+        this.parent();
+        /* if a container was passed in, use it instead of the one from the
+         * template
+         */
+        if (document.id(this.options.parent)) {
+            this.domObj = document.id(this.options.parent);
+            this.elements = new Hash({
+                'jxBarContainer': this.domObj
             });
-            this.domLabel.empty();
-            this.domLabel.addClass('jxComboInput');
-            this.domLabel.adopt(this.domInput);
+            this.domObj.addClass('jxBarContainer');
+            this.domObj.grab(this.controls);
+            this.domObj.addEvent('sizeChange', this.update);
+        }
+
+        if (!['center', 'right'].contains(this.options.align) && this.options.scroll) {
+            this.processElements(this.options.scrollerTemplate, this.classes);
+            this.domObj.grab(this.scroller, 'top');
+        }
+
+        /* this allows toolbars to add themselves to this bar container
+         * once it already exists without requiring an explicit reference
+         * to the toolbar container
+         */
+        this.domObj.store('jxBarContainer', this);
+
+        if (['top', 'right', 'bottom', 'left'].contains(this.options.position)) {
+            this.domObj.addClass('jxBar' +
+            this.options.position.capitalize());
         } else {
-            this.discloser.dispose();
-            this.domA.addClass('jxButtonCombo');
-            this.addEvent('click', (function(e){
-                this.discloser.fireEvent('click', e);
-            }).bindWithEvent(this));
+            this.domObj.addClass('jxBarTop');
+            this.options.position = 'top';
         }
-        this.buttonSet = new Jx.ButtonSet({
-            onChange: (function(set) {
-                var button = set.activeButton;            
-                this.domA.removeClass('jxButtonComboDefault');
-                if (this.options.editable) {
-                    this.domInput.value = button.options.label;
+
+        if (this.options.scroll && ['top', 'bottom'].contains(this.options.position)) {
+            // make sure we update our size when we get added to the DOM
+            this.addEvent('addTo', function(){
+              this.domObj.getParent().addEvent('sizeChange', this.update);
+              this.update();
+            });
+
+            this.scrollLeft = new Jx.Button({
+                image: Jx.aPixel.src
+            }).addTo(this.controls, 'bottom');
+            document.id(this.scrollLeft).addClass('jxBarScrollLeft');
+            this.scrollLeft.addEvents({
+                click: this.scroll.bind(this, 'left')
+            });
+
+            this.scrollRight = new Jx.Button({
+                image: Jx.aPixel.src
+            }).addTo(this.controls, 'bottom');
+            document.id(this.scrollRight).addClass('jxBarScrollRight');
+            this.scrollRight.addEvents({
+                click: this.scroll.bind(this, 'right')
+            });
+
+        } else if (this.options.scroll && ['left', 'right'].contains(this.options.position)) {
+            //do we do scrolling up and down?
+            //for now disable scroll in this case
+            this.options.scroll = false;
+        } else {
+            this.options.scroll = false;
+        }
+
+        this.addEvent('add', this.update);
+        if (this.options.toolbars) {
+            this.add(this.options.toolbars);
+        }
+    },
+
+    /**
+     * APIMethod: update
+     * Updates the scroller enablement dependent on the total size of the
+     * toolbar(s).
+     */
+    update: function() {
+        if (this.options.scroll) {
+            if (['top', 'bottom'].contains(this.options.position)) {
+                var tbcSize = this.domObj.getContentBoxSize().width;
+
+                var s = 0;
+                //next check to see if we need the scrollers or not.
+                var children = this.wrapper.getChildren();
+                if (children.length > 0) {
+                    children.each(function(tb) {
+                        s += tb.getMarginBoxSize().width;
+                    },
+                    this);
+
+                    var scrollerSize = tbcSize;
+
+                    if (s === 0) {
+                        this.scrollLeft.setEnabled(false);
+                        this.scrollRight.setEnabled(false);
+                    } else {
+
+
+                        var leftMargin = this.wrapper.getStyle('margin-left').toInt();
+                        scrollerSize -= this.controls.getMarginBoxSize().width;
+
+
+                        if (leftMargin < 0) {
+                            //has been scrolled left so activate the right scroller
+                            this.scrollLeft.setEnabled(true);
+                        } else {
+                            //we don't need it
+                            this.scrollLeft.setEnabled(false);
+                        }
+
+                        if (s + leftMargin > scrollerSize) {
+                            //we need the right one
+                            this.scrollRight.setEnabled(true);
+                        } else {
+                            //we don't need it
+                            this.scrollRight.setEnabled(false);
+                        }
+                    }
+
                 } else {
-                    var l = button.options.label;
-                    if (l == '&nbsp;') {
-                        l = '';
-                    }
-                    this.setLabel(l);
+                    this.scrollRight.setEnabled(false);
+                    this.scrollLeft.setEnabled(false);
                 }
-                var img = button.options.image;
-                if (img.indexOf('a_pixel') != -1) {
-                    img = '';
+                this.scroller.setStyle('width', scrollerSize);
+
+                this.findFirstVisible();
+                this.updating = false;
+            }
+        }
+    },
+    /**
+     * Method: findFirstVisible
+     * Finds the first visible button on the toolbar and saves a reference in 
+     * the scroller object
+     */
+    findFirstVisible: function() {
+        if ($defined(this.scroller.retrieve('buttonPointer'))) {
+            return;
+        };
+
+        var children = this.wrapper.getChildren();
+
+        if (children.length > 0) {
+            children.each(function(toolbar) {
+                var buttons = toolbar.getChildren();
+                if (buttons.length > 1) {
+                    buttons.each(function(button) {
+                        var pos = button.getCoordinates(this.scroller);
+                        if (pos.left >= 0 && !$defined(this.scroller.retrieve('buttonPointer'))) {
+                            //this is the first visible button
+                            this.scroller.store('buttonPointer', button);
+                        }
+                    },
+                    this);
                 }
-                this.setImage(img);
-                if (this.options.imageClass && this.domImg) {
-                    this.domImg.removeClass(this.options.imageClass);
-                }
-                if (button.options.imageClass && this.domImg) {
-                    this.options.imageClass = button.options.imageClass;
-                    this.domImg.addClass(button.options.imageClass);
-                }
-                this.fireEvent('change', this);
-            }).bind(this)
-        });
-        if (this.options.items) {
-            this.add(this.options.items);
+            },
+            this);
         }
-        this.setEnabled(this.options.enabled);
     },
-    
+
     /**
-     * Method: setEnabled
-     * enable or disable the combo button.
+     * APIMethod: add
+     * Add a toolbar to the container.
      *
      * Parameters:
-     * enabled - {Boolean} the new enabled state of the button
+     * toolbar - {Object} the toolbar to add.  More than one toolbar
+     *    can be added by passing multiple arguments.
      */
-    setEnabled: function(enabled) {
-        this.options.enabled = enabled;
-        if (this.options.enabled) {
-            this.domObj.removeClass('jxDisabled');
-            if (this.domInput) {
-                this.domInput.disabled = false;
+    add: function() {
+        $A(arguments).flatten().each(function(thing) {
+            if (this.options.scroll) {
+                /* we potentially need to show or hide scroller buttons
+                 * when the toolbar contents change
+                 */
+                thing.addEvent('update', this.update.bind(this));
+                thing.addEvent('show', this.scrollIntoView.bind(this));
             }
-        } else {
-            this.domObj.addClass('jxDisabled');
-            if (this.domInput) {
-                this.domInput.disabled = true;
+            if (this.wrapper) {
+                this.wrapper.adopt(thing.domObj);
+            } else {
+                this.domObj.adopt(thing.domObj);
             }
+            this.domObj.addClass('jxBar' + this.options.position.capitalize());
+        },
+        this);
+        if (arguments.length > 0) {
+            this.fireEvent('add', this);
         }
+        return this;
     },
-    
+
     /**
-     * Method: valueChanged
-     * invoked when the current value is changed
+     * Method: scroll
+     * Does the work of scrolling the toolbar to a specific position.
+     *
+     * Parameters:
+     * direction - whether to scroll left or right
      */
-    valueChanged: function() {
-        this.fireEvent('change', this);
+    scroll: function(direction) {
+        if (this.updating) {
+            return
+        };
+        this.updating = true;
+
+        var currentButton = this.scroller.retrieve('buttonPointer');
+        if (direction === 'left') {
+            //need to tween the amount of the previous button
+            var previousButton = this.scroller.retrieve('previousPointer');
+            if (!previousButton) {
+                previousButton = this.getPreviousButton(currentButton);
+            }
+            if (previousButton) {
+                var w = previousButton.getMarginBoxSize().width;
+                var ml = this.wrapper.getStyle('margin-left').toInt();
+                ml += w;
+                if (typeof Fx != 'undefined' && typeof Fx.Tween != 'undefined') {
+                    //scroll it
+                    this.wrapper.get('tween', {
+                        property: 'margin-left',
+                        onComplete: this.afterTweenLeft.bind(this, previousButton)
+                    }).start(ml);
+                } else {
+                    //set it
+                    this.wrapper.setStyle('margin-left', ml);
+                    this.afterTweenLeft(previousButton);
+                }
+            } else {
+                this.update();
+            }
+        } else {
+            //must be right
+            var w = currentButton.getMarginBoxSize().width;
+
+            var ml = this.wrapper.getStyle('margin-left').toInt();
+            ml -= w;
+
+            //now, if Fx is defined tween the margin to the left to
+            //hide the current button
+            if (typeof Fx != 'undefined' && typeof Fx.Tween != 'undefined') {
+                //scroll it
+                this.wrapper.get('tween', {
+                    property: 'margin-left',
+                    onComplete: this.afterTweenRight.bind(this, currentButton)
+                }).start(ml);
+            } else {
+                //set it
+                this.wrapper.setStyle('margin-left', ml);
+                this.afterTweenRight(currentButton);
+            }
+
+        }
     },
-    
+
     /**
-     * Method: onKeyPress
-     * Handle the user pressing a key by looking for an ENTER key to set the
-     * value.
+     * Method: afterTweenRight
+     * Updates pointers to buttons after the toolbar scrolls right
      *
      * Parameters:
-     * e - {Event} the keypress event
+     * currentButton - the button that was currently first before the scroll
      */
-    onKeyPress: function(e) {
-        if (e.key == 'enter') {
-            this.valueChanged();
+    afterTweenRight: function(currentButton) {
+        var np = this.getNextButton(currentButton);
+        if (!np) {
+            np = currentButton;
         }
+        this.scroller.store('buttonPointer', np);
+        if (np !== currentButton) {
+            this.scroller.store('previousPointer', currentButton);
+        }
+        this.update();
     },
-    
     /**
-     * Method: add
-     * add a new item to the pick list
+     * Method: afterTweenLeft
+     * Updates pointers to buttons after the toolbar scrolls left
      *
      * Parameters:
-     * options - {Object} object with properties suitable to be passed to
-     * a <Jx.Menu.Item.Options> object.  More than one options object can be
-     * passed, comma separated or in an array.
+     * previousButton - the button that was to the left of the first visible
+     *      button.
      */
-    add: function() {
-        $A(arguments).flatten().each(function(opt) {
-            var button = new Jx.Menu.Item($merge(opt,{
-                toggle: true
-            }));
-            this.menu.add(button);
-            this.buttonSet.add(button);
-        }, this);
+    afterTweenLeft: function(previousButton) {
+        this.scroller.store('buttonPointer', previousButton);
+        var pp = this.getPreviousButton(previousButton);
+        if ($defined(pp)) {
+            this.scroller.store('previousPointer', pp);
+        } else {
+            this.scroller.eliminate('previousPointer');
+        }
+        this.update();
     },
-    
     /**
-     * Method: remove
-     * Remove the item at the given index.  Not implemented.
+     * APIMethod: remove
+     * remove an item from a toolbar.  If the item is not in this toolbar
+     * nothing happens
      *
      * Parameters:
-     * idx - {Integer} the item to remove.
+     * item - {Object} the object to remove
+     *
+     * Returns:
+     * {Object} the item that was removed, or null if the item was not
+     * removed.
      */
-    remove: function(idx) {
-        //TODO: implement remove?
+    remove: function(item) {
+        if (item instanceof Jx.Widget) {
+            item.dispose();
+        } else {
+            document.id(item).dispose();
+        }
+        this.update();
     },
-    
     /**
-     * Method: setValue
-     * set the value of the Combo
+     * APIMethod: scrollIntoView
+     * scrolls an item in one of the toolbars into the currently visible
+     * area of the container if it is not already fully visible
      *
      * Parameters:
-     * value - {Object} the new value.  May be a string, a text node, or
-     * another DOM element.
+     * item - the item to scroll.
      */
-    setValue: function(value) {
-        if (this.options.editable) {
-            this.domInput.value = value;
+    scrollIntoView: function(item) {
+        var currentButton = this.scroller.retrieve('buttonPointer');
+        // if (!$defined(currentButton)) return;
+        if (item instanceof Jx.Widget) {
+            item = item.domObj;
+            while (!item.hasClass('jxToolItem')) {
+                item = item.getParent();
+            }
+        }
+        var pos = item.getCoordinates(this.scroller);
+        var scrollerSize = this.scroller.getStyle('width').toInt();
+
+        if (pos.right > 0 && pos.right <= scrollerSize && pos.left > 0 && pos.left <= scrollerSize) {
+           //we are completely on screen 
+            return;
+        };
+
+        if (pos.right > scrollerSize) {
+            //it's right of the scroller
+            var diff = pos.right - scrollerSize;
+
+            //loop through toolbar items until we have enough width to
+            //make the item visible
+            var ml = this.wrapper.getStyle('margin-left').toInt();
+            var w = currentButton.getMarginBoxSize().width;
+            var np;
+            while (w < diff && $defined(currentButton)) {
+                np = this.getNextButton(currentButton);
+                if (np) {
+                    w += np.getMarginBoxSize().width;
+                } else {
+                    break;
+                }
+                currentButton = np;
+            }
+
+            ml -= w;
+
+            if (typeof Fx != 'undefined' && typeof Fx.Tween != 'undefined') {
+                //scroll it
+                this.wrapper.get('tween', {
+                    property: 'margin-left',
+                    onComplete: this.afterTweenRight.bind(this, currentButton)
+                }).start(ml);
+            } else {
+                //set it
+                this.wrapper.setStyle('margin-left', ml);
+                this.afterTweenRight(currentButton);
+            }
         } else {
-            this.setLabel(value);
+            //it's left of the scroller
+            var ml = this.wrapper.getStyle('margin-left').toInt();
+            ml -= pos.left;
+
+            if (typeof Fx != 'undefined' && typeof Fx.Tween != 'undefined') {
+                //scroll it
+                this.wrapper.get('tween', {
+                    property: 'margin-left',
+                    onComplete: this.afterTweenLeft.bind(this, item)
+                }).start(ml);
+            } else {
+                //set it
+                this.wrapper.setStyle('margin-left', ml);
+                this.afterTweenLeft(item);
+            }
         }
+
     },
-    
     /**
-     * Method: getValue
-     * Return the current value
+     * Method: getPreviousButton
+     * Finds the button to the left of the first visible button
      *
-     * Returns:
-     * {Object} returns the currently selected item
+     * Parameters:
+     * currentButton - the first visible button
      */
-    getValue: function() {
-        value = '';
-        if (this.options.editable) {
-            value = this.domInput.value;
-        } else {
-            value = this.getLabel();
+    getPreviousButton: function(currentButton) {
+        pp = currentButton.getPrevious();
+        if (!$defined(pp)) {
+            //check for a new toolbar
+            pp = currentButton.getParent().getPrevious();
+            if (pp) {
+                pp = pp.getLast();
+            }
         }
-        return value;
+        return pp;
+    },
+    /**
+     * Method: getNextButton
+     * Finds the button to the right of the first visible button
+     *
+     * Parameters:
+     * currentButton - the first visible button
+     */
+    getNextButton: function(currentButton) {
+        np = currentButton.getNext();
+        if (!np) {
+            np = currentButton.getParent().getNext();
+            if (np) {
+                np = np.getFirst();
+            }
+        }
+        return np;
     }
-});// $Id: panel.js 429 2009-05-12 16:10:47Z pagameba $
+
+});/*
+---
+
+name: Jx.Toolbar.Item
+
+description: A helper class to provide a container for something to go into a Jx.Toolbar.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Toolbar
+
+provides: [Jx.Toolbar.Item]
+
+...
+ */
+// $Id: toolbar.item.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.Panel
+ * Class: Jx.Toolbar.Item
  *
  * Extends: Object
  *
- * Implements: Options, Events, <Jx.ContentLoader>
+ * Implements: Options
  *
+ * A helper class to provide a container for something to go into
+ * a <Jx.Toolbar>.
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Toolbar.Item = new Class( {
+    Family: 'Jx.Toolbar.Item',
+    Extends: Jx.Widget,
+    options: {
+        /* Option: active
+         * is this item active or not?  Default is true.
+         */
+        active: true,
+        template: '<li class="jxToolItem"></li>'
+    },
+    classes: new Hash({
+        domObj: 'jxToolItem'
+    }),
+
+    parameters: ['jxThing', 'options'],
+
+    /**
+     * APIMethod: render
+     * Create a new instance of Jx.Toolbar.Item.
+     */
+    render: function() {
+        this.parent();
+        var el = document.id(this.options.jxThing);
+        if (el) {
+            this.domObj.adopt(el);
+        }
+    }
+});/*
+---
+
+name: Jx.Panel
+
+description: A panel is a fundamental container object that has a content area and optional toolbars around the content area.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+ - Jx.Menu.Item
+ - Jx.Layout
+ - Jx.Toolbar.Container
+ - Jx.Toolbar.Item
+
+provides: [Jx.Panel]
+
+css:
+ - panel
+
+images:
+ - panel_controls.png
+ - panelbar.png
+
+...
+ */
+// $Id: panel.js 980 2010-09-09 14:02:45Z pagameba $
+/**
+ * Class: Jx.Panel
+ *
+ * Extends: <Jx.Widget>
+ *
  * A panel is a fundamental container object that has a content
  * area and optional toolbars around the content area.  It also
  * has a title bar area that contains an optional label and
@@ -13139,30 +25583,42 @@
  * close - fired when the panel is closed
  * collapse - fired when the panel is collapsed
  * expand - fired when the panel is opened
+ * 
+ * MooTools.lang Keys:
+ * - panel.collapseTooltip
+ * - panel.collapseLabel
+ * - panel.expandlabel
+ * - panel.maximizeTooltip
+ * - panel.maximizeLabel
+ * - panel.restoreTooltip
+ * - panel.restoreLabel
+ * - panel.closeTooltip
+ * - panel.closeLabel
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Panel = new Class({
     Family: 'Jx.Panel',
-    Implements: [Options, Events, Jx.ContentLoader, Jx.Addable],
-    
+    Extends: Jx.Widget,
+
     toolbarContainers: {
         top: null,
         right: null,
         bottom: null,
         left: null
     },
-    
+
      options: {
-        position: 'absolute',
-        type: 'Panel',
-        /* Option: id
-         * String, an id to assign to the panel's container
-         */
-        id: '',
+        position: null,
+        collapsedClass: 'jxPanelMin',
+        collapseClass: 'jxPanelCollapse',
+        menuClass: 'jxPanelMenu',
+        maximizeClass: 'jxPanelMaximize',
+        closeClass: 'jxPanelClose',
+
         /* Option: label
          * String, the title of the Jx Panel
          */
@@ -13178,26 +25634,6 @@
          * to control the state of the panel.
          */
         collapse: true,
-        /* Option: collapseTooltip
-         * the tooltip to display over the collapse button
-         */
-        collapseTooltip: 'Collapse/Expand Panel',
-        /* Option: collapseLabel
-         * the label to use for the collapse menu item
-         */
-        collapseLabel: 'Collapse',
-        /* Option: expandLabel
-         * the label to use for the expand menu item
-         */
-        expandLabel: 'Expand',
-        /* Option: maximizeTooltip
-         * the tooltip to display over the maximize button
-         */
-        maximizeTooltip: 'Maximize Panel',
-        /* Option: maximizeLabel
-         * the label to use for the maximize menu item
-         */
-        maximizeLabel: 'Maximize',
         /* Option: close
          * boolean, determine if the panel can be closed (hidden) by the user.
          * The application needs to provide a way to re-open the panel after
@@ -13206,14 +25642,6 @@
          * the panel.
          */
         close: false,
-        /* Option: closeTooltip
-         * the tooltip to display over the close button
-         */
-        closeTooltip: 'Close Panel',
-        /* Option: closeLabel
-         * the label to use for the close menu item
-         */
-        closeLabel: 'Close',
         /* Option: closed
          * boolean, initial state of the panel (true to start the panel
          *  closed), default is false
@@ -13227,161 +25655,175 @@
          * array of Jx.Toolbar objects to put in the panel.  The position
          * of each toolbar is used to position the toolbar within the panel.
          */
-        toolbars: []
+        toolbars: [],
+        type: 'panel',
+        template: '<div class="jxPanel"><div class="jxPanelTitle"><img class="jxPanelIcon" src="'+Jx.aPixel.src+'" alt="" title=""/><span class="jxPanelLabel"></span><div class="jxPanelControls"></div></div><div class="jxPanelContentContainer"><div class="jxPanelContent"></div></div></div>',
+        controlButtonTemplate: '<a class="jxButtonContainer jxButton"><img class="jxButtonIcon" src="'+Jx.aPixel.src+'"></a>'
     },
-    
-    /** 
-     * Constructor: Jx.Panel
+    classes: new Hash({
+        domObj: 'jxPanel',
+        title: 'jxPanelTitle',
+        domImg: 'jxPanelIcon',
+        domLabel: 'jxPanelLabel',
+        domControls: 'jxPanelControls',
+        contentContainer: 'jxPanelContentContainer',
+        content: 'jxPanelContent'
+    }),
+
+    /**
+     * APIMethod: render
      * Initialize a new Jx.Panel instance
-     *
-     * Options: <Jx.Panel.Options>, <Jx.ContentLoader.Options>
      */
-    initialize : function(options){
-        this.setOptions(options);
-        this.toolbars = options ? options.toolbars || [] : [];
-        
-        if ($defined(this.options.height) && !$defined(options.position)) {
-            this.options.position = 'relative';
+    render : function(){
+        this.parent();
+
+        this.toolbars = this.options ? this.options.toolbars || [] : [];
+
+        this.options.position = ($defined(this.options.height) && !$defined(this.options.position)) ? 'relative' : 'absolute';
+
+        if (this.options.image && this.domImg) {
+            this.domImg.setStyle('backgroundImage', 'url('+this.options.image+')');
         }
+        if (this.options.label && this.domLabel) {
+            this.setLabel(this.options.label);
+        }
 
-        /* set up the title object */
-        this.title = new Element('div', {
-            'class': 'jx'+this.options.type+'Title'
-        });
-        
-        var i = new Element('img', {
-            'class': 'jx'+this.options.type+'Icon',
-            src: Jx.aPixel.src,
-            alt: '',
-            title: ''
-        });
-        if (this.options.image) {
-            i.setStyle('backgroundImage', 'url('+this.options.image+')');
-        }
-        this.title.adopt(i);
-        
-        this.labelObj = new Element('span', {
-            'class': 'jx'+this.options.type+'Label',
-            html: this.options.label
-        });
-        this.title.adopt(this.labelObj);
-        
-        var controls = new Element('div', {
-            'class': 'jx'+this.options.type+'Controls'
-        });
         var tbDiv = new Element('div');
-        controls.adopt(tbDiv);
-        this.toolbar = new Jx.Toolbar({parent:tbDiv});
-        this.title.adopt(controls);
-        
+        this.domControls.adopt(tbDiv);
+        this.toolbar = new Jx.Toolbar({parent:tbDiv, scroll: false});
+
         var that = this;
-        
         if (this.options.menu) {
             this.menu = new Jx.Menu({
                 image: Jx.aPixel.src
+            }, {
+              buttonTemplate: this.options.controlButtonTemplate
             });
-            this.menu.domObj.addClass('jx'+this.options.type+'Menu');
+            this.menu.domObj.addClass(this.options.menuClass);
             this.menu.domObj.addClass('jxButtonContentLeft');
             this.toolbar.add(this.menu);
         }
-        
+
+        //var b, item;
         if (this.options.collapse) {
-            var b = new Jx.Button({
+            if (this.title) {
+              this.title.addEvent('dblclick', function() {
+                that.toggleCollapse();
+              });
+            }
+            this.colB = new Jx.Button({
+                template: this.options.controlButtonTemplate,
                 image: Jx.aPixel.src,
-                tooltip: this.options.collapseTooltip,
+                tooltip: {set:'Jx',key:'panel',value:'collapseTooltip'},
                 onClick: function() {
                     that.toggleCollapse();
                 }
             });
-            b.domObj.addClass('jx'+this.options.type+'Collapse');
-            this.toolbar.add(b);
+            this.colB.domObj.addClass(this.options.collapseClass);
+            this.addEvents({
+                collapse: function() {
+                    this.colB.setTooltip({set:'Jx',key:'panel',value:'expandTooltip'});
+                }.bind(this),
+                expand: function() {
+                    this.colB.setTooltip({set:'Jx',key:'panel',value:'collapseTooltip'});
+                }.bind(this)
+            });
+            this.toolbar.add(this.colB);
             if (this.menu) {
-                var item = new Jx.Menu.Item({
+                this.colM = new Jx.Menu.Item({
                     label: this.options.collapseLabel,
                     onClick: function() { that.toggleCollapse(); }
                 });
+                var item = this.colM
                 this.addEvents({
                     collapse: function() {
-                        item.setLabel(this.options.expandLabel);
-                    },
+                        this.colM.setLabel({set:'Jx',key:'panel',value:'expandLabel'});
+                    }.bind(this),
                     expand: function() {
-                        item.setLabel(this.options.collapseLabel);
-                    }
+                        this.colM.setLabel({set:'Jx',key:'panel',value:'collapseLabel'});
+                    }.bind(this)
                 });
                 this.menu.add(item);
             }
         }
-        
+
         if (this.options.maximize) {
-            var b = new Jx.Button({
+            this.maxB = new Jx.Button({
+                template: this.options.controlButtonTemplate,
                 image: Jx.aPixel.src,
-                tooltip: this.options.maximizeTooltip,
+                tooltip: {set:'Jx',key:'panel',value:'maximizeTooltip'},
                 onClick: function() {
                     that.maximize();
                 }
             });
-            b.domObj.addClass('jx'+this.options.type+'Maximize');
-            this.toolbar.add(b);
+            this.maxB.domObj.addClass(this.options.maximizeClass);
+            this.addEvents({
+                maximize: function() {
+                    this.maxB.setTooltip({set:'Jx',key:'panel',value:'restoreTooltip'});
+                }.bind(this),
+                restore: function() {
+                    this.maxB.setTooltip({set:'Jx',key:'panel',value:'maximizeTooltip'});
+                }.bind(this)
+            });
+            this.toolbar.add(this.maxB);
             if (this.menu) {
-                var item = new Jx.Menu.Item({
+                this.maxM = new Jx.Menu.Item({
                     label: this.options.maximizeLabel,
                     onClick: function() { that.maximize(); }
                 });
-                this.menu.add(item);
+                
+                this.addEvents({
+                    maximize: function() {
+                        this.maxM.setLabel({set:'Jx',key:'panel',value:'maximizeLabel'});
+                    }.bind(this),
+                    restore: function() {
+                        this.maxM.setLabel({set:'Jx',key:'panel',value:'restoreLabel'});
+                    }.bind(this)
+                });
+                this.menu.add(this.maxM);
             }
         }
-        
+
         if (this.options.close) {
-            var b = new Jx.Button({
+            this.closeB = new Jx.Button({
+                template: this.options.controlButtonTemplate,
                 image: Jx.aPixel.src,
-                tooltip: this.options.closeTooltip,
+                tooltip: {set:'Jx',key:'panel',value:'closeTooltip'},
                 onClick: function() {
                     that.close();
                 }
             });
-            b.domObj.addClass('jx'+this.options.type+'Close');
-            this.toolbar.add(b);
+            this.closeB.domObj.addClass(this.options.closeClass);
+            this.toolbar.add(this.closeB);
             if (this.menu) {
-                var item = new Jx.Menu.Item({
-                    label: this.options.closeLabel,
+                this.closeM = new Jx.Menu.Item({
+                    label: {set:'Jx',key:'panel',value:'closeLabel'},
                     onClick: function() {
                         that.close();
                     }
                 });
                 this.menu.add(item);
             }
-            
+
         }
-        
-        this.title.addEvent('dblclick', function() {
-            that.toggleCollapse();
-        });
-        
-        this.domObj = new Element('div', {
-            'class': 'jx'+this.options.type
-        });
+
         if (this.options.id) {
             this.domObj.id = this.options.id;
         }
         var jxl = new Jx.Layout(this.domObj, $merge(this.options, {propagate:false}));
         var layoutHandler = this.layoutContent.bind(this);
         jxl.addEvent('sizeChange', layoutHandler);
-        
-        if (!this.options.hideTitle) {
-            this.domObj.adopt(this.title);
+
+        if (this.options.hideTitle) {
+            this.title.dispose();
         }
-        
-        this.contentContainer = new Element('div', {
-            'class': 'jx'+this.options.type+'ContentContainer'
-        });
-        this.domObj.adopt(this.contentContainer);
-        
-        if ($type(this.options.toolbars) == 'array') {
+
+        if (Jx.type(this.options.toolbars) == 'array') {
             this.options.toolbars.each(function(tb){
                 var position = tb.options.position;
                 var tbc = this.toolbarContainers[position];
                 if (!tbc) {
-                    var tbc = new Element('div');
+                    tbc = new Element('div');
                     new Jx.Layout(tbc);
                     this.contentContainer.adopt(tbc);
                     this.toolbarContainers[position] = tbc;
@@ -13389,19 +25831,16 @@
                 tb.addTo(tbc);
             }, this);
         }
-        
-        this.content = new Element('div', {
-            'class': 'jx'+this.options.type+'Content'
-        });
-        
-        this.contentContainer.adopt(this.content);
+
         new Jx.Layout(this.contentContainer);
         new Jx.Layout(this.content);
-        
-        this.loadContent(this.content);
 
+        if(this.shouldLoadContent()) {
+          this.loadContent(this.content);
+        }
+
         this.toggleCollapse(this.options.closed);
-        
+
         this.addEvent('addTo', function() {
             this.domObj.resize();
         });
@@ -13409,7 +25848,7 @@
             this.addTo(this.options.parent);
         }
     },
-    
+
     /**
      * Method: layoutContent
      * the sizeChange event of the <Jx.Layout> that manages the outer container
@@ -13434,8 +25873,8 @@
             this.contentContainer.setStyle('display','block');
             this.options.closed = false;
             this.contentContainer.resize({
-                top: titleHeight, 
-                height: null, 
+                top: titleHeight,
+                height: null,
                 bottom: 0
             });
             ['left','right'].each(function(position){
@@ -13445,31 +25884,29 @@
             }, this);
             ['top','bottom'].each(function(position){
                 if (this.toolbarContainers[position]) {
-                    this.toolbarContainers[position].style.height = '';                
+                    this.toolbarContainers[position].style.height = '';
                 }
             }, this);
-            if ($type(this.options.toolbars) == 'array') {
+            if (Jx.type(this.options.toolbars) == 'array') {
                 this.options.toolbars.each(function(tb){
+                    tb.update();
                     position = tb.options.position;
                     tbc = this.toolbarContainers[position];
-                    // IE 6 doesn't seem to want to measure the width of 
+                    // IE 6 doesn't seem to want to measure the width of
                     // things correctly
                     if (Browser.Engine.trident4) {
-                        var oldParent = $(tbc.parentNode);
+                        var oldParent = document.id(tbc.parentNode);
                         tbc.style.visibility = 'hidden';
-                        $(document.body).adopt(tbc);                    
+                        document.id(document.body).adopt(tbc);
                     }
                     var size = tbc.getBorderBoxSize();
-                    // put it back into its real parent now we are done 
+                    // put it back into its real parent now we are done
                     // measuring
                     if (Browser.Engine.trident4) {
                         oldParent.adopt(tbc);
                         tbc.style.visibility = '';
                     }
                     switch(position) {
-                        case 'top':
-                            top = size.height;
-                            break;
                         case 'bottom':
                             bottom = size.height;
                             break;
@@ -13479,7 +25916,11 @@
                         case 'right':
                             right = size.width;
                             break;
-                    }                    
+                        case 'top':
+                        default:
+                            top = size.height;
+                            break;
+                    }
                 },this);
             }
             tbc = this.toolbarContainers['top'];
@@ -13505,7 +25946,7 @@
         }
         this.fireEvent('sizeChange', this);
     },
-    
+
     /**
      * Method: setLabel
      * Set the label in the title bar of this panel
@@ -13514,17 +25955,17 @@
      * s - {String} the new label
      */
     setLabel: function(s) {
-        this.labelObj.innerHTML = s;
+        this.domLabel.set('html',this.getText(s));
     },
     /**
      * Method: getLabel
      * Get the label of the title bar of this panel
      *
-     * Returns: 
+     * Returns:
      * {String} the label
      */
     getLabel: function() {
-        return this.labelObj.innerHTML;
+        return this.domLabel.get('html');
     },
     /**
      * Method: finalize
@@ -13580,7 +26021,7 @@
     },
     /**
      * Method: panelContentLoaded
-     * When the content of the panel is loaded from a remote URL, this 
+     * When the content of the panel is loaded from a remote URL, this
      * method is called when the ajax request returns.
      *
      * Parameters:
@@ -13594,29 +26035,15 @@
             window.setTimeout(this.onContentReady.bind(this),1);
         }
     },
+
     /**
-     * Method: setBusy
-     * Set the panel as busy or not busy, which displays a loading image
-     * in the title bar.
-     *
-     * Parameters:
-     * isBusy - {Boolean} the busy state
-     */
-    setBusy : function(isBusy) {
-        this.busyCount += isBusy?1:-1;
-        if (this.loadingObj){
-            this.loadingObj.img.style.visibility = (this.busyCount>0)?'visible':'hidden';
-        }
-    },
-    
-    /**
      * Method: toggleCollapse
      * sets or toggles the collapsed state of the panel.  If a
      * new state is passed, it is used, otherwise the current
-     * state is toggled.    
+     * state is toggled.
      *
      * Parameters:
-     * state - optional, if passed then the state is used, 
+     * state - optional, if passed then the state is used,
      * otherwise the state is toggled.
      */
     toggleCollapse: function(state) {
@@ -13626,11 +26053,13 @@
             this.options.closed = !this.options.closed;
         }
         if (this.options.closed) {
-            if (!this.domObj.hasClass('jx'+this.options.type+'Min')) {
-                this.domObj.addClass('jx'+this.options.type+'Min');
+            if (!this.domObj.hasClass(this.options.collapsedClass)) {
+                this.domObj.addClass(this.options.collapsedClass);
                 this.contentContainer.setStyle('display','none');
-                var margin = this.domObj.getMarginSize();
-                var height = margin.top + margin.bottom;
+                var m = this.domObj.measure(function(){
+                    return this.getSizes(['margin'],['top','bottom']).margin;
+                });
+                var height = m.top + m.bottom;
                 if (this.title.parentNode == this.domObj) {
                     height += this.title.getMarginBoxSize().height;
                 }
@@ -13638,15 +26067,15 @@
                 this.fireEvent('collapse', this);
             }
         } else {
-            if (this.domObj.hasClass('jx'+this.options.type+'Min')) {
-                this.domObj.removeClass('jx'+this.options.type+'Min');
+            if (this.domObj.hasClass(this.options.collapsedClass)) {
+                this.domObj.removeClass(this.options.collapsedClass);
                 this.contentContainer.setStyle('display','block');
-                this.domObj.resize({height: this.options.height});            
+                this.domObj.resize({height: this.options.height});
                 this.fireEvent('expand', this);
             }
         }
     },
-    
+
     /**
      * Method: close
      * Closes the panel (completely hiding it).
@@ -13654,22 +26083,81 @@
     close: function() {
         this.domObj.dispose();
         this.fireEvent('close', this);
+    },
+    
+    changeText: function (lang) {
+    	this.parent();	//TODO: change this class so that we can access these properties without too much voodoo...
+    	if($defined(this.closeB)) {
+    		this.closeB.setTooltip({set:'Jx',key:'panel',value:'closeTooltip'});
+    	}
+    	if ($defined(this.closeM)) {
+    		this.closeM.setLabel({set:'Jx',key:'panel',value:'closeLabel'});
+    	}
+    	if ($defined(this.maxB)) {
+    		this.maxB.setTooltip({set:'Jx',key:'panel',value:'maximizeTooltip'});
+    	}
+    	if ($defined(this.colB)) {
+    		this.colB.setTooltip({set:'Jx',key:'panel',value:'collapseTooltip'});
+    	}
+    	if ($defined(this.colM)) {
+	    	if (this.options.closed == true) {
+	    		this.colM.setLabel({set:'Jx',key:'panel',value:'expandLabel'});
+	    	} else {
+	    		this.colM.setLabel({set:'Jx',key:'panel',value:'collapseLabel'});
+	    	}
+    	}
+      if (this.options.label && this.domLabel) {
+          this.setLabel(this.options.label);
+      }
+      // TODO: is this the right method to call?
+      // if toolbars left/right are used and localized, they may change their size..
+      this.layoutContent();
+    },
+
+    /**
+     * Method to be able to allow loadingOnDemand in subclasses but not here
+     */
+    shouldLoadContent: function() {
+      return true;
     }
-    
-});// $Id: dialog.js 477 2009-07-09 17:41:50Z pagameba $
+});/*
+---
+
+name: Jx.Dialog
+
+description: A Jx.Panel that implements a floating dialog.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Panel
+
+optional:
+ - More/Drag
+
+provides: [Jx.Dialog]
+
+css:
+ - dialog
+
+images:
+ - dialog_chrome.png
+ - dialog_resize.png
+
+...
+ */
+// $Id: dialog.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.Dialog
  *
  * Extends: <Jx.Panel>
  *
- * Implements: <Jx.AutoPosition>, <Jx.Chrome>
- *
  * A Jx.Dialog implements a floating dialog.  Dialogs represent a useful way
  * to present users with certain information or application controls.
  * Jx.Dialog is designed to provide the same types of features as traditional
  * operating system dialog boxes, including:
  *
- * - dialogs may be modal (user must dismiss the dialog to continue) or 
+ * - dialogs may be modal (user must dismiss the dialog to continue) or
  * non-modal
  *
  * - dialogs are movable (user can drag the title bar to move the dialog
@@ -13694,31 +26182,42 @@
  *
  * Extends:
  * Jx.Dialog extends <Jx.Panel>, please go there for more details.
+ * 
+ * MooTools.lang Keys:
+ * - dialog.resizeToolTip
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Dialog = new Class({
     Family: 'Jx.Dialog',
     Extends: Jx.Panel,
-    Implements: [Jx.AutoPosition, Jx.Chrome],
-    
-    /**
-     * Property: {HTMLElement} blanket
-     * modal dialogs prevent interaction with the rest of the application
-     * while they are open, this element is displayed just under the
-     * dialog to prevent the user from clicking anything.
-     */
-    blanket: null,
-    
+
     options: {
         /* Option: modal
          * (optional) {Boolean} controls whether the dialog will be modal
          * or not.  The default is to create modal dialogs.
          */
         modal: true,
+        /** 
+         * Option: maskOptions
+         */
+        maskOptions: {
+          'class':'jxModalMask',
+          maskMargins: true,
+          useIframeShim: true,
+          iframeShimOptions: {
+            className: 'jxIframeShim'
+          }
+        },
+        eventMaskOptions: {
+          'class':'jxEventMask',
+          maskMargins: false,
+          useIframeShim: false,
+          destroyOnHide: true
+        },
         /* just overrides default position of panel, don't document this */
         position: 'absolute',
         /* Option: width
@@ -13727,7 +26226,7 @@
          */
         width: 250,
         /* Option: height
-         * (optional) {Integer} the initial height in pixels of the 
+         * (optional) {Integer} the initial height in pixels of the
          * dialog. The default value is 250 if not specified.
          */
         height: 250,
@@ -13744,99 +26243,164 @@
          */
         vertical: 'center center',
         /* Option: label
-         * (optional) {String} the title of the dialog box.  "New Dialog"
-         * is the default value.
+         * (optional) {String} the title of the dialog box.
          */
-        label: 'New Dialog',
-        /* Option: id
-         * (optional) {String} an HTML ID to assign to the dialog, primarily
-         * used for applying CSS styles to specific dialogs
-         */
-        id: '',
+        label: '',
         /* Option: parent
          * (optional) {HTMLElement} a reference to an HTML element that
          * the dialog is to be contained by.  The default value is for the dialog
          * to be contained by the body element.
          */
-        parent: null,
+        //parent: null,
         /* Option: resize
          * (optional) {Boolean} determines whether the dialog is
          * resizeable by the user or not.  Default is false.
          */
         resize: false,
-        /* Option: resizeTooltip
-         * the tooltip to display for the resize handle, empty by default.
-         */
-        resizeTooltip: '',
+
         /* Option: move
          * (optional) {Boolean} determines whether the dialog is
          * moveable by the user or not.  Default is true.
          */
         move: true,
+        /*
+         * Option: limit
+         * (optional) {Object} || false
+         * passed to the Drag instance of this dialog to limit the movement
+         * {Object} must have x&y coordinates with a range, like {x:[0,500],y:[0,500]}.
+         * Set an id or a reference of a DOM Element (ie 'document', 'myContainerWithId', 
+         * $('myContainer'), $('domID').getParent()) to use these dimensions
+         * as boundaries. Default is false.
+         */
+        limit : false,
         /* Option: close
          * (optional) {Boolean} determines whether the dialog is
          * closeable by the user or not.  Default is true.
          */
-        close: true
+        close: true,
+        /**
+         * Option: useKeyboard
+         * (optional) {Boolean} determines whether the Dialog listens to keyboard events globally
+         * Default is false
+         */
+        useKeyboard : false,
+        /**
+         * Option: keys
+         * (optional) {Object} refers with the syntax for MooTools Keyboard Class
+         * to functions. Set key to false to disable it manually 
+         */
+        keys: {
+          'esc' : 'close'
+        },
+        /**
+         * Option: keyboardMethods
+         *
+         * can be used to overwrite existing keyboard methods that are used inside
+         * this.options.keys - also possible to add new ones.
+         * Functions are bound to the dialog when using 'this'
+         *
+         * example:
+         *  keys : {
+         *    'alt+enter' : 'maximizeDialog'
+         *  },
+         *  keyboardMethods: {
+         *    'maximizeDialog' : function(ev){
+         *      ev.preventDefault();
+         *      this.maximize();
+         *    }
+         *  }
+         */
+        keyboardMethods : {},
+        collapsedClass: 'jxDialogMin',
+        collapseClass: 'jxDialogCollapse',
+        menuClass: 'jxDialogMenu',
+        maximizeClass: 'jxDialogMaximize',
+        closeClass: 'jxDialogClose',
+        type: 'dialog',
+        template: '<div class="jxDialog"><div class="jxDialogTitle"><img class="jxDialogIcon" src="'+Jx.aPixel.src+'" alt="" title=""/><span class="jxDialogLabel"></span><div class="jxDialogControls"></div></div><div class="jxDialogContentContainer"><div class="jxDialogContent"></div></div></div>'
     },
+    classes: new Hash({
+        domObj: 'jxDialog',
+        title: 'jxDialogTitle',
+        domImg: 'jxDialogIcon',
+        domLabel: 'jxDialogLabel',
+        domControls: 'jxDialogControls',
+        contentContainer: 'jxDialogContentContainer',
+        content: 'jxDialogContent'
+    }),
     /**
-     * Constructor: Jx.Dialog
-     * Construct a new instance of Jx.Dialog
-     *
-     * Parameters: 
-     * options - {Object} an object containing options for the dialog.
-     *
-     * Options: <Jx.Dialog.Options>, <Jx.Panel.Options>, <Jx.ContentLoader.Options>
+     * MooTools Keyboard class for Events (mostly used in Dialog.Confirm, Prompt or Message)
+     * But also optional here with esc to close
      */
-    initialize: function(options) {
+    keyboard : null,
+    /**
+     * APIMethod: render
+     * renders Jx.Dialog
+     */
+    render: function() {
         this.isOpening = false;
         this.firstShow = true;
-        
-        /* initialize the panel overriding the type and position */
-        this.parent($merge(
+
+        this.options = $merge(
             {parent:document.body}, // these are defaults that can be overridden
-            options,
-            {type:'Dialog', position: 'absolute'} // these override anything passed to the options
-        ));
-        
-        this.options.parent = $(this.options.parent);
-        
-        if (this.options.modal) {
-            this.blanket = new Element('div',{
-                'class':'jxDialogModal',
-                styles:{
-                    display:'none',
-                    zIndex: -1
-                }
-            });
-            this.blanket.resize = (function() {
-                var ss = $(document.body).getScrollSize();
-                this.setStyles({
-                    width: ss.x,
-                    height: ss.y
-                });
-            }).bind(this.blanket);
-            this.options.parent.adopt(this.blanket);
-            window.addEvent('resize', this.blanket.resize);
-            
-        }
+            this.options,
+            {position: 'absolute'} // these override anything passed to the options
+        );
 
+        /* initialize the panel overriding the type and position */
+        this.parent();
+        this.openOnLoaded = this.open.bind(this);
+        this.options.parent = document.id(this.options.parent);
+
         this.domObj.setStyle('display','none');
         this.options.parent.adopt(this.domObj);
-        
+
         /* the dialog is moveable by its title bar */
         if (this.options.move && typeof Drag != 'undefined') {
             this.title.addClass('jxDialogMoveable');
+
+            this.options.limit = this.setDragLimit(this.options.limit);
+            // local reference to use Drag instance variables inside onDrag()
+            var self = this;
+            // COMMENT: any reason why the drag instance isn't referenced to the dialog?
             new Drag(this.domObj, {
                 handle: this.title,
+                limit: this.options.limit,
                 onBeforeStart: (function(){
-                    Jx.Dialog.orderDialogs(this);
+                    this.stack();
                 }).bind(this),
-                onStart: (function() {
-                    this.contentContainer.setStyle('visibility','hidden');
-                    this.chrome.addClass('jxChromeDrag');
-                }).bind(this),
+                onStart: function() {
+                    if (!self.options.modal && self.options.parent.mask) {
+                      self.options.parent.mask(self.options.eventMaskOptions);
+                    }
+                    self.contentContainer.setStyle('visibility','hidden');
+                    self.chrome.addClass('jxChromeDrag');
+                    if(self.options.limit) {
+                      var coords = self.options.limitOrig.getCoordinates();
+                      for(var i in coords) {
+                        window.console ? console.log(i, coords[i]) : false;
+                      }
+                      this.options.limit = self.setDragLimit(self.options.limitOrig);
+                    }
+                }, // COMMENT: removed bind(this) for setting the limit to the drag instance
+                onDrag: function() {
+                  if(this.options.limit) {
+                    // find out if the right border of the dragged element is out of range
+                    if(this.value.now.x+self.options.width >= this.options.limit.x[1]) {
+                      this.value.now.x = this.options.limit.x[1] - self.options.width;
+                      this.element.setStyle('left',this.value.now.x);
+                    }
+                    // find out if the bottom border of the dragged element is out of range
+                    if(this.value.now.y+self.options.height >= this.options.limit.y[1]) {
+                      this.value.now.y = this.options.limit.y[1] - self.options.height;
+                      this.element.setStyle('top',this.value.now.y);
+                    }
+                  }
+                },
                 onComplete: (function() {
+                    if (!this.options.modal && this.options.parent.unmask) {
+                      this.options.parent.unmask();
+                    }
                     this.chrome.removeClass('jxChromeDrag');
                     this.contentContainer.setStyle('visibility','');
                     var left = Math.max(this.chromeOffsets.left, parseInt(this.domObj.style.left,10));
@@ -13847,24 +26411,24 @@
                     this.options.left = parseInt(this.domObj.style.left,10);
                     this.options.top = parseInt(this.domObj.style.top,10);
                     if (!this.options.closed) {
-                        this.domObj.resize(this.options);                        
+                        this.domObj.resize(this.options);
                     }
                 }).bind(this)
-            });            
+            });
         }
-        
+
         /* the dialog is resizeable */
         if (this.options.resize && typeof Drag != 'undefined') {
             this.resizeHandle = new Element('div', {
                 'class':'jxDialogResize',
-                title: this.options.resizeTooltip,
+                title: this.getText({set:'Jx',key:'panel',value:'resizeTooltip'}),
                 styles: {
                     'display':this.options.closed?'none':'block'
                 }
             });
             this.domObj.appendChild(this.resizeHandle);
 
-            this.resizeHandleSize = this.resizeHandle.getSize(); 
+            this.resizeHandleSize = this.resizeHandle.getSize();
             this.resizeHandle.setStyles({
                 bottom: this.resizeHandleSize.height,
                 right: this.resizeHandleSize.width
@@ -13872,6 +26436,9 @@
             this.domObj.makeResizable({
                 handle:this.resizeHandle,
                 onStart: (function() {
+                    if (!this.options.modal && this.options.parent.mask) {
+                      this.options.parent.mask(this.options.eventMaskOptions);
+                    }
                     this.contentContainer.setStyle('visibility','hidden');
                     this.chrome.addClass('jxChromeDrag');
                 }).bind(this),
@@ -13879,6 +26446,9 @@
                     this.resizeChrome(this.domObj);
                 }).bind(this),
                 onComplete: (function() {
+                    if (!this.options.modal && this.options.parent.unmask) {
+                      this.options.parent.unmask();
+                    }
                     this.chrome.removeClass('jxChromeDrag');
                     var size = this.domObj.getMarginBoxSize();
                     this.options.width = size.width;
@@ -13888,16 +26458,19 @@
                     this.contentContainer.setStyle('visibility','');
                     this.fireEvent('resize');
                     this.resizeChrome(this.domObj);
-                    
+
                 }).bind(this)
             });
         }
         /* this adjusts the zIndex of the dialogs when activated */
         this.domObj.addEvent('mousedown', (function(){
-            Jx.Dialog.orderDialogs(this);
+            this.stack();
         }).bind(this));
+
+        // initialize keyboard class
+        this.initializeKeyboard();
     },
-    
+
     /**
      * Method: resize
      * resize the dialog.  This can be called when the dialog is closed
@@ -13919,13 +26492,13 @@
             this.fireEvent('resize');
             this.resizeChrome(this.domObj);
             if (autoPosition) {
-                this.position(this.domObj, this.options.parent, this.options);                
+                this.position(this.domObj, this.options.parent, this.options);
             }
         } else {
             this.firstShow = false;
         }
     },
-    
+
     /**
      * Method: sizeChanged
      * overload panel's sizeChanged method
@@ -13935,15 +26508,15 @@
             this.layoutContent();
         }
     },
-    
+
     /**
      * Method: toggleCollapse
      * sets or toggles the collapsed state of the panel.  If a
      * new state is passed, it is used, otherwise the current
-     * state is toggled.    
+     * state is toggled.
      *
      * Parameters:
-     * state - optional, if passed then the state is used, 
+     * state - optional, if passed then the state is used,
      * otherwise the state is toggled.
      */
     toggleCollapse: function(state) {
@@ -13953,27 +26526,29 @@
             this.options.closed = !this.options.closed;
         }
         if (this.options.closed) {
-            if (!this.domObj.hasClass('jx'+this.options.type+'Min')) {
-                this.domObj.addClass('jx'+this.options.type+'Min');
+            if (!this.domObj.hasClass(this.options.collapsedClass)) {
+                this.domObj.addClass(this.options.collapsedClass);
             }
             this.contentContainer.setStyle('display','none');
             if (this.resizeHandle) {
                 this.resizeHandle.setStyle('display','none');
             }
         } else {
-            if (this.domObj.hasClass('jx'+this.options.type+'Min')) {
-                this.domObj.removeClass('jx'+this.options.type+'Min');
+            if (this.domObj.hasClass(this.options.collapsedClass)) {
+                this.domObj.removeClass(this.options.collapsedClass);
             }
             this.contentContainer.setStyle('display','block');
             if (this.resizeHandle) {
                 this.resizeHandle.setStyle('display','block');
             }
         }
-        
+
         if (this.options.closed) {
-            var margin = this.domObj.getMarginSize();
+            var m = this.domObj.measure(function(){
+                return this.getSizes(['margin'],['top','bottom']).margin;
+            });
             var size = this.title.getMarginBoxSize();
-            this.domObj.resize({height: margin.top + size.height + margin.bottom});
+            this.domObj.resize({height: m.top + size.height + m.bottom});
             this.fireEvent('collapse');
         } else {
             this.domObj.resize(this.options);
@@ -13983,6 +26558,60 @@
     },
     
     /**
+     * Method: maximize
+     * Called when the maximize button of a dialog is clicked. It will maximize
+     * the dialog to match the size of its parent.
+     */
+    maximize: function () {
+        
+        if (!this.maximized) {
+            //get size of parent
+            var p = this.options.parent;
+            var size;
+            
+            if (p === document.body) {
+                size = Jx.getPageDimensions();
+            } else {
+                size = p.getBorderBoxSize();
+            }
+            this.previousSettings = {
+                width: this.options.width,
+                height: this.options.height,
+                horizontal: this.options.horizontal,
+                vertical: this.options.vertical,
+                left: this.options.left,
+                right: this.options.right,
+                top: this.options.top,
+                bottom: this.options.bottom
+            };
+            this.options.width = size.width;
+            this.options.height = size.height;
+            this.options.vertical = '0 top';
+            this.options.horizontal = '0 left';
+            this.options.right = 0;
+            this.options.left = 0;
+            this.options.top = 0;
+            this.options.bottom = 0;
+            this.domObj.resize(this.options);
+            this.fireEvent('resize');
+            this.resizeChrome(this.domObj);
+            this.maximized = true;
+            this.domObj.addClass('jxDialogMaximized');
+            this.fireEvent('maximize');
+        } else {
+            this.options = $merge(this.options, this.previousSettings);
+            this.domObj.resize(this.options);
+            this.fireEvent('resize');
+            this.resizeChrome(this.domObj);
+            this.maximized = false;
+            if (this.domObj.hasClass('jxDialogMaximized')) {
+                this.domObj.removeClass('jxDialogMaximized');
+            }
+            this.fireEvent('restore');
+        }
+    },
+
+    /**
      * Method: show
      * show the dialog, external code should use the <Jx.Dialog::open> method
      * to make the dialog visible.
@@ -13993,28 +26622,31 @@
             'display': 'block',
             'visibility': 'hidden'
         });
+        this.toolbar.update();
         
-        if (this.blanket) {
-            this.blanket.resize();            
+        /* do the modal thing */
+        if (this.options.modal && this.options.parent.mask) {
+          var opts = $merge(this.options.maskOptions || {}, {
+            style: {
+              'z-index': Jx.getNumber(this.domObj.getStyle('z-index')) - 1
+            }
+          });
+          this.options.parent.mask(opts);
+          Jx.Stack.stack(this.options.parent.get('mask').element);
         }
+        /* stack the dialog */
+        this.stack();
 
-        Jx.Dialog.orderDialogs(this);
-        
-        /* do the modal thing */
-        if (this.blanket) {
-            this.blanket.setStyles({
-                visibility: 'visible',
-                display: 'block'
+        if (this.options.closed) {
+            var m = this.domObj.measure(function(){
+                return this.getSizes(['margin'],['top','bottom']).margin;
             });
-        }
-        
-        if (this.options.closed) {
-            var margin = this.domObj.getMarginSize();
             var size = this.title.getMarginBoxSize();
-            this.domObj.resize({height: margin.top + size.height + margin.bottom});
+            this.domObj.resize({height: m.top + size.height + m.bottom});
         } else {
-            this.domObj.resize(this.options);            
+            this.domObj.resize(this.options);
         }
+        
         if (this.firstShow) {
             this.contentContainer.resize({forceResize: true});
             this.layoutContent();
@@ -14032,7 +26664,7 @@
         this.showChrome(this.domObj);
         /* put it in the right place using auto-positioning */
         this.position(this.domObj, this.options.parent, this.options);
-        this.domObj.setStyle('visibility', '');
+        this.domObj.setStyle('visibility', 'visible');
     },
     /**
      * Method: hide
@@ -14040,14 +26672,15 @@
      * method to hide the dialog.
      */
     hide : function() {
-        Jx.Dialog.Stack.erase(this);
-        Jx.Dialog.ZIndex--;
         this.domObj.setStyle('display','none');
-        if (this.blanket) {
-            this.blanket.setStyle('visibility', 'hidden');
-            Jx.Dialog.ZIndex--;
+        this.unstack();
+        if (this.options.modal && this.options.parent.unmask) {
+          Jx.Stack.unstack(this.options.parent.get('mask').element);
+          this.options.parent.unmask();
         }
-        
+        if(this.options.useKeyboard && this.keyboard != null) {
+          this.keyboard.deactivate();
+        }
     },
     /**
      * Method: openURL
@@ -14060,14 +26693,18 @@
     openURL: function(url) {
         if (url) {
             this.options.contentURL = url;
+            this.options.content = null;  //force Url loading
+            this.setBusy();
             this.loadContent(this.content);
+            this.addEvent('contentLoaded', this.openOnLoaded);
+        } else {
+            this.open();
         }
-        this.open();
     },
-    
+
     /**
      * Method: open
-     * open the dialog.  This may be delayed depending on the 
+     * open the dialog.  This may be delayed depending on the
      * asynchronous loading of dialog content.  The onOpen
      * callback function is called when the dialog actually
      * opens
@@ -14076,13 +26713,22 @@
         if (!this.isOpening) {
             this.isOpening = true;
         }
+        // COMMENT: this works only for onDemand -> NOT for cacheContent = false..
+        // for this loading an URL everytime, use this.openURL(url) 
+        if(!this.contentIsLoaded && this.options.loadOnDemand) {
+          this.loadContent(this.content);
+        }
         if (this.contentIsLoaded) {
+            this.removeEvent('contentLoaded', this.openOnLoaded);
             this.show();
             this.fireEvent('open', this);
             this.isOpening = false;
         } else {
-            this.addEvent('contentLoaded', this.open.bind(this));
+            this.addEvent('contentLoaded', this.openOnLoaded);
         }
+        if(this.options.useKeyboard && this.keyboard != null) {
+          this.keyboard.activate();
+        }
     },
     /**
      * Method: close
@@ -14093,37 +26739,155 @@
         this.isOpening = false;
         this.hide();
         this.fireEvent('close');
+    },
+
+    cleanup: function() { },
+    
+    /**
+     * APIMethod: isOpen
+     * returns true if the dialog is currently open, false otherwise
+     */
+    isOpen: function () {
+        //check to see if we're visible
+        return !((this.domObj.getStyle('display') === 'none') || (this.domObj.getStyle('visibility') === 'hidden'));
+    },
+    
+    changeText: function (lang) {
+    	this.parent();
+    	if ($defined(this.maxM)) {
+			if (this.maximize) {
+				this.maxM.setLabel(this.getText({set:'Jx',key:'panel',value:'restoreLabel'}));
+	    	} else {
+	    		this.maxM.setLabel(this.getText({set:'Jx',key:'panel',value:'maximizeLabel'}));
+	    	}
+    	}
+    	if ($defined(this.resizeHandle)) {
+    		this.resizeHandle.set('title', this.getText({set:'Jx',key:'dialog',value:'resizeTooltip'}));
+    	}
+      this.toggleCollapse(false);
+    },
+
+    initializeKeyboard: function() {
+      if(this.options.useKeyboard) {
+        var self = this;
+        this.keyboardEvents = {};
+        this.keyboardMethods = {
+          close : function(ev) {ev.preventDefault();self.close()}
+        }
+        this.keyboard = new Keyboard({
+          events: this.getKeyboardEvents()
+        });
+      }
+    },
+
+    /**
+     * Method: getKeyboardMethods
+     * used by this and all child classes to have methods listen to keyboard events,
+     * returned object will be parsed to the events object of a MooTools Keyboard instance
+     *
+     * @return Object
+     */
+    getKeyboardEvents : function() {
+      var self = this;
+      for(var i in this.options.keys) {
+        // only add a reference once, otherwise keyboard events will be fired twice in subclasses
+        if(!$defined(this.keyboardEvents[i])) {
+          if($defined(this.keyboardMethods[this.options.keys[i]])) {
+            this.keyboardEvents[i] = this.keyboardMethods[this.options.keys[i]];
+          }else if($defined(this.options.keyboardMethods[this.options.keys[i]])){
+            this.keyboardEvents[i] = this.options.keyboardMethods[this.options.keys[i]].bind(self);
+          }else if(Jx.type(this.options.keys[i]) == 'function') {
+            this.keyboardEvents[i] = this.options.keys[i].bind(self);
+          }else{
+            // allow disabling of special keys by setting them to false or null with having a warning
+            if(this.options.keyboardMethods[this.options.keys[i]] != false) {
+              $defined(console) ? console.warn("keyboard method %o not defined for %o", this.options.keys[i], this) : false;
+            }
+          }
+        }
+      }
+      return this.keyboardEvents;
+    },
+
+    /**
+     * Method: setDragLimit
+     * calculates the drag-dimensions of an given element to drag
+     *
+     * Parameters:
+     * - reference {Object} (optional) the element|elementId|object to set the limits
+     */
+    setDragLimit : function(reference) {
+      if($defined(reference)) this.options.limit = reference;
+      
+      // check drag limit if it is an container or string for an element and use dimensions
+      var limitType = this.options.limit != null ? Jx.type(this.options.limit) : false;
+      if(this.options.limit && limitType != 'object') {
+        var coords = false;
+        switch(limitType) {
+          case 'string':
+            if(document.id(this.options.limit)) {
+              coords = document.id(this.options.limit).getCoordinates();
+            }
+            break;
+          case 'element':
+          case 'document':
+          case 'window':
+            coords = this.options.limit.getCoordinates();
+            break;
+        }
+        if(coords) {
+          this.options.limitOrig = this.options.limit;
+          this.options.limit = {
+            x : [coords.left, coords.right],
+            y : [coords.top, coords.bottom]
+          }
+        }else{
+          this.options.limit = false;
+        }
+      }
+      return this.options.limit;
+    },
+
+    /**
+     * gets called by parent class Jx.Panel and decides whether to load content or not
+     */
+    shouldLoadContent: function() {
+      return !this.options.loadOnDemand;
     }
 });
 
-Jx.Dialog.Stack = [];
-Jx.Dialog.BaseZIndex = null;
-Jx.Dialog.orderDialogs = function(d) {
-    Jx.Dialog.Stack.erase(d).push(d);
-    if (Jx.Dialog.BaseZIndex === null) {
-        Jx.Dialog.BaseZIndex = Math.max(Jx.Dialog.Stack[0].domObj.getStyle('zIndex').toInt(), 1);
-    }
-    Jx.Dialog.Stack.each(function(d, i) {
-        var z = Jx.Dialog.BaseZIndex+(i*2);
-        if (d.blanket) {
-            d.blanket.setStyle('zIndex',z-1);
-        }
-        d.domObj.setStyle('zIndex',z);
-    });
-    
-};
-// $Id: splitter.js 424 2009-05-12 12:51:44Z pagameba $
+/*
+---
+
+name: Jx.Splitter
+
+description: A Jx.Splitter creates two or more containers within a parent container and provides user control over the size of the containers.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Layout
+
+optional:
+ - More/Drag
+
+provides: [Jx.Splitter]
+
+css:
+ - splitter
+
+...
+ */
+// $Id: splitter.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.Splitter
  *
- * Extends: Object
+ * Extends: <Jx.Object>
  *
- * Implements: Options
- *
  * a Jx.Splitter creates two or more containers within a parent container
  * and provides user control over the size of the containers.  The split
  * can be made horizontally or vertically.
- * 
+ *
  * A horizontal split creates containers that divide the space horizontally
  * with vertical bars between the containers.  A vertical split divides
  * the space vertically and creates horizontal bars between the containers.
@@ -14131,16 +26895,19 @@
  * Example:
  * (code)
  * (end)
+ * 
+ * MooTools.lang Keys:
+ * - splitter.barToolTip
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
- 
+
 Jx.Splitter = new Class({
     Family: 'Jx.Splitter',
-    Implements: [Options],
+    Extends: Jx.Object,
     /**
      * Property: domObj
      * {HTMLElement} the element being split
@@ -14148,7 +26915,7 @@
     domObj: null,
     /**
      * Property: elements
-     * {Array} an array of elements that are displayed in each of the split 
+     * {Array} an array of elements that are displayed in each of the split
      * areas
      */
     elements: null,
@@ -14210,11 +26977,6 @@
          * elements open or closed.
          */
         snaps: [],
-        /* Option: barTooltip
-         * the tooltip to display when the mouse hovers over a split bar, 
-         * used for i18n.
-         */
-        barTooltip: 'drag this bar to resize',
         /* Option: onStart
          * an optional function to call when a bar starts dragging
          */
@@ -14224,42 +26986,39 @@
          */
         onFinish: null
     },
+
+    parameters: ['domObj','options'],
+
     /**
-     * Constructor: Jx.Splitter
+     * APIMethod: init
      * Create a new instance of Jx.Splitter
-     *
-     * Parameters:
-     * domObj - {HTMLElement} the element or id of the element to split
-     * options - <Jx.Splitter.Options>
      */
-    initialize: function(domObj, options) {
-        this.setOptions(options);  
-        
-        this.domObj = $(domObj);
+    init: function() {
+        this.domObj = document.id(this.options.domObj);
         this.domObj.addClass('jxSplitContainer');
         var jxLayout = this.domObj.retrieve('jxLayout');
         if (jxLayout) {
             jxLayout.addEvent('sizeChange', this.sizeChanged.bind(this));
         }
-       
+
         this.elements = [];
         this.bars = [];
-        
+        var i;
         var nSplits = 2;
         if (this.options.useChildren) {
             this.elements = this.domObj.getChildren();
             nSplits = this.elements.length;
         } else {
-            nSplits = this.options.elements ? 
-                            this.options.elements.length : 
+            nSplits = this.options.elements ?
+                            this.options.elements.length :
                             this.options.splitInto;
-            for (var i=0; i<nSplits; i++) {
+            for (i=0; i<nSplits; i++) {
                 var el;
                 if (this.options.elements && this.options.elements[i]) {
-                    if (options.elements[i].domObj) {
-                        el = options.elements[i].domObj;
+                    if (this.options.elements[i].domObj) {
+                        el = this.options.elements[i].domObj;
                     } else {
-                        el = $(this.options.elements[i]);                        
+                        el = document.id(this.options.elements[i]);
                     }
                     if (!el) {
                         el = this.prepareElement();
@@ -14273,21 +27032,26 @@
             }
         }
         this.elements.each(function(el) { el.addClass('jxSplitArea'); });
-        for (var i=0; i<nSplits; i++) {
+        for (i=0; i<nSplits; i++) {
             var jxl = this.elements[i].retrieve('jxLayout');
             if (!jxl) {
                 new Jx.Layout(this.elements[i], this.options.containerOptions[i]);
             } else {
-                jxl.resize({position: 'absolute'});
+                if (this.options.containerOptions[i]) {
+                    jxl.resize($merge(this.options.containerOptions[i],
+                        {position:'absolute'}));
+                } else {
+                    jxl.resize({position: 'absolute'});
+                }
             }
         }
-        
-        for (var i=1; i<nSplits; i++) {
+
+        for (i=1; i<nSplits; i++) {
             var bar;
             if (this.options.prepareBar) {
-                bar = this.options.prepareBar(i-1);                
+                bar = this.options.prepareBar(i-1);
             } else {
-                bar = this.prepareBar();                
+                bar = this.prepareBar();
             }
             bar.store('splitterObj', this);
             bar.store('leftSide',this.elements[i-1]);
@@ -14297,13 +27061,13 @@
             this.domObj.adopt(bar);
             this.bars[i-1] = bar;
         }
-        
+
         //making dragging dependent on mootools Drag class
         if ($defined(Drag)) {
-        	this.establishConstraints();
+            this.establishConstraints();
         }
-        
-        for (var i=0; i<this.options.barOptions.length; i++) {
+
+        for (i=0; i<this.options.barOptions.length; i++) {
             if (!this.bars[i]) {
                 continue;
             }
@@ -14319,24 +27083,24 @@
                 var snapEvents;
                 if (opt.snapElement) {
                     snap = opt.snapElement;
-                    snapEvents = opt.snapEvents || ['click', 'dblclick'];                    
+                    snapEvents = opt.snapEvents || ['click', 'dblclick'];
                 } else {
                     snap = this.bars[i];
                     snapEvents = opt.snapEvents || ['dblclick'];
                 }
                 if (!snap.parentNode) {
-                    this.bars[i].adopt(snap);             
+                    this.bars[i].adopt(snap);
                 }
                 new Jx.Splitter.Snap(snap, element, this, snapEvents);
             }
         }
-        
-        for (var i=0; i<this.options.snaps.length; i++) {
+
+        for (i=0; i<this.options.snaps.length; i++) {
             if (this.options.snaps[i]) {
                 new Jx.Splitter.Snap(this.options.snaps[i], this.elements[i], this);
             }
         }
-        
+
         this.sizeChanged();
     },
     /**
@@ -14350,7 +27114,7 @@
         var o = new Element('div', {styles:{position:'absolute'}});
         return o;
     },
-    
+
     /**
      * Method: prepareBar
      * Prepare a new, empty bar to go into between split areas.
@@ -14361,11 +27125,11 @@
     prepareBar: function() {
         var o = new Element('div', {
             'class': 'jxSplitBar'+this.options.layout.capitalize(),
-            'title': this.options.barTitle
+            'title': this.getText({set:'Jx',key:'splitter',value:'barToolTip'})
         });
         return o;
     },
-    
+
     /**
      * Method: establishConstraints
      * Setup the initial set of constraints that set the behaviour of the
@@ -14387,9 +27151,17 @@
                 new Drag(bar, {
                     //limit: limit,
                     modifiers: modifiers,
-                    onSnap : function(obj) {
+                    onSnap : (function(obj) {
                         obj.addClass('jxSplitBarDrag');
-                    },
+                        this.fireEvent('snap',[obj]);
+                    }).bind(this),
+                    onCancel: (function(obj){
+                        mask.destroy();
+                        this.fireEvent('cancel',[obj]);
+                    }).bind(this),
+                    onDrag: (function(obj, event){
+                        this.fireEvent('drag',[obj,event]);
+                    }).bind(this),
                     onComplete : (function(obj) {
                         mask.destroy();
                         obj.removeClass('jxSplitBarDrag');
@@ -14397,23 +27169,21 @@
                             return;
                         }
                         fn.apply(this,[obj]);
+                        this.fireEvent('complete',[obj]);
+                        this.fireEvent('finish',[obj]);
                     }).bind(this),
-                    onStart: (function(obj) {
+                    onBeforeStart: (function(obj) {
+                        this.fireEvent('beforeStart',[obj]);
                         mask = new Element('div',{'class':'jxSplitterMask'}).inject(obj, 'after');
-                        if (this.options.onStart) {
-                            this.options.onStart();
-                        }
                     }).bind(this),
-                    onFinish: (function() {
-                        if (this.options.onFinish) {
-                            this.options.onFinish();
-                        }
+                    onStart: (function(obj, event) {
+                        this.fireEvent('start',[obj, event]);
                     }).bind(this)
                 });
-            }, this);            
+            }, this);
         }
     },
-    
+
     /**
      * Method: dragHorizontal
      * In a horizontally split container, handle a bar being dragged left or
@@ -14423,26 +27193,29 @@
      * obj - {HTMLElement} the bar that was dragged
      */
     dragHorizontal: function(obj) {
-        var leftEdge = parseInt(obj.style.left);
+        var leftEdge = parseInt(obj.style.left,10);
         var leftSide = obj.retrieve('leftSide');
         var rightSide = obj.retrieve('rightSide');
         var leftJxl = leftSide.retrieve('jxLayout');
         var rightJxl = rightSide.retrieve('jxLayout');
-        
-        var paddingLeft = this.domObj.getPaddingSize().left;
-        
+
+        var paddingLeft = this.domObj.measure(function(){
+            var m = this.getSizes(['padding'], ['left']);
+            return m.padding.left;
+        });
+
         /* process right side first */
         var rsLeft, rsWidth, rsRight;
-        
+
         var size = obj.retrieve('size');
         if (!size) {
             size = obj.getBorderBoxSize();
             obj.store('size',size);
         }
         rsLeft = leftEdge + size.width - paddingLeft;
-        
+
         var parentSize = this.domObj.getContentBoxSize();
-        
+
         if (rightJxl.options.width != null) {
             rsWidth = rightJxl.options.width + rightJxl.options.left - rsLeft;
             rsRight = parentSize.width - rsLeft - rsWidth;
@@ -14450,27 +27223,27 @@
             rsWidth = parentSize.width - rightJxl.options.right - rsLeft;
             rsRight = rightJxl.options.right;
         }
-        
+
         /* enforce constraints on right side */
         if (rsWidth < 0) {
             rsWidth = 0;
         }
-        
+
         if (rsWidth < rightJxl.options.minWidth) {
             rsWidth = rightJxl.options.minWidth;
         }
         if (rightJxl.options.maxWidth >= 0 && rsWidth > rightJxl.options.maxWidth) {
             rsWidth = rightJxl.options.maxWidth;
         }
-                
+
         rsLeft = parentSize.width - rsRight - rsWidth;
         leftEdge = rsLeft - size.width;
-        
+
         /* process left side */
         var lsLeft, lsWidth;
         lsLeft = leftJxl.options.left;
         lsWidth = leftEdge - lsLeft;
-        
+
         /* enforce constraints on left */
         if (lsWidth < 0) {
             lsWidth = 0;
@@ -14478,11 +27251,11 @@
         if (lsWidth < leftJxl.options.minWidth) {
             lsWidth = leftJxl.options.minWidth;
         }
-        if (leftJxl.options.maxWidth >= 0 && 
+        if (leftJxl.options.maxWidth >= 0 &&
             lsWidth > leftJxl.options.maxWidth) {
             lsWidth = leftJxl.options.maxWidth;
         }
-        
+
         /* update the leftEdge to accomodate constraints */
         if (lsLeft + lsWidth != leftEdge) {
             /* need to update right side, ignoring constraints because left side
@@ -14491,20 +27264,20 @@
             leftEdge = lsLeft + lsWidth;
             var delta = leftEdge + size.width - rsLeft;
             rsLeft += delta;
-            rsWidth -= delta; 
+            rsWidth -= delta;
         }
-        
+
         /* put bar in its final location based on constraints */
         obj.style.left = paddingLeft + leftEdge + 'px';
-        
+
         /* update leftSide positions */
         if (leftJxl.options.width == null) {
-            var parentSize = this.domObj.getContentBoxSize();
+            parentSize = this.domObj.getContentBoxSize();
             leftSide.resize({right: parentSize.width - lsLeft-lsWidth});
         } else {
             leftSide.resize({width: lsWidth});
         }
-        
+
         /* update rightSide position */
         if (rightJxl.options.width == null) {
             rightSide.resize({left:rsLeft});
@@ -14512,7 +27285,7 @@
             rightSide.resize({left: rsLeft, width: rsWidth});
         }
     },
-    
+
     /**
      * Method: dragVertical
      * In a vertically split container, handle a bar being dragged up or
@@ -14523,16 +27296,20 @@
      */
     dragVertical: function(obj) {
         /* top edge of the bar */
-        var topEdge = parseInt(obj.style.top);
-        
+        var topEdge = parseInt(obj.style.top,10);
+
         /* the containers on either side of the bar */
         var topSide = obj.retrieve('leftSide');
         var bottomSide = obj.retrieve('rightSide');
         var topJxl = topSide.retrieve('jxLayout');
         var bottomJxl = bottomSide.retrieve('jxLayout');
-        
-        var paddingTop = this.domObj.getPaddingSize().top;
-        
+
+        var paddingTop = this.domObj.measure(function(){
+            var m = this.getSizes(['padding'], ['top']);
+            return m.padding.top;
+        });
+
+
         /* measure the bar and parent container for later use */
         var size = obj.retrieve('size');
         if (!size) {
@@ -14543,10 +27320,10 @@
 
         /* process top side first */
         var bsTop, bsHeight, bsBottom;
-        
+
         /* top edge of bottom side is the top edge of bar plus the height of the bar */
         bsTop = topEdge + size.height - paddingTop;
-        
+
         if (bottomJxl.options.height != null) {
             /* bottom side height is fixed */
             bsHeight = bottomJxl.options.height + bottomJxl.options.top - bsTop;
@@ -14556,31 +27333,31 @@
             bsHeight = parentSize.height - bottomJxl.options.bottom - bsTop;
             bsBottom = bottomJxl.options.bottom;
         }
-        
+
         /* enforce constraints on bottom side */
         if (bsHeight < 0) {
             bsHeight = 0;
         }
-        
+
         if (bsHeight < bottomJxl.options.minHeight) {
             bsHeight = bottomJxl.options.minHeight;
         }
-        
+
         if (bottomJxl.options.maxHeight >= 0 && bsHeight > bottomJxl.options.maxHeight) {
             bsHeight = bottomJxl.options.maxHeight;
         }
-        
+
         /* recalculate the top of the bottom side in case it changed
            due to a constraint.  The bar may have moved also.
          */
         bsTop = parentSize.height - bsBottom - bsHeight;
         topEdge = bsTop - size.height;
-                
+
         /* process left side */
         var tsTop, tsHeight;
         tsTop = topJxl.options.top;
         tsHeight = topEdge - tsTop;
-                        
+
         /* enforce constraints on left */
         if (tsHeight < 0) {
             tsHeight = 0;
@@ -14588,11 +27365,11 @@
         if (tsHeight < topJxl.options.minHeight) {
             tsHeight = topJxl.options.minHeight;
         }
-        if (topJxl.options.maxHeight >= 0 && 
+        if (topJxl.options.maxHeight >= 0 &&
             tsHeight > topJxl.options.maxHeight) {
             tsHeight = topJxl.options.maxHeight;
         }
-        
+
         /* update the topEdge to accomodate constraints */
         if (tsTop + tsHeight != topEdge) {
             /* need to update right side, ignoring constraints because left side
@@ -14601,19 +27378,19 @@
             topEdge = tsTop + tsHeight;
             var delta = topEdge + size.height - bsTop;
             bsTop += delta;
-            bsHeight -= delta; 
+            bsHeight -= delta;
         }
-        
+
         /* put bar in its final location based on constraints */
         obj.style.top = paddingTop + topEdge + 'px';
-        
+
         /* update topSide positions */
         if (topJxl.options.height == null) {
             topSide.resize({bottom: parentSize.height - tsTop-tsHeight});
         } else {
             topSide.resize({height: tsHeight});
         }
-        
+
         /* update bottomSide position */
         if (bottomJxl.options.height == null) {
             bottomSide.resize({top:bsTop});
@@ -14621,7 +27398,7 @@
             bottomSide.resize({top: bsTop, height: bsHeight});
         }
     },
-    
+
     /**
      * Method: sizeChanged
      * handle the size of the container being changed.
@@ -14633,7 +27410,7 @@
             this.verticalResize();
         }
     },
-    
+
     /**
      * Method: horizontalResize
      * Resize a horizontally layed-out container
@@ -14641,8 +27418,8 @@
     horizontalResize: function() {
         var availableSpace = this.domObj.getContentBoxSize().width;
         var overallWidth = availableSpace;
-
-        for (var i=0; i<this.bars.length; i++) {
+        var i,e,jxo;
+        for (i=0; i<this.bars.length; i++) {
             var bar = this.bars[i];
             var size = bar.retrieve('size');
             if (!size || size.width == 0) {
@@ -14652,20 +27429,19 @@
             availableSpace -= size.width;
         }
 
-        var nVariable = 0;
-        var jxo;
-        for (var i=0; i<this.elements.length; i++) {
-            var e = this.elements[i];
+        var nVariable = 0, w = 0;
+        for (i=0; i<this.elements.length; i++) {
+            e = this.elements[i];
             jxo = e.retrieve('jxLayout').options;
             if (jxo.width != null) {
-                availableSpace -= parseInt(jxo.width);
+                availableSpace -= parseInt(jxo.width,10);
             } else {
-                var w = 0;
-                if (jxo.right != 0 || 
+                w = 0;
+                if (jxo.right != 0 ||
                     jxo.left != 0) {
                     w = e.getBorderBoxSize().width;
                 }
-                
+
                 availableSpace -= w;
                 nVariable++;
             }
@@ -14678,18 +27454,21 @@
             nVariable = 1;
         }
 
-        var amount = parseInt(availableSpace / nVariable);
+        var amount = parseInt(availableSpace / nVariable,10);
         /* account for rounding errors */
         var remainder = availableSpace % nVariable;
-        
-        var leftPadding = this.domObj.getPaddingSize().left;
 
+        var leftPadding = this.domObj.measure(function(){
+            var m = this.getSizes(['padding'], ['left']);
+            return m.padding.left;
+        });
+
         var currentPosition = 0;
 
-        for (var i=0; i<this.elements.length; i++) {
-             var e = this.elements[i];
+        for (i=0; i<this.elements.length; i++) {
+             e = this.elements[i];
              var jxl = e.retrieve('jxLayout');
-             var jxo = jxl.options;
+             jxo = jxl.options;
              if (jxo.width != null) {
                  jxl.resize({left: currentPosition});
                  currentPosition += jxo.width;
@@ -14699,14 +27478,13 @@
                      a += remainder;
                  }
                  nVariable--;
-                 
-                 var w = 0;
+
                  if (jxo.right != 0 || jxo.left != 0) {
                      w = e.getBorderBoxSize().width + a;
                  } else {
                      w = a;
                  }
-                 
+
                  if (w < 0) {
                      if (nVariable > 0) {
                          amount = amount + w/nVariable;
@@ -14725,7 +27503,7 @@
                      }
                      w = e.options.maxWidth;
                  }
-                 
+
                  var r = overallWidth - currentPosition - w;
                  jxl.resize({left: currentPosition, right: r});
                  currentPosition += w;
@@ -14737,16 +27515,16 @@
              }
          }
     },
-    
+
     /**
      * Method: verticalResize
      * Resize a vertically layed out container.
      */
-    verticalResize: function() { 
+    verticalResize: function() {
         var availableSpace = this.domObj.getContentBoxSize().height;
         var overallHeight = availableSpace;
-
-        for (var i=0; i<this.bars.length; i++) {
+        var i,e,jxo;
+        for (i=0; i<this.bars.length; i++) {
             var bar = this.bars[i];
             var size = bar.retrieve('size');
             if (!size || size.height == 0) {
@@ -14756,20 +27534,17 @@
             availableSpace -= size.height;
         }
 
-        var nVariable = 0;
-        
-        var jxo;
-        for (var i=0; i<this.elements.length; i++) {
-            var e = this.elements[i];
+        var nVariable = 0, h=0;
+        for (i=0; i<this.elements.length; i++) {
+            e = this.elements[i];
             jxo = e.retrieve('jxLayout').options;
             if (jxo.height != null) {
-                availableSpace -= parseInt(jxo.height);
+                availableSpace -= parseInt(jxo.height,10);
             } else {
-                var h = 0;
                 if (jxo.bottom != 0 || jxo.top != 0) {
                     h = e.getBorderBoxSize().height;
                 }
-                
+
                 availableSpace -= h;
                 nVariable++;
             }
@@ -14782,18 +27557,21 @@
             nVariable = 1;
         }
 
-        var amount = parseInt(availableSpace / nVariable);
+        var amount = parseInt(availableSpace / nVariable,10);
         /* account for rounding errors */
         var remainder = availableSpace % nVariable;
 
-        var paddingTop = this.domObj.getPaddingSize().top;
-        
+        var paddingTop = this.domObj.measure(function(){
+            var m = this.getSizes(['padding'], ['top']);
+            return m.padding.top;
+        });
+
         var currentPosition = 0;
 
-        for (var i=0; i<this.elements.length; i++) {
-             var e = this.elements[i];
+        for (i=0; i<this.elements.length; i++) {
+             e = this.elements[i];
              var jxl = e.retrieve('jxLayout');
-             var jxo = jxl.options;
+             jxo = jxl.options;
              if (jxo.height != null) {
                  jxl.resize({top: currentPosition});
                  currentPosition += jxo.height;
@@ -14803,14 +27581,14 @@
                      a += remainder;
                  }
                  nVariable--;
-                 
-                 var h = 0;
+
+                 h = 0;
                  if (jxo.bottom != 0 || jxo.top != 0) {
                      h = e.getBorderBoxSize().height + a;
                  } else {
                      h = a;
                  }
-                 
+
                  if (h < 0) {
                      if (nVariable > 0) {
                          amount = amount + h/nVariable;
@@ -14829,7 +27607,7 @@
                      }
                      h = jxo.maxHeight;
                  }
-                 
+
                  var r = overallHeight - currentPosition - h;
                  jxl.resize({top: currentPosition, bottom: r});
                  currentPosition += h;
@@ -14840,23 +27618,46 @@
                  currentPosition += rightBar.retrieve('size').height;
              }
          }
+    },
+    
+    changeText: function (lang) {
+    	this.parent();
+    	this.bars.each(function(bar){
+    		document.id(bar).set('title', this.getText({set:'Jx',key:'splitter',value:'barToolTip'}));
+    	},this);	
     }
-});// $Id: panelset.js 424 2009-05-12 12:51:44Z pagameba $
+});/*
+---
+
+name: Jx.PanelSet
+
+description: A panel set manages a set of panels within a DOM element.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Splitter
+ - Jx.Panel
+
+provides: [Jx.PanelSet]
+
+...
+ */
+// $Id: panelset.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.PanelSet
  *
- * Extends: Object
+ * Extends: <Jx.Widget>
  *
- * Implements: Options, Events, <Jx.Addable>
+ * A panel set manages a set of panels within a DOM element.  The PanelSet
+ * fills its container by resizing the panels in the set to fill the width and
+ * then distributing the height of the container across all the panels. 
+ * Panels can be resized by dragging their respective title bars to make them
+ * taller or shorter.  The maximize button on the panel title will cause all
+ * other panels to be closed and the target panel to be expanded to fill the
+ * remaining space.  In this respect, PanelSet works like a traditional
+ * Accordion control.
  *
- * A panel set manages a set of panels within a DOM element.  The PanelSet fills
- * its container by resizing the panels in the set to fill the width and then
- * distributing the height of the container across all the panels.  Panels
- * can be resized by dragging their respective title bars to make them taller
- * or shorter.  The maximize button on the panel title will cause all other
- * panels to be closed and the target panel to be expanded to fill the remaining
- * space.  In this respect, PanelSet works like a traditional Accordion control.
- *
  * When creating panels for use within a panel set, it is important to use the
  * proper options.  You must override the collapse option and set it to false
  * and add a maximize option set to true.  You must also not include options
@@ -14864,21 +27665,24 @@
  *
  * Example:
  * (code)
- * var p1 = new Jx.Panel({collapse: false, maximize: true, content: 'content1'});
- * var p2 = new Jx.Panel({collapse: false, maximize: true, content: 'content2'});
- * var p3 = new Jx.Panel({collapse: false, maximize: true, content: 'content3'});
+ * var p1 = new Jx.Panel({collapse: false, maximize: true, content: 'c1'});
+ * var p2 = new Jx.Panel({collapse: false, maximize: true, content: 'c2'});
+ * var p3 = new Jx.Panel({collapse: false, maximize: true, content: 'c3'});
  * var panelSet = new Jx.PanelSet('panels', [p1,p2,p3]);
  * (end)
+ * 
+ * MooTools.lang Keys:
+ * - panelset.barTooltip
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.PanelSet = new Class({
     Family: 'Jx.PanelSet',
-    Implements: [Options, Events, Jx.Addable],
-    
+    Extends: Jx.Widget,
+
     options: {
         /* Option: parent
          * the object to add the panel set to
@@ -14887,13 +27691,9 @@
         /* Option: panels
          * an array of <Jx.Panel> objects that will be managed by the set.
          */
-        panels: [],
-        /* Option: barTooltip
-         * the tooltip to place on the title bars of each panel
-         */
-        barTooltip: 'drag this bar to resize'
+        panels: []
     },
-    
+
     /**
      * Property: panels
      * {Array} the panels being managed by the set
@@ -14910,24 +27710,17 @@
      */
     firstLayout: true,
     /**
-     * Constructor: Jx.PanelSet
+     * APIMethod: render
      * Create a new instance of Jx.PanelSet.
-     *
-     * Parameters:
-     * options - <Jx.PanelSet.Options>
-     *
-     * TODO: Jx.PanelSet.initialize
-     * Remove the panels parameter in favour of an add method.
      */
-    initialize: function(options) {
-        if (options && options.panels) {
-            this.panels = options.panels;
-            options.panels = null;
+    render: function() {
+        if (this.options.panels) {
+            this.panels = this.options.panels;
+            this.options.panels = null;
         }
-        this.setOptions(options);
         this.domObj = new Element('div');
         new Jx.Layout(this.domObj);
-        
+
         //make a fake panel so we get the right number of splitters
         var d = new Element('div', {styles:{position:'absolute'}});
         new Jx.Layout(d, {minHeight:0,maxHeight:0,height:0});
@@ -14940,7 +27733,7 @@
             panel.domObj.store('Jx.Panel', panel);
             panel.manager = this;
         }, this);
-        
+
         this.splitter = new Jx.Splitter(this.domObj, {
             splitInto: this.panels.length+1,
             layout: 'vertical',
@@ -14948,31 +27741,31 @@
             prepareBar: (function(i) {
                 var bar = new Element('div', {
                     'class': 'jxPanelBar',
-                    'title': this.options.barTooltip
+                    'title': this.getText({set:'Jx',key:'panelset',value:'barToolTip'})
                 });
-                
+
                 var panel = this.panels[i];
                 panel.title.setStyle('visibility', 'hidden');
-                $(document.body).adopt(panel.title);
+                document.id(document.body).adopt(panel.title);
                 var size = panel.title.getBorderBoxSize();
                 bar.adopt(panel.title);
                 panel.title.setStyle('visibility','');
-                
+
                 bar.setStyle('height', size.height);
                 bar.store('size', size);
-                
+
                 return bar;
             }).bind(this)
         });
         this.addEvent('addTo', function() {
-            $(this.domObj.parentNode).setStyle('overflow', 'hidden');
+            document.id(this.domObj.parentNode).setStyle('overflow', 'hidden');
             this.domObj.resize();
         });
         if (this.options.parent) {
             this.addTo(this.options.parent);
         }
     },
-    
+
     /**
      * Method: maximizePanel
      * Maximize a panel, taking up all available space (taking into
@@ -14982,17 +27775,16 @@
         var domHeight = this.domObj.getContentBoxSize().height;
         var space = domHeight;
         var panelSize = panel.domObj.retrieve('jxLayout').options.maxHeight;
-        var panelIndex;
-        
+        var panelIndex,i,p,thePanel,o,panelHeight;
         /* calculate how much space might be left after setting all the panels to
          * their minimum height (except the one we are resizing of course)
          */
-        for (var i=1; i<this.splitter.elements.length; i++) {
-            var p = this.splitter.elements[i];
+        for (i=1; i<this.splitter.elements.length; i++) {
+            p = this.splitter.elements[i];
             space -= p.retrieve('leftBar').getBorderBoxSize().height;
             if (p !== panel.domObj) {
-                var thePanel = p.retrieve('Jx.Panel');
-                var o = p.retrieve('jxLayout').options;
+                thePanel = p.retrieve('Jx.Panel');
+                o = p.retrieve('jxLayout').options;
                 space -= o.minHeight;
             } else {
                 panelIndex = i;
@@ -15007,13 +27799,13 @@
             space = space - panelSize;
         }
         var top = 0;
-        for (var i=1; i<this.splitter.elements.length; i++) {
-            var p = this.splitter.elements[i];
+        for (i=1; i<this.splitter.elements.length; i++) {
+            p = this.splitter.elements[i];
             top += p.retrieve('leftBar').getBorderBoxSize().height;
             if (p !== panel.domObj) {
-                var thePanel = p.retrieve('Jx.Panel');
-                var o = p.retrieve('jxLayout').options;
-                var panelHeight = $chk(o.height) ? o.height : p.getBorderBoxSize().height;
+                thePanel = p.retrieve('Jx.Panel');
+                o = p.retrieve('jxLayout').options;
+                panelHeight = $chk(o.height) ? o.height : p.getBorderBoxSize().height;
                 if (space > 0) {
                     if (space >= panelHeight) {
                         // this panel can stay open at its current height
@@ -15042,14 +27834,14 @@
                 break;
             }
         }
-        
+
         /* now work from the bottom up */
         var bottom = domHeight;
-        for (var i=this.splitter.elements.length - 1; i > 0; i--) {
+        for (i=this.splitter.elements.length - 1; i > 0; i--) {
             p = this.splitter.elements[i];
             if (p !== panel.domObj) {
-                var o = p.retrieve('jxLayout').options;
-                var panelHeight = $chk(o.height) ? o.height : p.getBorderBoxSize().height;
+                o = p.retrieve('jxLayout').options;
+                panelHeight = $chk(o.height) ? o.height : p.getBorderBoxSize().height;
                 if (space > 0) {
                     if (space >= panelHeight) {
                         // panel can stay open
@@ -15068,1061 +27860,9825 @@
                     }
                 } else {
                     bottom -= o.minHeight;
-                    p.resize({top: bottom, height: o.minHeight, bottom: null});                    
+                    p.resize({top: bottom, height: o.minHeight, bottom: null});
                 }
                 bottom -= p.retrieve('leftBar').getBorderBoxSize().height;
                 p.retrieve('leftBar').style.top = bottom + 'px';
-                
+
             } else {
                 break;
             }
         }
         panel.domObj.resize({top: top, height:panelSize, bottom: null});
+        this.fireEvent('panelMaximize',panel);
+    },
+    
+    createText: function (lang) {
+      this.parent();
+      //barTooltip is handled by the splitter's createText() function
     }
-});// $Id: grid.js 424 2009-05-12 12:51:44Z pagameba $
+});/*
+---
+
+name: Jx.Dialog.Message
+
+description: A subclass of jx.Dialog for displaying messages w/a single OK button.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Dialog
+ - Jx.Button
+ - Jx.Toolbar.Item
+
+provides: [Jx.Dialog.Message]
+
+css:
+ - message
+
+...
+ */
+// $Id: message.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.Grid
+ * Class: Jx.Dialog.Message
+ *
+ * Extends: <Jx.Dialog>
+ *
+ * Jx.Dialog.Message is an extension of Jx.Dialog that allows the developer
+ * to display a message to the user. It only presents an OK button.
  * 
- * Extends: Object
+ * MooTools.lang Keys:
+ * - message.okButton
  *
- * Implements: Options, Events, <Jx.Addable>
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner
  *
- * A tabular control that has fixed scrolling headers on the rows and columns
- * like a spreadsheet.
+ * This file is licensed under an MIT style license
+ */
+Jx.Dialog.Message = new Class({
+    Family: 'Jx.Dialog.Message',
+    Extends: Jx.Dialog,
+    Binds: ['onOk'],
+    options: {
+        /**
+         * Option: message
+         * The message to display to the user
+         */
+        message: '',
+        /**
+         * Option: width
+         * default width of message dialogs is 300px
+         */
+        width: 300,
+        /**
+         * Option: height
+         * default height of message dialogs is 150px
+         */
+        height: 150,
+        /**
+         * Option: close
+         * by default, message dialogs are closable
+         */
+        close: true,
+        /**
+         * Option: resize
+         * by default, message dialogs are resizable
+         */
+        resize: true,
+        /**
+         * Option: collapse
+         * by default, message dialogs are not collapsible
+         */
+        collapse: false,
+        useKeyboard : true,
+        keys : {
+          'enter' : 'ok'
+        }
+    },
+    /**
+     * Method: render
+     * constructs the dialog.
+     */
+    render: function () {
+        //create content to be added
+        this.buttons = new Jx.Toolbar({position: 'bottom',scroll:false});
+        this.ok = new Jx.Button({
+            label: this.getText({set:'Jx',key:'message',value:'okButton'}),
+            onClick: this.onOk
+        });
+        this.buttons.add(this.ok);
+        this.options.toolbars = [this.buttons];
+        var type = Jx.type(this.options.message);
+        if (type === 'string' || type == 'object' || type == 'element') {
+            this.question = new Element('div', {
+                'class': 'jxMessage'
+            });
+            switch(type) {
+              case 'string':
+              case 'object':
+                this.question.set('html', this.getText(this.options.message));
+              break;
+              case 'element':
+                this.options.message.inject(this.question);
+                break;
+            }
+        } else {
+            this.question = this.options.question;
+            document.id(this.question).addClass('jxMessage');
+        }
+        this.options.content = this.question;
+        if(this.options.useKeyboard) {
+          var self = this;
+          this.options.keyboardMethods.ok = function(ev) { ev.preventDefault(); self.close(); }
+        }
+        this.parent();
+        if(this.options.useKeyboard) {
+          this.keyboard.addEvents(this.getKeyboardEvents());
+        }
+    },
+    /**
+     * Method: onOk
+     * Called when the OK button is clicked. Closes the dialog.
+     */
+    onOk: function () {
+        this.close();
+    },
+    
+    /**
+     * APIMethod: setMessage
+     * set the message of the dialog, useful for responding to language
+     * changes on the fly.
+     *
+     * Parameters
+     * message - {String} the new message
+     */
+    setMessage: function(message) {
+      this.options.message = message;
+      if ($defined(this.question)) {
+        this.question.set('html',this.getText(message));
+      }
+    },
+    
+    /**
+     * Method: createText
+     * handle change in language
+     */
+    changeText: function (lang) {
+      this.parent();
+      if ($defined(this.ok)) {
+        this.ok.setLabel({set:'Jx',key:'message',value:'okButton'});
+      }
+      if(Jx.type(this.options.message) === 'object') {
+        this.question.set('html', this.getText(this.options.message))
+      }
+    }
+});
+/*
+---
+
+name: Jx.Dialog.Confirm
+
+description: A subclass of Jx.dialog for asking a yes/no type question of the user.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Dialog
+ - Jx.Button
+ - Jx.Toolbar.Item
+
+provides: [Jx.Dialog.Confirm]
+
+css:
+ - confirm
+
+...
+ */
+// $Id: confirm.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Dialog.Confirm
  *
- * Jx.Grid is a tabular control with convenient controls for resizing columns,
- * sorting, and inline editing.  It is created inside another element, typically a
- * div.  If the div is resizable (for instance it fills the page or there is a
- * user control allowing it to be resized), you must call the resize() method
- * of the grid to let it know that its container has been resized.
+ * Extends: <Jx.Dialog>
  *
- * When creating a new Jx.Grid, you can specify a number of options for the grid
- * that control its appearance and functionality.
+ * Jx.Dialog.Confirm is an extension of Jx.Dialog that allows the developer
+ * to prompt their user with e yes/no question.
+ * 
+ * MooTools.lang Keys:
+ * - confirm.affirmitiveLabel
+ * - confirm.negativeLabel
+ * 
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner
  *
- * Jx.Grid renders data that comes from an external source.  This external 
- * source, called the model, must implement the following interface.
+ * This file is licensed under an MIT style license
+ */
+Jx.Dialog.Confirm = new Class({
+
+    Extends: Jx.Dialog,
+
+    options: {
+        /**
+         * Option: question
+         * The question to ask the user
+         */
+        question: '',
+        /**
+         * Jx.Dialog option defaults
+         */
+        useKeyboard : true,
+        keys : {
+          'esc'   : 'cancel',
+          'enter' : 'ok'
+        },
+        width: 300,
+        height: 150,
+        close: false,
+        resize: true,
+        collapse: false
+    },
+    /**
+     * Reference to MooTools keyboards Class for handling keypress events like Enter or ESC
+     */
+    keyboard : null,
+    /**
+     * APIMethod: render
+     * creates the dialog
+     */
+    render: function () {
+        //create content to be added
+        //turn scrolling off as confirm only has 2 buttons.
+        this.buttons = new Jx.Toolbar({position: 'bottom',scroll: false});
+
+        // COMMENT: returning boolean would be more what people expect instead of a localized label of a button?
+        this.ok = new Jx.Button({
+            label: this.getText({set:'Jx',key:'confirm',value:'affirmativeLabel'}),
+            onClick: this.onClick.bind(this, true)
+        }),
+        this.cancel = new Jx.Button({
+            label: this.getText({set:'Jx',key:'confirm',value:'negativeLabel'}),
+            onClick: this.onClick.bind(this, false)
+        })
+        this.buttons.add(this.ok, this.cancel);
+        this.options.toolbars = [this.buttons];
+        var type = Jx.type(this.options.question);
+        if (type === 'string' || type === 'object' || type == 'element'){
+            this.question = new Element('div', {
+                'class': 'jxConfirmQuestion'
+            });
+            switch(type) {
+              case 'string':
+              case 'object':
+                this.question.set('html', this.getText(this.options.question));
+              break;
+              case 'element':
+                this.options.question.inject(this.question);
+                break;
+            }
+        } else {
+            this.question = this.options.question;
+            document.id(this.question).addClass('jxConfirmQuestion');
+        }
+        this.options.content = this.question;
+
+        // add default key functions
+        if(this.options.useKeyboard) {
+          var self = this;
+          this.options.keyboardMethods.ok     = function(ev) { ev.preventDefault(); self.onClick(true); }
+          this.options.keyboardMethods.cancel = function(ev) { ev.preventDefault(); self.onClick(false); }
+        }
+        this.parent();
+        // add new ones
+        if(this.options.useKeyboard) {
+          this.keyboard.addEvents(this.getKeyboardEvents());
+        }
+    },
+    /**
+     * Method: onClick
+     * called when any button is clicked. It hides the dialog and fires
+     * the close event passing it the value of the button that was pressed.
+     */
+    onClick: function (value) {
+        this.isOpening = false;
+        this.hide();
+        this.fireEvent('close', [this, value]);
+    },
+    
+    changeText: function (lang) {
+    	this.parent();
+    	if ($defined(this.ok)) {
+    		this.ok.setLabel({set:'Jx',key:'confirm',value:'affirmativeLabel'});
+    	}
+    	if ($defined(this.cancel)) {
+    		this.cancel.setLabel({set:'Jx',key:'confirm',value:'negativeLabel'});
+    	}
+      if(Jx.type(this.options.question) === 'object') {
+        this.question.set('html', this.getText(this.options.question))
+      }
+    }
+
+});/*
+---
+
+name: Jx.Tooltip
+
+description: These are very simple tooltips that are designed to be instantiated in javascript and directly attached to the object that they are the tip for.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+
+provides: [Jx.Tooltip]
+
+css:
+ - tooltip
+
+...
+ */
+// $Id: tooltip.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Tooltip
  *
+ * Extends: <Jx.Widget>
  *
+ * An implementation of tooltips. These are very simple tooltips that are
+ * designed to be instantiated in javascript and directly attached to the
+ * object that they are the tip for. We can only have one Tip per element so
+ * we use element storage to store the tip object and check for it's presence
+ * before creating a new tip. If one is there we remove it and create this new
+ * one.
+ *
  * Example:
  * (code)
  * (end)
  *
- * License: 
- * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
  * This file is licensed under an MIT style license
  */
-Jx.Grid = new Class({
-    Family: 'Jx.Grid',
-    Implements: [Options, Events, Jx.Addable],
-    domObj : null,
-    model : null,
-    options: {
-         /* Option: parent
-          * the HTML element to create the grid inside. The grid will resize
-          * to fill the domObj.
-          */
-         parent: null,
-         /* Option: alternateRowColors
-          * defaults to false.  If set to true, then alternating CSS classes
-          * are used for rows.
-          */
-        alternateRowColors: false,
-         /* Option: rowHeaders
-          * defaults to false.  If set to true, then a column of row header
-          * cells are displayed.
-          */
-        rowHeaders: false,
-         /* Option: columnHeaders
-          * defaults to false.  If set to true, then a column of row header
-          * cells are displayed.
-          */
-        columnHeaders: false,
-         /* Option: rowSelection
-          * defaults to false.  If set to true, allow the user to select rows.
-          */
-        rowSelection: false,
-         /* Option: columnSelection
-          * defaults to false.  If set to true, allow the user to select
-          * columns.
-          */
-        columnSelection: false,
-         /* Option: cellPrelight
-          * defaults to false.  If set to true, the cell under the mouse is
-          * highlighted as the mouse moves.
-          */
-        cellPrelight: false,
-         /* Option: rowPrelight
-          * defaults to false.  If set to true, the row under the mouse is
-          * highlighted as the mouse moves.
-          */
-        rowPrelight: false,
-         /* Option: columnPrelight
-          * defaults to false.  If set to true, the column under the mouse is
-          * highlighted as the mouse moves.
-          */
-        columnPrelight: false,
-        /* Option: rowHeaderPrelight
-         * defaults to false.  If set to true, the row header of the row under
-         * the mouse is highlighted as the mouse moves.
+Jx.Tooltip = new Class({
+    Family: 'Jx.Widget',
+    Extends : Jx.Widget,
+    Binds: ['enter', 'leave', 'move'],
+    options : {
+        /**
+         * Option: offsets
+         * An object with x and y components for where to put the tip related
+         * to the mouse cursor.
          */
-        rowHeaderPrelight: false,
-        /* Option: columnHeaderPrelight
-         * defaults to false.  If set to true, the column header of the column
-         * under the mouse is highlighted as the mouse moves.
+        offsets : {
+            x : 15,
+            y : 15
+        },
+        /**
+         * Option: showDelay
+         * The amount of time to delay before showing the tip. This ensures we
+         * don't show a tip if we're just passing over an element quickly.
          */
-        columnHeaderPrelight: false,
-         /* Option: cellSelection
-          * defaults to false.  If set to true, allow the user to select
-          * cells.
-          */
-        cellSelection: false
+        showDelay : 100,
+        /**
+         * Option: cssClass
+         * a class to be added to the tip's container. This can be used to
+         * style the tip.
+         */
+        cssClass : null
     },
+
     /**
-     * Constructor: Jx.Grid
-     * construct a new instance of Jx.Grid within the domObj
+     * Parameters:
+     * target - The DOM element that triggers the toltip when moused over.
+     * tip - The contents of the tip itself. This can be either a string or
+     *       an Element.
+     * options - <Jx.Tooltip.Options> and <Jx.Widget.Options>
+     */
+    parameters: ['target','tip','options'],
+
+    /**
+     * Method: render
+     * Creates the tooltip
      *
+     */
+    render : function () {
+        this.parent();
+        this.target = document.id(this.options.target);
+
+        var t = this.target.retrieve('Tip');
+        if (t) {
+            this.target.eliminate('Tip');
+        }
+
+        //set up the tip options
+        this.domObj = new Element('div', {
+            styles : {
+                'position' : 'absolute',
+                'top' : 0,
+                'left' : 0,
+                'visibility' : 'hidden'
+            }
+        }).inject(document.body);
+
+        if (Jx.type(this.options.tip) === 'string' || Jx.type(this.options.tip) == 'object') {
+            this.domObj.set('html', this.getText(this.options.tip));
+        } else {
+            this.domObj.grab(this.options.tip);
+        }
+
+        this.domObj.addClass('jxTooltip');
+        if ($defined(this.options.cssClass)) {
+            this.domObj.addClass(this.options.cssClass);
+        }
+
+        this.options.target.store('Tip', this);
+
+        //add events
+        this.options.target.addEvent('mouseenter', this.enter);
+        this.options.target.addEvent('mouseleave', this.leave);
+        this.options.target.addEvent('mousemove', this.move);
+    },
+
+    /**
+     * Method: enter
+     * Method run when the cursor passes over an element with a tip
+     *
      * Parameters:
-     * options - <Jx.Grid.Options>
+     * event - the event object
      */
-    initialize : function( options ) {
-        this.setOptions(options);
+    enter : function (event) {
+        this.timer = $clear(this.timer);
+        this.timer = (function () {
+            this.domObj.setStyle('visibility', 'visible');
+            this.position(event);
+        }).delay(this.options.delay, this);
+    },
+    /**
+     * Method: leave
+     * Executed when the mouse moves out of an element with a tip
+     *
+     * Parameters:
+     * event - the event object
+     */
+    leave : function (event) {
+        this.timer = $clear(this.timer);
+        this.timer = (function () {
+            this.domObj.setStyle('visibility', 'hidden');
+        }).delay(this.options.delay, this);
+    },
+    /**
+     * Method: move
+     * Called when the mouse moves over an element with a tip.
+     *
+     * Parameters:
+     * event - the event object
+     */
+    move : function (event) {
+        this.position(event);
+    },
+    /**
+     * Method: position
+     * Called to position the tooltip.
+     *
+     * Parameters:
+     * event - the event object
+     */
+    position : function (event) {
+        var size = window.getSize(), scroll = window.getScroll();
+        var tipSize = this.domObj.getMarginBoxSize();
+        var tip = {
+            x : this.domObj.offsetWidth,
+            y : this.domObj.offsetHeight
+        };
+        var tipPlacement = {
+            x: event.page.x + this.options.offsets.x,
+            y: event.page.y + this.options.offsets.y
+        };
 
-        this.domObj = new Element('div');
-        new Jx.Layout(this.domObj, {
-            onSizeChange: this.resize.bind(this)
+        if (event.page.y + this.options.offsets.y + tip.y + tipSize.height - scroll.y > size.y) {
+            tipPlacement.y = event.page.y - this.options.offsets.y - tipSize.height - scroll.y;
+        }
+
+        if (event.page.x + this.options.offsets.x + tip.x + tipSize.width - scroll.x > size.x) {
+            tipPlacement.x = event.page.x - this.options.offsets.x - tipSize.width - scroll.x;
+        }
+
+        this.domObj.setStyle('top', tipPlacement.y);
+        this.domObj.setStyle('left', tipPlacement.x);
+    },
+    /**
+     * APIMethod: detach
+     * Called to manually remove a tooltip.
+     */
+    detach : function () {
+        this.target.eliminate('Tip');
+        this.destroy();
+    }
+});
+/*
+---
+
+name: Jx.Fieldset
+
+description: Used to create fieldsets in Forms
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+
+provides: [Jx.Fieldset]
+
+...
+ */
+// $Id: fieldset.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Fieldset
+ *
+ * Extends: <Jx.Widget>
+ *
+ * This class represents a fieldset. It can be used to group fields together.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ *
+ */
+Jx.Fieldset = new Class({
+    Family: 'Jx.Fieldset',
+    Extends : Jx.Widget,
+
+    options : {
+        /**
+         * Option: legend
+         * The text for the legend of a fieldset. Default is null
+         * or no legend.
+         */
+        legend : null,
+        /**
+         * Option: id
+         * The id to assign to this element
+         */
+        id : null,
+        /**
+         * Option: fieldsetClass
+         * A CSS class to assign to the fieldset. Useful for custom styling of
+         * the element
+         */
+        fieldsetClass : null,
+        /**
+         * Option: legendClass
+         * A CSS class to assign to the legend. Useful for custom styling of
+         * the element
+         */
+        legendClass : null,
+        /**
+         * Option: template
+         * a template for how this element should be rendered
+         */
+        template : '<fieldset class="jxFieldset"><legend><span class="jxFieldsetLegend"></span></legend></fieldset>',
+        /**
+         * Option: form
+         * The <Jx.Form> that this fieldset should be added to
+         */
+        form : null
+    },
+
+    classes: new Hash({
+        domObj: 'jxFieldset',
+        legend: 'jxFieldsetLegend'
+    }),
+
+    /**
+     * Property: legend
+     * a holder for the legend Element
+     */
+    legend : null,
+
+    /**
+     * APIMethod: render
+     * Creates a fieldset.
+     */
+    render : function () {
+        this.parent();
+
+        this.id = this.options.id;
+
+        if ($defined(this.options.form)
+                && this.options.form instanceof Jx.Form) {
+            this.form = this.options.form;
+        }
+
+        //FIELDSET
+        if (this.domObj) {
+            if ($defined(this.options.id)) {
+                this.domObj.set('id', this.options.id);
+            }
+            if ($defined(this.options.fieldsetClass)) {
+                this.domObj.addClass(this.options.fieldsetClass);
+            }
+        }
+
+        if (this.legend) {
+            if ($defined(this.options.legend)) {
+                this.legend.set('html', this.getText(this.options.legend));
+                if ($defined(this.options.legendClass)) {
+                    this.legend.addClass(this.options.legendClass);
+                }
+            } else {
+                this.legend.destroy();
+            }
+        }
+    },
+    /**
+     * APIMethod: add
+     * Adds fields to this fieldset
+     *
+     * Parameters:
+     * pass as many fields to this method as you like. They should be
+     * <Jx.Field> objects
+     */
+    add : function () {
+        var field;
+        for (var x = 0; x < arguments.length; x++) {
+            field = arguments[x];
+            //add form to the field and field to the form if not already there
+            if ($defined(field.jxFamily) && !$defined(field.form) && $defined(this.form)) {
+                field.form = this.form;
+                this.form.addField(field);
+            }
+            this.domObj.grab(field);
+        }
+        return this;
+    },
+    
+    /**
+     * APIMethod: addTo
+     *
+     */
+    addTo: function(what) {
+        if (what instanceof Jx.Form) {
+            this.form = what;
+        } else if (what instanceof Jx.Fieldset) {
+            this.form = what.form;
+        }
+        return this.parent(what);
+    }
+    
+});
+/*
+---
+
+name: Jx.Form
+
+description: Represents a HTML Form
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+ - More/String.QueryString
+ - More/Form.Validator
+
+provides: [Jx.Form]
+
+css:
+ - form
+
+...
+ */
+// $Id: form.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Form
+ *
+ * Extends: <Jx.Widget>
+ *
+ * A class that represents an HTML form. You add fields using either
+ * Jx.Form.add() or by using the field's .addTo() method. You can get all form
+ * values or set them using this class. It also handles validation of fields
+ * through the use of a plugin (Jx.Plugin.Form.Validator).
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Form = new Class({
+    Family: 'Jx.Form',
+    Extends: Jx.Widget,
+
+    options: {
+        /**
+         * Option: method
+         * the method used to submit the form
+         */
+        method: 'post',
+        /**
+         * Option: action
+         * where to submit it to
+         */
+        action: '',
+        /**
+         * Option: fileUpload
+         * whether this form handles file uploads or not.
+         */
+        fileUpload: false,
+        /**
+         * Option: formClass
+         */
+        formClass: null,
+        /**
+         * Option: name
+         * the name property for the form
+         */
+        name: '',
+        /**
+         * Option: acceptCharset
+         * the character encoding to be used. Defaults to utf-8.
+         */
+        acceptCharset: 'utf-8',
+
+        template: '<form class="jxForm"></form>'
+    },
+    
+    /**
+     * Property: defaultAction
+     * the default field to activate if the user hits the enter key in this
+     * form.  Set by specifying default: true as an option to a field.  Will
+     * only work if the default is a Jx button field or an input of a type
+     * that is a button
+     */
+    defaultAction: null,
+
+    /**
+     * Property: fields
+     * An array of all of the single fields (not contained in a fieldset) for
+     * this form
+     */
+    fields : null,
+    /**
+     * Property: pluginNamespace
+     * required variable for plugins
+     */
+    pluginNamespace: 'Form',
+
+    classes: $H({
+        domObj: 'jxForm'
+    }),
+    
+    init: function() {
+      this.parent();
+      this.fields = new Hash();
+    },
+    /**
+     * APIMethod: render
+     * Constructs the form but does not add it to anything to be shown. The
+     * caller should use form.addTo() to add the form to the DOM.
+     */
+    render : function () {
+        this.parent();
+        //create the form first
+        this.domObj.set({
+            'method' : this.options.method,
+            'action' : this.options.action,
+            'name' : this.options.name,
+            'accept-charset': this.options.acceptCharset,
+            events: {
+                keypress: function(e) {
+                    if (e.key == 'enter' && 
+                        e.target.tagName != "TEXTAREA" && 
+                        this.defaultAction &&
+                        this.defaultAction.click) {
+                        document.id(this.defaultAction).focus();
+                        this.defaultAction.click();
+                        e.stop();
+                    }
+                }.bind(this)
+            }
         });
+
+        if (this.options.fileUpload) {
+            this.domObj.set('enctype', 'multipart/form-data');
+        }
         
-        if (this.options.parent) {
-            this.addTo(this.options.parent);
-        }        
-        
-        this.rowColObj = new Element('div', {'class':'jxGridContainer'});
-        
-        this.colObj = new Element('div', {'class':'jxGridContainer'});
-        this.colTable = new Element('table', {'class':'jxGridTable'});
-        this.colTableHead = new Element('thead');
-        this.colTable.appendChild(this.colTableHead);
-        this.colTableBody = new Element('tbody');
-        this.colTable.appendChild(this.colTableBody);
-        this.colObj.appendChild(this.colTable);
-        
-        this.rowObj = new Element('div', {'class':'jxGridContainer'});
-        this.rowTable = new Element('table', {'class':'jxGridTable'});
-        this.rowTableHead = new Element('thead');
-        this.rowTable.appendChild(this.rowTableHead);
-        this.rowObj.appendChild(this.rowTable);
-        
-        this.gridObj = new Element('div', {'class':'jxGridContainer',styles:{overflow:'scroll'}});
-        this.gridTable = new Element('table', {'class':'jxGridTable'});
-        this.gridTableBody = new Element('tbody');
-        this.gridTable.appendChild(this.gridTableBody);
-        this.gridObj.appendChild(this.gridTable);
-        
-        this.domObj.appendChild(this.rowColObj);
-        this.domObj.appendChild(this.rowObj);
-        this.domObj.appendChild(this.colObj);
-        this.domObj.appendChild(this.gridObj);
-                        
-        this.gridObj.addEvent('scroll', this.onScroll.bind(this));
-        this.gridObj.addEvent('click', this.onClickGrid.bindWithEvent(this));
-        this.rowObj.addEvent('click', this.onClickRowHeader.bindWithEvent(this));
-        this.colObj.addEvent('click', this.onClickColumnHeader.bindWithEvent(this));
-        this.gridObj.addEvent('mousemove', this.onMouseMoveGrid.bindWithEvent(this));
-        this.rowObj.addEvent('mousemove', this.onMouseMoveRowHeader.bindWithEvent(this));
-        this.colObj.addEvent('mousemove', this.onMouseMoveColumnHeader.bindWithEvent(this));
+        if ($defined(this.options.formClass)) {
+            this.domObj.addClass(this.options.formClass);
+        }
     },
+
+    /**
+     * APIMethod: addField
+     * Adds a <Jx.Field> subclass to this form's fields hash
+     *
+     * Parameters:
+     * field - <Jx.Field> to add
+     */
+    addField : function (field) {
+        this.fields.set(field.id, field);
+        if (field.options.defaultAction) {
+            this.defaultAction = field;
+        }
+    },
+
+    /**
+     * Method: isValid
+     * Determines if the form passes validation
+     *
+     * Parameters:
+     * evt - the MooTools event object
+     */
+    isValid : function (evt) {
+        return true;
+    },
+
+    /**
+     * APIMethod: getValues
+     * Gets the values of all the fields in the form as a Hash object. This
+     * uses the mootools function Element.toQueryString to get the values and
+     * will either return the values as a querystring or as an object (using
+     * mootools-more's String.parseQueryString method).
+     *
+     * Parameters:
+     * asQueryString - {boolean} indicates whether to return the value as a
+     *                  query string or an object.
+     */
+    getValues : function (asQueryString) {
+        var queryString = this.domObj.toQueryString();
+        if ($defined(asQueryString) && asQueryString) {
+            return queryString;
+        } else {
+            return queryString.parseQueryString();
+        }
+    },
+    /**
+     * APIMethod: setValues
+     * Used to set values on the form
+     *
+     * Parameters:
+     * values - A Hash of values to set keyed by field name.
+     */
+    setValues : function (values) {
+        if (Jx.type(values) === 'object') {
+            values = new Hash(values);
+        }
+        this.fields.each(function (item) {
+            item.setValue(values.get(item.name));
+        }, this);
+    },
+
+    /**
+     * APIMethod: add
+     *
+     * Parameters:
+     * Pass as many parameters as you like. However, they should all be
+     * <Jx.Field> objects.
+     */
+    add : function () {
+        var field;
+        for (var x = 0; x < arguments.length; x++) {
+            field = arguments[x];
+            //add form to the field and field to the form if not already there
+            if (field instanceof Jx.Field && !$defined(field.form)) {
+                field.form = this;
+                this.addField(field);
+            } else if (field instanceof Jx.Fieldset && !$defined(field.form)) {
+                field.form = this;
+            }
+            
+            this.domObj.grab(field);
+        }
+        return this;
+    },
+
+    /**
+     * APIMethod: reset
+     * Resets all fields back to their original value
+     */
+    reset : function () {
+        this.fields.each(function (field, name) {
+            field.reset();
+        }, this);
+        this.fireEvent('reset',this);
+    },
+    /**
+     * APIMethod: getFieldsByName
+     * Allows retrieving a field from a form by the name of the field (NOT the
+     * ID).
+     *
+     * Parameters:
+     * name - {string} the name of the field to find
+     */
+    getFieldsByName: function (name) {
+        var fields = [];
+        this.fields.each(function(val, id){
+            if (val.name === name) {
+                fields.push(val);
+            }
+        },this);
+        return fields;
+    },
+    /**
+     * APIMethod: getField
+     * Returns a Jx.Field object by its ID.
+     *
+     * Parameters:
+     * id - {string} the id of the field to find.
+     */
+    getField: function (id) {
+        if (this.fields.has(id)) {
+            return this.fields.get(id);
+        } 
+        return null;
+    },
+    /**
+     * APIMethod: setBusy
+     * Sets the busy state of the Form and all of it's fields.
+     *
+     * Parameters:
+     * state - {boolean} indicated whether the form is busy or not.
+     */
+    setBusy: function(state) {
+      if (this.busy == state) {
+        return;
+      }
+      this.parent(state);
+      this.fields.each(function(field) {
+        field.setBusy(state, true);
+      });
+    }
+});
+/*
+---
+
+name: Jx.Field
+
+description: Base class for all inputs
+
+license: MIT-style license.
+
+requires:
+ - Jx.Fieldset
+ - Jx.Form
+
+provides: [Jx.Field]
+
+
+...
+ */
+// $Id: field.js 969 2010-08-20 12:14:54Z pagameba $
+/**
+ * Class: Jx.Field
+ *
+ * Extends: <Jx.Widget>
+ *
+ * This class is the base class for all form fields.
+ *
+ *
+ * Example:
+ * (code)
+ * (end)
+ * 
+ * MooTools.lang Keys:
+ * - field.requiredText
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field = new Class({
+    Family: 'Jx.Field',
+    Extends : Jx.Widget,
+    pluginNamespace: 'Field',
+    Binds: ['changeText'],
     
+    options : {
+        /**
+         * Option: id
+         * The ID assigned to the container of the Jx.Field element, this is
+         * not the id of the input element (which is internally computed to be
+         * unique)
+         */
+        id : null,
+        /**
+         * Option: name
+         * The name of the field (used when submitting to the server). Will also be used for the
+         * name attribute of the field.
+         */
+        name : null,
+        /**
+         * Option: label
+         * The text that goes next to the field.
+         */
+        label : null,
+        /**
+         * Option: labelSeparator
+         * A character to use as the separator between the label and the input.
+         * Make it an empty string for no separator.
+         */
+        labelSeparator : ":",
+        /**
+         * Option: value
+         * A default value to populate the field with.
+         */
+        value : null,
+        /**
+         * Option: tag
+         * a string to use as the HTML of the tag element (default is a
+         * <span> element).
+         */
+        tag : null,
+        /**
+         * Option: tip
+         * A string that will eventually serve as a tooltip for an input field.
+         * Currently only implemented as OverText for text fields.
+         */
+        tip : null,
+        /**
+         * Option: template
+         * A string holding the template for the field.
+         */
+        template : null,
+        /**
+         * Option: containerClass
+         * a CSS class that will be added to the containing element.
+         */
+        containerClass : null,
+        /**
+         * Option: labelClass
+         * a CSS to add to the label
+         */
+        labelClass : null,
+        /**
+         * Option: fieldClass
+         * a CSS class to add to the input field
+         */
+        fieldClass : null,
+        /**
+         * Option: tagClass
+         * a CSS class to add to the tag field
+         */
+        tagClass : null,
+        /**
+         * Option: required
+         * Whether the field is required. Setting this to true will trigger
+         * the addition of a "required" validator class and the form
+         * will not submit until it is filled in and validates provided
+         * that the plugin Jx.Plugin.Field.Validator has been added to this
+         * field.
+         */
+        required : false,
+        /**
+         * Option: readonly
+         * {True|False} defaults to false. Whether this field is readonly.
+         */
+        readonly : false,
+        /**
+         * Option: disabled
+         * {True|False} defaults to false. Whether this field is disabled.
+         */
+        disabled : false,
+        /**
+         * Option: defaultAction
+         * {Boolean} defaults to false, if true and this field is a button
+         * of some kind (Jx.Button, a button or an input of type submit) then
+         * if the user hits the enter key on any field in the form except a
+         * textarea, this field will be activated as if clicked
+         */
+        defaultAction: false
+    },
+
     /**
-     * Method: onScroll
-     * handle the grid scrolling by updating the position of the headers
+     * Property: overtextOptions
+     * The default options Jx uses for mootools-more's OverText
+     * plugin
      */
-    onScroll: function() {
-        this.colObj.scrollLeft = this.gridObj.scrollLeft;
-        this.rowObj.scrollTop = this.gridObj.scrollTop;        
+    overtextOptions : {
+        element : 'label'
     },
+
+    /**
+     * Property: field
+     * An element representing the input field itself.
+     */
+    field : null,
+    /**
+     * Property: label
+     * A reference to the label element for this field
+     */
+    label : null,
+    /**
+     * Property: tag
+     * A reference to the "tag" field of this input if available
+     */
+    tag : null,
+    /**
+     * Property: id
+     * A computed, unique id attached to the input element of this field.
+     */
+    id : null,
+    /**
+     * Property: overText
+     * The overText instance for this field.
+     */
+    overText : null,
+    /**
+     * Property: type
+     * Indicates that this is a field type
+     */
+    type : 'field',
+    /**
+     * Property: classes
+     * The classes to search for in the template. Not
+     * required, but we look for them.
+     */
+    classes : new Hash({
+        domObj: 'jxInputContainer',
+        label: 'jxInputLabel',
+        tag: 'jxInputTag'
+    }),
+
+    /**
+     * APIMethod: render
+     */
+    render : function () {
+        this.classes.set('field', 'jxInput'+this.type);
+        var name = $defined(this.options.name) ? this.options.name : '';
+        this.options.template = this.options.template.substitute({name:name});
+        this.parent();
+
+        this.id = this.generateId();
+        this.name = this.options.name;
+
+        if ($defined(this.type)) {
+            this.domObj.addClass('jxInputContainer'+this.type);
+        }
+
+        if ($defined(this.options.containerClass)) {
+            this.domObj.addClass(this.options.containerClass);
+        }
+        if ($defined(this.options.required) && this.options.required) {
+            this.domObj.addClass('jxFieldRequired');
+            if ($defined(this.options.validatorClasses)) {
+                this.options.validatorClasses = 'required ' + this.options.validatorClasses;
+            } else {
+                this.options.validatorClasses = 'required';
+            }
+        }
+
+
+        // FIELD
+        if (this.field) {
+            if ($defined(this.options.fieldClass)) {
+                this.field.addClass(this.options.fieldClass);
+            }
+
+            if ($defined(this.options.value)) {
+                this.field.set('value', this.options.value);
+            }
+
+            this.field.set('id', this.id);
+
+            if ($defined(this.options.readonly)
+                    && this.options.readonly) {
+                this.field.set("readonly", "readonly");
+                this.field.addClass('jxFieldReadonly');
+            }
+
+            if ($defined(this.options.disabled)
+                    && this.options.disabled) {
+                this.field.set("disabled", "disabled");
+                this.field.addClass('jxFieldDisabled');
+            }
+            
+            //add events
+            this.field.addEvents({
+              'focus': this.onFocus.bind(this),
+              'blur': this.onBlur.bind(this),
+              'change': this.onChange.bind(this)
+            });
+
+            this.field.store('field', this);
+
+            // add click event to label to set the focus to the field
+            // COMMENT: tried it without a function using addEvent('click', this.field.focus.bind(this)) but crashed in IE
+            if(this.label) {
+              this.label.addEvent('click', function() {
+                this.field.focus();
+              }.bind(this));
+            }
+        }
+        // LABEL
+        if (this.label) {
+            if ($defined(this.options.labelClass)) {
+                this.label.addClass(this.options.labelClass);
+            }
+            if ($defined(this.options.label)) {
+                this.label.set('html', this.getText(this.options.label)
+                        + this.options.labelSeparator);
+            }
+
+            this.label.set('for', this.id);
+
+            if (this.options.required) {
+                this.requiredText = new Element('em', {
+                    'html' : this.getText({set:'Jx',key:'field',value:'requiredText'}),
+                    'class' : 'required'
+                });
+                this.requiredText.inject(this.label);
+            }
+
+        }
+
+        // TAG
+        if (this.tag) {
+            if ($defined(this.options.tagClass)) {
+                this.tag.addClass(this.options.tagClass);
+            }
+            if ($defined(this.options.tag)) {
+                this.tag.set('html', this.options.tag);
+            }
+        }
+
+        if ($defined(this.options.form)
+                && this.options.form instanceof Jx.Form) {
+            this.form = this.options.form;
+            this.form.addField(this);
+        }
+
+    },
+    /**
+     * APIMethod: setValue 
+     * Sets the value property of the field
+     *
+     * Parameters:
+     * v - The value to set the field to.
+     */
+    setValue : function (v) {
+        if (!this.options.readonly) {
+            this.field.set('value', v);
+        }
+    },
+
+    /**
+     * APIMethod: getValue
+     * Returns the current value of the field.
+     */
+    getValue : function () {
+        return this.field.get("value");
+    },
+
+    /**
+     * APIMethod: reset
+     * Sets the field back to the value passed in the
+     * original options
+     */
+    reset : function () {
+        this.setValue(this.options.value);
+        this.fireEvent('reset', this);
+    },
+    /**
+     * APIMethod: disable
+     * Disabled the field
+     */
+    disable : function () {
+        this.options.disabled = true;
+        this.field.set("disabled", "disabled");
+        this.field.addClass('jxFieldDisabled');
+    },
+    /**
+     * APIMethod: enable
+     * Enables the field
+     */
+    enable : function () {
+        this.options.disabled = false;
+        this.field.erase("disabled");
+        this.field.removeClass('jxFieldDisabled');
+    },
     
     /**
-     * Method: resize
-     * resize the grid to fit inside its container.  This involves knowing something
-     * about the model it is displaying (the height of the column header and the
-     * width of the row header) so nothing happens if no model is set
+     * APIMethod: addTo
+     * Overrides default Jx.Widget AddTo() so that we can call .add() if
+     * adding to a Jx.Form or Jx.Fieldset object.
+     *
+     * Parameters:
+     * what - the element or object to add this field to.
+     * where - where in the object to place it. Not valid if adding to Jx.Form
+     *      or Jx.Fieldset.
      */
-    resize: function() {
-        if (!this.model) {
-            return;
+    addTo: function(what, where) {
+        if (what instanceof Jx.Fieldset || what instanceof Jx.Form) {
+            what.add(this);
+        } else {
+            this.parent(what, where);
         }
+        return this;
+    },
+    
+    /**
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     * 
+     * Parameters:
+     * lang - the language being changed to or that had it's data set of 
+     *    translations changed.
+     */
+    changeText: function (lang) {
+        this.parent();
+        if ($defined(this.options.label) && this.label) {
+          this.label.set('html', this.getText(this.options.label) + this.options.labelSeparator);
+        }
+        if(this.options.required) {
+          this.requiredText = new Element('em', {
+              'html' : this.getText({set:'Jx',key:'field',value:'requiredText'}),
+              'class' : 'required'
+          });
+          this.requiredText.inject(this.label);
+        }
+        if ($defined(this.requiredText)) {
+          this.requiredText.set('html',this.getText({set:'Jx',key:'field',value:'requiredText'}));
+        }
+    }, 
+    
+    onFocus: function() {
+      this.fireEvent('focus', this);
+    },
+    
+    onBlur: function () {
+      this.fireEvent('blur',this);
+    },
+    
+    onChange: function () {
+      this.fireEvent('change', this);
+    },
+    
+    setBusy: function(state, withoutMask) {
+      if (!withoutMask) {
+        this.parent(state);
+      }
+      this.field.set('readonly', state || this.options.readonly);
+    }
+
+});
+/*
+---
+
+name: Jx.Field.Text
+
+description: Represents a text input
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field
+
+optional:
+ - More/OverText
+
+provides: [Jx.Field.Text]
+
+...
+ */
+// $Id: text.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Field.Text
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a text input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Text = new Class({
+
+    Extends: Jx.Field,
+
+    options: {
+        /**
+         * Option: overText
+         * an object holding options for mootools-more's OverText class. Leave it null to
+         * not enable it, make it an object to enable.
+         */
+        overText: null,
+        /**
+         * Option: template
+         * The template used to render this field
+         */
+        template: '<span class="jxInputContainer"><label class="jxInputLabel"></label><input class="jxInputText" type="text" name="{name}"/><span class="jxInputTag"></span></span>'
+    },
+    /**
+     * Property: type
+     * The type of this field
+     */
+    type: 'Text',
+
+    /**
+     * APIMethod: render
+     * Creates a text input field.
+     */
+    render: function () {
+        this.parent();
+
+        //create the overText instance if needed
+        if ($defined(this.options.overText)) {
+            var opts = $extend({}, this.options.overText);
+            this.field.set('alt', this.options.tip);
+            this.overText = new OverText(this.field, opts);
+            this.overText.show();
+        }
+
+    }
+
+});/*
+---
+
+name: Jx.Dialog.Prompt
+
+description: A subclass of Jx.dialog for prompting the user for text input.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Dialog
+ - Jx.Button
+ - Jx.Toolbar.Item
+ - Jx.Field.Text
+
+provides: [Jx.Dialog.Prompt]
+
+...
+ */
+// $Id: prompt.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Dialog.Prompt
+ *
+ * Extends: <Jx.Dialog>
+ *
+ * Jx.Dialog.Prompt is an extension of Jx.Dialog that allows the developer
+ * to display a message to the user and ask for a text response. 
+ * 
+ * MooTools.lang Keys:
+ * - prompt.okButton
+ * - prompt.cancelButton
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Dialog.Prompt = new Class({
+
+    Extends: Jx.Dialog,
+
+    options: {
+        /**
+         * Option: prompt
+         * The message to display to the user
+         */
+        prompt: '',
+        /**
+         * Option: startingValue
+         * The startingvalue to place in the input field
+         */
+        startingValue: '',
+        /**
+         * Option: fieldOptions,
+         * Object with various
+         */
+        fieldOptions: {
+          type : 'Text',
+          options: {},
+          validate : true,
+          validatorOptions: {
+            validators: ['required'],
+            validateOnBlur: true,
+            validateOnChange : false
+          },
+          showErrorMsg : true
+        },
+        /**
+         * Jx.Dialog option defaults
+         */
+        width: 400,
+        height: 200,
+        close: true,
+        resize: true,
+        collapse: false,
+        useKeyboard : true,
+        keys : {
+          'esc'   : 'cancel',
+          'enter' : 'ok'
+        }
+    },
+    /**
+     * APIMethod: render
+     * constructs the dialog.
+     */
+    render: function () {
+        //create content to be added
+        this.buttons = new Jx.Toolbar({position: 'bottom',scroll:false});
+        this.ok = new Jx.Button({
+                label: this.getText({set:'Jx',key:'prompt',value:'okButton'}),
+                onClick: this.onClick.bind(this, true)
+            });
+        this.cancel = new Jx.Button({
+                label: this.getText({set:'Jx',key:'prompt',value:'cancelButton'}),
+                onClick: this.onClick.bind(this, false)
+            });
+        this.buttons.add(this.ok, this.cancel);
+        this.options.toolbars = [this.buttons];
+
+        var fOpts = this.options.fieldOptions;
+            fOpts.options.label = this.getText(this.options.prompt);
+            fOpts.options.value = this.options.startingValue;
+            fOpts.options.containerClass = 'jxPrompt';
+
+        if(Jx.type(fOpts.type) === 'string' && $defined(Jx.Field[fOpts.type.capitalize()])) {
+          this.field = new Jx.Field[fOpts.type.capitalize()](fOpts.options);
+        }else if(Jx.type(fOpts.type) === 'Jx.Object'){
+          this.field = fOpts.type;
+        }else{
+          // warning and fallback?
+          window.console ? console.warn("Field type does not exist %o, using Jx.Field.Text", fOpts.type) : false;
+          this.field = new Jx.Field.Text(fOpts.options);
+        }
+
+        if(this.options.fieldOptions.validate) {
+          this.validator = new Jx.Plugin.Field.Validator(this.options.fieldOptions.validatorOptions);
+          this.validator.attach(this.field);
+        }
+
+        this.options.content = document.id(this.field);
         
-        /* TODO: Jx.Grid.resize
-         * if not showing column or row, should we handle the resize differently
+        if(this.options.useKeyboard) {
+          var self = this;
+          this.options.keyboardMethods.ok     = function(ev) { ev.preventDefault(); self.onClick(true); }
+          this.options.keyboardMethods.cancel = function(ev) { ev.preventDefault(); self.onClick(false); }
+        }
+        this.parent();
+        if(this.options.useKeyboard) {
+          this.keyboard.addEvents(this.getKeyboardEvents());
+        }
+    },
+    /**
+     * Method: onClick
+     * Called when the OK button is clicked. Closes the dialog.
+     */
+    onClick: function (value) {
+        if(value && $defined(this.validator)) {
+          if(this.validator.isValid()) {
+            this.isOpening = false;
+            this.hide();
+            this.fireEvent('close', [this, value, this.field.getValue()]);
+          }else{
+            //this.options.content.adopt(this.validator.getError());
+            this.field.field.focus.delay(50, this.field.field);
+            //todo: show error messages ?
+          }
+        }else{
+          this.isOpening = false;
+          this.hide();
+          this.fireEvent('close', [this, value, this.field.getValue()]);
+        }
+    },
+    
+    changeText: function (lang) {
+    	this.parent();
+    	if ($defined(this.ok)) {
+    		this.ok.setLabel({set:'Jx',key:'prompt',value:'okButton'});
+    	}
+    	if ($defined(this.cancel)) {
+    		this.cancel.setLabel({set:'Jx',key:'prompt',value:'cancelButton'});
+    	}
+      this.field.label.set('html', this.getText(this.options.prompt));
+    }
+
+
+});
+/*
+---
+
+name: Jx.Panel.DataView
+
+description: A panel used for displaying records from a store in a list-style interface rather than a grid.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Panel
+ - Jx.Store
+ - Jx.List
+
+provides: [Jx.Panel.DataView]
+
+...
+ */
+// $Id: dataview.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Panel.DataView
+ *
+ * Extends: <Jx.Panel>
+ *
+ * This panel extension takes a standard Jx.Store (or subclass) and displays
+ * each record as an item using a provided template. It sorts the store as requested
+ * before doing so. The class only creates the HTML and has no default CSS display. All
+ * styling must be done by the developer using the control.
+ *
+ *
+ * Events:
+ * renderDone - fires when the panel completes creating all of the items.
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Panel.DataView = new Class({
+
+    Extends: Jx.Panel,
+
+    options: {
+        /**
+         * Option: data
+         * The store containing the data
          */
-        var colHeight = this.options.columnHeaders ? this.model.getColumnHeaderHeight() : 1;
-        var rowWidth = this.options.rowHeaders ? this.model.getRowHeaderWidth() : 1;
-        
-        var size = Element.getContentBoxSize(this.domObj);
-        
-        /* -1 because of the right/bottom borders */
-        this.rowColObj.setStyles({
-            width: rowWidth-1, 
-            height: colHeight-1
+        data: null,
+        /**
+         * Option: sortColumns
+         * An array of columns to sort the store by.
+         */
+        sortColumns: null,
+        /**
+         * Option: itemTemplate
+         * The template to use in rendering records
+         */
+        itemTemplate: null,
+        /**
+         * Option: emptyTemplate
+         * the template that is displayed when there are no records in the
+         * store.
+         */
+        emptyTemplate: null,
+        /**
+         * Option: containerClass
+         * The class added to the container. It can be used to target the items
+         * in the panel.
+         */
+        containerClass: null,
+        /**
+         * Option: itemClass
+         * The class to add to each item. Used for styling purposes
+         */
+        itemClass: null,
+        /**
+         * Option: itemOptions
+         * Options to pass to the list object
+         */
+        listOptions: {
+            select: true,
+            hover: true
+        }
+    },
+
+    init: function () {
+        this.domA = new Element('div');
+        this.list = this.createList(this.domA, this.options.listOptions);
+        this.parent();
+    },
+    /**
+     * APIMethod: render
+     * Renders the dataview. If the store already has data loaded it will be rendered
+     * at the end of the method.
+     */
+    render: function () {
+        if (!$defined(this.options.data)) {
+            //we can't do anything without data
+            return;
+        }
+
+        this.options.content = this.domA;
+
+        //pass to parent
+        this.parent();
+
+        this.domA.addClass(this.options.containerClass);
+
+        //parse templates so we know what values are needed in each
+        this.itemCols = this.parseTemplate(this.options.itemTemplate);
+
+        this.bound.update = this.update.bind(this);
+        //listen for data updates
+        this.options.data.addEvent('storeDataLoaded', this.bound.update);
+        this.options.data.addEvent('storeSortFinished', this.bound.update);
+        this.options.data.addEvent('storeDataLoadFailed', this.bound.update);
+
+        if (this.options.data.loaded) {
+            this.update();
+        }
+
+    },
+
+    /**
+     * Method: draw
+     * begins the process of creating the items
+     */
+    draw: function () {
+        var n = this.options.data.count();
+        if ($defined(n) && n > 0) {
+            for (var i = 0; i < n; i++) {
+                this.options.data.moveTo(i);
+
+                var item = this.createItem();
+                this.list.add(item);
+            }
+        } else {
+            var empty = new Element('div', {html: this.options.emptyTemplate});
+            this.list.add(item);
+        }
+        this.fireEvent('renderDone', this);
+    },
+    /**
+     * Method: createItem
+     * Actually does the work of getting the data from the store
+     * and creating a single item based on the provided template
+     */
+    createItem: function () {
+        //create the item
+        var itemObj = {};
+        this.itemCols.each(function (col) {
+            itemObj[col] = this.options.data.get(col);
+        }, this);
+        var itemTemp = this.options.itemTemplate.substitute(itemObj);
+        var item = new Element('div', {
+            'class': this.options.itemClass,
+            html: itemTemp
         });
-        this.rowObj.setStyles({
-            top:colHeight,
-            left:0,
-            width:rowWidth-1,
-            height:size.height-colHeight-1
+        return item;
+    },
+    /**
+     * APIMethod: update
+     * This method begins the process of creating the items. It is called when
+     * the store is loaded or can be called to manually recreate the view.
+     */
+    update: function () {
+        if (!this.updating) {
+            this.updating = true;
+            this.list.empty();
+            this.options.data.sort(this.options.sortColumns);
+            this.draw();
+            this.updating = false;
+        }
+    },
+    /**
+     * Method: parseTemplate
+     * parses the provided template to determine which store columns are
+     * required to complete it.
+     *
+     * Parameters:
+     * template - the template to parse
+     */
+    parseTemplate: function (template) {
+        //we parse the template based on the columns in the data store looking
+        //for the pattern {column-name}. If it's in there we add it to the
+        //array of ones to look for
+        var columns = this.options.data.getColumns();
+        var arr = [];
+        columns.each(function (col) {
+            var s = '{' + col.name + '}';
+            if (template.contains(s)) {
+                arr.push(col.name);
+            }
+        }, this);
+        return arr;
+    },
+    /**
+     * Method: enterItem
+     * Fires mouseenter event
+     *
+     * Parameters:
+     * item - the item that is the target of the event
+     * list - the list this item is in.
+     */
+    enterItem: function(item, list){
+        this.fireEvent('mouseenter', item, list);
+    },
+    /**
+     * Method: leaveItem
+     * Fires mouseleave event
+     *
+     * Parameters:
+     * item - the item that is the target of the event
+     * list - the list this item is in.
+     */
+    leaveItem: function(item, list){
+        this.fireEvent('mouseleave', item, list);
+    },
+    /**
+     * Method: selectItem
+     * Fires select event
+     *
+     * Parameters:
+     * item - the item that is the target of the event
+     * list - the list this item is in.
+     */
+    selectItem: function(item, list){
+        this.fireEvent('select', item, list);
+    },
+    /**
+     * Method: unselectItem
+     * Fires unselect event
+     *
+     * Parameters:
+     * item - the item that is the target of the event
+     * list - the list this item is in.
+     */
+    unselectItem: function(item, list){
+        this.fireEvent('unselect', item, list);
+    },
+    /**
+     * Method: addItem
+     * Fires add event
+     *
+     * Parameters:
+     * item - the item that is the target of the event
+     * list - the list this item is in.
+     */
+    addItem: function(item, list) {
+        this.fireEvent('add', item, list);
+    },
+    /**
+     * Method: removeItem
+     * Fires remove event
+     *
+     * Parameters:
+     * item - the item that is the target of the event
+     * list - the list this item is in.
+     */
+    removeItem: function(item, list) {
+        this.fireEvent('remove', item, list);
+    },
+    /**
+     * Method: createList
+     * Creates the list object
+     *
+     * Parameters:
+     * container - the container to use in the list
+     * options - the options for the list
+     */
+    createList: function(container, options){
+        return new Jx.List(container, $extend({
+            onMouseenter: this.enterItem.bind(this),
+            onMouseleave: this.leaveItem.bind(this),
+            onSelect:  this.selectItem.bind(this),
+            onAdd: this.addItem.bind(this),
+            onRemove: this.removeItem.bind(this),
+            onUnselect: this.unselectItem.bind(this)
+        }, options));
+    }
+});
+/*
+---
+
+name: Jx.Panel.DataView.Group
+
+description: A subclass of Dataview that can display records in groups.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Panel.DataView
+ - Jx.Selection
+
+provides: [Jx.Panel.DataView.Group]
+
+...
+ */
+// $Id: group.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Panel.DataView.Group
+ *
+ * Extends: <Jx.Panel.DataView>
+ *
+ * This extension of Jx.Panel.DataView that provides for grouping the items
+ * by a particular column.
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Panel.DataView.Group = new Class({
+
+    Extends: Jx.Panel.DataView,
+
+    options: {
+        /**
+         * Option: groupTemplate
+         * The template used to render the group heading
+         */
+        groupTemplate: null,
+        /**
+         * Option: groupContainerClass
+         * The class added to the group container. All of the items and header
+         * for a single grouping is contained by a div that has this class added.
+         */
+        groupContainerClass: null,
+        /**
+         * Option: groupHeaderClass
+         * The class added to the heading. Used for styling.
+         */
+        groupHeaderClass: null,
+        /**
+         * Option: listOption
+         * Options to pass to the main list
+         */
+        listOptions: {
+            select: false,
+            hover: false
+        },
+        /**
+         * Option: itemOption
+         * Options to pass to the item lists
+         */
+        itemOptions: {
+            select: true,
+            hover: true,
+            hoverClass: 'jxItemHover',
+            selectClass: 'jxItemSelect'
+        }
+    },
+
+    init: function() {
+        this.groupCols = this.parseTemplate(this.options.groupTemplate);
+        this.itemManager = new Jx.Selection({
+            eventToFire: {
+                select: 'itemselect',
+                unselect: 'itemunselect'
+            },
+            selectClass: 'jxItemSelected'
         });
+        this.groupManager = new Jx.Selection({
+            eventToFire: {
+                select: 'groupselect',
+                unselect: 'groupunselect'
+            },
+            selectClass: 'jxGroupSelected'
+        });
+        this.parent();
 
-        this.colObj.setStyles({
-            top: 0,
-            left: rowWidth,
-            width: size.width - rowWidth - 1,
-            height: colHeight - 1
+    },
+    /**
+     * APIMethod: render
+     * sets up the list container and calls the parent class' render function.
+     */
+    render: function () {
+        this.list = this.createList(this.domA, this.listOptions, this.groupManager);
+        this.parent();
+
+    },
+    /**
+     * Method: draw
+     * actually does the work of creating the view
+     */
+    draw: function () {
+        var d = this.options.data;
+        var n = d.count();
+
+        if ($defined(n) && n > 0) {
+            var currentGroup = '';
+            var itemList = null;
+
+            for (var i = 0; i < n; i++) {
+                d.moveTo(i);
+                var group = d.get(this.options.sortColumns[0]);
+
+                if (group !== currentGroup) {
+                    //we have a new grouping
+
+                    //group container
+                    var container =  new Element('div', {
+                        'class': this.options.groupContainerClass
+                    });
+                    var l = this.createList(container,{
+                        select: false,
+                        hover: false
+                    });
+                    this.list.add(l.container);
+
+                    //group header
+                    currentGroup = group;
+                    var obj = {};
+                    this.groupCols.each(function (col) {
+                        obj[col] = d.get(col);
+                    }, this);
+                    var temp = this.options.groupTemplate.substitute(obj);
+                    var g = new Element('div', {
+                        'class': this.options.groupHeaderClass,
+                        'html': temp,
+                        id: 'group-' + group.replace(" ","-","g")
+                    });
+                    l.add(g);
+
+                    //items container
+                    var currentItemContainer = new Element('div', {
+                        'class': this.options.containerClass
+                    });
+                    itemList = this.createList(currentItemContainer, this.options.itemOptions, this.itemManager);
+                    l.add(itemList.container);
+                }
+
+                var item = this.createItem();
+                itemList.add(item);
+            }
+        } else {
+            var empty = new Element('div', {html: this.options.emptyTemplate});
+            this.list.add(empty);
+        }
+        this.fireEvent('renderDone', this);
+    },
+
+    /**
+     * Method: createList
+     * Creates the list object
+     *
+     * Parameters:
+     * container - the container to use in the list
+     * options - the options for the list
+     * manager - <Jx.Selection> which selection obj to connect to this list
+     */
+    createList: function(container, options, manager){
+        return new Jx.List(container, $extend({
+            onMouseenter: this.enterItem.bind(this),
+            onMouseleave: this.leaveItem.bind(this),
+            onAdd: this.addItem.bind(this),
+            onRemove: this.removeItem.bind(this)
+        }, options), manager);
+    }
+
+});
+/*
+---
+
+name: Jx.ListItem
+
+description: Represents a single item in a listview.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+
+provides: [Jx.ListItem]
+
+...
+ */
+// $Id: listitem.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.ListItem
+ *
+ * Extends: <Jx.Widget>
+ *
+ * Events:
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.ListItem = new Class({
+    Family: 'Jx.ListItem',
+    Extends: Jx.Widget,
+
+    options: {
+        enabled: true,
+        template: '<li class="jxListItemContainer jxListItem"></li>'
+    },
+
+    classes: new Hash({
+        domObj: 'jxListItemContainer',
+        domContent: 'jxListItem'
+    }),
+
+    /**
+     * APIMethod: render
+     */
+    render: function () {
+        this.parent();
+        this.domContent.store('jxListItem', this);
+        this.domObj.store('jxListTarget', this.domContent);
+        this.loadContent(this.domContent);
+    },
+
+    enable: function(state) {
+
+    }
+});/*
+---
+
+name: Jx.ListView
+
+description: A widget that displays items in a list format.
+
+license: MIT-style license.
+
+requires:
+ - Jx.List
+ - Jx.ListItem
+
+provides: [Jx.ListView]
+
+css:
+ - list
+
+images:
+ - listitem.png
+...
+ */
+// $Id: listview.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.ListView
+ *
+ * Extends: <Jx.Widget>
+ *
+ * Events:
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.ListView = new Class({
+    Family: 'Jx.Widget',
+    Extends: Jx.Widget,
+
+    pluginNamespace: 'ListView',
+
+    options: {
+        template: '<ul class="jxListView jxList"></ul>',
+        /**
+         * Option: listOptions
+         * control the behaviour of the list, see <Jx.List>
+         */
+        listOptions: {
+            hover: true,
+            press: true,
+            select: true
+        }
+    },
+
+    classes: new Hash({
+        domObj: 'jxListView',
+        listObj: 'jxList'
+    }),
+
+    /**
+     * APIMethod: render
+     */
+    render: function () {
+        this.parent();
+
+        if (this.options.selection) {
+            this.selection = this.options.selection;
+        } else if (this.options.select) {
+            this.selection = new Jx.Selection(this.options);
+            this.ownsSelection = true;
+        }
+
+        this.list = new Jx.List(this.listObj, this.options.listOptions, this.selection);
+
+    },
+
+    cleanup: function() {
+        if (this.ownsSelection) {
+            this.selection.destroy();
+        }
+        this.list.destroy();
+    },
+
+    add: function(item, where) {
+        this.list.add(item, where);
+        return this;
+    },
+
+    remove: function(item) {
+        this.list.remove(item);
+        return this;
+    },
+
+    replace: function(item, withItem) {
+        this.list.replace(item, withItem);
+        return this;
+    },
+
+    empty: function () {
+        this.list.empty();
+        return this;
+    }
+});/*
+---
+
+name: Jx.Field.Hidden
+
+description: Represents a hidden input
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field
+
+provides: [Jx.Field.Hidden]
+
+...
+ */
+// $Id: hidden.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Field.Hidden
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a hidden input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Hidden = new Class({
+
+    Extends: Jx.Field,
+
+    options: {
+        /**
+         * Option: template
+         * The template used to render this field
+         */
+        template: '<span class="jxInputContainer"><input class="jxInputHidden" type="hidden" name="{name}"/></span>'
+    },
+    /**
+     * Property: type
+     * The type of this field
+     */
+    type: 'Hidden'
+
+});
+
+
+
+
+/*
+---
+
+name: Jx.Field.File
+
+description: Represents a file input w/upload and progress tracking capabilities (requires APC for progress)
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field.Text
+ - Jx.Button
+ - Core/Request.JSON
+ - Jx.Field.Hidden
+ - Jx.Form
+
+provides: [Jx.Field.File]
+
+css:
+ - file
+
+
+...
+ */
+/**
+ * Class: Jx.Field.File
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class is designed to work with an iFrame and APC upload progress.
+ * APC is a php specific technology but any server side implementation that
+ * works in the same manner should work. You can then wire this class to the
+ * progress bar class to show progress.
+ *
+ * The other option is to not use progress tracking and just use the base
+ * upload which works through a hidden iFrame. In order to use this with Jx.Form
+ * you'll need to add it normally but keep a reference to it. When you call
+ * Jx.Form.getValues() it will not return any file information. You can then
+ * call the Jx.Field.File.upload() method for each file input directly and
+ * then submit the rest of the form via ajax.
+ *
+ * MooTools.lang Keys:
+ * - file.browseLabel
+ * 
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.File = new Class({
+
+    Extends: Jx.Field,
+
+    options: {
+        /**
+         * Option: template
+         * The template used to render the field
+         */
+        template: '<span class="jxInputContainer"><label class="jxInputLabel"></label><div class="jxFileInputs"><input class="jxInputFile" type="file" name="{name}" /></div><span class="jxInputTag"></span></span>',
+        /**
+         * Option: autoUpload
+         * Whether to upload the file immediatelly upon selection
+         */
+        autoUpload: false,
+        /**
+         * Option: Progress
+         * Whether to use the APC, or similar, progress method.
+         */
+        progress: false,
+        /**
+         * Option: progressIDUrl
+         * The url to call in order to get the ID, or key, to use
+         * with the APC upload process
+         */
+        progressIDUrl: '',
+        /**
+         * Option: progressName
+         * The name to give the field that holds the generated progress ID retrieved
+         * from the server. Defaults to 'APC_UPLOAD_PROGRESS' which is the default
+         * for APC.
+         */
+        progressName: 'APC_UPLOAD_PROGRESS',
+        /**
+         * Option: progressId
+         * The id to give the form element that holds the generated progress ID
+         * retrieved from the server. Defaults to 'progress_key'.
+         */
+        progressId: 'progress_key',
+        /**
+         * Option: handlerUrl
+         * The url to send the file to.
+         */
+        handlerUrl: '',
+        /**
+         * Option: progressUrl
+         * The url used to retrieve the upload prgress of the file.
+         */
+        progressUrl: '',
+        /**
+         * Option: debug
+         * Defaults to false. If set to true it will prevent the hidden form
+         * and IFrame from being destroyed at the end of the upload so it can be
+         * inspected during development
+         */
+        debug: false,
+        /**
+         * Option: mode
+         * determines whether this file field acts in single upload mode or
+         * multiple file upload mode. The multiple upload mode was done to work with
+         * Jx.Panel.FileUpload. When in multiple mode, this field will remove the actual
+         * file control after a file is selected, fires an event to signify the selection but will
+         * hold on to the files until told to upload them. Obviously 'multiple' mode isn't designed
+         * to be used when the control is outside of the Upload Panel (unless the user designs
+         * their own upload panel around it).
+         */
+        mode: 'single',
+        /**
+         * Events
+         */
+        onFileUploadBegin: $empty,
+        onFileUploadComplete: $empty,
+        onFileUploadProgress: $empty,
+        onFileUploadError: $empty,
+        onFileSelected: $empty
+
+    },
+    /**
+     * Property: type
+     * The Field type used in rendering
+     */
+    type: 'File',
+    /**
+     * Property: forms
+     * holds all form references when we're in multiple mode
+     */
+    forms: null,
+
+    init: function () {
+        this.parent();
+
+        this.forms = new Hash();
+        //create the iframe
+        //we use the same iFrame for each so we don't have to recreate it each time
+        this.setupIframe = true;
+        this.iframe = new Element('iframe', {
+          name: this.generateId(),
+          styles: {
+            'display':'none',
+            'visibility':'hidden'
+          }
         });
+        // this.iframe = new IFrame(null, {
+        //     styles: {
+        //         'display':'none',
+        //         'visibility':'hidden'
+        //     },
+        //     name : this.generateId()
+        // });
+        this.iframe.inject(document.body);
+        this.iframe.addEvent('load', this.processIFrameUpload.bind(this));
 
-        this.gridObj.setStyles({
-            top: colHeight,
-            left: rowWidth,
-            width: size.width - rowWidth - 1,
-            height: size.height - colHeight - 1 
+    },
+
+    /**
+     * APIMethod: render
+     * renders the file input
+     */
+    render: function () {
+        this.parent();
+
+        //add a unique ID if no id is defined
+        if (!$defined(this.options.id)) {
+            this.field.set('id', this.generateId());
+        }
+
+        //now, create the fake inputs
+
+        this.fake = new Element('div', {
+            'class' : 'jxFileFake'
         });
+        this.text = new Jx.Field.Text({
+            template : '<span class="jxInputContainer"><input class="jxInputText" type="text" /></span>'
+        });
+        this.browseButton = new Jx.Button({
+            label: this.getText({set:'Jx',key:'file',value:'browseLabel'})
+        });
+
+        this.fake.adopt(this.text, this.browseButton);
+        this.field.grab(this.fake, 'after');
+
+        this.field.addEvents({
+            change : this.copyValue.bind(this),
+            //mouseout : this.copyValue.bind(this),
+            mouseenter : this.mouseEnter.bind(this),
+            mouseleave : this.mouseLeave.bind(this)
+        });
+
     },
-    
     /**
-     * Method: setModel
-     * set the model for the grid to display.  If a model is attached to the grid
-     * it is removed and the new model is displayed.
-     * 
+     * Method: copyValue
+     * Called when the value in the actual file input changes and when
+     * the mouse moves out of it to copy the value into the "fake" text box.
+     */
+    copyValue: function () {
+        if (this.options.mode=='single' && this.field.value !== '' && (this.text.field.value !== this.field.value)) {
+            this.text.field.value = this.field.value;
+            this.fireEvent('fileSelected', this);
+            if (this.options.autoUpload) {
+                this.uploadSingle();
+            }
+        } else if (this.options.mode=='multiple') {
+            var filename = this.field.value;
+            var form = this.prepForm();
+            this.forms.set(filename, form);
+            this.text.setValue('');
+            //fire the selected event.
+            this.fireEvent('fileSelected', filename);
+        }
+    },
+    /**
+     * Method: mouseEnter
+     * Called when the mouse enters the actual file input to make the
+     * fake button highlight.
+     */
+    mouseEnter: function () {
+        this.browseButton.domA.addClass('jxButtonPressed');
+    },
+    /**
+     * Method: mouseLeave
+     * called when the mouse leaves the actual file input to turn off
+     * the highlight of the fake button.
+     */
+    mouseLeave: function () {
+        this.browseButton.domA.removeClass('jxButtonPressed');
+    },
+
+    prepForm: function () {
+        //load in the form
+        var form = new Jx.Form({
+            action : this.options.handlerUrl,
+            name : 'jxUploadForm',
+            fileUpload: true
+        });
+
+        //move the form input into it (cloneNode)
+        var parent = document.id(this.field.getParent());
+        var sibling = document.id(this.field).getPrevious();
+        var clone = this.field.clone().cloneEvents(this.field);
+        document.id(form).grab(this.field);
+        // tried clone.replaces(this.field) but it didn't seem to work
+        if (sibling) {
+          clone.inject(sibling, 'after');
+        } else if (parent) {
+            clone.inject(parent, 'top');
+        }
+        this.field = clone;
+
+        this.mouseLeave();
+
+        return form;
+    },
+
+    upload: function () {
+        //do we have files to upload?
+        if (this.forms.getLength() > 0) {
+            var keys = this.forms.getKeys();
+            this.currentKey = keys[0];
+            var form = this.forms.get(this.currentKey);
+            this.forms.erase(this.currentKey);
+            this.uploadSingle(form);
+        } else {
+            //fire finished event...
+            this.fireEvent('allUploadsComplete', this);
+        }
+    },
+    /**
+     * APIMethod: uploadSingle
+     * Call this to upload the file to the server
+     */
+    uploadSingle: function (form) {
+        this.form = $defined(form) ? form : this.prepForm();
+
+        this.fireEvent('fileUploadBegin', [this.currentKey, this]);
+
+        this.text.setValue('');
+
+        document.id(this.form).set('target', this.iframe.get('name')).setStyles({
+            visibility: 'hidden',
+            display: 'none'
+        }).inject(document.body);
+
+        //if polling the server we need an APC_UPLOAD_PROGRESS id.
+        //get it from the server.
+        if (this.options.progress) {
+            var req = new Request.JSON({
+                url: this.options.progressIDUrl,
+                method: 'get',
+                onSuccess: this.submitUpload.bind(this)
+            });
+            req.send();
+        } else {
+            this.submitUpload();
+        }
+    },
+    /**
+     * Method: submitUpload
+     * Called either after upload() or as a result of a successful call
+     * to get a progress ID.
+     *
      * Parameters:
-     * model - {Object} the model to use for this grid
+     * data - Optional. The data returned from the call for a progress ID.
      */
-    setModel: function(model) {
-        this.model = model;
-        if (this.model) {
-            if (this.domObj.resize) {
-                this.domObj.resize();
+    submitUpload: function (data) {
+        //check for ID in data
+        if ($defined(data) && data.success && $defined(data.id)) {
+            this.progressID = data.id;
+            //if have id, create hidden progress field
+            var id = new Jx.Field.Hidden({
+                name : this.options.progressName,
+                id : this.options.progressId,
+                value : this.progressID
+            });
+            id.addTo(this.form, 'top');
+        }
+
+        //submit the form
+        document.id(this.form).submit();
+        //begin polling if needed
+        if (this.options.progress && $defined(this.progressID)) {
+            this.pollUpload();
+        }
+    },
+    /**
+     * Method: pollUpload
+     * polls the server for upload progress information
+     */
+    pollUpload: function () {
+        var d = { id : this.progressID };
+        var r = new Request.JSON({
+            data: d,
+            url : this.options.progressUrl,
+            method : 'get',
+            onSuccess : this.processProgress.bind(this),
+            onFailure : this.uploadFailure.bind(this)
+        });
+        r.send();
+    },
+
+    /**
+     * Method: processProgress
+     * process the data returned from the request
+     *
+     * Parameters:
+     * data - The data from the request as an object.
+     */
+    processProgress: function (data) {
+        if ($defined(data)) {
+            this.fireEvent('fileUploadProgress', [data, this.currentKey, this]);
+            if (data.current < data.total) {
+                this.polling = true;
+                this.pollUpload();
+            } else {
+                this.polling = false;
             }
-            this.createGrid();
-            this.resize();
+        }
+    },
+    /**
+     * Method: uploadFailure
+     * called if there is a problem getting progress on the upload
+     */
+    uploadFailure: function (xhr) {
+        this.fireEvent('fileUploadProgressError', [this, xhr]);
+    },
+    /**
+     * Method: processIFrameUpload
+     * Called if we are not using progress and the IFrame finished loading the
+     * server response.
+     */
+    processIFrameUpload: function () {
+        //the body text should be a JSON structure
+        //get the body
+        if (!this.setupIframe) {
+            if (this.iframe.contentDocument  && this.iframe.contentDocument.defaultView) {
+              var iframeBody = this.iframe.contentDocument.defaultView.document.body.innerHTML;
+            } else {
+              // seems to be needed for ie7
+              var iframeBody = this.iframe.contentWindow.document.body.innerHTML;
+            }
+
+            var data = JSON.decode(iframeBody);
+            if ($defined(data.success) && data.success) {
+                this.done = true;
+                this.doneData = data;
+                this.uploadCleanUp();
+                this.fireEvent('fileUploadComplete', [data, this.currentKey, this]);
+            } else {
+                this.fireEvent('fileUploadError', [data , this.currentKey, this]);
+            }
+
+            if (this.options.mode == 'multiple') {
+                this.upload();
+            } else {
+                this.fireEvent('allUploadsComplete', this);
+            }
         } else {
-            this.destroyGrid();
+            this.setupIframe = false;
         }
     },
+    /**
+     * Method: uploadCleanUp
+     * Cleans up the hidden form and IFrame after a completed upload. Set
+     * this.options.debug to true to keep this from happening
+     */
+    uploadCleanUp: function () {
+        if (!this.options.debug) {
+            this.form.destroy();
+            if (this.options.mode == 'single') {
+                this.iframe.destroy();
+            }
+        }
+    },
+    /**
+     * APIMethod: remove
+     * Removes a file from the hash of forms to upload.
+     *
+     * Parameters:
+     * filename - the filename indicating which file to remove.
+     */
+    remove: function (filename) {
+        if (this.forms.has(filename)) {
+            this.forms.erase(filename);
+        }
+    },
     
     /**
-     * Method: destroyGrid
-     * destroy the contents of the grid safely
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     * 
+     * Parameters:
+     * lang - the language being changed to or that had it's data set of 
+     * 		translations changed.
      */
-    destroyGrid: function() {
-        var n = this.colTableHead.cloneNode(false);
-        this.colTable.replaceChild(n, this.colTableHead);
-        this.colTableHead = n;
+    changeText: function (lang) {
+    	this.parent();
+    	if ($defined(this.browseButton)) {
+    		this.browseButton.setLabel( this.getText({set:'Jx',key:'file',value:'browseLabel'}) );
+    	}
+    }
+});/*
+---
+
+name: Jx.Progressbar
+
+description: A css-based progress bar.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+ - Core/Fx.Tween
+
+provides: [Jx.Progressbar]
+
+css:
+ - progressbar
+
+images:
+ - progressbar.png
+
+...
+ */
+/**
+ * Class: Jx.Progressbar
+ *
+ * 
+ * Example:
+ * The following just uses the defaults.
+ * (code)
+ * var progressBar = new Jx.Progressbar();
+ * progressBar.addEvent('update',function(){alert('updated!');});
+ * progressBar.addEvent('complete',function(){
+ *      alert('completed!');
+ *      this.destroy();
+ * });
+ * 
+ * progressbar.addTo('container');
+ * 
+ * var total = 90;
+ * for (i=0; i < total; i++) {
+ *      progressbar.update(total, i);
+ * }
+ * (end)
+ * 
+ * Events:
+ * onUpdate - Fired when the bar is updated
+ * onComplete - fires when the progress bar completes it's fill
+ * 
+ * MooTools.lang keys:
+ * - progressbar.messageText
+ * - progressbar.progressText
+ *
+ * Copyright (c) 2010 by Jonathan Bomgardner
+ * Licensed under an mit-style license
+ */
+Jx.Progressbar = new Class({
+    Family: 'Jx.Progressbar',
+    Extends: Jx.Widget,
+    
+    options: {
+        onUpdate: $empty,
+        onComplete: $empty,
+        /**
+         * Option: parent
+         * The element to put this progressbar into
+         */
+        parent: null,
+        /**
+         * Option: progressText
+         * Text to show while processing, uses 
+         * {progress} von {total}
+         */
+        progressText : null,
+        /**
+         * Option: template
+         * The template used to create the progressbar
+         */
+        template: '<div class="jxProgressBar-container"><div class="jxProgressBar-message"></div><div class="jxProgressBar"><div class="jxProgressBar-outline"></div><div class="jxProgressBar-fill"></div><div class="jxProgressBar-text"></div></div></div>'
+    },
+    /**
+     * Property: classes
+     * The classes used in the template
+     */
+    classes: new Hash({
+        domObj: 'jxProgressBar-container',
+        message: 'jxProgressBar-message', 
+        container: 'jxProgressBar',
+        outline: 'jxProgressBar-outline',
+        fill: 'jxProgressBar-fill',
+        text: 'jxProgressBar-text'
+    }),
+    /**
+     * Property: bar
+     * the bar that is filled
+     */
+    bar: null,
+    /**
+     * Property: text
+     * the element that contains the text that's shown on the bar (if any).
+     */
+    text: null,
+    
+    /**
+     * APIMethod: render
+     * Creates a new progressbar.
+     */
+    render: function () {
+        this.parent();
         
-        n = this.colTableBody.cloneNode(false);
-        this.colTable.replaceChild(n, this.colTableBody);
-        this.colTableBody = n;
+        if ($defined(this.options.parent)) {
+            this.domObj.inject(document.id(this.options.parent));
+        }
         
-        n = this.rowTableHead.cloneNode(false);
-        this.rowTable.replaceChild(n, this.rowTableHead);
-        this.rowTableHead = n;
+        this.domObj.addClass('jxProgressStarting');
+
+        //we need to know the width of the bar
+        this.width = document.id(this.domObj).getContentBoxSize().width;
         
-        n = this.gridTableBody.cloneNode(false);
-        this.gridTable.replaceChild(n, this.gridTableBody);
-        this.gridTableBody = n;
+        //Message
+        if (this.message) {
+            if ($defined(MooTools.lang.get('Jx','progressbar').messageText)) {
+                this.message.set('html', this.getText({set:'Jx',key:'progressbar',value:'messageText'}));
+            } else {
+                this.message.destroy();
+            }
+        }
+
+        //Fill
+        if (this.fill) {
+            this.fill.setStyles({
+                'width': 0
+            });
+        }
         
+        //TODO: check for {progress} and {total} in progressText
+        var obj = {};
+        var progressText = this.options.progressText == null ? 
+                              this.getText({set:'Jx',key:'progressbar',value:'progressText'}) :
+                              this.getText(this.options.progressText);
+        if (progressText.contains('{progress}')) {
+            obj.progress = 0;
+        }
+        if (progressText.contains('{total}')) {
+            obj.total = 0;
+        }
+        
+        //Progress text
+        if (this.text) {
+            this.text.set('html', progressText.substitute(obj));
+        }
+        
     },
-    
     /**
-     * Method: createGrid
-     * create the grid for the current model
+     * APIMethod: update
+     * called to update the progress bar with new percentage.
+     * 
+     * Parameters: 
+     * total - the total # to progress up to
+     * progress - the current position in the progress (must be less than or
+     *              equal to the total)
      */
-    createGrid: function() {
-        this.destroyGrid();
-        if (this.model) {
-            var model = this.model;
-            var nColumns = model.getColumnCount();
-            var nRows = model.getRowCount();
+    update: function (total, progress) {
+    	
+    	//check for starting class
+    	if (this.domObj.hasClass('jxProgressStarting')) {
+    		this.domObj.removeClass('jxProgressStarting').addClass('jxProgressWorking');
+    	}
+    	
+        var newWidth = (progress * this.width) / total;
+        
+        //update bar width
+        this.text.get('tween', {property:'width', onComplete: function() {
+            var obj = {};
+            var progressText = this.options.progressText == null ?
+                                  this.getText({set:'Jx',key:'progressbar',value:'progressText'}) :
+                                  this.getText(this.options.progressText);
+            if (progressText.contains('{progress}')) {
+                obj.progress = progress;
+            }
+            if (progressText.contains('{total}')) {
+                obj.total = total;
+            }
+            var t = progressText.substitute(obj);
+            this.text.set('text', t);
+        }.bind(this)}).start(newWidth);
+        
+        this.fill.get('tween', {property: 'width', onComplete: (function () {
             
-            /* create header if necessary */
-            if (this.options.columnHeaders) {
-                var colHeight = model.getColumnHeaderHeight();
-                var trHead = new Element('tr');
-                this.colTableHead.appendChild(trHead);
-                var trBody = new Element('tr');
-                this.colTableBody.appendChild(trBody);
-                
-                var th = new Element('th', {styles:{width:0,height:0}});
-                trHead.appendChild(th);
-                th = th.cloneNode(true);
-                th.setStyle('height',colHeight);
-                trBody.appendChild(th);
-                for (var i=0; i<nColumns; i++) {
-                    var colWidth = model.getColumnWidth(i);
-                    th = new Element('th', {'class':'jxGridColHeadHide',styles:{width:colWidth}});
-                    var p = new Element('p', {styles:{height:0,width:colWidth}});
-                    th.appendChild(p);
-                    trHead.appendChild(th);
-                    th = new Element('th', {
-                        'class':'jxGridColHead', 
-                        html:model.getColumnHeaderHTML(i)
-                    });
-                    trBody.appendChild(th);
-                }
-                /* one extra column at the end for filler */
-                var th = new Element('th',{styles:{width:1000,height:0}});
-                trHead.appendChild(th);
-                th = th.cloneNode(true);
-                th.setStyle('height',colHeight - 1);
-                th.className = 'jxGridColHead';
-                trBody.appendChild(th);
-                
+            if (total === progress) {
+                this.complete = true;
+                this.domObj.removeClass('jxProgressWorking').addClass('jxProgressFinished');
+                this.fireEvent('complete');
+            } else {
+                this.fireEvent('update');
             }
-            
-            if (this.options.rowHeaders) {
-                var rowWidth = model.getRowHeaderWidth();
-                var tr = new Element('tr');
-                var td = new Element('td', {styles:{width:0,height:0}});
-                tr.appendChild(td);
-                var th = new Element('th', {styles:{width:rowWidth,height:0}});
-                tr.appendChild(th);
-                this.rowTableHead.appendChild(tr);
-                for (var i=0; i<nRows; i++) {
-                    var rowHeight = model.getRowHeight(i);
-                    var tr = new Element('tr');
-                    var td = new Element('td', {'class':'jxGridRowHeadHide', styles:{width:0,height:rowHeight}});
-                    var p = new Element('p', {styles:{width:0,height:rowHeight}});
-                    td.appendChild(p);
-                    tr.appendChild(td);
-                    var th = new Element('th', {'class':'jxGridRowHead', html:model.getRowHeaderHTML(i)});
-                    tr.appendChild(th);
-                    this.rowTableHead.appendChild(tr);
-                }
-                /* one extra row at the end for filler */
-                var tr = new Element('tr');
-                var td = new Element('td',{
-                    styles:{
-                        width:0,
-                        height:1000
-                    }
+        }).bind(this)}).start(newWidth);
+        
+    },
+    
+    /**
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     * 
+     * Parameters:
+     * lang - the language being changed to or that had it's data set of 
+     * 		translations changed.
+     */
+    changeText: function (lang) {
+    	this.parent();
+    	if (this.message) {
+    		this.message.set('html',this.getText({set:'Jx',key:'progressbar',value:'messageText'}));
+    	}
+        //progress text will update on next update.
+    }
+    
+});/*
+---
+
+name: Jx.Panel.FileUpload
+
+description: A panel subclass that is designed to be a multiple file upload panel with a queue listing.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Panel
+ - Jx.ListView
+ - Jx.Field.File
+ - Jx.Progressbar
+ - Jx.Button
+ - Jx.Toolbar.Item
+ - Jx.Tooltip
+
+provides: [Jx.Panel.FileUpload]
+
+css:
+ - upload
+
+images:
+ - icons.png
+...
+ */
+// $Id: upload.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Panel.FileUpload
+ *
+ * Extends: <Jx.Panel>
+ *
+ * This class extends Jx.Panel to provide a consistent interface for uploading
+ * files in an application.
+ * 
+ * MooTools.lang Keys:
+ * - upload.buttonText
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Panel.FileUpload = new Class({
+
+    Family: 'Jx.Panel.FileUpload',
+    Extends: Jx.Panel,
+    Binds: ['moveToQueue','fileUploadBegin', 'fileUploadComplete','allUploadsComplete', 'fileUploadProgressError,', 'fileUploadError', 'fileUploadProgress'],
+
+    options: {
+        /**
+         * Option: file
+         * An object containing the options for Jx.Field.File
+         */
+        file: {
+            autoUpload: false,
+            progress: false,
+            progressIDUrl: '',
+            handlerUrl: '',
+            progressUrl: ''
+        },
+
+        progressOptions: {
+            template: "<li class='jxListItemContainer jxProgressBar-container' id='{id}'><div class='jxProgressBar'><div class='jxProgressBar-outline'></div><div class='jxProgressBar-fill'></div><div class='jxProgressBar-text'></div></div></li>",
+            containerClass: 'progress-container',
+            messageText: null,
+            messageClass: 'progress-message',
+            progressText: 'Uploading {filename}',
+            progressClass: 'progress-bar'
+        },
+        /**
+         * Option: onFileComplete
+         * An event handler that is called when a file has been uploaded
+         */
+        onFileComplete: $empty,
+        /**
+         * Option: onComplete
+         * An event handler that is called when all files have been uploaded
+         */
+        onComplete: $empty,
+        /**
+         * Option: prompt
+         * The prompt to display at the top of the panel - before the
+         * file input
+         */
+        prompt: null,
+        /**
+         * Option: removeOnComplete
+         * Determines whether a file is removed from the queue after uploading
+         */
+        removeOnComplete: false
+    },
+    /**
+     * Property: domObjA
+     * An HTML Element used to hold the interface while it is being
+     * constructed.
+     */
+    domObjA: null,
+    /**
+     * Property: fileQueue
+     * An array holding Jx.Field.File elements that are to be uploaded
+     */
+    fileQueue: [],
+
+    listTemplate: "<li class='jxListItemContainer' id='{id}'><a class='jxListItem' href='javascript:void(0);'><span class='itemLabel jxUploadFileName'>{name}</span><span class='jxUploadFileDelete' title='Remove this file from the queue.'></span></a></li>",
+    /**
+     * Method: render
+     * Sets up the upload panel.
+     */
+    render: function () {
+        //first create panel content
+        this.domObjA = new Element('div', {'class' : 'jxFileUploadPanel'});
+
+
+        if ($defined(this.options.prompt)) {
+            var desc;
+            if (Jx.type(this.options.prompt === 'string')) {
+                desc = new Element('p', {
+                    html: this.options.prompt
                 });
-                tr.appendChild(td);
-                var th = new Element('th',{
-                    'class':'jxGridRowHead',
-                    styles:{
-                        width:rowWidth,
-                        height:1000
-                    }
-                });
-                tr.appendChild(th);
-                this.rowTableHead.appendChild(tr);
+            } else {
+                desc = this.options.prompt;
             }
+            desc.inject(this.domObjA);
+        }
+
+        //add the file field
+        this.fileOpt = this.options.file;
+        this.fileOpt.template = '<div class="jxInputContainer jxFileInputs"><input class="jxInputFile" type="file" name={name} /></div>';
+
+        this.file = new Jx.Field.File(this.fileOpt);
+        this.file.addEvent('fileSelected', this.moveToQueue);
+        this.file.addTo(this.domObjA);
+
+        this.listView = new Jx.ListView({
+            template: '<ul class="jxListView jxList jxUploadQueue"></ul>'
             
-            var colHeight = model.getColumnHeaderHeight();
-            var trBody = new Element('tr');
-            this.gridTableBody.appendChild(trBody);
-            
-            var td = new Element('td', {styles:{width:0,height:0}});
-            trBody.appendChild(td);
-            for (var i=0; i<nColumns; i++) {
-                var colWidth = model.getColumnWidth(i);
-                td = new Element('td', {'class':'jxGridColHeadHide', styles:{width:colWidth}});
-                var p = new Element('p', {styles:{width:colWidth,height:0}});
-                td.appendChild(p);
-                trBody.appendChild(td);
+        }).addTo(this.domObjA);
+
+        //this is the upload button at the bottom of the panel.
+        this.uploadBtn = new Jx.Button({
+            label : this.getText({set:'Jx',key:'upload',value:'buttonText'}),
+            onClick: this.upload.bind(this)
+        });
+        var tlb = new Jx.Toolbar({position: 'bottom', scroll: false}).add(this.uploadBtn);
+        this.uploadBtn.setEnabled(false);
+        this.options.toolbars = [tlb];
+        //then pass it on to the Panel constructor
+        this.options.content = this.domObjA;
+        this.parent(this.options);
+    },
+    /**
+     * Method: moveToQueue
+     * Called by Jx.Field.File's fileSelected event. Moves the selected file
+     * into the upload queue.
+     */
+    moveToQueue: function (filename) {
+        var theTemplate = new String(this.listTemplate).substitute({
+            name: filename,
+            id: filename
+        });
+        var item = new Jx.ListItem({template:theTemplate, enabled: true});
+
+        $(item).getElement('.jxUploadFileDelete').addEvent('click', function(){
+            this.listView.remove(item);
+            this.file.remove(filename);
+            if (this.listView.list.count() == 0) {
+                this.uploadBtn.setEnabled(false);
             }
-            
-            for (var j=0; j<nRows; j++) {
-                var rowHeight = model.getRowHeight(j);
-                var actualRowHeight = rowHeight;
-                var tr = new Element('tr');
-                this.gridTableBody.appendChild(tr);
-                
-                var td = new Element('td', {
-                    'class':'jxGridRowHeadHide',
-                    styles: {
-                        width: 0,
-                        height:rowHeight
+        }.bind(this));
+        this.listView.add(item);
+
+        if (!this.uploadBtn.isEnabled()) {
+            this.uploadBtn.setEnabled(true);
+        }
+
+    },
+    /**
+     * Method: upload
+     * Called when the user clicks the upload button. Runs the upload process.
+     */
+    upload: function () {
+
+        this.file.addEvents({
+            'fileUploadBegin': this.fileUploadBegin ,
+            'fileUploadComplete': this.fileUploadComplete,
+            'allUploadsComplete': this.allUploadsComplete,
+            'fileUploadError': this.fileUploadError,
+            'fileUploadProgress': this.fileUploadProgress,
+            'fileUploadProgressError': this.fileUploadError
+        });
+
+
+        this.file.upload();
+    },
+
+    fileUploadBegin: function (filename) {
+        if (this.options.file.progress) {
+            //progressbar
+            //setup options
+            // TODO: should (at least some of) these options be available to
+            // the developer?
+            var options = $merge({},this.options.progressOptions);
+            options.progressText = options.progressText.substitute({filename: filename});
+            options.template = options.template.substitute({id: filename});
+            this.pb = new Jx.Progressbar(options);
+            var item = document.id(filename);
+            this.oldContents = item;
+            this.listView.replace(item,$(this.pb));
+        } else {
+            var icon = document.id(filename).getElement('.jxUploadFileDelete')
+            icon.addClass('jxUploadFileProgress').set('title','File Uploading...');
+        }
+    },
+
+    /**
+     * Method: fileUploadComplete
+     * Called when a single file is uploaded completely .
+     *
+     * Parameters:
+     * data - the data returned from the event
+     * filename - the filename of the file we're tracking
+     */
+    fileUploadComplete: function (data, file) {
+        if ($defined(data.success) && data.success ){
+            this.removeUploadedFile(file);
+        } else {
+            this.fileUploadError(data, file);
+        }
+    },
+    /**
+     * Method: fileUploadError
+     * Called when there is an error uploading a file.
+     *
+     * Parameters:
+     * data - the data passed back from the server, if any.
+     * file - the file we're tracking
+     */
+    fileUploadError: function (data, filename) {
+
+        if (this.options.file.progress) {
+            //show this old contents...
+            this.listView.replace(document.id(filename),this.oldContents);
+        }
+        var icon = document.id(filename).getElement('.jxUploadFileDelete');
+        icon.erase('title');
+        if (icon.hasClass('jxUploadFileProgress')) {
+            icon.removeClass('jxUploadFileProgress').addClass('jxUploadFileError');
+        } else {
+            icon.addClass('jxUploadFileError');
+        }
+        if ($defined(data.error) && $defined(data.error.message)) {
+            var tt = new Jx.Tooltip(icon, data.error.message, {
+                cssClass : 'jxUploadFileErrorTip'
+            });
+        }
+    },
+    /**
+     * Method: removeUploadedFile
+     * Removes the passed file from the upload queue upon it's completion.
+     *
+     * Parameters:
+     * file - the file we're tracking
+     */
+    removeUploadedFile: function (filename) {
+
+        if (this.options.removeOnComplete) {
+           this.listView.remove(document.id(filename));
+        } else {
+            if (this.options.file.progress) {
+                this.listView.replace(document.id(filename),this.oldContents);
+            }
+            var l = document.id(filename).getElement('.jxUploadFileDelete');
+            if (l.hasClass('jxUploadFileDelete')) {
+                l.addClass('jxUploadFileComplete');
+            } else if (l.hasClass('jxUploadFileProgress')) {
+                l.removeClass('jxUploadFileProgress').addClass('jxUploadFileComplete');
+            }
+        }
+
+        this.fireEvent('fileUploadComplete', filename);
+    },
+    /**
+     * Method: fileUploadProgress
+     * Function to pass progress information to the progressbar instance
+     * in the file. Only used if we're tracking progress.
+     */
+    fileUploadProgress: function (data, file) {
+        if (this.options.progress) {
+            this.pb.update(data.total, data.current);
+        }
+    },
+    /**
+     * Method: allUploadCompleted
+     * Called when the Jx.Field.File completes uploading
+     * all files. Sets upload button to disabled and fires the allUploadCompleted
+     * event.
+     */
+    allUploadsComplete: function () {
+        this.uploadBtn.setEnabled(false);
+        this.fireEvent('allUploadsCompleted',this);
+    },
+    /**
+     * Method: createText
+     * handle change in language
+     */
+    changeText: function (lang) {
+      this.parent();
+      if ($defined(this.uploadBtn)) {
+        this.uploadBtn.setLabel({set:'Jx',key:'upload',value:'buttonText'});
+      }
+    }
+});
+/*
+---
+
+name: Jx.Column
+
+description: A representation of a single grid column
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+
+provides: [Jx.Column]
+
+...
+ */
+// $Id: column.js 992 2010-10-07 19:28:37Z pagameba $
+/**
+ * Class: Jx.Column
+ *
+ * Extends: <Jx.Object>
+ *
+ * The class used for defining columns for grids.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Column = new Class({
+
+    Family: 'Jx.Column',
+    Extends: Jx.Widget,
+
+    options: {
+        /**
+         * Option: renderMode
+         * The mode to use in rendering this column to determine its width.
+         * Valid options include
+         *
+         * fit - auto calculates the width for the best fit to the text. This
+         *      is the default.
+         * fixed - uses the value passed in the width option, doesn't
+         *      auto calculate.
+         * expand - uses the value in the width option as a minimum width and
+         *      allows this column to expand and take up any leftover space.
+         *      NOTE: there can be only 1 expand column in a grid. The
+         *      Jx.Columns object will only take the first column with this
+         *      option as the expanding column. All remaining columns marked
+         *      "expand" will be treated as "fixed".
+         */
+        renderMode: 'fixed',
+        /**
+         * Option: width
+         * Determines the width of the column when using 'fixed' rendering mode
+         * or acts as a minimum width when using 'expand' mode.
+         */
+        width: 100,
+
+        /**
+         * Option: isEditable
+         * allows/disallows editing of the column contents
+         */
+        isEditable: false,
+        /**
+         * Option: isSortable
+         * allows/disallows sorting based on this column
+         */
+        isSortable: false,
+        /**
+         * Option: isResizable
+         * allows/disallows resizing this column dynamically
+         */
+        isResizable: false,
+        /**
+         * Option: isHidden
+         * determines if this column can be shown or not
+         */
+        isHidden: false,
+        /**
+         * Option: name
+         * The name given to this column
+         */
+        name: '',
+
+        /**
+         * Option: template
+         */
+        template: null,
+        /**
+         * Option: renderer
+         * an instance of a Jx.Grid.Renderer to use in rendering the content
+         * of this column or a config object for creating one like so:
+         *
+         * (code)
+         * {
+         *     name: 'Text',
+         *     options: { ... renderer options ... }
+         * }
+         */
+        renderer: null
+    },
+
+    classes: $H({
+      domObj: 'jxGridCellContent'
+    }),
+
+    /**
+     * Property: grid
+     * holds a reference to the grid (an instance of <Jx.Grid>)
+     */
+    grid: null,
+
+    parameters: ['options','grid'],
+
+    /**
+     * Constructor: Jx.Column
+     * initializes the column object
+     */
+    init : function () {
+
+        this.name = this.options.name;
+
+        //adjust header for column
+        if (!$defined(this.options.template)) {
+            this.options.template = '<span class="jxGridCellContent">' + this.name.capitalize() + '</span>';
+        }
+
+        this.parent();
+        if ($defined(this.options.grid) && this.options.grid instanceof Jx.Grid) {
+            this.grid = this.options.grid;
+        }
+
+        //check renderer
+        if (!$defined(this.options.renderer)) {
+            //set a default renderer
+            this.options.renderer = new Jx.Grid.Renderer.Text({
+                textTemplate: '{' + this.name + '}'
+            });
+        } else {
+            if (!(this.options.renderer instanceof Jx.Grid.Renderer)) {
+                var t = Jx.type(this.options.renderer);
+                if (t === 'object') {
+                    if(!$defined(this.options.renderer.options.textTemplate)) {
+                      this.options.renderer.options.textTemplate = '{' + this.name + '}';
                     }
-                });
-                var p = new Element('p',{styles:{height:rowHeight}});
-                td.appendChild(p);
-                tr.appendChild(td);
-                for (var i=0; i<nColumns; i++) {
-                    var colWidth = model.getColumnWidth(i);
-                    td = new Element('td', {'class':'jxGridCell'});
-                    td.innerHTML = model.getValueAt(j,i);
-                    tr.appendChild(td);
-                    var tdSize = td.getSize();
-                    if (tdSize.height > actualRowHeight) {
-                        actualRowHeight = tdSize.height;
+                    if(!$defined(this.options.renderer.name)) {
+                      this.options.renderer.name = 'Text';
                     }
+                    this.options.renderer = new Jx.Grid.Renderer[this.options.renderer.name.capitalize()](
+                            this.options.renderer.options);
                 }
-                /* some notes about row sizing
-                 * In Safari, the height of a TR is always returned as 0
-                 * In Safari, the height of any given TD is the height it would
-                 * render at, not the actual height of the row
-                 * In IE, the height is returned 1px bigger than any other browser
-                 * Firefox just works
-                 *
-                 * So, for Safari, we have to measure every TD and take the highest one
-                 * and if its IE, we subtract 1 from the overall height, making all
-                 * browsers identical
-                 *
-                 * Using document.all is not a good hack for this
-                 */
-                if (document.all) {
-                    actualRowHeight -= 1;
-                }
-                if (this.options.rowHeaders) {
-                    this.setRowHeaderHeight(j, actualRowHeight);                    
-                }
-                /* if we apply the class before adding content, it
-                 * causes a rendering error in IE (off by 1) that is 'fixed'
-                 * when another class is applied to the row, causing dynamic
-                 * shifting of the row heights
-                 */
-                if (this.options.alternateRowColors) {
-                    tr.className = (j%2) ? 'jxGridRowOdd' : 'jxGridRowEven';
-                } else {
-                    tr.className = 'jxGridRowAll';
-                }
             }
-            
         }
+
+        this.options.renderer.setColumn(this);
     },
-    
+
+    getTemplate: function(idx) {
+      return "<span class='jxGridCellContent' title='{col"+idx+"}'>{col"+idx+"}</span>";
+    },
+
     /**
-     * Method: setRowHeaderHeight
-     * set the height of a row.  This is used internally to adjust the height of
-     * the row header when cell contents wrap.  A limitation of the table structure
-     * is that overflow: hidden on a td will work horizontally but not vertically
+     * APIMethod: getHeaderHTML
+     */
+    getHeaderHTML : function () {
+      if (this.isSortable() && !this.sortImage) {
+        this.sortImage = new Element('img', {
+            src: Jx.aPixel.src
+        });
+        this.sortImage.inject(this.domObj);
+      } else {
+        if (!this.isSortable() && this.sortImage) {
+          this.sortImage.dispose();
+          this.sortImage = null;
+        }
+      }
+      return this.domObj;
+    },
+
+    setWidth: function(newWidth, asCellWidth) {
+        asCellWidth = $defined(asCellWidth) ? asCellWidth : false;
+
+        var delta = this.cellWidth - this.width;
+        if (!asCellWidth) {
+          this.width = parseInt(newWidth,10);
+          this.cellWidth = this.width + delta;
+          this.options.width = newWidth;
+        } else {
+            this.width = parseInt(newWidth,10) - delta;
+            this.cellWidth = newWidth;
+            this.options.width = this.width;
+        }
+      if (this.rule && parseInt(this.width,10) >= 0) {
+          this.rule.style.width = parseInt(this.width,10) + "px";
+      }
+      if (this.cellRule && parseInt(this.cellWidth,10) >= 0) {
+          this.cellRule.style.width = parseInt(this.cellWidth,10) + "px";
+      }
+    },
+
+    /**
+     * APIMethod: getWidth
+     * return the width of the column
+     */
+    getWidth: function () {
+      return this.width;
+    },
+
+    /**
+     * APIMethod: getCellWidth
+     * return the cellWidth of the column
+     */
+    getCellWidth: function() {
+      return this.cellWidth;
+    },
+
+    /**
+     * APIMethod: calculateWidth
+     * returns the width of the column.
      *
      * Parameters:
-     * row - {Integer} the row to set the height for
-     * height - {Integer} the height to set the row (in pixels)
+     * rowHeader - flag to tell us if this calculation is for the row header
      */
-    setRowHeaderHeight: function(row, height) {
-        //this.rowTableHead.childNodes[row+1].childNodes[0].style.height = (height) + 'px';
-        this.rowTableHead.childNodes[row+1].childNodes[0].childNodes[0].style.height = (height) + 'px';
+    calculateWidth : function (rowHeader) {
+        //if this gets called then we assume that we want to calculate the width
+      rowHeader = $defined(rowHeader) ? rowHeader : false;
+      var maxWidth,
+          maxCellWidth,
+          store = this.grid.getStore(),
+          t,
+          s,
+          oldPos,
+          text,
+          klass;
+      store.first();
+      if ((this.options.renderMode == 'fixed' ||
+           this.options.renderMode == 'expand') &&
+          store.valid()) {
+        t = new Element('span', {
+          'class': 'jxGridCellContent',
+          html: 'a',
+          styles: {
+            width: this.options.width
+          }
+        });
+        s = this.measure(t,'jxGridCell');
+        maxWidth = s.content.width;
+        maxCellWidth = s.cell.width;
+      } else {
+          //calculate the width
+          oldPos = store.getPosition();
+          maxWidth = maxCellWidth = 0;
+          while (store.valid()) {
+              //check size by placing text into a TD and measuring it.
+              this.options.renderer.render();
+              text = document.id(this.options.renderer);
+              klass = 'jxGridCell';
+              if (this.grid.row.useHeaders()
+                      && this.options.name === this.grid.row
+                      .getRowHeaderColumn()) {
+                  klass = 'jxGridRowHead';
+              }
+              s = this.measure(text, klass, rowHeader, store.getPosition());
+              if (s.content.width > maxWidth) {
+                  maxWidth = s.content.width;
+              }
+              if (s.cell.width > maxCellWidth) {
+                maxCellWidth = s.cell.width;
+              }
+              if (store.hasNext()) {
+                  store.next();
+              } else {
+                  break;
+              }
+          }
+
+          //check the column header as well (unless this is the row header)
+          if (!(this.grid.row.useHeaders() &&
+              this.options.name === this.grid.row.getRowHeaderColumn())) {
+              klass = 'jxGridColHead';
+              if (this.isEditable()) {
+                  klass += ' jxColEditable';
+              }
+              if (this.isResizable()) {
+                  klass += ' jxColResizable';
+              }
+              if (this.isSortable()) {
+                  klass += ' jxColSortable';
+              }
+              s = this.measure(this.domObj.clone(), klass);
+              if (s.content.width > maxWidth) {
+                  maxWidth = s.content.width;
+              }
+              if (s.cell.width > maxCellWidth) {
+                maxCellWidth = s.cell.width;
+              }
+          }
+      }
+
+      this.width = maxWidth;
+      this.cellWidth = maxCellWidth;
+      store.moveTo(oldPos);
+      return this.width;
     },
-    
     /**
-     * Method: gridChanged
-     * called through the grid listener interface when data has changed in the
-     * underlying model
+     * Method: measure
+     * This method does the dirty work of actually measuring a cell
      *
      * Parameters:
-     * model - {Object} the model that changed
-     * row - {Integer} the row that changed
-     * col - {Integer} the column that changed
-     * value - {Mixed} the new value
+     * text - the text to measure
+     * klass - a string indicating and extra classes to add so that
+     *          css classes can be taken into account.
+     * rowHeader -
+     * row -
      */
-    gridChanged: function(model, row, col, value) {
-        if (this.model == model) {
-            this.gridObj.childNodes[row].childNodes[col].innerHTML = value;
+    measure : function (text, klass, rowHeader, row) {
+        var d = new Element('span', {
+            'class' : klass
+        }),
+        s;
+        text.inject(d);
+        //d.setStyle('height', this.grid.row.getHeight(row));
+        d.setStyles({
+            'visibility' : 'hidden',
+            'width' : 'auto'
+        });
+
+        d.inject(document.body, 'bottom');
+        s = d.measure(function () {
+            var el = this;
+            //if not rowHeader, get size of innner span
+            if (!rowHeader) {
+                el = el.getFirst();
+            }
+            return {
+              content: el.getMarginBoxSize(),
+              cell: el.getMarginBoxSize()
+            };
+        });
+        d.destroy();
+        return s;
+    },
+    /**
+     * APIMethod: isEditable
+     * Returns whether this column can be edited
+     */
+    isEditable : function () {
+        return this.options.isEditable;
+    },
+    /**
+     * APIMethod: isSortable
+     * Returns whether this column can be sorted
+     */
+    isSortable : function () {
+        return this.options.isSortable;
+    },
+    /**
+     * APIMethod: isResizable
+     * Returns whether this column can be resized
+     */
+    isResizable : function () {
+        return this.options.isResizable;
+    },
+    /**
+     * APIMethod: isHidden
+     * Returns whether this column is hidden
+     */
+    isHidden : function () {
+        return this.options.isHidden;
+    },
+    /**
+     * APIMethod: isAttached
+     * returns whether this column is attached to a store.
+     */
+    isAttached: function () {
+        return this.options.renderer.attached;
+    },
+
+    /**
+     * APIMethod: getHTML
+     * calls render method of the renderer object passed in.
+     */
+    getHTML : function () {
+        this.options.renderer.render();
+        return document.id(this.options.renderer);
+    }
+
+});/*
+---
+
+name: Jx.Columns
+
+description: A container for defining and holding individual columns
+
+license: MIT-style license.
+
+requires:
+ - Jx.Column
+ - Jx.Object
+
+provides: [Jx.Columns]
+
+...
+ */
+// $Id: columns.js 992 2010-10-07 19:28:37Z pagameba $
+/**
+ * Class: Jx.Columns
+ *
+ * Extends: <Jx.Object>
+ *
+ * This class is the container for all columns needed for a grid. It
+ * consolidates many functions that didn't make sense to put directly
+ * in the column class. Think of it as a model for columns.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Columns = new Class({
+
+  Family: 'Jx.Columns',
+    Extends : Jx.Object,
+
+    options : {
+        /**
+         * Option: headerRowHeight
+         * the default height of the header row. Set to null or 'auto' to
+         * have this class attempt to figure out a suitable height.
+         */
+        headerRowHeight : 20,
+        /**
+         * Option: useHeaders
+         * Determines if the column headers should be displayed or not
+         */
+        useHeaders : false,
+        /**
+         * Option: columns
+         * an array holding all of the column instances or objects containing
+         * configuration info for the column
+         */
+        columns : []
+    },
+    /**
+     * Property: columns
+     * an array holding the actual instantiated column objects
+     */
+    columns : [],
+    
+    /**
+     * Property: rowTemplate
+     * a string holding a template for a single row of cells to be populated
+     * when rendering the store into a grid.  The template is constructed from
+     * the individual column templates once the store has been loaded.
+     */
+    rowTemplate: null,
+
+    parameters: ['options','grid'],
+    /**
+     * Property: hasExpandable
+     * boolean indicates whether any of the columns are expandable or not,
+     * which affects some calculations for column widths
+     */
+    hasExpandable: null,
+
+    /**
+     * APIMethod: init
+     * Creates the class.
+     */
+    init : function () {
+        this.parent();
+
+        if ($defined(this.options.grid) && 
+            this.options.grid instanceof Jx.Grid) {
+          this.grid = this.options.grid;
         }
+
+        this.hasExpandable = false;
+
+        this.options.columns.each(function (col) {
+            //check the column to see if it's a Jx.Grid.Column or an object
+            if (col instanceof Jx.Column) {
+                this.columns.push(col);
+            } else if (Jx.type(col) === "object") {
+                this.columns.push(new Jx.Column(col,this.grid));
+            }
+            var c = this.columns[this.columns.length - 1 ];
+        }, this);
+        
+        this.buildTemplates();
     },
     
-    /** 
-     * Method: prelightRowHeader
-     * apply the jxGridRowHeaderPrelight style to the header cell of a row.
-     * This removes the style from the previously pre-lit row header.
+    /**
+     * APIMethod: addColumns
+     * add new columns to the columns object after construction.  Causes
+     * the template to change.
      * 
      * Parameters:
-     * row - {Integer} the row to pre-light the header cell of
+     * columns - {Array} an array of columns to add
      */
-    prelightRowHeader: function(row) {
-        var cell = (row >= 0 && row < this.rowTableHead.rows.length-1) ? this.rowTableHead.rows[row+1].cells[1] : null;
-        if (this.prelitRowHeader != cell) {
-            if (this.prelitRowHeader) {
-                this.prelitRowHeader.removeClass('jxGridRowHeaderPrelight');
-            }
-            this.prelitRowHeader = cell;
-            if (this.prelitRowHeader) {
-                this.prelitRowHeader.addClass('jxGridRowHeaderPrelight');
-            }
+    addColumns: function(columns) {
+      this.columns.extend(columns);
+      this.buildTemplates();
+    },
+    
+    /**
+     * Method: buildTemplates
+     * create the row template based on the current columns
+     */
+    buildTemplates: function() {
+      if (!this.grid) {
+        return;
+      }
+      var rowTemplate = '',
+          hasExpandable = false,
+          grid = this.grid,
+          row = grid.row,
+          rhc = grid.row.useHeaders() ? this.getByName(row.options.headerColumn) : null,
+          colTemplate;
+      
+      this.columns.each(function(col, idx) {
+        var colTemplate = '';
+        if (!col.isHidden() && col != rhc) {
+          hasExpandable |= col.options.renderMode == 'expand';
+          if (!col.options.renderer || !col.options.renderer.domInsert) {
+            colTemplate = col.getTemplate(idx);
+          }
+          rowTemplate += "<td class='jxGridCell jxGridCol"+idx+" jxGridCol"+col.options.name+"'>" + colTemplate + "</td>";
         }
+      });
+      if (!hasExpandable) {
+        rowTemplate += "<td><span class='jxGridCellUnattached'></span></td>";
+      }
+      this.rowTemplate = rowTemplate;
+      this.hasExpandable = hasExpandable;
     },
-    
-    /** 
-     * Method: prelightColumnHeader
-     * apply the jxGridColumnHeaderPrelight style to the header cell of a column.
-     * This removes the style from the previously pre-lit column header.
-     * 
+    /**
+     * APIMethod: getHeaderHeight
+     * returns the height of the column header row
+     *
      * Parameters:
-     * col - {Integer} the column to pre-light the header cell of
+     * recalculate - determines if we should recalculate the height. Currently
+     * does nothing.
      */
-    prelightColumnHeader: function(col) {
-        if (this.colTableBody.rows.length == 0) {
-            return;
+    getHeaderHeight : function (recalculate) {
+        if (!$defined(this.height) || recalculate) {
+            if ($defined(this.options.headerRowHeight)
+                    && this.options.headerRowHeight !== 'auto') {
+                this.height = this.options.headerRowHeight;
+            } //else {
+                //figure out a height.
+            //}
         }
-        var cell = (col >= 0 && col < this.colTableBody.rows[0].cells.length-1) ? this.colTableBody.rows[0].cells[col+1] : null;
-        if (this.prelitColumnHeader != cell) {
-            if (this.prelitColumnHeader) {
-                this.prelitColumnHeader.removeClass('jxGridColumnHeaderPrelight');
+        return this.height;
+    },
+    /**
+     * APIMethod: useHeaders
+     * returns whether the grid is/should display headers or not
+     */
+    useHeaders : function () {
+        return this.options.useHeaders;
+    },
+    /**
+     * APIMethod: getByName
+     * Used to get a column object by the name of the column
+     *
+     * Parameters:
+     * colName - the name of the column
+     */
+    getByName : function (colName) {
+        var ret;
+        this.columns.each(function (col) {
+            if (col.name === colName) {
+                ret = col;
             }
-            this.prelitColumnHeader = cell;
-            if (this.prelitColumnHeader) {
-                this.prelitColumnHeader.addClass('jxGridColumnHeaderPrelight');
+        }, this);
+        return ret;
+    },
+    /**
+     * APIMethod: getByField
+     * Used to get a column by the model field it represents
+     *
+     *  Parameters:
+     *  field - the field name to search by
+     */
+    getByField : function (field) {
+        var ret;
+        this.columns.each(function (col) {
+            if (col.options.modelField === field) {
+                ret = col;
             }
+        }, this);
+        return ret;
+    },
+    /**
+     * APIMethod: getByGridIndex
+     * Used to get a column when all you know is the cell index in the grid
+     *
+     * Parameters:
+     * index - an integer denoting the placement of the column in the grid
+     * (zero-based)
+     */
+    getByGridIndex : function (index) {
+        var headers = this.options.useHeaders ? 
+                        this.grid.colTableBody.getFirst().getChildren() :
+                        this.grid.gridTableBody.getFirst().getChildren();
+        var cell = headers[index];
+          var hClasses = cell.get('class').split(' ').filter(function (cls) {
+            if(this.options.useHeaders)
+              return cls.test('jxColHead-');
+            else
+              return cls.test('jxCol-');
+          }.bind(this));
+        var parts = hClasses[0].split('-');
+        return this.getByName(parts[1]);
+    },
+
+    /**
+     * APIMethod: getHeaders
+     * Returns a row with the headers in it.
+     *
+     * Parameters:
+     * row - the row to add the headers to.
+     */
+    getHeaders : function (tr) {
+      var grid = this.grid,
+          row = grid.row,
+          rhc = grid.row.useHeaders() ? this.getByName(row.options.headerColumn) : null;
+      if (this.useHeaders()) {
+        this.columns.each(function(col, idx) {
+          if (!col.isHidden() && col != rhc) {
+            var classes = ['jxGridColHead', 'jxGridCol'+idx, 'jxCol-'+col.options.name, 'jxColHead-'+col.options.name],
+                th;
+            if (col.isEditable()) { classes.push('jxColEditable'); }
+            if (col.isResizable()) { classes.push('jxColResizable'); }
+            if (col.isSortable()) { classes.push('jxColSortable'); }
+            th = new Element('th', {
+              'class': classes.join(' ')
+            });
+            th.store('jxCellData', {
+              column: col,
+              colHeader: true,
+              index: idx
+            });
+            th.adopt(col.getHeaderHTML());
+            th.inject(tr);
+          }
+        });
+        if (!this.hasExpandable) {
+          new Element('th', {
+            'class': 'jxGridColHead jxGridCellUnattached'
+          }).inject(tr);
         }
+      }
     },
     
-    /** 
-     * Method: prelightRow
-     * apply the jxGridRowPrelight style to row.
-     * This removes the style from the previously pre-lit row.
-     * 
+    /**
+     * Method: getRow
+     * create a single row in the grid for a single record and populate
+     * the DOM elements for it.
+     *
      * Parameters:
-     * row - {Integer} the row to pre-light
+     * tr - {DOMElement} the TR element to insert the row into
+     * record - {<Jx.Record>} the record to create the row for
      */
-    prelightRow: function(row) {
-        var tr = (row >= 0 && row < this.gridTableBody.rows.length-1) ? this.gridTableBody.rows[row+1] : null;
-        
-        if (this.prelitRow != row) {
-            if (this.prelitRow) {
-                this.prelitRow.removeClass('jxGridRowPrelight');
+    getRow: function(tr, record) {
+      var data = {},
+          grid = this.grid,
+          store = grid.store,
+          row = grid.row,
+          rhc = grid.row.useHeaders() ? 
+                     this.getByName(row.options.headerColumn) : null,
+          domInserts = [],
+          i = 0;
+      this.columns.each(function(column, index) {
+        if (!column.isHidden() && column != rhc) {
+          if (column.options.renderer && column.options.renderer.domInsert) {
+            domInserts.push({column: column, index: i});
+          } else {
+            var renderer = column.options.renderer,
+                formatter = renderer.options.formatter,
+                text = '';
+            if (renderer.options.textTemplate) {
+              text = store.fillTemplate(null, renderer.options.textTemplate, renderer.columnsNeeded);
+            } else {
+              text = record.data.get(column.name);
             }
-            this.prelitRow = tr;
-            if (this.prelitRow) {
-                this.prelightRowHeader(row);
-                this.prelitRow.addClass('jxGridRowPrelight');
+            if (formatter) {
+              text = formatter.format(text);
             }
+            data['col'+index] = text;
+          }
+          i++;
         }
+      });
+      tr.set('html', this.rowTemplate.substitute(data));
+      domInserts.each(function(obj) {
+        tr.childNodes[obj.index].adopt(obj.column.getHTML());
+      });
     },
+
+    /**
+     * APIMethod: calculateWidths
+     * force calculation of column widths.  For columns with 'fit' this will
+     * cause the column to test every value in the store to compute the
+     * optimal width of the column.  Columns marked as 'expand' will get
+     * any extra space left over between the column widths and the width
+     * of the grid container (if any).
+     */
+    calculateWidths: function () {
+      //to calculate widths we loop through each column
+      var expand = null,
+          totalWidth = 0,
+          rowHeaderWidth = 0,
+          gridSize = this.grid.contentContainer.getContentBoxSize(),
+          leftOverSpace = 0;
+      this.columns.each(function(col,idx){
+        //are we checking the rowheader?
+        var rowHeader = false;
+        // if (col.name == this.grid.row.options.headerColumn) {
+        //   rowHeader = true;
+        // }
+        //if it's fixed, set the width to the passed in width
+        if (col.options.renderMode == 'fixed') {
+          col.calculateWidth(); //col.setWidth(col.options.width);
+          
+        } else if (col.options.renderMode == 'fit') {
+          col.calculateWidth(rowHeader);
+        } else if (col.options.renderMode == 'expand' && !$defined(expand)) {
+          expand = col;
+        } else {
+          //treat it as fixed if has width, otherwise as fit
+          if ($defined(col.options.width)) {
+            col.setWidth(col.options.width);
+          } else {
+            col.calculateWidth(rowHeader);
+          }
+        }
+        if (!col.isHidden() /* && !(col.name == this.grid.row.options.headerColumn) */) {
+            totalWidth += Jx.getNumber(col.getCellWidth());
+            if (rowHeader) {
+                rowHeaderWidth = col.getWidth();
+            }
+        }
+      },this);
+      
+      // width of the container
+      if (gridSize.width > totalWidth) {
+        //now figure the expand column
+        if ($defined(expand)) {
+          // var leftOverSpace = gridSize.width - totalWidth + rowHeaderWidth;
+          leftOverSpace = gridSize.width - totalWidth;
+          //account for right borders in firefox...
+          if (Browser.Engine.gecko) {
+            leftOverSpace -= this.getColumnCount(true);
+          } else {
+            // -2 is for the right hand border on the cell and the table for all other browsers
+            leftOverSpace -= 2;
+          }
+          if (leftOverSpace >= expand.options.width) {
+            //in order for this to be set properly the cellWidth must be the
+            //leftover space. we need to figure out the delta value and
+            //subtract it from the leftover width
+            expand.options.width = leftOverSpace;
+            expand.calculateWidth();
+            expand.setWidth(leftOverSpace, true);
+            totalWidth += leftOverSpace;
+          } else {
+            expand.setWidth(expand.options.width);
+          }
+        }
+      }
+      this.grid.gridObj.setContentBoxSize({'width': totalWidth});
+      this.grid.colObj.setContentBoxSize({'width': totalWidth});
+    },
+
+    /**
+     * Method: createRules
+     * create CSS rules for the current grid object
+     */
+    createRules: function(styleSheet, scope) {
+      var autoRowHeight = this.grid.row.options.rowHeight == 'auto';
+      this.columns.each(function(col, idx) {
+        var selector = scope+' .jxGridCol'+idx,
+            dec = '';
+        if (autoRowHeight) {
+          //set the white-space to 'normal !important'
+          dec = 'white-space: normal !important';
+        }
+        col.cellRule = Jx.Styles.insertCssRule(selector, dec, styleSheet);
+        col.cellRule.style.width = col.getCellWidth() + "px";
+
+        selector = scope+" .jxGridCol" + idx + " .jxGridCellContent";
+        col.rule = Jx.Styles.insertCssRule(selector, dec, styleSheet);
+        col.rule.style.width = col.getWidth() + "px";
+      }, this);
+    },
+
+    updateRule: function(column) {
+        var col = this.getByName(column);
+        if (col.options.renderMode === 'fit') {
+          col.calculateWidth();
+        }
+        col.rule.style.width = col.getWidth() + "px";
+        col.cellRule.style.width = col.getCellWidth() + "px";
+    },
     
-    /** 
-     * Method: prelightColumn
-     * apply the jxGridColumnPrelight style to a column.
-     * This removes the style from the previously pre-lit column.
-     * 
+    /**
+     * APIMethod: getColumnCount
+     * returns the number of columns in this model (including hidden).
+     */
+    getColumnCount : function (noHidden) {
+        noHidden = $defined(noHidden) ? false : true;
+        var total = this.columns.length;
+        if (noHidden) {
+            this.columns.each(function(col){
+                if (col.isHidden()) {
+                    total -= 1;
+                }
+            },this);
+        }
+        return total;
+    },
+    /**
+     * APIMethod: getIndexFromGrid
+     * Gets the index of a column from its place in the grid.
+     *
      * Parameters:
-     * col - {Integer} the column to pre-light
-     *
-     * TODO: Jx.Grid.prelightColumn
-     * Not Yet Implemented.
+     * name - the name of the column to get an index for
      */
-    prelightColumn: function(col) {
-        /* TODO: Jx.Grid.prelightColumn
-         * implement column prelighting (possibly) 
+    getIndexFromGrid : function (name) {
+        var headers = this.options.useHeaders ? 
+                        this.grid.colTableBody.getFirst().getChildren() :
+                        this.grid.gridTableBody.getFirst().getChildren(),
+            c,
+            i = -1,
+            self = this;
+        headers.each(function (h) {
+            i++;
+            var hClasses = h.get('class').split(' ').filter(function (cls) {
+                if(self.options.useHeaders)
+                  return cls.test('jxColHead-');
+                else
+                  return cls.test('jxCol-');
+            });
+            hClasses.each(function (cls) {
+                if (cls.test(name)) {
+                    c = i;
+                }
+            });
+        }, this);
+        return c;
+    }
+
+});
+/*
+---
+
+name: Jx.Row
+
+description: Holds information related to display of rows in the grid.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Object
+
+provides: [Jx.Row]
+
+...
+ */
+// $Id: row.js 986 2010-09-15 19:01:47Z pagameba $
+/**
+ * Class: Jx.Row
+ *
+ * Extends: <Jx.Object>
+ *
+ * A class defining a grid row.
+ *
+ * Inspired by code in the original Jx.Grid class
+ *
+ * License:
+ * Original Copyright (c) 2008, DM Solutions Group Inc.
+ * This version Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Row = new Class({
+
+  Family: 'Jx.Row',
+    Extends : Jx.Object,
+
+    options : {
+        /**
+         * Option: useHeaders
+         * defaults to false.  If set to true, then a column of row header
+         * cells are displayed.
          */
-        if (col >= 0 && col < this.gridTable.rows[0].cells.length) {
-            if ($chk(this.prelitColumn)) {
-                for (var i=0; i<this.gridTable.rows.length; i++) {
-                    this.gridTable.rows[i].cells[this.prelitColumn + 1].removeClass('jxGridColumnPrelight');
-                }
+        useHeaders : false,
+        /**
+         * Option: alternateRowColors
+         * defaults to false.  If set to true, then alternating CSS classes
+         * are used for rows.
+         */
+        alternateRowColors : false,
+        /**
+         * Option: rowClasses
+         * object containing class names to apply to rows
+         */
+        rowClasses : {
+            odd : 'jxGridRowOdd',
+            even : 'jxGridRowEven',
+            all : 'jxGridRowAll'
+        },
+        /**
+         * Option: rowHeight
+         * The height of the row. Make it null or 'auto' to auto-calculate.
+         */
+        rowHeight : 20,
+        /**
+         * Option: headerWidth
+         * The width of the row header. Make it null or 'auto' to auto-calculate
+         */
+        headerWidth : 40,
+        /**
+         * Option: headerColumn
+         * The name of the column in the model to use as the header
+         */
+        headerColumn : null
+    },
+    /**
+     * Property: grid
+     * A reference to the grid that this row model belongs to
+     */
+    grid : null,
+    /**
+     * Property: heights
+     * This will hold the calculated height of each row in the grid.
+     */
+    heights: [],
+    /**
+     * Property: rules
+     * A hash that will hold all of the CSS rules for the rows.
+     */
+    rules: $H(),
+
+    parameters: ['options','grid'],
+
+    /**
+     * APIMethod: init
+     * Creates the row model object.
+     */
+    init : function () {
+        this.parent();
+
+        if ($defined(this.options.grid) && this.options.grid instanceof Jx.Grid) {
+            this.grid = this.options.grid;
+        }
+    },
+    /**
+     * APIMethod: getGridRowElement
+     * Used to create the TR for the main grid row
+     */
+    getGridRowElement : function (row, text) {
+        var o = this.options,
+            rc = o.rowClasses,
+            c = o.alternateRowColors ?(row % 2 ? rc.even : rc.odd) : rc.all,
+            tr = new Element('tr', {
+              'class' : 'jxGridRow'+row+' '+ c,
+              html: text || ''
+            });
+        return tr;
+    },
+    /**
+     * Method: getRowHeaderCell
+     * creates the TH for the row's header
+     */
+    getRowHeaderCell : function (text) {
+      text = text ? '<span class="jxGridCellContent">'+text + '</span>' : '';
+      return new Element('th', {
+        'class' : 'jxGridRowHead',
+        html: text
+      });
+    },
+    /**
+     * APIMethod: getRowHeaderWidth
+     * determines the row header's width.
+     */
+    getRowHeaderWidth : function () {
+      var col, width;
+      if (this.options.headerColumn) {
+        col = this.grid.columns.getByName(this.options.headerColumn);
+        if (!$defined(col.getWidth())) {
+          col.calculateWidth(true);
+        }
+        width = col.getWidth();
+      } else {
+        width = this.options.headerWidth;
+      }
+      return width;
+    },
+
+    /**
+     * APIMethod: getHeight
+     * determines and returns the height of a row
+     */
+    getHeight : function (row) {
+      var h = this.options.rowHeight,
+          rowEl;
+      //this should eventually compute a height, however, we would need
+      //a fixed width to do so reliably. For right now, we use a fixed height
+      //for all rows.
+      if ($defined(this.heights[row])) {
+        h = this.heights[row];
+      } else if ($defined(this.options.rowHeight)) {
+        if (this.options.rowHeight == 'auto') {
+          // this.calculateHeight(row);
+          h = 20; // TODO calculate?
+          rowEl = this.grid.gridTableBody.rows[row]
+          if (rowEl) {
+            h = rowEl.getContentBoxSize().height; 
+          }
+        } else if (Jx.type(this.options.rowHeight) !== 'number') {
+          h = 20; // TODO calculate?
+        }
+      }
+      return h;
+    },
+    /**
+     * Method: calculateHeights
+     */
+    calculateHeights : function () {
+      if (this.options.rowHeight === 'auto' ||
+          !$defined(this.options.rowHeight)) {
+        //grab all rows in the grid body
+        document.id(this.grid.gridTableBody).getChildren().each(function(row){
+          row = document.id(row);
+          var data = row.retrieve('jxRowData');
+          var s = row.getContentBoxSize();
+          this.heights[data.row] = s.height;
+        },this);
+        document.id(this.grid.rowTableHead).getChildren().each(function(row){
+          row = document.id(row);
+          var data = row.retrieve('jxRowData');
+          if (data) {
+            var s = row.getContentBoxSize();
+            this.heights[data.row] = Math.max(this.heights[data.row],s.height);
+            if (Browser.Engine.webkit) {
+                //for some reason webkit (Safari and Chrome)
+                this.heights[data.row] -= 1;
             }
-            this.prelitColumn = col;
-            for (var i=0; i<this.gridTable.rows.length; i++) {
-                this.gridTable.rows[i].cells[col + 1].addClass('jxGridColumnPrelight');
-            }            
+          }
+        },this);
+      } else {
+        document.id(this.grid.rowTableHead).getChildren().each(function(row,idx){
+          this.heights[idx] = this.options.rowHeight;
+        }, this);
+      }
+    },
+
+    /**
+     * APIMethod: useHeaders
+     * determines and returns whether row headers should be used
+     */
+    useHeaders : function () {
+        return this.options.useHeaders;
+    },
+    /**
+     * APIMethod: getRowHeader
+     * creates and returns the header for the current row
+     *
+     * Parameters:
+     * list - Jx.List instance to add the header to
+     */
+    getRowHeader : function (list) {
+        var th = this.getRowHeaderCell();
+        //if (this.grid.model.getPosition() === 0) {
+        //    var rowWidth = this.getRowHeaderWidth();
+        //    th.setStyle("width", rowWidth);
+        //}
+        th.store('jxCellData', {
+            rowHeader: true,
+            row: this.grid.model.getPosition()
+        });
+        list.add(th);
+    },
+    /**
+     * APIMethod: getRowHeaderColumn
+     * returns the name of the column that is used for the row header
+     */
+    getRowHeaderColumn : function () {
+        return this.options.headerColumn;
+    }
+});
+/*
+---
+
+name: Jx.Plugin
+
+description: Base class for all plugins
+
+license: MIT-style license.
+
+requires:
+ - Jx.Object
+
+provides: [Jx.Plugin]
+
+...
+ */
+// $Id: plugin.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Plugin
+ *
+ * Extend: <Jx.Object>
+ *
+ * Base class for all plugins. In order for a plugin to be used it must
+ * extend from this class.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin = new Class({
+
+    Family: "Jx.Plugin",
+
+    Extends: Jx.Object,
+
+    options: {},
+
+    /**
+     * APIMethod: attach
+     * Empty method that must be overridden by subclasses. It is
+     * called by the user of the plugin to setup the plugin for use.
+     */
+    attach: function(obj){
+        obj.registerPlugin(this);
+    },
+
+    /**
+     * APIMethod: detach
+     * Empty method that must be overridden by subclasses. It is
+     * called by the user of the plugin to remove the plugin.
+     */
+    detach: function(obj){
+        obj.deregisterPlugin(this);
+    },
+
+    /**
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     *
+     * Parameters:
+     * lang - the language being changed to or that had it's data set of
+     *    translations changed.
+     */
+    changeText: function (lang) {
+        //if the mask is being used then recreate it. The code will pull
+        //the new text automatically
+        if (this.busy) {
+            this.setBusy(false);
+            this.setBusy(true);
         }
+    }
+});/*
+---
+
+name: Jx.Plugin.Grid
+
+description: Namespace for grid plugins
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin
+
+provides: [Jx.Plugin.Grid]
+
+...
+ */
+// $Id: plugin.grid.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Plugin.Grid
+ * Grid plugin namespace
+ *
+ *
+ * License:
+ * This version Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid = {};/*
+---
+
+name: Jx.Grid
+
+description: A tabular control that has fixed scrolling headers on the rows and columns like a spreadsheet.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+ - Jx.Styles
+ - Jx.Layout
+ - Jx.Columns
+ - Jx.Row
+ - Jx.Plugin.Grid
+ - Jx.Store
+ - Jx.List
+
+provides: [Jx.Grid]
+
+css:
+ - grid
+
+images:
+ - table_col.png
+ - table_row.png
+
+...
+ */
+// $Id: grid.js 992 2010-10-07 19:28:37Z pagameba $
+/**
+ * Class: Jx.Grid
+ *
+ * Extends: <Jx.Widget>
+ *
+ * A tabular control that has fixed, optional, scrolling headers on the rows
+ * and columns like a spreadsheet.
+ *
+ * Jx.Grid is a tabular control with convenient controls for resizing columns,
+ * sorting, and inline editing.  It is created inside another element,
+ * typically a div.  If the div is resizable (for instance it fills the page
+ * or there is a user control allowing it to be resized), you must call the
+ * resize() method of the grid to let it know that its container has been
+ * resized.
+ *
+ * When creating a new Jx.Grid, you can specify a number of options for the
+ * grid that control its appearance and functionality. You can also specify
+ * plugins to load for additional functionality. Currently Jx provides the
+ * following plugins
+ *
+ * Prelighter - prelights rows, columns, and cells
+ * Selector - selects rows, columns, and cells
+ * Sorter - sorts rows by specific column
+ * Editor - allows editing of cells if the column permits editing
+ *
+ * Jx.Grid renders data that comes from an external source.  This external
+ * source, called the store, must be a Jx.Store or extended from it.
+ *
+ * Events:
+ * gridCellEnter(cell, list) - called when the mouse enters a cell
+ * gridCellLeave(cell, list) - called when the mouse leaves a cell
+ * gridCellClick(cell) - called when a cell is clicked
+ * gridRowEnter(cell, list) - called when the mouse enters a row header
+ * gridRowLeave(cell, list) - called when the mouse leaves a row header
+ * gridRowClick(cell) - called when a row header is clicked
+ * gridColumnEnter(cell, list) - called when the mouse enters a column header
+ * gridColumnLeave(cell, list) - called when the mouse leaves a column header
+ * gridColumnClick(cell) - called when a column header is clicked
+ * gridMouseLeave() - called when the mouse leaves the grid at any point.
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ * This version Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Grid = new Class({
+  Family : 'Jx.Grid',
+  Extends: Jx.Widget,
+  Binds: ['storeLoaded', 'clickColumnHeader', 'moveColumnHeader', 'clickRowHeader', 'moveRowHeader', 'clickCell', 'dblclickCell', 'moveCell', 'leaveGrid', 'resize', 'drawStore', 'scroll', 'addRow', 'removeRow', 'removeRows', 'updateRow'],
+
+  /**
+   * Property: pluginNamespace
+   * the required variable for plugins
+   */
+  pluginNamespace: 'Grid',
+  
+  options: {
+    /**
+     * Option: parent
+     * the HTML element to create the grid inside. The grid will resize
+     * to fill the domObj.
+     */
+    parent: null,
+    
+    template: "<div class='jxWidget'><div class='jxGridContainer jxGridRowCol'></div><div class='jxGridContainer jxGridColumnsContainer'><table class='jxGridTable jxGridHeader jxGridColumns'><thead class='jxGridColumnHead'></thead></table></div><div class='jxGridContainer jxGridHeader jxGridRowContainer'><table class='jxGridTable jxGridRows'><thead class='jxGridRowBody'></thead></table></div><div class='jxGridContainer jxGridContentContainer'><table class='jxGridTable jxGridContent'><tbody class='jxGridTableBody'></tbody></table></div></div>",
+    
+    /**
+     * Options: columns
+     * an object consisting of a columns array that defines the individuals
+     * columns as well as containing any options for Jx.Grid.Columns or
+     * a Jx.Grid.Columns object itself.
+     */
+    columns: null,
+    
+    /**
+     * Option: row
+     * Either a Jx.Grid.Row object or a json object defining options for
+     * the class
+     */
+    row : null,
+
+    /**
+     * Option: store
+     * An instance of Jx.Store
+     */
+    store: null
+  },
+   
+  classes: $H({
+    domObj: 'jxWidget',
+    columnContainer: 'jxGridColumnsContainer',
+    colObj: 'jxGridColumns',
+    colTableBody: 'jxGridColumnHead',
+    rowContainer: 'jxGridRowContainer',
+    rowObj: 'jxGridRows',
+    rowColContainer: 'jxGridRowCol',
+    rowTableBody: 'jxGridRowBody',
+    contentContainer: 'jxGridContentContainer',
+    gridObj: 'jxGridContent',
+    gridTableBody: 'jxGridTableBody'
+  }),
+  
+  /**
+   * Property: columns
+   * holds a reference to the columns object
+   */
+  columns: null,
+  
+  /**
+   * Property: row
+   * Holds a reference to the row object
+   */
+  row: null,
+  
+  parameters: ['store', 'options'],
+  
+  /**
+   * Property: store
+   * holds a reference to the <Jx.Store> that is the store for this
+   * grid
+   */
+  store: null,
+  
+  /**
+   * Property: styleSheet
+   * the name of the dynamic style sheet to use for manipulating styles
+   */
+  styleSheet: 'JxGridStyles',
+  
+  /**
+   * Property: hooks
+   * a {Hash} of event names for tracking which events have actually been attached
+   * to the grid.
+   */
+  hooks: null,
+  
+  /**
+   * Property: uniqueId
+   * an auto-generated id that is assigned as a class name to the grid's
+   * container for scoping generated CSS rules to just this grid
+   */
+  uniqueId: null,
+  
+  /**
+   * Constructor: Jx.Grid
+   */
+  init: function() {
+    this.uniqueId = this.generateId('jxGrid_');
+    this.store = this.options.store;
+    var options = this.options,
+        opts;
+
+    if ($defined(options.row)) {
+      if (options.row instanceof Jx.Row) {
+        this.row = options.row;
+        this.row.grid = this;
+      } else if (Jx.type(options.row) == 'object') {
+        this.row = new Jx.Row($extend({grid: this}, options.row));
+      }
+    } else {
+      this.row = new Jx.Row({grid: this});
+    }
+
+    if ($defined(options.columns)) {
+        if (options.columns instanceof Jx.Columns) {
+            this.columns = options.columns;
+            this.columns.grid = this;
+        } else if (Jx.type(options.columns) === 'object') {
+            this.columns = new Jx.Columns($extend({grid:this}, options.columns));
+        }
+    } else {
+      this.columns = new Jx.Columns({grid: this});
+    }
+    
+    this.hooks = $H({
+      'gridScroll': false,
+      'gridColumnEnter': false,
+      'gridColumnLeave': false,
+      'gridColumnClick': false,
+      'gridRowEnter': false,
+      'gridRowLeave': false,
+      'gridRowClick': false,
+      'gridCellClick': false,
+      'gridCellDblClick': false,
+      'gridCellEnter': false,
+      'gridCellLeave': false,
+      'gridMouseLeave': false
+    });
+    
+    this.storeEvents = {
+      'storeDataLoaded': this.storeLoaded,
+      // 'storeSortFinished': this.drawStore,
+      'storeRecordAdded': this.addRow,
+      'storeColumnChanged': this.updateRow,
+      'storeRecordRemoved': this.removeRow,
+      'storeMultipleRecordsRemoved': this.removeRows
+    };
+    
+    
+    this.parent();
+  },
+  
+  wantEvent: function(eventName) {
+    var hook = this.hooks.get(eventName);
+    if (hook === false) {
+      switch(eventName) {
+        case 'gridColumnEnter':
+        case 'gridColumnLeave':
+          this.colObj.addEvent('mousemove', this.moveColumnHeader);
+          this.hooks.set({
+            'gridColumnEnter': true,
+            'gridColumnLeave': true
+          });
+          break;
+        case 'gridColumnClick':
+          this.colObj.addEvent('click', this.clickColumnHeader);
+          this.hooks.set({
+            'gridColumnClick': true
+          });
+          break;
+        case 'gridRowEnter':
+        case 'gridRowLeave':
+          this.rowObj.addEvent('mousemove', this.moveRowHeader);
+          this.hooks.set({
+            'gridRowEnter': true,
+            'gridRowLeave': true
+          });
+          break;
+        case 'gridRowClick':
+          this.rowObj.addEvent('click', this.clickRowHeader);
+          this.hooks.set({
+            'gridRowClick': true
+          });
+          break;
+        case 'gridCellEnter':
+        case 'gridCellLeave':
+          this.gridObj.addEvent('mousemove', this.moveCell);
+          this.hooks.set({
+            'gridCellEnter': true,
+            'gridCellLeave': true
+          });
+          break;
+        case 'gridCellClick':
+          this.gridObj.addEvent('click', this.clickCell);
+          this.hooks.set('gridCellClick', true);
+          break;
+        case 'gridCellDblClick':
+          this.gridObj.addEvent('dblclick', this.dblclickCell);
+          this.hooks.set('gridCellDblClick', true);
+          break;
+        case 'gridMouseLeave':
+          this.rowObj.addEvent('mouseleave', this.leaveGrid);
+          this.colObj.addEvent('mouseleave', this.leaveGrid);
+          this.gridObj.addEvent('mouseleave', this.leaveGrid);
+          this.hooks.set('gridMouseLeave', true);
+          break;
+        case 'gridScroll':
+          this.contentContainer.addEvent('scroll', this.scroll);
+        default:
+          break;
+      }
+    }
+  },
+  
+  /**
+   * Method: scroll
+   * handle the grid scrolling by updating the position of the headers
+   */
+  scroll : function () {
+      this.columnContainer.scrollLeft = this.contentContainer.scrollLeft;
+      this.rowContainer.scrollTop = this.contentContainer.scrollTop;
+  },
+  
+  /**
+   * APIMethod: render
+   * Create the grid for the current model
+   */
+  render: function() {
+    if (this.domObj) {
+      this.redraw();
+      return;
+    }
+    this.parent();
+    var store = this.store;
+    
+    this.domObj.addClass(this.uniqueId);
+    new Jx.Layout(this.domObj, {
+      onSizeChange: this.resize
+    });
+    
+    if (store instanceof Jx.Store) {
+      store.addEvents(this.storeEvents);
+      if (store.loaded) {
+        this.storeLoaded(store);
+      }
+    }
+    if (!this.columns.useHeaders()) {
+      this.columnContainer.dispose();
+    } else {
+      this.wantEvent('gridScroll');
+    }
+    
+    if (!this.row.useHeaders()) {
+      this.rowContainer.dispose();
+    } else {
+      this.wantEvent('gridScroll');
+    }
+
+    this.contentContainer.setStyle('overflow', 'auto');
+    
+    // todo: very hacky!  can plugins 'wantEvent' between init and render?
+    this.hooks.each(function(value, key) {
+      if (value) {
+        this.hooks.set(key, false);
+        this.wantEvent(key);
+      }
+    }, this);
+    
+    if (document.id(this.options.parent)) {
+      this.addTo(this.options.parent);
+      this.resize();
+    }
+  },
+  
+  /**
+   * APIMethod: resize
+   * resize the grid to fit inside its container.  This involves knowing
+   * something about the model it is displaying (the height of the column
+   * header and the width of the row header) so nothing happens if no model is
+   * set
+   */
+  resize: function() {
+    var p = this.domObj.getParent(),
+        parentSize = p.getSize(),
+        colHeaderHeight = 0,
+        rowHeaderWidth = 0;
+    
+    if (this.columns.useHeaders()) {
+      colHeaderHeight = this.columns.getHeaderHeight();
+    }
+    
+    if (this.row.useHeaders()) {
+      rowHeaderWidth = this.row.getRowHeaderWidth();
+    }
+    
+    this.rowColContainer.setBorderBoxSize({
+        width : rowHeaderWidth,
+        height : colHeaderHeight
+    });
+    
+    this.columnContainer.setStyles({
+      top: 0,
+      left: rowHeaderWidth
+    }).setBorderBoxSize({
+      width: parentSize.x - rowHeaderWidth,
+      height: colHeaderHeight
+    });
+
+    this.rowContainer.setStyles({
+      top: colHeaderHeight,
+      left: 0
+    }).setBorderBoxSize({
+      width: rowHeaderWidth,
+      height: parentSize.y - colHeaderHeight
+    });
+
+    this.contentContainer.setStyles({
+      top: colHeaderHeight,
+      left: rowHeaderWidth
+    }).setBorderBoxSize({
+      width: parentSize.x - rowHeaderWidth,
+      height: parentSize.y - colHeaderHeight
+    });
+  },
+  
+  /**
+   * APIMethod: setStore
+   * set the store for the grid to display.  If a store is attached to the
+   * grid it is removed and the new store is displayed.
+   *
+   * Parameters:
+   * store - {Object} the store to use for this grid
+   */
+  setStore: function(store) {
+    if (this.store) {
+      this.store.removeEvents(this.storeEvents);
+    }
+    if (store instanceof Jx.Store) {
+      this.store = store;
+      store.addEvents(this.storeEvents);
+      if (store.loaded) {
+        this.storeLoaded(store);
+      }
+      this.render();
+      this.domObj.resize();
+    } else {
+      this.destroyGrid();
+    }
+  },
+  
+  /**
+   * APIMethod: getStore
+   * gets the store set for this grid.
+   */
+  getStore: function() { 
+    return this.store;
+  },
+  
+  storeLoaded: function(store) {
+    this.redraw();
+  },
+  
+  redraw: function() {
+    var store = this.store,
+        template = '',
+        tr,
+        columns = [],
+        useRowHeaders = this.row.useHeaders();
+    this.fireEvent('beginCreateGrid');
+    
+    this.gridObj.getElement('tbody').empty();
+    
+    this.hoverColumn = this.hoverRow = this.hoverCell = null;
+    
+    // TODO: consider moving whole thing into Jx.Columns ??
+    // create a suitable column representation for everything
+    // in the store that doesn't already have a representation
+    store.options.columns.each(function(col, index) {
+      if (!this.columns.getByName(col.name)) {
+        var renderer = new Jx.Grid.Renderer.Text(),
+            format = $defined(col.format) ? col.format : null,
+            template = "<span class='jxGridCellContent'>"+ ($defined(col.label) ? col.label : col.name).capitalize() + "</span>",
+            column;
+        if ($defined(col.renderer)) {
+          if ($type(col.renderer) == 'string') {
+            if (Jx.Grid.Renderer[col.renderer.capitalize()]) {
+              renderer = new Jx.Grid.Renderer[col.renderer.capitalize()]();
+            }
+          } else if ($type(col.renderer) == 'object' && 
+                     $defined(col.renderer.type) && 
+                     Jx.Grid.Renderer[col.renderer.type.capitalize()]) {
+            renderer = new Jx.Grid.Renderer[col.renderer.type.capitalize()](col.renderer);
+          }
+        }
+        if (format) {
+          if ($type(format) == 'string' && 
+              $defined(Jx.Formatter[format.capitalize()])) {
+            renderer.options.formatter = new Jx.Formatter[format.capitalize()]();
+          } else if ($type(format) == 'object' && 
+                     $defined(format.type) && 
+                     $defined(Jx.Formatter[format.type.capitalize()])) {
+             renderer.options.formatter = new Jx.Formatter[format.type.capitalize()](format);
+          }
+        }
+        column = new Jx.Column({
+          grid: this,
+          template: template,
+          renderMode: $defined(col.renderMode) ? col.renderMode : $defined(col.width) ? 'fixed' : 'fit',
+          width: $defined(col.width) ? col.width : null,
+          isEditable: $defined(col.editable) ? col.editable : false,
+          isSortable: $defined(col.sortable) ? col.sortable : false,
+          isResizable: $defined(col.resizable) ? col.resizable : false,
+          isHidden: $defined(col.hidden) ? col.hidden : false,
+          name: col.name || '',
+          renderer: renderer 
+        });
+        columns.push(column);
+      }
+    }, this);
+    this.columns.addColumns(columns);
+    if (this.columns.useHeaders()) {
+      tr = new Element('tr');
+      this.columns.getHeaders(tr);
+      tr.adopt(new Element('th', {
+        'class': 'jxGridColHead',
+        'html': '&nbsp',
+        styles: {
+          width: 1000
+        }
+      }))
+      this.colObj.getElement('thead').empty().adopt(tr);
+    }
+    this.columns.calculateWidths();
+    this.columns.createRules(this.styleSheet+'Columns', '.'+this.uniqueId);
+    this.drawStore();
+    this.fireEvent('doneCreateGrid');
+  },
+  
+  /**
+   * APIMethod: addRow
+   * Adds a row to the table. Can add to either the beginning or the end 
+   * based on passed flag
+   */
+  addRow: function (store, record, position) {
+    if (this.store.loaded) {
+      if (position === 'bottom') {
+        this.store.last();
+      } else {
+        this.store.first();
+      }
+      this.drawRow(record, this.store.index, position);
+    }
+  },
+  
+  /**
+   * APIMethod: updateRow
+   * update a single row in the grid
+   *
+   * Parameters:
+   * index - the row to update
+   */
+  updateRow: function(index) {
+    var record = this.store.getRecord(index);
+    this.drawRow(record, index, 'replace');
+  },
+  
+  /**
+   * APIMethod: removeRow
+   * remove a single row from the grid
+   *
+   * Parameters:
+   * store
+   * index
+   */
+  removeRow: function (store, index) {
+    this.gridObj.deleteRow(index);
+    this.rowObj.deleteRow(index);
+  },
+  
+  /**
+   * APIMethod: removeRows
+   * removes multiple rows from the grid
+   *
+   * Parameters:
+   * store
+   * index
+   */
+  removeRows: function (store, first, last) {
+    for (var i = first; i <= last; i++) {
+        this.removeRow(store, first);
+    }
+  },
+  
+  /**
+   * APIMethod: setColumnWidth
+   * set the width of a column in pixels
+   *
+   * Parameters:
+   * column
+   * width
+   */
+  setColumnWidth: function(column, width) {
+    if (column) {
+      column.width = width;
+      if (column.rule) {
+        column.rule.style.width = width + 'px';
+      }
+      if (column.cellRule) {
+        column.cellRule.style.width = width + 'px';
+      }
+    }
+  },
+  
+  /**
+   * Method: drawStore
+   * clears the grid and redraws the store.  Does not draw the column headers,
+   * that is handled by the render() method
+   */
+  drawStore: function() {
+    var useHeaders = this.row.useHeaders(), 
+        blank;
+    this.domObj.resize();
+    this.gridTableBody.empty();
+    if (useHeaders) {
+      this.rowTableBody.empty();
+    }
+    this.store.each(function(record,index) {
+      this.store.index = index;
+      this.drawRow(record, index);
+    }, this);
+    if (useHeaders) {
+      blank = new Element('tr', {
+        styles: { height: 1000 }
+      });
+      blank.adopt(new Element('th', {
+        'class':'jxGridRowHead', 
+        html: '&nbsp'
+      }));
+      this.rowTableBody.adopt(blank);
+    }
+  },
+  
+  /**
+   * Method: drawRow
+   * this method does the heavy lifting of drawing a single record into the
+   * grid
+   *
+   * Parameters:
+   * record - {Jx.Record} the record to render
+   * index - {Integer} the row index of the record in the store
+   * position - {String} 'top' or 'bottom' (default 'bottom') position to put
+   *     the new row in the grid.
+   */
+  drawRow: function(record, index, position) {
+    var columns = this.columns,
+        body = this.gridTableBody,
+        row = this.row,
+        store = this.store,
+        rowHeaders = row.useHeaders(),
+        autoRowHeight = row.options.rowHeight == 'auto',
+        rowBody = this.rowTableBody,
+        rowHeaderColumn,
+        rowHeaderColumnIndex,
+        renderer,
+        formatter, 
+        getData,
+        tr,
+        th,
+        text = index + 1,
+        rh;
+    if (!$defined(position) || !['top','bottom','replace'].contains(position)) {
+      position = 'bottom';
+    }
+    tr = row.getGridRowElement(index, '');
+    if (position == 'replace' && index < body.childNodes.length) {
+      tr.inject(body.childNodes[index], 'after');
+      body.childNodes[index].dispose();
+    } else {
+      tr.inject(body, position);
+    }
+    columns.getRow(tr, record);
+    if (rowHeaders) {
+      if (row.options.headerColumn) {
+        rowHeaderColumn = columns.getByName(row.options.headerColumn);
+        renderer = rowHeaderColumn.options.renderer;
+        if (!renderer.domInsert) {
+          formatter = rowHeaderColumn.options.formatter;
+          rowHeaderColumnIndex = columns.columns.indexOf(rowHeaderColumn);
+          getData = function(record) {
+            var data = {},
+                text = '';
+            if (renderer.options.textTemplate) {
+              text = store.fillTemplate(null, renderer.options.textTemplate, renderer.columnsNeeded);
+            } else {
+              text = record.data.get(rowHeaderColumn.name);
+            }
+            data['col'+rowHeaderColumnIndex] = text;
+            return data;
+          };
+          text = rowHeaderColumn.getTemplate(rowHeaderColumnIndex).substitute(getData(record));
+        } else {
+          text = '';
+        }
+      }
+      th = row.getRowHeaderCell(text);
+      if (row.options.headerColumn && renderer.domInsert) {
+        th.adopt(rowHeaderColumn.getHTML());
+      }
+      rh = new Element('tr').adopt(th);
+      if (position == 'replace' && index < rowBody.childNodes.length) {
+        rh.inject(rowBody.childNodes[index], 'after');
+        rowBody.childNodes[index].dispose();
+      } else {
+        rh.inject(rowBody, position);
+      }
+      if (autoRowHeight) {
+        // th.setBorderBoxSize({height: tr.childNodes[0].getBorderBoxSize().height});
+        rh.setBorderBoxSize({height: tr.getBorderBoxSize().height});
+      }
+    }
+    this.fireEvent('gridDrawRow', [index, record]);
+  },
+  
+  /**
+   * Method: clickColumnHeader
+   * handle clicks on the column header
+   */
+  clickColumnHeader: function(e) {
+    var target = e.target;
+    if (target.getParent('thead')) {
+      target = target.tagName == 'TH' ? target : target.getParent('th');
+      this.fireEvent('gridColumnClick', target);
+    }
+  },
+  
+  /**
+   * Method: moveColumnHeader
+   * handle the mouse moving over the column header
+   */
+  moveColumnHeader: function(e) {
+    var target = e.target;
+    target = target.tagName == 'TH' ? target : target.getParent('th.jxGridColHead');
+    if (target) {
+      if (this.hoverColumn != target) {
+        if (this.hoverColumn) {
+          this.fireEvent('gridColumnLeave', this.hoverColumn);
+        }
+        if (!target.hasClass('jxGridColHead')) {
+          this.leaveGrid(e);
+        } else {
+          this.hoverColumn = target;
+          this.fireEvent('gridColumnEnter', target);
+        }
+      }
+    }
+  },
+
+  /**
+   * Method: clickRowHeader
+   * handle clicks on the row header
+   */
+  clickRowHeader: function(e) {
+    var target = e.target;
+    if (target.getParent('tbody')) {
+      target = target.tagName == 'TH' ? target : target.getParent('th');
+      this.fireEvent('gridRowClick', target);
+    }
+  },
+  
+  /**
+   * Method: moveRowHeader
+   * handle the mouse moving over the row header
+   */
+  moveRowHeader: function(e) {
+    var target = e.target;
+    target = target.tagName == 'TH' ? target : target.getParent('th.jxGridRowHead');
+    if (target) {
+      if (this.hoverRow != target) {
+        if (this.hoverRow) {
+          this.fireEvent('gridRowLeave', this.hoverRow);
+        }
+        if (!target.hasClass('jxGridRowHead')) {
+          this.leaveGrid(e);
+        } else {
+          this.hoverRow = target;
+          this.fireEvent('gridRowEnter', target);
+        }
+      }
+    }
+  },
+  
+  /**
+   * Method: clickCell
+   * handle clicks on cells in the grid
+   */
+  clickCell: function(e) {
+    var target = e.target;
+    if (target.getParent('tbody')) {
+      target = target.tagName == 'TD' ? target : target.getParent('td');
+      this.fireEvent('gridCellClick', target);
+    }
+  },
+  
+  /**
+   * Method: dblclickCell
+   * handle doubleclicks on cells in the grid
+   */
+  dblclickCell: function(e) {
+    var target = e.target;
+    if (target.getParent('tbody')) {
+      target = target.tagName == 'TD' ? target : target.getParent('td');
+      this.fireEvent('gridCellDblClick', target);
+    }
+  },
+  
+  /**
+   * Method: moveCell
+   * handle the mouse moving over cells in the grid
+   */
+  moveCell: function(e) {
+    var target = e.target,
+        data,
+        body,
+        row,
+        index,
+        column;
+    target = target.tagName == 'TD' ? target : target.getParent('td.jxGridCell');
+    if (target) {
+      if (this.hoverCell != target) {
+        if (this.hoverCell) {
+          this.fireEvent('gridCellLeave', this.hoverCell);
+        }
+        if (!target.hasClass('jxGridCell')) {
+          this.leaveGrid(e);
+        } else {
+          this.hoverCell = target;
+          this.getCellData(target);
+          this.fireEvent('gridCellEnter', target);
+        }
+      }
+    }
+  },
+  
+  getCellData: function(cell) {
+    var data = null,
+        index,
+        column,
+        row;
+    if (!cell.hasClass('jxGridCell')) {
+      cell = cell.getParent('td.jxGridCell');
+    }
+    if (cell) {
+      body = this.gridTableBody;
+      row = body.getChildren().indexOf(cell.getParent('tr'));
+      this.columns.columns.some(function(col,idx){
+        if (cell.hasClass('jxGridCol'+idx)) {
+          index = idx;
+          column = col;
+          return true;
+        }
+        return false;
+      });
+      data = {
+        row: row,
+        column: column,
+        index: index
+      };
+      cell.store('jxCellData', data);
+    }
+    return data;
+  },
+  
+  /**
+   * Method: leaveGrid
+   * handle the mouse leaving the grid
+   */
+  leaveGrid: function(e) {
+    this.hoverCell = null;
+    this.fireEvent('gridMouseLeave');
+  },
+  
+  /**
+   * Method: changeText
+   * rerender the grid when the language changes
+   */
+  changeText : function(lang) {
+      this.parent();
+      this.render();
+  },
+  
+  /**
+   * Method: addEvent
+   * override default addEvent to also trigger wanting the event
+   * which will then cause the underlying events to be registered
+   */
+  addEvent: function(name, fn) {
+    this.wantEvent(name);
+    this.parent(name, fn);
+  }
+});
+/*
+---
+
+name: Jx.Grid.Renderer
+
+description: Base class for all renderers. Used to create the contents of column.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Grid
+
+provides: [Jx.Grid.Renderer]
+
+...
+ */
+/**
+ * Class: Jx.Grid.Renderer
+ * This is the base class and namespace for all grid renderers.
+ * 
+ * Extends: <Jx.Widget>
+ * We extended Jx.Widget to take advantage of templating support.
+ */
+Jx.Grid.Renderer = new Class({
+  
+  Family: 'Jx.Grid.Renderer',
+  Extends: Jx.Widget,
+  
+  parameters: ['options'],
+  
+  options: {
+    deferRender: true,
+    /**
+     * Option: template
+     * The template for rendering this cell. Will be processed as per
+     * the Jx.Widget standard.
+     */
+    template: '<span class="jxGridCellContent"></span>'
+  },
+    /**
+     * APIProperty: attached
+     * tells whether this renderer is used in attached mode
+     * or not. Should be set by renderers that get a reference to
+     * the store.
+     */
+  attached: null,
+  
+  /**
+   * Property: domInsert
+   * boolean, indicates if the renderer needs to insert a DOM element
+   * instead of just outputing some templated HTML.  Renderers that
+   * do use domInsert will be slower.
+   */
+  domInsert: false,
+
+  classes: $H({
+    domObj: 'jxGridCellContent'
+  }),
+
+  column: null,
+
+  init: function () {
+    this.parent();
+    this.attached = false;
+  },
+  
+  render: function () {
+    this.parent();
+  },
+  
+  setColumn: function (column) {
+    if (column instanceof Jx.Column) {
+      this.column = column;
+    }
+  }
+  
+});/*
+---
+
+name: Jx.Grid.Renderer.Text
+
+description: Renders data as straight text.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Grid.Renderer
+
+provides: [Jx.Grid.Renderer.Text]
+
+...
+ */
+/**
+ * Class: Jx.Grid.Renderer.Text
+ * This is the default renderer for grid cells. It works the same as the
+ * original column implementation. It needs a store, a field name, and an
+ * optional formatter as well as other options.
+ *
+ * Extends: <Jx.Grid.Renderer>
+ *
+ */
+Jx.Grid.Renderer.Text = new Class({
+
+  Family: 'Jx.Grid.Renderer.Text',
+  Extends: Jx.Grid.Renderer,
+
+  options: {
+        /**
+         * Option: formatter
+         * an instance of <Jx.Formatter> or one of its subclasses which
+         * will be used to format the data in this column. It can also be
+         * an object containing the name (This should be the part after
+         * Jx.Formatter in the class name. For instance, to get a currency
+         * formatter, specify 'Currency' as the name.) and options for the
+         * needed formatter (see individual formatters for options).
+         * (code)
+         * {
+         *    name: 'formatter name',
+         *    options: {}
+         * }
+         * (end)
+         */
+        formatter: null,
+        /**
+         * Option: textTemplate
+         * Will be used for creating the text that goes iside the template. Use
+         * placeholders for indicating the field(s). You can add as much text
+         * as you want. for example, if you wanted to display someone's full
+         * name that is brokem up in the model with first and last names you
+         * can write a template like '{lastName}, {firstName}' and as long as
+         * the text between { and } are field names in the store they will be
+         * substituted properly.
+         */
+        textTemplate: null,
+        /**
+         * Option: css
+         * A string or function to use in adding classes to the text
+         */
+        css: null
+  },
+
+  store: null,
+
+  columnsNeeded: null,
+
+  init: function () {
+      this.parent();
+      var options = this.options,
+          t;
+      //check the formatter
+      if ($defined(options.formatter) &&
+          !(options.formatter instanceof Jx.Formatter)) {
+          t = Jx.type(options.formatter);
+          if (t === 'object') {
+              // allow users to leave the options object blank
+              if(!$defined(options.formatter.options)) {
+                  options.formatter.options = {};
+              }
+              options.formatter = new Jx.Formatter[options.formatter.name](
+                      options.formatter.options);
+          }
+      }
+  },
+
+  setColumn: function (column) {
+    this.parent();
+
+    this.store = column.grid.getStore();
+    this.attached = true;
+
+    if ($defined(this.options.textTemplate)) {
+      this.columnsNeeded = this.store.parseTemplate(this.options.textTemplate);
+    }
+  },
+
+  render: function () {
+    this.parent();
+
+    var text = '';
+    if ($defined(this.options.textTemplate)) {
+        if (!$defined(this.columnsNeeded) || (Jx.type(this.columnsNeeded) === 'array' && this.columnsNeeded.length === 0)) {
+            this.columnsNeeded = this.store.parseTemplate(this.options.textTemplate);
+        }
+        text = this.store.fillTemplate(null,this.options.textTemplate,this.columnsNeeded);
+    }
+    if ($defined(this.options.formatter)) {
+        text = this.options.formatter.format(text);
+    }
+
+    this.domObj.set('html',text);
+
+    if ($defined(this.options.css) && Jx.type(this.options.css) === 'function') {
+      this.domObj.addClass(this.options.css.run(text));
+    } else if ($defined(this.options.css) && Jx.type(this.options.css) === 'string'){
+      this.domObj.addClass(this.options.css);
+    }
+
+  }
+
+});/*
+---
+
+name: Jx.Grid.Renderer.Checkbox
+
+description: Renders a checkbox in a column. Can be connected to a store column or as a standalone check column.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Grid.Renderer
+ - Jx.Field.Checkbox
+
+provides: [Jx.Grid.Renderer.Checkbox]
+
+...
+ */
+/**
+ * Class: Jx.Grid.Renderer.CheckBox
+ * Renders a checkbox into the cell. Allows options for connecting the cell
+ * to a model field and propogating changes back to the store.
+ * 
+ * Extends: <Jx.Grid.Renderer>
+ * 
+ */
+Jx.Grid.Renderer.Checkbox = new Class({
+  
+  Family: 'Jx.Grid.Renderer.Checkbox',
+  Extends: Jx.Grid.Renderer,
+  
+  Binds: ['onBlur','onChange'],
+  
+  options: {
+    useStore: false,
+    field: null,
+    updateStore: false,
+    checkboxOptions: {
+      template : '<input class="jxInputContainer jxInputCheck" type="checkbox" name="{name}"/>',
+      name: ''
+    }
+  },
+  
+  domInsert: true,
+  
+  init: function () {
+    this.parent();
+  },
+  
+  render: function () {
+    this.parent();
+    var checkbox = new Jx.Field.Checkbox(this.options.checkboxOptions);
+    this.domObj.adopt(document.id(checkbox));
+    
+    if (this.options.useStore) {
+      //set initial state
+      checkbox.setValue(this.store.get(this.options.field));
+    }
+    
+    //hook up change and blur events to change store field
+    checkbox.addEvents({
+      'blur': this.onBlur,
+      'change': this.onChange
+    });
+  },
+  
+  setColumn: function (column) {
+    this.column = column;
+    
+    if (this.options.useStore) {
+      this.store = this.column.grid.getStore();
+      this.attached = true;
+    }
+  },
+  
+  onBlur: function (field) {
+    if (this.options.updateStore) {
+      this.updateStore(field);
+    }
+    this.column.grid.fireEvent('checkBlur',[this.column, field]);
+  },
+  
+  onChange: function (field) {
+    if (this.options.updateStore) {
+      this.updateStore(field);
+    }
+    this.fireEvent('change',[this.column, field]);
+  },
+  
+  updateStore: function (field) {
+    var newValue = field.getValue();
+    
+    var data = document.id(field).getParent().retrieve('jxCellData');
+    var row = data.row;
+    
+    if (this.store.get(this.options.field, row) !== newValue) {
+      this.store.set(this.options.field, newValue, row);
+    }
+  }
+  
+  
+});/*
+---
+
+name: Jx.Grid.Renderer.Button
+
+description: "Renders one or more buttons in a single column.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Grid.Renderer
+ - Jx.Button
+
+
+provides: [Jx.Grid.Renderer.Button]
+
+...
+ */
+/**
+ * Class: Jx.Grid.Renderer.Button
+ * Renders a <Jx.Button> into the cell. You can add s many buttons as you'd like per column by passing button configs
+ * in as an array option to options.buttonOptions
+ *
+ * Extends: <Jx.Grid.Renderer>
+ *
+ */
+Jx.Grid.Renderer.Button = new Class({
+
+    Family: 'Jx.Grid.Renderer.Button',
+    Extends: Jx.Grid.Renderer,
+
+    Binds: [],
+
+    options: {
+        template: '<span class="buttons"></span>',
+        /**
+         * Option: buttonOptions
+         * an array of option configurations for <Jx.Button>
+         */
+        buttonOptions: null
+    },
+    
+    domInsert: true,
+
+    classes:  $H({
+        domObj: 'buttons'
+    }),
+
+    init: function () {
+        this.parent();
+    },
+
+    render: function () {
+        this.parent();
+
+        $A(this.options.buttonOptions).each(function(opts){
+            var button = new Jx.Button(opts);
+            this.domObj.grab(document.id(button));
+        },this);
+
+    }
+});/*
+---
+
+name: Jx.Plugin.Grid.Selector
+
+description: Allows selecting rows, columns, and cells in grids
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin.Grid
+
+provides: [Jx.Plugin.Grid.Selector]
+
+...
+ */
+// $Id: grid.selector.js 994 2010-10-07 20:07:31Z pagameba $
+/**
+ * Class: Jx.Plugin.Grid.Selector
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Grid plugin to select rows, columns, and/or cells.
+ *
+ * Original selection code from Jx.Grid's original class
+ *
+ * License:
+ * Original Copyright (c) 2008, DM Solutions Group Inc.
+ * This version Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid.Selector = new Class({
+
+    Family: 'Jx.Plugin.Grid.Selector',
+    Extends : Jx.Plugin,
+    
+    name: 'Selector',
+
+    Binds: ['select','checkSelection','checkAll','afterGridRender','onCellClick', 'sort', 'updateCheckColumn'],
+
+    options : {
+        /**
+         * Option: cell
+         * determines if cells are selectable
+         */
+        cell : false,
+        /**
+         * Option: row
+         * determines if rows are selectable
+         */
+        row : false,
+        /**
+         * Option: column
+         * determines if columns are selectable
+         */
+        column : false,
+        /**
+         * Option: multiple
+         * Allow multiple selections
+         */
+        multiple: false,
+        /**
+         * Option: useCheckColumn
+         * Whether to use a check box column as the row header or as the
+         * first column in the grid and use it for manipulating selections.
+         */
+        useCheckColumn: false,
+        /**
+         * Option: checkAsHeader
+         * Determines if the check column is the header of the rows
+         */
+        checkAsHeader: false,
+        /**
+         * Option: sortableColumn
+         * Determines if the check column is sortable
+         */
+        sortableColumn: false
+    },
+    
+    domInsert: true,
+    
+    /**
+     * Property: selected
+     * Holds arrays of selected rows and/or columns and their headers
+     */
+    selected: null,
+
+    /**
+     * APIMethod: init
+     * construct a new instance of the plugin.  The plugin must be attached
+     * to a Jx.Grid instance to be useful though.
+     */
+    init: function() {
+        this.parent();
+        this.selected = $H({
+            cells: [],
+            columns: [],
+            rows: [],
+            rowHeads: [],
+            columnHeads: []
+        });
+    },
+    /**
+     * APIMethod: attach
+     * Sets up the plugin and attaches the plugin to the grid events it
+     * will be monitoring
+     *
+     * Parameters:
+     * grid - The instance of Jx.Grid to attach to
+     */
+    attach: function (grid) {
+        if (!$defined(grid) && !(grid instanceof Jx.Grid)) {
+            return;
+        }
+        this.parent(grid);
+        var options = this.options,
+            template;
+        this.grid = grid;
         
-        this.prelightColumnHeader(col);
+
+        //setup check column if needed
+        if (options.useCheckColumn) {
+          grid.addEvent('gridDrawRow', this.updateCheckColumn);
+          template = '<span class="jxGridCellContent">';
+          if (options.multiple) {
+            template += '<span class="jxInputContainer jxInputContainerCheck"><input class="jxInputCheck" type="checkbox" name="checkAll" id="checkAll"/></span>';
+          } else {
+            template += '</span>';
+          }
+
+          template += "</span>";
+
+          this.checkColumn = new Jx.Column({
+            template: template,
+            renderMode: 'fixed',
+            width: 20,
+            renderer: null,
+            name: 'selection',
+            isSortable: options.sortableColumn || false,
+            sort: options.sortableColumn ? this.sort : null
+          }, grid);
+          this.checkColumn.options.renderer = this;
+          grid.columns.columns.reverse();
+          grid.columns.columns.push(this.checkColumn);
+          grid.columns.columns.reverse();
+
+          if (options.checkAsHeader) {
+              this.oldHeaderColumn = grid.row.options.headerColumn;
+              grid.row.options.useHeaders = true;
+              grid.row.options.headerColumn = 'selection';
+
+              if (options.multiple) {
+                  grid.addEvent('doneCreateGrid', this.afterGridRender);
+              }
+          }
+          //attach event to header
+          if (options.multiple) {
+              document.id(this.checkColumn).getElement('input').addEvents({
+                  'change': this.checkAll
+              });
+          }
+        } else {
+          grid.addEvent('gridCellClick', this.onCellClick);
+        }
     },
     
-    /** 
-     * Method: prelightCell
-     * apply the jxGridCellPrelight style to a cell.
-     * This removes the style from the previously pre-lit cell.
+    /**
+     * Method: render
+     * required for the renderer interface
+     */
+    render: function() {
+      this.domObj = new Element('span', {
+        'class': 'jxGridCellContent'
+      });
+      new Element('input', {
+        'class': 'jxGridSelector',
+        type: 'checkbox',
+        events: {
+          change: this.checkSelection
+        }
+      }).inject(this.domObj);
+    },
+    
+    /**
+     * Method: toElement
+     * required for the Renderer interface
+     */
+    toElement: function() {
+      return this.domObj;
+    },
+    
+    /**
+     * Method: updateCheckColumn
+     * check to see if a row needs to have its checkbox updated after its been drawn
      *
      * Parameters:
-     * row - {Integer} the row of the cell to pre-light
-     * col - {Integer} the column of the cell to pre-light
+     * index - {Integer} the row that was just rendered
+     * record - {<Jx.Record>} the record that was rendered into that row
      */
-    prelightCell: function(row, col) {
-         var td = (row >=0 && col >=0 && row < this.gridTableBody.rows.length - 1 && col < this.gridTableBody.rows[row+1].cells.length - 1) ? this.gridTableBody.rows[row+1].cells[col+1] : null;
-        if (this.prelitCell != td) {
-            if (this.prelitCell) {
-                this.prelitCell.removeClass('jxGridCellPrelight');
+    updateCheckColumn: function(index, record) {
+      var state = this.selected.get('rows').contains(index),
+          r = this.grid.gridTableBody.rows,
+          tr = document.id((index >= 0 && index < r.length) ? r[index] : null);
+      
+      if (tr) {
+        if (state) {
+          tr.addClass('jxGridRowSelected');
+        } else {
+          tr.removeClass('jxGridRowSelected');
+        }
+        this.setCheckField(index, state);
+      }
+    },
+
+    /**
+     * Method: afterGridRender
+     */
+    afterGridRender: function () {
+        if (this.options.checkAsHeader) {
+            var chkCol = document.id(this.checkColumn).clone();
+            chkCol.getElement('input').addEvent('change',this.checkAll);
+            this.grid.rowColContainer.adopt(chkCol);
+        }
+        this.grid.removeEvent('doneCreateGrid',this.afterGridRender);
+    },
+    /**
+     * APIMethod: detach
+     */
+    detach: function() {
+        var grid = this.grid,
+            options = this.options,
+            col;
+        if (grid) {
+            grid.gridTableBody.removeEvents({
+              click: this.onCellClick
+            });
+            if (this.checkColumn) {
+                grid.columns.columns.erase(this.checkColumn);
+                this.checkColumn.destroy();
+                this.checkColumn = null;
             }
-            this.prelitCell = td;
-            if (this.prelitCell) {
-                this.prelitCell.addClass('jxGridCellPrelight');
+        }
+        if (options.useCheckColumn) {
+            if (options.checkAsHeader) {
+                grid.row.options.headerColumn = this.oldHeaderColumn;
             }
-        }    
+        }
+        this.grid = null;
     },
+    /**
+     * APIMethod: activate
+     * Allows programatic access to turning selections on.
+     *
+     * Parameters:
+     * opt - the option to turn on. One of 'cell', 'column', or 'row'
+     */
+    activate: function (opt) {
+        this.options[opt] = true;
+    },
+    /**
+     * APIMethod: deactivate
+     * Allows programatic access to turning selections off.
+     *
+     * Parameters:
+     * opt - the option to turn off. One of 'cell', 'column', or 'row'
+     */
+    deactivate: function (opt) {
+        var gridTableRows = this.grid.gridTableBody.rows,
+            selected = this.selected,
+            i;
+        this.options[opt] = false;
+        if (opt === 'cell') {
+            selected.get('cells').each(function(cell) {
+              cell.removeClass('jxGridCellSelected');
+            });
+            selected.set('cells',[]);
+        } else if (opt === 'row') {
+          this.getSelectedRows().each(function(row){
+            row.removeClass('jxGridRowSelected');
+          });
+          selected.set('rows',[]);
+          selected.get('rowHeads').each(function(rowHead){
+            rowHead.removeClass('jxGridRowHeaderSelected');
+          });
+          selected.set('rowHeads',[]);
+        } else {
+            selected.get('columns').each(function(column){
+                for (i = 0; i < gridTableRows.length; i++) {
+                    gridTableRows[i].cells[column].removeClass('jxGridColumnSelected');
+                }
+            });
+            selected.set('columns',[]);
+
+            selected.get('columnHeads').each(function(rowHead){
+            rowHead.removeClass('jxGridColumnHeaderSelected');
+          },this);
+          selected.set('columnHeads',[]);
+        }
+    },
     
-    /** 
+    /**
+     * Method: onCellClick
+     * dispatch clicking on a table cell
+     */
+    onCellClick: function(cell) {
+        if (cell) {
+            this.select(cell);
+        }
+    },
+    
+    /**
+     * Method: select
+     * dispatches the grid click to the various selection methods
+     */
+    select : function (cell) {
+        var data = cell.retrieve('jxCellData'),
+            options = this.options,
+            col;
+
+        if (options.cell && $defined(data.row) && $defined(data.index)) {
+          this.selectCell(cell);
+        }
+        
+        if (options.row && $defined(data.row)) {
+            this.selectRow(data.row);
+        }
+
+        if (options.column && $defined(data.index)) {
+            if (this.grid.row.useHeaders()) {
+                this.selectColumn(data.index - 1);
+            } else {
+                this.selectColumn(data.index);
+            }
+        }
+    },
+    
+    /**
      * Method: selectCell
-     * Select a cell and apply the jxGridCellSelected style to it.
-     * This deselects a previously selected cell.
+     * select a cell
      *
-     * If the model supports cell selection, it should implement
-     * a cellSelected function to receive notification of the selection.
+     * Parameters: 
+     * cell - {DOMElement} the cell element to select
+     */
+    selectCell: function(cell) {
+        if (!this.options.cell) { return; }
+        var cells = this.selected.get('cells');
+        if (cell.hasClass('jxGridCellSelected')) {
+          cell.removeClass('jxGridCellSelected');
+          cells.erase(cell);
+          this.fireEvent('unselectCell', cell);
+        } else {
+          cell.addClass('jxGridCellSelected');
+          cells.push(cell);
+          this.fireEvent('selectCell', cell);
+        }
+    },
+    
+    /**
+     * Method: selectRow
+     * Select a row and apply the jxGridRowSelected style to it.
      *
      * Parameters:
-     * row - {Integer} the row of the cell to select
-     * col - {Integer} the column of the cell to select
+     * row - {Integer} the row to select
      */
-    selectCell: function(row, col) {
-         var td = (row >=0 && col >=0 && row < this.gridTableBody.rows.length - 1 && col < this.gridTableBody.rows[row+1].cells.length - 1) ? this.gridTableBody.rows[row+1].cells[col+1] : null;
-         if (!td) {
-             return;
-         }
-         
-         if (this.selectedCell) {
-             this.selectedCell.removeClass('jxGridCellSelected');
-         }
-         this.selectedCell = td;
-         this.selectedCell.addClass('jxGridCellSelected');
+    selectRow: function (row, silently) {
+        if (!this.options.row) { return; }
+        var options = this.options,
+            r = this.grid.gridTableBody.rows,
+            tr = document.id((row >= 0 && row < r.length) ? r[row] : null),
+            rows = this.selected.get('rows'),
+            silently = $defined(silently) ? silently : false;
+        if (tr) {
+            if (tr.hasClass('jxGridRowSelected')) {
+                tr.removeClass('jxGridRowSelected');
+                this.setCheckField(row, false);
+                if (options.multiple && options.useCheckColumn) {
+                    if (options.checkAsHeader) {
+                        document.id(this.grid.rowColContainer).getElement('input').removeProperty('checked');
+                    } else {
+                        document.id(this.checkColumn).getElement('input').removeProperty('checked');
+                    }
+                }
+                //search array and remove this item
+                rows.erase(row);
+                if (!silently) {
+                  this.fireEvent('unselectRow', row);
+                }
+            } else {
+                tr.store('jxRowData', {row: row});
+                rows.push(row);
+                tr.addClass('jxGridRowSelected');
+                this.setCheckField(row, true);
+                if (!silently) {
+                  this.fireEvent('selectRow', row);
+                }
+            }
+
+            if (!this.options.multiple) {
+                var unselected = [];
+                this.getSelectedRows().each(function(row) {
+                  var idx;
+                  if (row !== tr) {
+                    idx = row.retrieve('jxRowData').row;
+                    row.removeClass('jxGridRowSelected');
+                    this.setCheckField(idx,false);
+                    rows.erase(row);
+                    unselected.push(idx);
+                    if (!silently) {
+                      this.fireEvent('unselectRow', row);
+                    }
+                  }
+                  
+                }, this);
+                if (unselected.length && !silently) {
+                  this.fireEvent('unselectRows', [unselected]);
+                }
+            }
+        }
+        this.selectRowHeader(row);
     },
-    
-    /** 
+
+    /**
+     * Method: setCheckField
+     */
+    setCheckField: function (row, checked) {
+        var grid = this.grid,
+            options = this.options,
+            check,
+            col,
+            cell;
+        if (options.useCheckColumn) {
+            if (options.checkAsHeader) {
+              cell = document.id(grid.rowTableBody.rows[row].cells[0]);
+            } else {
+              col = grid.columns.getIndexFromGrid(this.checkColumn.name);
+              cell = document.id(grid.gridTableBody.rows[row].cells[col]);
+            }
+            check = cell.getElement('.jxGridSelector')
+            check.set('checked', checked);
+        }
+    },
+    /**
      * Method: selectRowHeader
      * Apply the jxGridRowHeaderSelected style to the row header cell of a
      * selected row.
      *
      * Parameters:
      * row - {Integer} the row header to select
-     * selected - {Boolean} the new state of the row header
      */
-    selectRowHeader: function(row, selected) {
-        var cell = (row >= 0 && row < this.rowTableHead.rows.length-1) ? this.rowTableHead.rows[row+1].cells[1] : null;
+    selectRowHeader: function (row) {
+        if (!this.grid.row.useHeaders()) {
+            return;
+        }
+        var rows = this.grid.rowTableBody.rows,
+            cell = document.id((row >= 0 && row < rows.length) ? 
+                              rows[row].cells[0] : null),
+            cells;
+
         if (!cell) {
             return;
         }
-        if (selected) {
-            cell.addClass('jxGridRowHeaderSelected');
+        cells = this.selected.get('rowHeads');
+        if (cells.contains(cell)) {
+            cell.removeClass('jxGridRowHeaderSelected');
+            cells.erase(cell);
         } else {
-            cell.removeClass('jxGridRowHeaderSelected');
+          cell.addClass('jxGridRowHeaderSelected');
+          cells.push(cell);
         }
+
+        if (!this.options.multiple) {
+          cells.each(function(c){
+            if (c !== cell) {
+              c.removeClass('jxGridRowHeaderSelected');
+              cells.erase(c);
+            }
+          },this);
+        }
+
     },
-    
-    /** 
-     * Method: selectRow
-     * Select a row and apply the jxGridRowSelected style to it.
+    /**
+     * Method: selectColumn
+     * Select a column.
+     * This deselects a previously selected column.
      *
-     * If the model supports row selection, it should implement
-     * a rowSelected function to receive notification of the selection.
-     *
      * Parameters:
-     * row - {Integer} the row to select
-     * selected - {Boolean} the new state of the row
+     * col - {Integer} the column to select
      */
-    selectRow: function(row, selected) {
-        var tr = (row >= 0 && row < this.gridTableBody.rows.length - 1) ? this.gridTableBody.rows[row+1] : null;
-        if (tr) {
-            if (selected) {
-                tr.addClass('jxGridRowSelected');
+    selectColumn: function (col) {
+        var gridTable = this.grid.gridTableBody,
+            cols = this.selected.get('columns'),
+            m = '',
+            i;
+        if (col >= 0 && col < gridTable.rows[0].cells.length) {
+            if (cols.contains(col)) {
+                //deselect
+                m = 'removeClass';
+                cols.erase(col);
+                this.fireEvent('unselectColumn', col);
             } else {
-                tr.removeClass('jxGridRowSelected');
+                //select
+                m = 'addClass';
+                cols.push(col);
+                this.fireEvent('selectColumn', col);
             }
-            this.selectRowHeader(row, selected);
+            for (i = 0; i < gridTable.rows.length; i++) {
+                gridTable.rows[i].cells[col][m]('jxGridColumnSelected');
+            }
+
+            if (!this.options.multiple) {
+                cols.each(function(c){
+                  if (c !== col) {
+                      for (i = 0; i < gridTable.rows.length; i++) {
+                          gridTable.rows[i].cells[c].removeClass('jxGridColumnSelected');
+                      }
+                      cols.erase(c);
+                      this.fireEvent('unselectColumn', c);
+                  }
+                }, this);
+            }
+            this.selectColumnHeader(col);
         }
     },
-    
-    /** 
+    /**
      * method: selectColumnHeader
      * Apply the jxGridColumnHeaderSelected style to the column header cell of a
      * selected column.
      *
      * Parameters:
      * col - {Integer} the column header to select
-     * selected - {Boolean} the new state of the column header
      */
-    selectColumnHeader: function(col, selected) {
-        if (this.colTableBody.rows.length == 0) {
+    selectColumnHeader: function (col) {
+        var rows = this.grid.colTableBody;
+        if (rows.length === 0 || !this.grid.row.useHeaders()) {
             return;
         }
-        var cell = (col >= 0 && col < this.colTableBody.rows[0].cells.length-1) ? this.colTableBody.rows[0].cells[col+1] : null;
-        if (cell == null) { 
-            return; 
+
+        var cell = (col >= 0 && col < rows[0].cells.length) ?
+            rows[0].cells[col] : null;
+
+        if (cell === null) {
+            return;
         }
-        
-        if (selected) {
-            cell.addClass('jxGridColumnHeaderSelected');
+
+        cell = document.id(cell);
+        cells = this.selected.get('columnHeads');
+
+        if (cells.contains(cell)) {
+            cell.removeClass('jxGridColumnHeaderSelected');
+            cells.erase(cell);
         } else {
-            cell.removeClass('jxGridColumnHeaderSelected');
+          cell.addClass('jxGridColumnHeaderSelected');
+          cells.push(cell);
         }
+
+        if (!this.options.multiple) {
+          cells.each(function(c){
+            if (c !== cell) {
+              c.removeClass('jxGridColumnHeaderSelected');
+              cells.erase(c);
+            }
+          });
+        }
     },
-    
-    /** 
-     * Method: selectColumn
-     * Select a column.
-     * This deselects a previously selected column.
+    /**
+     * Method: checkSelection
+     * Checks whether a row's check box is/isn't checked and modifies the
+     * selection appropriately.
      *
      * Parameters:
-     * col - {Integer} the column to select
-     * selected - {Boolean} the new state of the column
+     * column - <Jx.Column> that created the checkbox
+     * field - <Jx.Field.Checkbox> instance that was checked/unchecked
+     * created the checkbox
      */
-    selectColumn: function(col, selected) {
-        /* todo: implement column selection */
-        if (col >= 0 && col < this.gridTable.rows[0].cells.length) {
-            if (selected) {
-                for (var i=0; i<this.gridTable.rows.length; i++) {
-                    this.gridTable.rows[i].cells[col + 1].addClass('jxGridColumnSelected');
+    checkSelection: function (event) {
+      var cell =  event.target.getParent('tr'),
+          row;
+      if (cell) {
+        row = cell.getParent().getChildren().indexOf(cell);
+        this.selectRow(row);
+      }
+    },
+    /**
+     * Method: checkAll
+     * Checks all checkboxes in the column the selector inserted.
+     */
+    checkAll: function () {
+        var grid = this.grid,
+            col,
+            rows,
+            selection = [],
+            checked = this.options.checkAsHeader ? 
+                          grid.rowColContainer.getElement('input').get('checked') :
+                          this.checkColumn.domObj.getElement('input').get('checked'),
+            event = checked ? 'selectRows' : 'unselectRows';
+
+        if (this.options.checkAsHeader) {
+            col = 0;
+            rows = grid.rowTableBody.rows;
+        } else {
+            col = grid.columns.getIndexFromGrid(this.checkColumn.name);
+            rows = grid.gridTableBody.rows;
+        }
+
+        $A(rows).each(function(row, idx) {
+            var check = row.cells[col].getElement('input');
+            if ($defined(check)) {
+                var rowChecked = check.get('checked');
+                if (rowChecked !== checked) {
+                    this.selectRow(idx, true);
+                    selection.push(idx);
                 }
-            } else {
-                for (var i=0; i<this.gridTable.rows.length; i++) {
-                    this.gridTable.rows[i].cells[col + 1].removeClass('jxGridColumnSelected');
-                }   
             }
-            this.selectColumnHeader(col, selected);
+        }, this);
+        
+        this.fireEvent(event, [selection]);
+    },
+    
+    sort: function(dir) {
+      var grid = this.grid,
+          store = grid.store,
+          data = store.data,
+          gridTableBody= grid.gridTableBody,
+          gridParent = gridTableBody.getParent(),
+          useHeaders = grid.row.useHeaders(),
+          rowTableBody = grid.rowTableBody,
+          rowParent = rowTableBody.getParent(),
+          selected = this.getSelectedRows();
+      
+      // sorting only works for rows and when more than zero are selected
+      // in fact it is probably only useful if multiple selections are also enabled
+      // but that is not a hard rule for this method
+      if (!this.options.row || selected.length == 0) {
+        console.log('not sorting by selection, nothing to sort');
+        return;
+      }
+      
+      store.each(function(record, index) {
+        record.dom = {
+          cell: gridTableBody.childNodes[index],
+          row: useHeaders ? rowTableBody.childNodes[index] : null
+        };
+      });
+
+      gridTableBody.dispose();
+      if (useHeaders) {
+        rowTableBody.dispose();
+      }
+      selected.sort(function(a,b) {
+        return a.retrieve('jxRowData').row - b.retrieve('jxRowData').row;
+      }).each(function(row) {
+        console.log('moving row ' + row.retrieve('jxRowData').row + ' to beginning of array');
+        data.unshift(data.splice(row.retrieve('jxRowData').row,1)[0]);
+      });
+
+      if (dir == 'desc') {
+        data.reverse();
+      }
+
+      store.each(function(record, index) {
+        record.dom.cell.inject(gridTableBody);
+        record.dom.cell.store('jxRowData', {row: index});
+        if (useHeaders) {
+          record.dom.row.inject(rowTableBody);
         }
+      });
+
+      if (gridParent) {
+        gridParent.adopt(gridTableBody);
+      }
+      if (useHeaders && rowParent) {
+        rowParent.adopt(rowTableBody);
+      }
     },
     
+    getSelectedRows: function() {
+      var rows = [],
+          selected = this.selected.get('rows'),
+          r = this.grid.gridTableBody.rows;
+      selected.each(function(row) {
+        var tr = document.id((row >= 0 && row < r.length) ? r[row] : null);
+        if (tr) {
+          rows.push(tr);
+        }
+      });
+      return rows;
+    }
+});
+/*
+---
+
+name: Jx.Plugin.Grid.Prelighter
+
+description: Highlights rows, columns, cells, and headers in grids
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin.Grid
+
+provides: [Jx.Plugin.Grid.Prelighter]
+
+...
+ */
+// $Id: grid.prelighter.js 981 2010-09-13 12:18:35Z pagameba $
+/**
+ * Class: Jx.Plugin.Grid.Prelighter
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Grid plugin to prelight rows, columns, and cells
+ *
+ * Inspired by the original code in Jx.Grid
+ *
+ * License:
+ * Original Copyright (c) 2008, DM Solutions Group Inc.
+ * This version Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid.Prelighter = new Class({
+
+    Extends : Jx.Plugin,
+    
+    name: 'Prelighter',
+    
+    options : {
+        /**
+         * Option: cell
+         * defaults to false.  If set to true, the cell under the mouse is
+         * highlighted as the mouse moves.
+         */
+        cell : false,
+        /**
+         * Option: row
+         * defaults to false.  If set to true, the row under the mouse is
+         * highlighted as the mouse moves.
+         */
+        row : false,
+        /**
+         * Option: column
+         * defaults to false.  If set to true, the column under the mouse is
+         * highlighted as the mouse moves.
+         */
+        column : false,
+        /**
+         * Option: rowHeader
+         * defaults to false.  If set to true, the row header of the row under
+         * the mouse is highlighted as the mouse moves.
+         */
+        rowHeader : false,
+        /**
+         * Option: columnHeader
+         * defaults to false.  If set to true, the column header of the column
+         * under the mouse is highlighted as the mouse moves.
+         */
+        columnHeader : false
+    },
     /**
-     * Method: onMouseMoveGrid
-     * handle the mouse moving over the main grid.  This pre-lights the cell,
-     * and subsquently the row and column (and headers).
-     *
+     * APIMethod: init
+     * construct a new instance of the plugin.  The plugin must be attached
+     * to a Jx.Grid instance to be useful though.
+     */
+    init: function() {
+        this.parent();
+        this.bound.lighton = this.lighton.bind(this);
+        this.bound.lightoff = this.lightoff.bind(this);
+        this.bound.mouseleave = this.mouseleave.bind(this);
+    },
+    /**
+     * APIMethod: attach
+     * Sets up the plugin and connects it to the grid
+     */
+    attach: function (grid) {
+        if (!$defined(grid) && !(grid instanceof Jx.Grid)) {
+            return;
+        }
+        this.parent(grid);
+        this.grid = grid;
+        // this.grid.wantEvent('gridCellEnter');
+        // this.grid.wantEvent('gridCellLeave');
+        // this.grid.wantEvent('gridRowEnter');
+        // this.grid.wantEvent('gridRowLeave');
+        // this.grid.wantEvent('gridColumnEnter');
+        // this.grid.wantEvent('gridColumnLeave');
+        // this.grid.wantEvent('gridMouseLeave');
+        
+        this.grid.addEvent('gridCellEnter', this.bound.lighton);
+        this.grid.addEvent('gridCellLeave', this.bound.lightoff);
+        this.grid.addEvent('gridRowEnter', this.bound.lighton);
+        this.grid.addEvent('gridRowLeave', this.bound.lightoff);
+        this.grid.addEvent('gridColumnEnter', this.bound.lighton);
+        this.grid.addEvent('gridColumnLeave', this.bound.lightoff);
+        this.grid.addEvent('gridMouseLeave', this.bound.mouseleave);
+    },
+    /**
+     * APIMethod: detach
+     */
+    detach: function() {
+        if (this.grid) {
+            this.grid.removeEvent('gridCellEnter', this.bound.lighton);
+            this.grid.removeEvent('gridCellLeave', this.bound.lightoff);
+            this.grid.removeEvent('gridRowEnter', this.bound.lighton);
+            this.grid.removeEvent('gridRowLeave', this.bound.lightoff);
+            this.grid.removeEvent('gridColumnEnter', this.bound.lighton);
+            this.grid.removeEvent('gridColumnLeave', this.bound.lightoff);
+            this.grid.removeEvent('gridMouseLeave', this.bound.mouseleave);
+        }
+        this.grid = null;
+    },
+    /**
+     * APIMethod: activate
+     * Allows programatic access to turning prelighting on.
+     * 
      * Parameters:
-     * e - {Event} the browser event object
+     * opt - the option to turn on. One of 'cell', 'row', 'rowHeader', 'column', or 'columnHeader'
      */
-    onMouseMoveGrid: function(e) {
-        var rc = this.getRowColumnFromEvent(e);
-        if (this.options.cellPrelight) {
-            this.prelightCell(rc.row, rc.column);            
+    activate: function (opt) {
+        this.options[opt] = true;
+    },
+    /**
+     * APIMethod: deactivate
+     * Allows programatic access to turning prelighting off.
+     * 
+     * Parameters:
+     * opt - the option to turn off. One of 'cell', 'row', 'rowHeader', 'column', or 'columnHeader'
+     */
+    deactivate: function (opt) {
+        this.options[opt] = false;
+    },
+    /**
+     * Method: lighton
+     */
+    lighton : function (cell) {
+        this.light(cell, true);
+
+    },
+    /**
+     * Method: lightoff
+     */
+    lightoff : function (cell) {
+        this.light(cell, false);
+
+    },
+    /**
+     * Method: light
+     * dispatches the event to the various prelight methods.
+     */
+    light: function (cell, on) {
+        var parent = cell.getParent(),
+            rowIndex = parent.getParent().getChildren().indexOf(parent),
+            colIndex = cell.getParent().getChildren().indexOf(cell);
+
+        if (this.options.cell) {
+            this.prelightCell(cell, on);
         }
-        if (this.options.rowPrelight) {
-            this.prelightRow(rc.row);            
+        if (this.options.row) {
+            this.prelightRow(rowIndex, on);
         }
-        if (this.options.rowHeaderPrelight) {
-            this.prelightRowHeader(rc.row);            
+        if (this.options.column) {
+            this.prelightColumn(colIndex, on);
         }
-        if (this.options.columnPrelight) {
-            this.prelightColumn(rc.column);
-        }        
-        if (this.options.columnHeaderPrelight) {
-            this.prelightColumnHeader(rc.column);
-        }        
+        if (this.options.rowHeader) {
+            this.prelightRowHeader(rowIndex, on);
+        }
+        if (this.options.columnHeader) {
+            this.prelightColumnHeader(colIndex, on);
+        }
     },
-    
+
     /**
-     * Method: onMouseMoveRowHeader
-     * handle the mouse moving over the row header cells.  This pre-lights
-     * the row and subsequently the row header.
+     * Method: prelightRowHeader
+     * apply the jxGridRowHeaderPrelight style to the header cell of a row.
+     * This removes the style from the previously pre-lit row header.
      *
      * Parameters:
-     * e - {Event} the browser event object
+     * row - {Integer} the row to pre-light the header cell of
      */
-    onMouseMoveRowHeader: function(e) {
-        if (this.options.rowPrelight) {
-            var rc = this.getRowColumnFromEvent(e);
-            this.prelightRow(rc.row);            
+    prelightRowHeader : function (row, on) {
+        if ($defined(this.prelitRowHeader) && !on) {
+            this.prelitRowHeader.removeClass('jxGridRowHeaderPrelight');
+        } else if (on) {
+            this.prelitRowHeader = (row >= 0 && row < this.grid.rowTableBody.rows.length) ? this.grid.rowTableBody.rows[row].cells[0] : null;
+            if (this.prelitRowHeader) {
+                this.prelitRowHeader.addClass('jxGridRowHeaderPrelight');
+            }
         }
     },
+    /**
+     * Method: prelightColumnHeader
+     * apply the jxGridColumnHeaderPrelight style to the header cell of a column.
+     * This removes the style from the previously pre-lit column header.
+     *
+     * Parameters:
+     * col - {Integer} the column to pre-light the header cell of
+     * on - flag to tell if we're lighting on or off
+     */
+    prelightColumnHeader : function (col, on) {
+        if (this.grid.colTableBody.rows.length === 0) {
+            return;
+        }
 
+        if ($defined(this.prelitColumnHeader) && !on) {
+            this.prelitColumnHeader.removeClass('jxGridColumnHeaderPrelight');
+        } else if (on) {
+            this.prelitColumnHeader = (col >= 0 && col < this.grid.colTableBody.rows[0].cells.length) ? this.grid.colTableBody.rows[0].cells[col] : null;
+            if (this.prelitColumnHeader) {
+                this.prelitColumnHeader.addClass('jxGridColumnHeaderPrelight');
+            }
+        }
+
+    },
     /**
-     * Method: onMouseMoveColumnHeader
-     * handle the mouse moving over the column header cells.  This pre-lights
-     * the column and subsequently the column header.
+     * Method: prelightRow
+     * apply the jxGridRowPrelight style to row.
+     * This removes the style from the previously pre-lit row.
      *
      * Parameters:
-     * e - {Event} the browser event object
+     * row - {Integer} the row to pre-light
+     * on - flag to tell if we're lighting on or off
      */
-    onMouseMoveColumnHeader: function(e) {
-        if (this.options.columnPrelight) {
-            var rc = this.getRowColumnFromEvent(e);
-            this.prelightColumn(rc.column);
+    prelightRow : function (row, on) {
+       if ($defined(this.prelitRow) && !on) {
+            this.prelitRow.removeClass('jxGridRowPrelight');
+        } else if (on) {
+            this.prelitRow = (row >= 0 && row < this.grid.gridTableBody.rows.length) ? this.grid.gridTableBody.rows[row] : null;
+            if (this.prelitRow) {
+                this.prelitRow.addClass('jxGridRowPrelight');
+            }
         }
+        this.prelightRowHeader(row, on);
     },
+    /**
+     * Method: prelightColumn
+     * apply the jxGridColumnPrelight style to a column.
+     * This removes the style from the previously pre-lit column.
+     *
+     * Parameters:
+     * col - {Integer} the column to pre-light
+     * on - flag to tell if we're lighting on or off
+     */
+    prelightColumn : function (col, on) {
+        if (col >= 0 && col < this.grid.gridTableBody.rows[0].cells.length) {
+            if ($defined(this.prelitColumn) && !on) {
+                for (var i = 0; i < this.grid.gridTableBody.rows.length; i++) {
+                    this.grid.gridTableBody.rows[i].cells[this.prelitColumn].removeClass('jxGridColumnPrelight');
+                }
+            } else if (on) {
+                this.prelitColumn = col;
+                for (i = 0; i < this.grid.gridTableBody.rows.length; i++) {
+                    this.grid.gridTableBody.rows[i].cells[col].addClass('jxGridColumnPrelight');
+                }
+            }
+            this.prelightColumnHeader(col, on);
+        }
+    },
+    /**
+     * Method: prelightCell
+     * apply the jxGridCellPrelight style to a cell.
+     * This removes the style from the previously pre-lit cell.
+     *
+     * Parameters:
+     * cell - the cell to lighton/off
+     * on - flag to tell if we're lighting on or off
+     */
+    prelightCell : function (cell, on) {
+        if ($defined(this.prelitCell) && !on) {
+            this.prelitCell.removeClass('jxGridCellPrelight');
+        } else if (on) {
+            this.prelitCell = cell;
+            if (this.prelitCell) {
+                this.prelitCell.addClass('jxGridCellPrelight');
+            }
+        }
+    },
     
+    mouseleave: function() {
+        //turn off all prelights when the mouse leaves the grid
+        if ($defined(this.prelitCell)) {
+            this.prelitCell.removeClass('jxGridCellPrelight');
+        }
+        if ($defined(this.prelitColumn)) {
+            for (var i = 0; i < this.grid.gridTableBody.rows.length; i++) {
+                this.grid.gridTableBody.rows[i].cells[this.prelitColumn].removeClass('jxGridColumnPrelight');
+            }
+        }
+        if ($defined(this.prelitRow)) {
+            this.prelitRow.removeClass('jxGridRowPrelight');
+        }
+        if ($defined(this.prelitColumnHeader)) {
+            this.prelitColumnHeader.removeClass('jxGridColumnHeaderPrelight');
+        }
+        if ($defined(this.prelitRowHeader)) {
+            this.prelitRowHeader.removeClass('jxGridRowHeaderPrelight');
+        }
+    }
+});
+/*
+---
+
+name: Jx.Plugin.Grid.Sorter
+
+description: Enables column sorting in grids
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin.Grid
+
+provides: [Jx.Plugin.Grid.Sorter]
+
+images:
+ - emblems.png
+...
+ */
+// $Id: grid.sorter.js 992 2010-10-07 19:28:37Z pagameba $
+/**
+ * Class: Jx.Plugin.Grid.Sorter
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Grid plugin to sort the grid by a single column.
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid.Sorter = new Class({
+  Family: 'Jx.Plugin.Grid.Sorter',
+  Extends: Jx.Plugin,
+  name: 'Prelighter',
+
+  Binds: ['sort', 'modifyHeaders'],
+
+  /**
+   * Property: current
+   * refernce to the currently sorted column
+   */
+  current: null,
+
+  /**
+   * Property: direction
+   * tell us what direction the sort is in (either 'asc' or 'desc')
+   */
+  direction: null,
+
+  options: {
+    sortableClass: 'jxColSortable',
+    ascendingClass: 'jxGridColumnSortedAsc',
+    descendingClass: 'jxGridColumnSortedDesc'
+  },
+
+  /**
+   * APIMethod: attach
+   * Sets up the plugin and attaches the plugin to the grid events it
+   * will be monitoring
+   */
+  attach: function(grid) {
+    if (!$defined(grid) && !(grid instanceof Jx.Grid)) {
+        return;
+    }
+    this.parent(grid);
+
+    this.grid = grid;
+
+    // this.grid.wantEvent('gridColumnClick');
+    this.grid.addEvent('gridColumnClick', this.sort);
+    this.grid.addEvent('doneCreateGrid', this.modifyHeaders);
+  },
+
+  /**
+   * APIMethod: detach
+   */
+  detach: function() {
+    if (this.grid) {
+        this.grid.removeEvent('gridColumnClick', this.sort);
+    }
+    this.grid = null;
+  },
+
+  /**
+   * Method: modifyHeaders
+   */
+  modifyHeaders: function() {
+    var grid = this.grid,
+        columnTable = grid.colObj,
+        store = grid.store,
+        c = this.options.sortableClass;
+    if (grid.columns.useHeaders()) {
+      grid.columns.columns.each(function(col, index) {
+        if (!col.isHidden() && col.isSortable()) {
+          var th = columnTable.getElement('.jxGridCol'+index);
+          th.addClass(c);
+        }
+      });
+    }
+  },
+
+  /**
+   * Method: sort
+   * called when a grid header is clicked.
+   *
+   * Parameters:
+   * cell - The cell clicked
+   */
+  sort: function(el) {
+    var current = this.current,
+        grid = this.grid,
+        gridTableBody = grid.gridTableBody,
+        gridParent = gridTableBody.getParent(),
+        rowTableBody = grid.rowTableBody,
+        rowParent = rowTableBody.getParent(),
+        useHeaders = grid.row.useHeaders(),
+        store = grid.store,
+        sorter = store.getStrategy('sort'),
+        data = el.retrieve('jxCellData'),
+        dir = 'asc',
+        opt = this.options;
+    
+    if ($defined(data.column) && data.column.isSortable()){
+      if (el.hasClass(opt.ascendingClass)) {
+        el.removeClass(opt.ascendingClass).addClass(opt.descendingClass);
+        dir = 'desc';
+      } else if (el.hasClass(opt.descendingClass)) {
+        el.removeClass(opt.descendingClass).addClass(opt.ascendingClass);
+      } else {
+        el.addClass(opt.ascendingClass);
+      }
+      if (current && el != current) {
+        current.removeClass(opt.ascendingClass).removeClass(opt.descendingClass);
+      }
+      this.current = el;
+      if ($defined(data.column.options.sort) && Jx.type(data.column.options.sort) == 'function') {
+        data.column.options.sort(dir);
+      } else {
+        if (sorter) {
+          gridTableBody.dispose();
+          if (useHeaders) {
+            rowTableBody.dispose();
+          }
+          store.each(function(record, index) {
+            record.dom = {
+              cell: gridTableBody.childNodes[index],
+              row: useHeaders ? rowTableBody.childNodes[index] : null
+            };
+          });
+    
+          // store.removeEvent('storeSortFinished', grid.drawStore);
+          sorter.sort(data.column.name, null, dir);
+          // store.addEvent('storeSortFinished', grid.drawStore);
+    
+          store.each(function(record, index) {
+            record.dom.cell.inject(gridTableBody);
+            if (useHeaders) {
+              record.dom.row.inject(rowTableBody);
+            }
+          });
+    
+          if (gridParent) {
+            gridParent.adopt(gridTableBody);
+          }
+          if (useHeaders && rowParent) {
+            rowParent.adopt(rowTableBody);
+          }
+        }
+      }
+    }
+  }
+});/*
+---
+
+name: Jx.Plugin.Grid.Resize
+
+description: Enables column resizing in grids
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin.Grid
+
+provides: [Jx.Plugin.Grid.Resize]
+
+...
+ */
+// $Id: grid.resize.js 992 2010-10-07 19:28:37Z pagameba $
+/**
+ * Class: Jx.Plugin.Grid.Resize
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Grid plugin to enable dynamic resizing of column width and row height
+ *
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid.Resize = new Class({
+
+    Extends : Jx.Plugin,
+    
+    name: 'Resize',
+    
+    Binds: ['createHandles','removeHandles'],
+    options: {
+        /**
+         * Option: column
+         * set to true to make column widths resizeable
+         */
+        column: false,
+        /**
+         * Option: row
+         * set to true to make row heights resizeable
+         */
+        row: false,
+        /**
+         * Option: tooltip
+         * the tooltip to display for the draggable portion of the
+         * cell header, localized with MooTools.lang.get('Jx','plugin.resize').tooltip for default
+         */
+        tooltip: ''
+    },
     /**
-     * Method: onClickGrid
-     * handle the user clicking on the grid.  This triggers an
-     * event to the model (if a cellSelected function is provided).
+     * Property: els
+     * the DOM elements by which the rows/columns are resized.
+     */
+    els: {
+      column: [],
+      row: []
+    },
+
+    /**
+     * Property: drags
+     * the Drag instances
+     */
+    drags: {
+      column: [],
+      row: []
+    },
+
+    /**
+     * APIMethod: attach
+     * Sets up the plugin and connects it to the grid
+     */
+    attach: function (grid) {
+      if (!$defined(grid) && !(grid instanceof Jx.Grid)) {
+          return;
+      }
+      this.parent(grid);
+      this.grid = grid;
+      if (grid.columns.useHeaders()) {
+        this.grid.addEvent('doneCreateGrid', this.createHandles);
+        this.grid.addEvent('beginCreateGrid', this.removeHandles);
+        this.createHandles();
+      }
+    },
+    /**
+     * APIMethod: detach
+     */
+    detach: function() {
+      this.parent();
+      if (this.grid) {
+          this.grid.removeEvent('doneCreateGrid', this.createHandles);
+          this.grid.removeEvent('beginCreateGrid', this.removeHandles);
+      }
+      this.grid = null;
+    },
+
+    /**
+     * APIMethod: activate
+     */
+    activate: function(option) {
+        if ($defined(this.options[option])) {
+          this.options[option] = true;
+        }
+        if (this.grid.columns.useHeaders()) {
+          this.createHandles();
+        }
+    },
+
+    /**
+     * APIMethod: deactivate
+     */
+    deactivate: function(option) {
+        if ($defined(this.options[option])) {
+          this.options[option] = false;
+        }
+        this.createHandles();
+    },
+    /**
+     * Method: removeHandles
+     * clean up any handles we created
+     */
+    removeHandles: function() {
+        ['column','row'].each(function(option) {
+          this.els[option].each(function(el) { el.dispose(); } );
+          this.els[option] = [];
+          this.drags[option].each(function(drag){ drag.detach(); });
+          this.drags[option] = [];
+        }, this);
+    },
+    /**
+     * Method: createHandles
+     * create handles that let the user drag to resize columns and rows
+     */
+    createHandles: function() {
+      var grid = this.grid,
+          store = grid.store;
+      this.removeHandles();
+      if (this.options.column && grid.columns.useHeaders()) {
+        grid.columns.columns.each(function(col, idx) {
+          if (col.isResizable() && !col.isHidden()) {
+            var colEl = grid.colObj.getElement('.jxGridCol'+idx+ ' .jxGridCellContent');
+            var el = new Element('div', {
+              'class':'jxGridColumnResize',
+              title: this.options.tooltip == '' ? this.getText({set:'Jx',key:'plugin.resize',value:'tooltip'}) : this.getText(this.options.tooltip),
+              events: {
+                dblclick: function() {
+                  // size to fit?
+                }
+              }
+            }).inject(colEl);
+            this.els.column.push(el);
+            this.drags.column.push(new Drag(el, {
+                limit: {y:[0,0]},
+                snap: 2,
+                onBeforeStart: function(el) {
+                  var l = el.getPosition(el.parentNode).x.toInt();
+                  el.setStyles({
+                    left: l,
+                    right: null
+                  });
+
+                },
+                onStart: function(el) {
+                  var l = el.getPosition(el.parentNode).x.toInt();
+                  el.setStyles({
+                    left: l,
+                    right: null
+                  });
+                },
+                onDrag: function(el) {
+                    var w = el.getPosition(el.parentNode).x.toInt();
+                    col.setWidth(w);
+                },
+                onComplete: function(el) {
+                  el.setStyle('left', null);
+                }
+            }));
+          }
+        }, this);
+      }
+      //if (this.options.row && this.grid.row.useHeaders()) {}
+    },
+    /**
+     * Method: createText
+     * respond to a language change by updating the tooltip
+     */
+    changeText: function (lang) {
+      this.parent();
+      var txt = this.options.tooltip == '' ? this.getText({set:'Jx',key:'plugin.resize',value:'tooltip'}) : this.getText(this.options.tooltip);
+      ['column','row'].each(function(option) {
+        this.els[option].each(function(el) { el.set('title',txt); } );
+      }, this);
+    }
+});/*
+---
+
+name: Jx.Plugin.Grid.Editor
+
+description: Enables inline editing in grids
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin.Grid
+ - More/Keyboard
+
+provides: [Jx.Plugin.Grid.Editor]
+
+images:
+ - icons.png
+...
+ */
+// $Id: grid.editor.js 981 2010-09-13 12:18:35Z pagameba $
+/**
+ * Class: Jx.Plugin.Grid.Editor
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Grid plugin to enable inline editing within a cell
+ *
+ * Original selection code from Jx.Grid's original class
+ *
+ * License:
+ * Original Copyright (c) 2008, DM Solutions Group Inc.
+ * This version Copyright (c) 2009, Conrad Barthelmes.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Grid.Editor = new Class({
+
+    Extends : Jx.Plugin,
+    
+    name: 'Editor',
+    
+    Binds: ['activate','deactivate','changeText','onCellClick'],
+
+    options : {
+      /**
+       * Option: enabled
+       * Determines if inline editing is avaiable
+       */
+      enabled : true,
+      /**
+       * Option: blurDelay
+       * Set the time in miliseconds when the inputfield/popup shall hide. When
+       * the user refocuses the input/popup within this time, the timeout will be cleared
+       *
+       * set to 'false' if no hiding on blur is wanted
+       */
+      blurDelay : 500,
+      /**
+       * Option: popup
+       *
+       * Definitions for a PopUp to use.
+       * - use        - determines whether to use a PopUp or simply the input
+       * - useLabel   - determines whether to use labels on top of the input.
+       *                Text will be the column header
+       * - useButtons - determines whether to use Submit and Cancel Buttons
+       * - buttonLabel.submit - Text for Submit Button, uses MooTools.lang.get('Jx', 'plugin.editor').submitButton for default
+       * - buttonLabel.cancel - Text for Cancel Button, uses MooTools.lang.get('Jx', 'plugin.editor').cancelButton for default
+       */
+      popup : {
+        use           : true,
+        useLabels     : false,
+        useButtons    : true,
+        button        : {
+          submit : {
+            label : '',
+            image : 'images/accept.png'
+          },
+          cancel : {
+            label : '',
+            image : 'images/cancel.png'
+          }
+        },
+        template: '<div class="jxGridEditorPopup"><div class="jxGridEditorPopupInnerWrapper"></div></div>'
+      },
+      /**
+       * Option {boolean} validate
+       * - set to true to have all editable input fields as mandatory field
+       *   if they don't have 'mandatory:true' in their colOptions
+       */
+      validate : true,
+      /**
+       * Option: {Array} fieldOptions with objects
+       * Contains objects with options for the Jx.Field instances to show up.
+       * Default options will be added automatically if custom options are entered.
+       *
+       * Preferences:
+       *   field             - Default * for all types or the name of the column in the store (Jx.Store)
+       *   type              - Input type to show (Text, Password, Textarea, Select, Checkbox)
+       *   options           - All Jx.Field options for this column. More options depend on what type you are using.
+       *                       See Jx.Form.[yourField] for details
+       *   validatorOptions: - See Jx.Plugin.Field.Validator Options for details
+       *                       will only be used if this.options.validate is set to true
+       */
+      fieldOptions : [
+        {
+          field   : '*',
+          type    : 'Text',
+          options : {},
+          validatorOptions: {
+            validators : [],
+            validateOnBlur: true,
+            validateOnChange : false
+          }
+        }
+      ],
+      /**
+       * Option: {Boolean} fieldFormatted
+       * Displays the cell value also inside the input field as formatted
+       */
+      fieldFormatted : true,
+      /**
+       * Option cellChangeFx
+       * set use to false if no highlighting effect is wanted.
+       *
+       * this is just an idea how successfully changing could be highlighed for the user
+       */
+      cellChangeFx : {
+        use     : true,
+        success : '#090',
+        error   : '#F00'
+      },
+      /**
+       * Option cellOutline
+       * shows an outline style to the currently active cell to make it easier to see
+       * which cell is active
+       */
+      cellOutline : {
+        use   : true,
+        style : '2px solid #88c3e7'
+      },
+      /**
+       * Option: useKeyboard
+       * Set to false if no keyboard support is needed
+       */
+      useKeyboard : true,
+      /**
+       * Option: keys
+       * Contains the event codes for several commands that can be used when
+       * a field is active. Syntax is the same like for the Mootools Keyboard Class
+       * http://mootools.net/docs/more/Interface/Keyboard
+       */
+      keys : {
+        'ctrl+shift+enter' : 'saveNGoUp',
+        'tab'              : 'saveNGoRight',
+        'ctrl+enter'       : 'saveNGoDown',
+        'shift+tab'        : 'saveNGoLeft',
+        'enter'            : 'saveNClose',
+        'ctrl+up'          : 'cancelNGoUp',
+        'ctrl+right'       : 'cancelNGoRight',
+        'ctrl+down'        : 'cancelNGoDown',
+        'ctrl+left'        : 'cancelNGoLeft',
+        'esc'              : 'cancelNClose',
+        'up'               : 'valueIncrement',
+        'down'             : 'valueDecrement'
+      },
+      /**
+       * Option: keyboardMethods
+       *
+       * can be used to overwrite existing keyboard methods that are used inside
+       * this.options.keys - also possible to add new ones.
+       * Functions are bound to the editor plugin when using 'this'
+       *
+       * example:
+       *  keys : {
+       *    'ctrl+u' : 'cancelNGoRightNDown'
+       *  },
+       *  keyboardMethods: {
+       *    'cancelNGoRightNDown' : function(ev){
+       *      ev.preventDefault();
+       *      this.getNextCellInRow(false);
+       *      this.getNextCellInCol(false);
+       *    }
+       *  }
+       */
+      keyboardMethods : {},
+      /**
+       * Option: keypressLoop
+       * loop through the grid when pressing TAB (or some other method that uses
+       * this.getNextCellInRow() or this.getPrevCellInRow()). If set to false,
+       * the input field/popup will not start at the opposite site of the grid
+       * Defaults to true
+       */
+      keypressLoop : true,
+      /**
+       * Option: linkClickListener
+       * disables all click events on links that are formatted with Jx.Formatter.Uri
+       * - otherwise the link will open directly instead of open the input editor)
+       * - hold [ctrl] to open the link in a new tab
+       */
+      linkClickListener : true
+    },
+    classes: ['jxGridEditorPopup', 'jxGridEditorPopupInnerWrapper'],
+    /**
+     * Property: activeCell
      *
-     * The following is an example of a function in the model that selects
-     * a row when the cellSelected function is called and deselects any rows
-     * that are currently selected.
+     * Containing Objects:
+     *   field        : Reference to the Jx.Field instance that will be created
+     *   cell         : Reference to the cell inside the table 
+     *   span         : Reference to the Dom Element inside the selected cell of the grid
+     *   oldValue     : Old value of the cell from the grid's store
+     *   newValue     : Object with <data> and <error> for better validation possibilites
+     *   timeoutId    : TimeoutId if the focus blurs the input.
+     *   data         : Reference to the cell data
+     *   fieldOptions : Reference to the field options of this column
+     */
+    activeCell : {
+      field       : null,
+      cell        : null,
+      span        : null,
+      oldValue    : null,
+      newValue    : { data: null, error: false },
+      timeoutId   : null,
+      data        : {},
+      fieldOptions: {}
+    },
+    /**
+     * Property : popup
      *
-     * (code)
-     * cellSelected: function(grid, row,col) { 
-     *    if (this.selectedRow != null) {
-     *        grid.selectRow(this.selectedRow, false);
-     *    }
-     *    this.selectedRow = row;
-     *    grid.selectRow(row, true);
-     * }
+     * References to all contents within a popup (only 1 popup for 1 grid initialization)
      *
+     * COMMENT: I don't know how deep we need to go into that.. innerWrapper and closeLink probably don't need
+     * own references.. I just made them here in case they are needed at some time..
+     *
+     * Containing Objects:
+     *   domObj         : Reference to the Dom Element of the popup (absolutely positioned)
+     *   innerWrapper   : Reference to the inner Wrapper inside the popup to provide relative positioning
+     *   closeIcon      : Reference to the Dom Element of a little [x] in the upper right to close it (not saving)
+     *   buttons        : References to all Jx.Buttons used inside the popup
+     *   buttons.submit : Reference to the Submit Button
+     *   buttons.cancel : Reference to the Cancel Button
+     */
+    popup : {
+      domObj       : null,
+      innerWarpper : null,
+      closeIcon    : null,
+      button       : {
+        submit : null,
+        cancel : null
+      }
+    },
+    /**
+     * Property: keyboard
+     * Instance of a Mootols Keyboard Class
+     */
+    keyboard : null,
+    /**
+     * Property keyboardMethods
+     * Editing and grid functions for keyboard functionality.
+     * Methods are defined and implemented inside this.attach() because of referencing troubles
+     */
+    keyboardMethods : {},
+    /**
+     * APIMethod: init
+     * construct a new instance of the plugin.  The plugin must be attached
+     * to a Jx.Grid instance to be useful though.
+     */
+    init: function() {
+      this.parent();
+    },
+    /**
+     * APIMethod: attach
+     * Sets up the plugin and attaches the plugin to the grid events it
+     * will be monitoring
+     *
+     * @var {Object} grid - Instance of Class Jx.Grid
+     */
+    attach: function (grid) {
+      if (!$defined(grid) && !(grid instanceof Jx.Grid)) {
+        return;
+      }
+      this.parent(grid);
+      this.grid = grid;
+
+      //this.grid.gridTableBody.addEvent('click', this.onCellClick);
+      // this.grid.wantEvent('gridCellClick');
+      this.grid.addEvent('gridCellClick', this.onCellClick);
+
+      /*
+       * add default field options to the options in case some new options were entered
+       * to be still able to use them for the rest of the fields
+       */
+      if(this.getFieldOptionsByColName('*').field != '*') {
+        this.options.fieldOptions.unshift({
+          field   : '*',
+          type    : 'Text',
+          options : {},
+          validatorOptions: {
+            validators : [],
+            validateOnBlur: true,
+            validateOnChange : false
+          }
+        });
+      }
+
+      /**
+       * set the keyboard methods here to have a correct reference to the instance of
+       * the editor plugin
+       *
+       * @todo other names maybe? or even completely different way of handling the keyboard events?
+       * @todo more documentation than method name
+       */
+      var self = this;
+      this.keyboardMethods = {
+        saveNClose     : function(ev) {
+          if(self.activeCell.fieldOptions.type != 'Textarea' || (self.activeCell.fieldOptions.type == 'Textarea' && ev.key != 'enter')) {
+            self.deactivate();
+          }
+        },
+        saveNGoUp      : function(ev) {ev.preventDefault();self.getPrevCellInCol();},
+        saveNGoRight   : function(ev) {ev.preventDefault();self.getNextCellInRow();},
+        saveNGoDown    : function(ev) {ev.preventDefault();self.getNextCellInCol();},
+        saveNGoLeft    : function(ev) {ev.preventDefault();self.getPrevCellInRow();},
+        cancelNClose   : function(ev) {ev.preventDefault();self.deactivate(false);},
+        cancelNGoUp    : function(ev) {ev.preventDefault();self.getPrevCellInCol(false);},
+        cancelNGoRight : function(ev) {ev.preventDefault();self.getNextCellInRow(false);},
+        cancelNGoDown  : function(ev) {ev.preventDefault();self.getNextCellInCol(false);},
+        cancelNGoLeft  : function(ev) {ev.preventDefault();self.getPrevCellInRow(false);},
+        valueIncrement : function(ev) {ev.preventDefault();self.cellValueIncrement(true);},
+        valueDecrement : function(ev) {ev.preventDefault();self.cellValueIncrement(false);}
+      };
+
+      var keyboardEvents = {};
+      for(var i in this.options.keys) {
+        if($defined(this.keyboardMethods[this.options.keys[i]])) {
+          keyboardEvents[i] = this.keyboardMethods[this.options.keys[i]];
+        }else if($defined(this.options.keyboardMethods[this.options.keys[i]])){
+          keyboardEvents[i] = this.options.keyboardMethods[this.options.keys[i]].bind(self);
+        }else if(Jx.type(this.options.keys[i]) == 'function') {
+          keyboardEvents[i] = this.options.keys[i].bind(self);
+        }else{
+          $defined(console) ? console.warn("keyboard method %o not defined", this.options.keys[i]) : false;
+        }
+      }
+
+      // initalize keyboard support but do NOT activate it (this is done inside this.activate()).
+      this.keyboard = new Keyboard({
+        events: keyboardEvents
+      });
+
+      this.addFormatterUriClickListener();
+    },
+    /**
+     * APIMethod: detach
+     * detaches from the grid
+     * 
+     * @return void
+     */
+    detach: function() {
+      if (this.grid) {
+        this.grid.removeEvent('gridCellClick', this.onCellClick);
+      }
+      this.grid = null;
+      this.keyboard = null;
+    },
+    /**
+     * APIMethod: enable
+     * enables the grid 'externally'
+     *
+     * @return void
+     */
+    enable : function () {
+      this.options.enabled = true;
+    },
+    /**
+     * APIMethod: disable
+     * disables the grid 'externally'
+     *
+     * @var Boolean close - default true: also closes the currently open input/popup
+     * @var Boolean save - default false: also changes the currently open input/popup
+     * @return void
+     */
+    disable : function(close, save) {
+      close = $defined(close) ? close : true;
+      save = $defined(save) ? save : false;
+      if(close && this.activeCell.cell != null) {
+        this.deactivate(save);
+      }
+      this.options.enabled = false;
+    },
+
+    /**
+     * Method: onCellClick
+     * dispatch clicking on a table cell
+     */
+    onCellClick: function(cell) {
+      this.activate(cell);
+    },
+    /**
+     * Method: activate
+     * activates the input field or breaks up if conditions are not fulfilled
+     *
+     * @todo Field validation
+     *
      * Parameters:
-     * e - {Event} the browser event object
+     * @var {Object} cell Table Element
+     * @return void
      */
-    onClickGrid: function(e) {
-        var rc = this.getRowColumnFromEvent(e);
-        
-        if (this.options.cellSelection && this.model.cellSelected) {
-            this.model.cellSelected(this, rc.row, rc.column);
+    activate: function(cell) {
+      // if not enabled or the cell is null, do nothing at all
+      if(!this.options.enabled || !cell)
+        return;
+
+      // activate can be called by clicking on the same cell or a
+      // different one
+      if (this.activeCell.cell) {
+        if (this.activeCell.cell != cell) {
+          if (!this.deactivate()) {
+            return;
+          }
+        } else {
+          // they are the same, ignore?
+          return;
         }
-        if (this.options.rowSelection && this.model.rowSelected) {
-            this.model.rowSelected(this, rc.row);
+      }
+      
+      var data  = this.grid.getCellData(cell); //.retrieve('jxCellData');
+
+      if (!data || !$defined(data.row) || !$defined(data.column)) {
+        if($defined(console)) {
+          console.warn('out of grid %o',cell);
+          console.warn('data was %o', data);
         }
-        if (this.options.columnSelection && this.model.columnSelected) {
-            this.model.columnSelected(this, rc.column);
+        return;
+      }
+
+      // column marked as not editable
+      if (!data.column.options.isEditable) {
+        return;
+      }
+
+      if (this.activeCell.timeoutId) {
+        clearTimeout(activeCell.timeoutId);
+      }
+
+      // set active record index to selected row
+      this.grid.store.moveTo(data.row);
+
+      // set up the data objects we need
+      var options = this.options,
+          grid = this.grid,
+          store = grid.getStore(),
+          index = grid.columns.getIndexFromGrid(data.column.name),
+          colOptions = data.column.options,
+          activeCell = {
+            oldValue      : store.get(data.column.name),
+            newValue      : {data: null, error: false},
+            fieldOptions  : this.getFieldOptionsByColName(data.column.name),
+            data          : data,
+            cell          : cell,
+            span          : cell.getElement('span.jxGridCellContent'),
+            validator     : null,
+            field         : null,
+            timeoutId     : null
+          },
+          jxFieldOptions = activeCell.fieldOptions.options,
+          oldValue,
+          groups,
+          k,
+          n;
+
+      // check if this column has special validation settings - 
+      // otherwise use default from this.options.validate
+      if(!$defined(data.column.options.validate) || typeof(data.column.options.validate) != 'boolean') {
+        data.column.options.validate = options.validate;
+        cell.store('jxCellData', data);
+      }
+
+      // check for different input field types
+      switch(activeCell.fieldOptions.type) {
+        case 'Text':
+        case 'Color':
+        case 'Password':
+        case 'File':
+          jxFieldOptions.value = activeCell.oldValue;
+          break;
+        case 'Textarea':
+          jxFieldOptions.value = activeCell.oldValue.replace(/<br \/>/gi, '\n');
+          break;
+        case 'Select':
+          // find out which visible value fits to the value inside
+          // <option>{value}</option> and set it to selected
+          jxFieldOptions.value = oldValue  = activeCell.oldValue.toString();
+          function setCombos(opts, oldValue) {
+            for(var i = 0, j = opts.length; i < j; i++) {
+              if(opts[i].value == oldValue) {
+                opts[i].selected = true;
+              }else{
+                opts[i].selected = false;
+              }
+            }
+            return opts;
+          }
+
+          if(jxFieldOptions.comboOpts) {
+            jxFieldOptions.comboOpts = setCombos(jxFieldOptions.comboOpts, oldValue);
+          }else if(jxFieldOptions.optGroups) {
+            groups = jxFieldOptions.optGroups;
+            for(k = 0, n = groups.length; k < n; k++) {
+              groups[k].options = setCombos(groups[k].options, oldValue);
+            }
+            jxFieldOptions.optGroups = groups;
+          }
+          break;
+        case 'Radio':
+        case 'Checkbox':
+        default:
+          $defined(console) ? console.warn("Fieldtype %o is not supported yet. If you have set a validator for a column, you maybe have forgotton to enter a field type.", activeCell.fieldOptions.type) : false;
+          return;
+          break;
+      }
+
+      // update the 'oldValue' to the formatted style, to compare the new value with the formatted one instead with the non-formatted-one
+      if(options.fieldFormatted && colOptions.renderer.options.formatter != null) {
+        if(!$defined(colOptions.fieldFormatted) || colOptions.fieldFormatted == true ) {
+          jxFieldOptions.value = colOptions.renderer.options.formatter.format(jxFieldOptions.value);
+          activeCell.oldValue = jxFieldOptions.value;
         }
-        
+      }
+
+      // create jx.field
+      activeCell.field = new Jx.Field[activeCell.fieldOptions.type.capitalize()](jxFieldOptions);
+      // create validator
+      if(options.validate && colOptions.validate) {
+        activeCell.validator = new Jx.Plugin.Field.Validator(activeCell.fieldOptions.validatorOptions);
+        activeCell.validator.attach(activeCell.field);
+      }
+
+      // store properties of the active cell
+      this.activeCell = activeCell;
+      this.setStyles(cell);
+
+      if(options.useKeyboard) {
+        this.keyboard.activate();
+      }
+
+      // convert a string to an integer if somebody entered a numeric value in quotes, if it failes: make false
+      if(typeof(options.blurDelay) == 'string') {
+        options.blurDelay = options.blurDelay.toInt() ? options.blurDelay.toInt() : false;
+      }
+
+      // add a onblur() and onfocus() event to the input field if enabled.
+      if(options.blurDelay !== false && typeof(options.blurDelay) == 'number') {
+        activeCell.field.field.addEvents({
+          // activate the timeout to close the input/poup
+          'blur' : function() {
+            // @todo For some reason, webkit does not clear the timeout correctly when navigating through the grid with keyboard
+            clearTimeout(activeCell.timeoutId);
+            activeCell.timeoutId = this.deactivate.delay(this.options.blurDelay);
+          }.bind(this),
+          // clear the timeout when the user focusses again
+          'focus' : function() {
+            clearTimeout(activeCell.timeoutId);
+          }, 
+          // clear the timeout when the user puts the mouse over the input
+          'mouseover' : function() {
+            clearTimeout(activeCell.timeoutId);
+          }
+        });
+        if(this.popup.domObj != null) {
+          this.popup.domObj.addEvent('mouseenter', function() {
+            clearTimeout(activeCell.timeoutId);
+          });
+        }
+      }
+
+      activeCell.field.field.focus();
+    }, 
+    /**
+     * APIMethod: deactivate
+     * hides the currently active field and stores the new entered data if the
+     * value has changed
+     *
+     * Parameters:
+     * @var {Boolean} save (Optional, default: true) - force aborting
+     * @return true if no data error occured, false if error (popup/input stays visible)
+     */
+    deactivate: function(save) {
+      var newValue = {data : null, error : false},
+          index,
+          activeCell = this.activeCell,
+          grid = this.grid,
+          store = grid.store,
+          options = this.options,
+          highlighter,
+          cellBg;
+
+      clearTimeout(activeCell.timeoutId);
+
+      if(activeCell.field !== null) {
+        save = $defined(save) ? save : true;
+
+
+        // update the value in the column
+        if(save && activeCell.field.getValue().toString() != activeCell.oldValue.toString()) {
+          store.moveTo(activeCell.data.row);
+          /*
+           * @todo webkit shrinks the rows when the value is updated... but refreshing the grid
+           *       immidiately returns in a wrong calculating of the cell position (getCoordinates)
+           */
+          switch (activeCell.fieldOptions.type) {
+            case 'Select':
+              index = activeCell.field.field.selectedIndex;
+              newValue.data = document.id(activeCell.field.field.options[index]).get('value');
+              break;
+            case 'Textarea':
+              newValue.data = activeCell.field.getValue().replace(/\n/gi, '<br />');
+              break;
+            default:
+              newValue.data = activeCell.field.getValue();
+              break;
+          }
+          if (save) {
+            activeCell.newValue.data = newValue.data;
+          }
+          // validation only if it should be saved!
+          if (activeCell.validator != null && !activeCell.validator.isValid()) {
+            newValue.error = true;
+            activeCell.field.field.focus.delay(50, activeCell.field.field);
+          }
+        } else {
+          activeCell.span.show();
+        }
+
+        // var data = activeCell.cell.retrieve('jxCellData');
+        if (save && newValue.data != null && newValue.error == false) {
+          store.set(activeCell.data.column.name, newValue.data);
+          this.addFormatterUriClickListener();
+        // else show error message and cell
+        } else if (newValue.error == true) {
+          activeCell.span.show();
+        }
+
+        // update reference to activeCell
+        if ($defined(activeCell.data.row) && $defined(activeCell.data.index)) {
+          var colIndex = grid.row.useHeaders() ? activeCell.data.index-1 : activeCell.data.index;
+          this.activeCell.cell = grid.gridTableBody.rows[this.activeCell.data.row].cells[colIndex];
+        }
+
+        if (options.useKeyboard) {
+          activeCell.field.removeEvent('keypress', this.setKeyboard);
+        }
+
+        /**
+         * COMMENT: this is just an idea how changing a value could be visualized
+         * we could also pass an Fx.Tween element?
+         * the row could probably be highlighted as well?
+         */
+        if(options.cellChangeFx.use) {
+          highlighter = new Fx.Tween(this.activeCell.cell, {
+            duration: 250,
+            onComplete: function(ev) {
+              this.element.removeProperty('style');
+            }
+          });
+          cellBg = activeCell.cell.getStyle('background-color');
+          cellBg = cellBg == 'transparent' ? '#fff' : cellBg;
+          if (newValue.data != null && newValue.error == false) {
+            highlighter.start('background-color',options.cellChangeFx.success, cellBg);
+          } else if (newValue.error){
+            highlighter.start('background-color',options.cellChangeFx.error, cellBg);
+          }
+        }
+
+        // check for error and keep input field alive
+        if (newValue.error) {
+          if(options.cellChangeFx.use) {
+            activeCell.field.field.highlight(options.cellChangeFx.error);
+          }
+          activeCell.field.field.setStyle('border','1px solid '+options.cellChangeFx.error);
+          activeCell.field.field.focus();
+          return false;
+        // otherwise hide it
+        }else{
+          this.keyboard.deactivate();
+          this.unsetActiveField();
+          return true;
+        }
+      }
     },
-    
     /**
-     * Method: onClickRowHeader
-     * handle the user clicking on the row header.  This triggers an
-     * event to the model (if a rowSelected function is provided) which
-     * can then select the row if desired.  
+     * Method: setStyles
+     * 
+     * sets some styles for the Jx.Field elements...
      *
-     * The following is an example of a function in the model that selects
-     * a row when the rowSelected function is called and deselects any rows
-     * that are currently selected.  More complex code could be written to 
-     * allow the user to select multiple rows.
+     * Parameters:
+     * @var cell - table cell of the grid
+     * @return void
+     */
+    setStyles : function(cell) {
+      var styles, 
+          size,
+          options = this.options,
+          activeCell = this.activeCell;
+      // popup
+      if (options.popup.use) {
+        if (options.popup.useLabels) {
+          activeCell.field.options.label = activeCell.data.column.options.header;
+          activeCell.field.render();
+        }
+        styles = {
+          field : {
+            'width'  : activeCell.field.type == 'Select' ?
+                         cell.getContentBoxSize().width + 5 + "px" :
+                         cell.getContentBoxSize().width - 14 + "px",
+            'margin' : 'auto 0'
+          }
+        };
+        activeCell.field.field.setStyles(styles.field);
+        this.showPopUp(cell);
+      // No popup
+      } else {
+        size   = cell.getContentBoxSize();
+        styles = {
+          domObj : {
+            position: 'absolute'
+          },
+          field : {
+            width : size.width + "px",
+            'margin-left' : 0
+          }
+        };
+
+        activeCell.field.domObj.setStyles(styles.domObj);
+        activeCell.field.field.setStyles(styles.field);
+
+        activeCell.field.domObj.inject(document.body);
+        Jx.Widget.prototype.position(activeCell.field.domObj, cell, {
+            horizontal: ['left left'],
+            vertical: ['top top']
+        });
+
+        activeCell.span.hide();
+      }
+
+      // COMMENT: an outline of the cell helps identifying the currently active cell
+      if(options.cellOutline.use) {
+        cell.setStyle('outline', options.cellOutline.style);
+      }
+    },
+    /**
+     * Method: showPopUp
      *
-     * (code)
-     * rowSelected: function(grid, row) {
-     *    if (this.selectedRow != null) {
-     *        grid.selectRow(this.selectedRow, false);
-     *    }
-     *    this.selectedRow = row;
-     *    grid.selectRow(row, true);
-     * }
-     * (end)
+     * Shows the PopUp of of the editor if it already exists, otherwise calls Method
+     * this.createPopUp
      *
      * Parameters:
-     * e - {Event} the browser event object
+     * @var cell - table cell of the grid
      */
-    onClickRowHeader: function(e) {
-        var rc = this.getRowColumnFromEvent(e);
-        
-        if (this.options.rowSelection && this.model.rowSelected) {
-            this.model.rowSelected(this, rc.row);
+    showPopUp : function(cell) {
+      if(this.popup.domObj != null) {
+        Jx.Widget.prototype.position(this.popup.domObj, cell, {
+            horizontal: ['left left'],
+            vertical: ['top top']
+        });
+        this.activeCell.field.domObj.inject(this.popup.innerWrapper, 'top');
+        this.popup.domObj.show();
+        this.setPopUpButtons();
+        this.setPopUpStylesAfterRendering();
+      }else{
+        this.createPopUp(cell);
+      }
+    },
+    /**
+     * Method: createPopUp
+     *
+     * creates the popup for the requested cell.
+     *
+     * COMMENT: this could also be an jx.dialog..? if we use jx.dialog, maybe without a title element?
+     *          Maybe a jx.dialog is too much for this little thing?
+     *
+     * Parameters:
+     * @var cell - table cell of the grid
+     */
+    createPopUp : function(cell) {
+      var coords = cell.getCoordinates(),
+          self      = this, popup  = null, innerWrapper = null,
+          closeIcon = null, submit = null, cancel       = null,
+          template  = Jx.Widget.prototype.processTemplate(this.options.popup.template, this.classes);
+
+      popup = template.jxGridEditorPopup;
+
+      innerWrapper = template.jxGridEditorPopupInnerWrapper;
+      /**
+       * COMMENT: first positioning is always in the top left of the grid..
+       * don't know why
+       * manual positioning is needed..?
+       */
+      popup.setStyles({
+        'left' : coords.left+'px',
+        'top'  : coords.top +'px'
+      });
+      /*
+      Jx.Widget.prototype.position(popup, cell, {
+            horizontal: ['left left'],
+            vertical: ['top top']
+      });
+      */
+
+      this.popup.domObj         = popup;
+      this.popup.innerWrapper   = innerWrapper;
+      this.popup.closeIcon      = closeIcon;
+      this.setPopUpButtons();
+
+      this.activeCell.field.domObj.inject(this.popup.innerWrapper, 'top');
+      this.popup.domObj.inject(document.body);
+
+      this.setPopUpStylesAfterRendering();
+    },
+    /**
+     * Method: setPopUpStylesAfterRendering
+     *
+     * - measures the widths of the buttons to set a new min-width for the popup
+     *   because custom labels could break the min-width and force a line-break
+     * - resets the size of the field to make it fit inside the popup (looks nicer)
+     *
+     * @return void
+     */
+    setPopUpStylesAfterRendering: function() {
+      if(this.options.popup.useButtons && this.popup.button.submit != null && this.popup.button.cancel != null) {
+        this.popup.domObj.setStyle('min-width', this.popup.button.submit.domObj.getSize().x + this.popup.button.cancel.domObj.getSize().x + "px");
+      }else{
+        if(this.popup.button.submit != null)
+          this.popup.button.submit.domObj.hide();
+        if(this.popup.button.cancel != null)
+          this.popup.button.cancel.domObj.hide();
+      }
+      this.activeCell.field.field.setStyle('width',
+        this.activeCell.field.type == 'Select' ?
+          this.popup.domObj.getSize().x - 7 + "px" :
+          this.popup.domObj.getSize().x - 17 + "px");
+    },
+    /**
+     * Method: setPopUpButtons
+     * creates the PopUp Buttons if enabled in options or deletes them if set to false
+     *
+     * @return void
+     */
+    setPopUpButtons : function() {
+      var self = this,
+          button = {
+            submit : null,
+            cancel : null
+          };
+      // check if buttons are needed, innerWrapper exists and no buttons already exist
+      if(this.options.popup.useButtons && this.popup.innerWrapper != null && this.popup.button.submit == null) {
+        button.submit = new Jx.Button({
+          label : this.options.popup.button.submit.label.length == 0 ? 
+                    this.getText({set:'Jx',key:'plugin.editor',value:'submitButton'}) :
+                    this.getText(this.options.popup.button.submit.label),
+          image : this.options.popup.button.submit.image,
+          onClick: function() {
+            self.deactivate(true);
+          }
+        }).addTo(this.popup.innerWrapper);
+        button.cancel = new Jx.Button({
+          label : this.options.popup.button.cancel.label.length == 0 ? 
+                    this.getText({set:'Jx',key:'plugin.editor',value:'cancelButton'}) :
+                    this.getText(this.options.popup.button.cancel.label),
+          image : this.options.popup.button.cancel.image,
+          onClick: function() {
+            self.deactivate(false);
+          }
+        }).addTo(this.popup.innerWrapper);
+      }else if(this.options.popup.useButtons && this.popup.button.submit != null) {
+        button = {
+          submit : this.popup.button.submit,
+          cancel : this.popup.button.cancel
+        };
+      // check if buttons are not needed and buttons already exist to remove them
+      }else if(this.options.popup.useButtons == false && this.popup.button.submit != null) {
+        this.popup.button.submit.cleanup();
+        this.popup.button.cancel.cleanup();
+      }
+
+      this.popup.button = button;
+    },
+    /**
+     * Method: unsetActiveField
+     * resets the activeField and hides the popup
+     *
+     * @return void
+     */
+    unsetActiveField: function() {
+      this.activeCell.field.destroy();
+      if(this.popup.domObj != null) {
+        this.popup.domObj.removeEvent('mouseenter');
+        this.popup.domObj.hide();
+      }
+
+      this.activeCell.cell.setStyle('outline', '0px');
+
+      this.activeCell = {
+        field         : null,
+        oldValue      : null,
+        newValue      : { data: null, error: false},
+        cell          : null,
+        span          : null,
+        timeoutId     : null,
+        //popup         : null,   // do not destroy the popup, it might be used again
+        data           : {},
+        fieldOptions  : {},
+        validator     : null
+      };
+    },
+    /**
+     * Method: unsetPopUp
+     * resets the popup manually to be able to use it with different settings
+     */
+    unsetPopUp : function() {
+      if(this.popup.domObj != null) {
+        this.popup.domObj.destroy();
+        this.popup.innerWrapper   = null;
+        this.popup.closeIcon      = null;
+        this.popup.button.submit = null;
+        this.popup.button.cancel = null;
+      }
+    },
+    /**
+     * APIMethod: getNextCellInRow
+     * activates the next cell in a row if it is editable
+     * otherwise the focus jumps to the next editable cell in the next row
+     * or starts at the beginning
+     *
+     * @var  {Boolean} save (Optional, default: true)
+     * @return void
+     */
+    getNextCellInRow: function(save) {
+      save = $defined(save) ? save : true;
+      var nextCell = true,
+          nextRow = true,
+          sumCols = this.grid.columns.columns.length,
+          jxCellClass = 'td.jxGridCell:not(.jxGridCellUnattached)',
+          i = 0,
+          data,
+          cell = this.activeCell.cell,
+          options = this.options;
+      if (this.activeCell.cell != null) {
+        do {
+          nextCell = i > 0 ? nextCell.getNext(jxCellClass) : cell.getNext(jxCellClass);
+          // check if cell is still in row, otherwise returns null
+          if (nextCell == null) {
+            nextRow  = cell.getParent('tr').getNext();
+            // check if this was the last row in the table
+            if (nextRow == null && options.keypressLoop) {
+              nextRow = cell.getParent('tbody').getFirst();
+            } else if(nextRow == null && !options.keypressLoop){
+              return;
+            }
+            nextCell = nextRow.getFirst(jxCellClass);
+          }
+          data = this.grid.getCellData(nextCell);
+          i++;
+          // if all columns are set to uneditable during runtime, jump out of the loop after
+          // running through 2 times to prevent an endless-loop and browser crash :)
+          if (i == sumCols*2) {
+            this.deactivate(save);
+            return;
+          }
+        } while(data && !data.column.options.isEditable);
+
+        if (save === false) {
+          this.deactivate(save);
         }
+        this.activate(nextCell);
+      }
     },
-    
     /**
-     * Method: onClickColumnHeader
-     * handle the user clicking on the column header.  This triggers column
-     * selection and column (and header) styling changes and an
-     * event to the model (if a columnSelected function is provided)
+     * APIMethod: getPrevCellInRow
+     * activates the previous cell in a row if it is editable
+     * otherwise the focus jumps to the previous editable cell in the previous row
+     * or starts at the last cell in the last row at the end
      *
-     * The following is an example of a function in the model that selects
-     * a column when the columnSelected function is called and deselects any 
-     * columns that are currently selected.  More complex code could be written
-     * to allow the user to select multiple columns.
+     * @var  {Boolean} save (Optional, default: true)
+     * @return void
+     */
+    getPrevCellInRow: function(save) {
+      save = $defined(save) ? save : true;
+      var prevCell, 
+          prevRow, 
+          i = 0,
+          data,
+          row,
+          index,
+          cell = this.activeCell.cell,
+          sumCols = this.grid.columns.columns.length,
+          jxCellClass = 'td.jxGridCell:not(.jxGridCellUnattached)',
+          options = this.options;
+      if(cell != null) {
+        do {
+          prevCell = i > 0 ? prevCell.getPrevious(jxCellClass) : cell.getPrevious(jxCellClass);
+          // check if cell is still in row, otherwise returns null
+          if(prevCell == null) {
+            prevRow  = cell.getParent('tr').getPrevious();
+            // check if this was the last row in the table
+            if(prevRow == null && options.keypressLoop) {
+              prevRow = cell.getParent('tbody').getLast();
+            }else if(prevRow == null && !options.keypressLoop) {
+              return;
+            }
+            prevCell = prevRow.getLast(jxCellClass);
+          }
+          data  = this.grid.getCellData(prevCell);
+          row   = data.row;
+          index = data.index;
+          i++;
+          // if all columns are set to uneditable during runtime, jump out of the loop after
+          // running through 2 times to prevent an endless-loop and browser crash :)
+          if(i == sumCols*2) {
+            this.deactivate(save);
+            return;
+          }
+        }while(data && !data.column.options.isEditable);
+
+        if(save === false) {
+          this.deactivate(save);
+        }
+        this.activate(prevCell);
+      }
+    },
+    /**
+     * APIMethod: getNextCellInCol
+     * activates the next cell in a column under the currently active one
+     * if the active cell is in the last row, the first one will be used
      *
-     * (code)
-     * colSelected: function(grid, col) {
-     *    if (this.selectedColumn != null) {
-     *        grid.selectColumn(this.selectedColumn, false);
-     *    }
-     *    this.selectedColumn = col;
-     *    grid.selectColumn(col, true);
-     * }
-     * (end)
+     * @var  {Boolean} save (Optional, default: true)
+     * @return void
+     */
+    getNextCellInCol : function(save) {
+      var nextRow,
+          nextCell,
+          activeCell = this.activeCell;
+      save = $defined(save) ? save : true;
+      if (activeCell.cell != null) {
+        nextRow = activeCell.cell.getParent().getNext();
+        if (nextRow == null) {
+          nextRow = activeCell.cell.getParent('tbody').getFirst();
+        }
+        nextCell = nextRow.getElement('td.jxGridCol'+activeCell.data.index);
+        if (save === false) {
+          this.deactivate(save);
+        }
+        this.activate(nextCell);
+      }
+    },
+    /**
+     * APIMethod: getPrevCellInCol
+     * activates the previous cell in a column above the currently active one
+     * if the active cell is in the first row, the last one will be used
      *
+     * @var  {Boolean} save (Optional, default: true)
+     * @return void
+     */
+    getPrevCellInCol : function(save) {
+      var prevRow,
+          prevCell,
+          activeCell = this.activeCell;
+      save = $defined(save) ? save : true;
+      if (activeCell.cell != null) {
+        prevRow = activeCell.cell.getParent().getPrevious();
+        if (prevRow == null) {
+          prevRow = activeCell.cell.getParent('tbody').getLast();
+        }
+        prevCell = prevRow.getElement('td.jxGridCol'+activeCell.data.index);
+        if (save === false) {
+          this.deactivate(save);
+        }
+        this.activate(prevCell);
+      }
+    },
+    /**
+     * Method: cellValueIncrement
+     * Whether increments or decrements the value of the active cell if the dataType is numeric
+     *
+     * Parameters
+     * @var {Boolean} bool
+     * @return void
+     */
+    cellValueIncrement : function(bool) {
+      var activeCell = this.activeCell,
+          dataType = activeCell.data.column.options.dataType,
+          valueNew = null,
+          formatter;
+      switch (dataType) {
+        case 'numeric':
+        case 'currency':
+          valueNew = activeCell.field.getValue().toInt();
+          if (typeof(valueNew) == 'number') {
+            if (bool) {
+              valueNew++;
+            } else {
+              valueNew--;
+            }
+          }
+          break;
+        case 'date':
+          valueNew = Date.parse(activeCell.field.getValue());
+          if (valueNew instanceof Date) {
+            if (bool) {
+              valueNew.increment();
+            } else {
+              valueNew.decrement();
+            }
+            formatter = new Jx.Formatter.Date();
+            valueNew = formatter.format(valueNew);
+          }
+          break;
+      }
+      if (valueNew != null) {
+        activeCell.field.setValue(valueNew);
+      }
+    },
+    /**
+     * Method: cellIsInGrid
+     * determins if the given coordinates are within the grid
+     *
      * Parameters:
-     * e - {Event} the browser event object
+     * @var {Integer} row
+     * @var {Integer} index
+     * @return {Boolean}
      */
-    onClickColumnHeader: function(e) {
-        var rc = this.getRowColumnFromEvent(e);
-        
-        if (this.options.columnSelection && this.model.columnSelected) {
-            this.model.columnSelected(this, rc.column);
+    cellIsInGrid: function(row, index) {
+      if($defined(row) && $defined(index)) {
+        //console.log("Row %i - max Rows: %i, Col %i - max Cols %i", row, this.grid.gridTableBody.rows.length, index, this.grid.gridTableBody.rows[row].cells.length);
+        if( row >= 0 && index >= 0 &&
+            row <= this.grid.gridTableBody.rows.length &&
+            index <= this.grid.gridTableBody.rows[row].cells.length
+        ) {
+          return true;
+        }else{
+          return false;
         }
+      }else{
+        return false;
+      }
     },
-    
     /**
-     * method: getRowColumnFromEvent
-     * retrieve the row and column indexes from an event click.
-     * This function is used by the grid, row header and column
-     * header to safely get these numbers.
+     * APIMethod: getFieldOptionsByColName
+     * checks for the name of a column inside the fieldOptions and returns
+     * the object if found, otherwise the default options for the field
      *
-     * If the event isn't valid (i.e. it wasn't on a TD or TH) then
-     * the returned values will be -1, -1
+     * Parameters:
+     * @var {String} colName
+     * @return {Object} default field options
+     */
+    getFieldOptionsByColName : function(colName) {
+      var fo = this.options.fieldOptions,
+          r  = this.options.fieldOptions[0];
+      for(var i = 0, j = fo.length; i < j; i++) {
+        if(fo[i].field == colName) {
+          r = fo[i];
+          break;
+        }
+      }
+      return r;
+    },
+    /**
+     * Method: addFormatterUriClickListener
      *
+     * looks up for Jx.Formatter.Uri columns to disable the link and open the
+     * inline editor instead when CTRL is NOT pressed.
+     * set option linkClickListener to false to disable this
+     *
+     */
+    addFormatterUriClickListener : function() {
+      if(this.options.linkClickListener) {
+        // prevent a link from beeing opened if the editor should appear and the uri formatter is activated
+        var uriCols = [], tableCols, anchor;
+        // find out which columns are using a Jx.Formatter.Uri
+        this.grid.columns.columns.each(function(col,i) {
+          if(col.options.renderer.options.formatter != null && col.options.renderer.options.formatter instanceof Jx.Formatter.Uri) {
+            uriCols.push(i);
+          }
+        });
+        // add an event to all anchors inside these columns
+        this.grid.gridObj.getElements('tr').each(function(tr,i) {
+          tableCols = tr.getElements('td.jxGridCell');
+          for(var j = 0, k = uriCols.length; j < k; j++) {
+            anchor = tableCols[uriCols[j]-1].getElement('a');
+            if(anchor) {
+              anchor.removeEvent('click');
+              anchor.addEvent('click', function(ev) {
+                // open link if ctrl was clicked
+                if(!ev.control) {
+                  ev.preventDefault();
+                }
+              });
+            }
+          }
+        });
+      }
+    },
+    /**
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     *
      * Parameters:
-     * e - {Event} the browser event object
+     * lang - the language being changed to or that had it's data set of
+     * 		translations changed.
+     */
+    changeText: function (lang) {
+    	this.parent();
+    	if (this.options.popup.use && this.options.popup.useButtons) {
+        if(this.popup.button.submit != null) {
+          this.popup.button.submit.cleanup();
+          this.popup.button.cancel.cleanup();
+          this.popup.button.submit = null;
+          this.popup.button.cancel = null;
+          this.setPopUpButtons();
+        }
+    	}
+    }
+}); 
+/*
+---
+
+name: Jx.Plugin.DataView
+
+description: Namespace for DataView plugins
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin
+
+provides: [Jx.Plugin.DataView]
+...
+ */
+/**
+ * Namespace: Jx.Plugin.DataView
+ * The namespace for all dataview plugins
+ */
+Jx.Plugin.DataView = {};/*
+---
+
+name: Jx.Slide
+
+description: A class that shows and hides elements using a slide effect. Does not use a wrapper element or require a fixed width or height.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Object
+ - Core/Fx.Tween
+
+provides: [Jx.Slide]
+
+...
+ */
+// $Id: slide.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Slide
+ * Hides and shows an element without depending on a fixed width or height
+ *
+ * Copyright 2009 by Jonathan Bomgardner
+ * License: MIT-style
+ */
+Jx.Slide = new Class({
+    Family: 'Jx.Slide',
+    Implements: Jx.Object,
+    Binds: ['handleClick'],
+    options: {
+        /**
+         * Option: target
+         * The element to slide
+         */
+        target: null,
+        /**
+         * Option: trigger
+         * The element that will have a click event added to start the slide
+         */
+        trigger: null,
+        /**
+         * Option: type
+         * The type of slide. Can be either "width" or "height". defaults to "height"
+         */
+        type: 'height',
+        /**
+         * Option: setOpenTo
+         * Allows the caller to determine what the open target is set to. Defaults to 'auto'.
+         */
+        setOpenTo: 'auto',
+        /**
+         * Option: onSlideOut
+         * function called when the target is revealed.
+         */
+        onSlideOut: $empty,
+        /**
+         * Option: onSlideIn
+         * function called when a panel is hidden.
+         */
+        onSlideIn: $empty
+    },
+    /**
+     * Method: init
+     * sets up the slide
+     */
+    init: function () {
+
+        this.target = document.id(this.options.target);
+
+        this.target.set('tween', {onComplete: this.setDisplay.bind(this)});
+
+        if ($defined(this.options.trigger)) {
+            this.trigger = document.id(this.options.trigger);
+            this.trigger.addEvent('click', this.handleClick);
+        }
+
+        this.target.store('slider', this);
+
+    },
+    /**
+     * Method: handleClick
+     * event handler for clicks on the trigger. Starts the slide process
+     */
+    handleClick: function () {
+        var sizes = this.target.getMarginBoxSize();
+        if (sizes.height === 0) {
+            this.slide('in');
+        } else {
+            this.slide('out');
+        }
+    },
+    /**
+     * Method: setDisplay
+     * called at the end of the animation to set the target's width or
+     * height as well as other css values to the appropriate values
+     */
+    setDisplay: function () {
+        var h = this.target.getStyle(this.options.type).toInt();
+        if (h === 0) {
+            this.target.setStyle('display', 'none');
+            this.fireEvent('slideOut', this.target);
+        } else {
+            //this.target.setStyle('overflow', 'auto');
+            if (this.target.getStyle('position') !== 'absolute') {
+                this.target.setStyle(this.options.type, this.options.setOpenTo);
+            }
+            this.fireEvent('slideIn', this.target);
+        }
+    },
+    /**
+     * APIMethod: slide
+     * Actually determines how to slide and initiates the animation.
      *
-     * @return Object an object with two properties, row and column,
-     *         that contain the row and column that was clicked
+     * Parameters:
+     * dir - the direction to slide (either "in" or "out")
      */
-    getRowColumnFromEvent: function(e) {
-        var td = e.target;
-        if (td.tagName != 'TD' && td.tagName != 'TH') {
-            return {row:-1,column:-1};
+    slide: function (dir) {
+        var h;
+        if (dir === 'in') {
+            h = this.target.retrieve(this.options.type);
+            this.target.setStyles({
+                overflow: 'hidden',
+                display: 'block'
+            });
+            this.target.setStyles(this.options.type, 0);
+            this.target.tween(this.options.type, h);
+        } else {
+            if (this.options.type === 'height') {
+                h = this.target.getMarginBoxSize().height;
+            } else {
+                h = this.target.getMarginBoxSize().width;
+            }
+            this.target.store(this.options.type, h);
+            this.target.setStyle('overflow', 'hidden');
+            this.target.setStyle(this.options.type, h);
+            this.target.tween(this.options.type, 0);
         }
-        var tr = td.parentNode;
-        var col = td.cellIndex - 1; /* because of hidden spacer column */
-        var row = tr.rowIndex - 1; /* because of hidden spacer row */
-        
-        if (col == -1) { 
-            /* bug in safari returns 0 for cellIndex - only choice seems
-             * to be to loop through the row
-             */
-            for (var i=0; i<tr.childNodes.length; i++) {
-                if (tr.childNodes[i] == td) {
-                    col = i - 1;
-                    break;
+    }
+});/*
+---
+
+name: Jx.Plugin.DataView.GroupFolder
+
+description: Enables closing and opening groups in a group dataview
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin.DataView
+ - Jx.Slide
+
+provides: [Jx.Plugin.DataView.GroupFolder]
+
+...
+ */
+/**
+ * Class: Jx.Plugin.DataView.GroupFolder
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Plugin for DataView - allows folding/unfolding of the groups in the
+ * grouped dataview
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.DataView.GroupFolder = new Class({
+
+    Extends: Jx.Plugin,
+
+    options: {
+        /**
+         * Option: headerClass
+         * The base for styling the header. Gets '-open' or '-closed' added
+         * to it.
+         */
+        headerClass: null
+    },
+    /**
+     * Property: headerState
+     * Hash that holds the open/closed state of each header
+     */
+    headerState: null,
+    init: function() {
+      this.headerState = new Hash();
+    },
+    /**
+     * APIMethod: attach
+     * Attaches this plugin to a dataview
+     */
+    attach: function (dataView) {
+        if (!$defined(dataView) && !(dataview instanceof Jx.Panel.DataView)) {
+            return;
+        }
+
+        this.dv = dataView;
+        this.dv.addEvent('renderDone', this.setHeaders.bind(this));
+    },
+    /**
+     * Method: setHeaders
+     * Called after the dataview is rendered. Sets up the Jx.Slide instance
+     * for each header. It also sets the initial state of each header so that
+     * if the dataview is redrawn for some reason the open/closed state is
+     * preserved.
+     */
+    setHeaders: function () {
+        var headers = this.dv.domA.getElements('.' + this.dv.options.groupHeaderClass);
+
+        headers.each(function (header) {
+            var id = header.get('id');
+            var s = new Jx.Slide({
+                target: header.getNext(),
+                trigger: id,
+                onSlideOut: this.onSlideOut.bind(this, header),
+                onSlideIn: this.onSlideIn.bind(this, header)
+            });
+
+            if (this.headerState.has(id)) {
+                var state = this.headerState.get(id);
+                if (state === 'open') {
+                    s.slide('in');
+                } else {
+                    s.slide('out');
                 }
+            } else {
+                s.slide('in');
             }
+        }, this);
+    },
+
+    /**
+     * Method: onSlideIn
+     * Called when a group opens.
+     *
+     * Parameters:
+     * header - the header that was clicked.
+     */
+    onSlideIn: function (header) {
+        this.headerState.set(header.get('id'), 'open');
+        if (header.hasClass(this.options.headerClass + '-closed')) {
+            header.removeClass(this.options.headerClass + '-closed');
         }
-        return {row:row,column:col};
+        header.addClass(this.options.headerClass + '-open');
+    },
+    /**
+     * Method: onSlideOut
+     * Called when a group closes.
+     *
+     * Parameters:
+     * header - the header that was clicked.
+     */
+    onSlideOut: function (header) {
+        this.headerState.set(header.get('id'), 'closed');
+        if (header.hasClass(this.options.headerClass + '-open')) {
+            header.removeClass(this.options.headerClass + '-open');
+        }
+        header.addClass(this.options.headerClass + '-closed');
     }
-});/**
- * Class: Jx.Grid.Model
+});
+/*
+---
+
+name: Jx.Plugin.Field
+
+description: Namespace for Jx.Field plugins
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin
+
+provides: [Jx.Plugin.Field]
+
+...
+ */
+// $Id: plugin.field.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Plugin.Field
+ * Field plugin namespace
  *
- * Extends: Object
  *
- * Implements: Options, Events
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
  *
- * A Jx.Grid.Model is the source of data for a <Jx.Grid> instance.  The
- * default implementation of the grid model works with two-dimensional
- * arrays of data and acts as a convenient base class for custom models
- * based on other sources of data.
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Field = {};/*
+---
+
+name: Jx.Plugin.Field.Validator
+
+description: Provides validation services for Jx.Field subclasses
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin.Field
+ - More/Form.Validator
+ - More/Form.Validator.Extras
+
+provides: [Jx.Plugin.Field.Validator]
+
+...
+ */
+// $Id: field.validator.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Plugin.Field.Validator
  *
- * License: 
- * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ * Extends: <Jx.Plugin>
+ *
+ * Field plugin for enforcing validation when a field is not used in a form.
+ *
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ * Parts inspired by mootools-more's Form.Validator class
+ *
  * This file is licensed under an MIT style license
  */
-Jx.Grid.Model = new Class({
-    Family: 'Jx.Grid.Model',
-    Implements: [Events, Options],
+Jx.Plugin.Field.Validator = new Class({
+
+    Extends : Jx.Plugin,
+    name: 'Field.Validator',
+
     options: {
-        /* Option: colHeaderHeight
-         * default 28, the height of the column header row
+        /**
+         * Option: validators
+         * An array that contains either a string that names the predefined
+         * validator to use with its needed options or an object that defines
+         * the options of an InputValidator (also with needed options) defined
+         * like so:
+         *
+         * (code)
+         * {
+         *     validatorClass: 'name:with options',    //gets applied to the field
+         *     validator: {                         //used to create the InputValidator
+         *         name: 'validatorName',
+         *         options: {
+         *             errorMsg: 'error message',
+         *             test: function(field,props){}
+         *         }
+         *     }
+         * }
+         * (end)
          */
-        colHeaderHeight: 28,
-        /* Option: colHeaderHeight
-         * default 28, the height of the column header row
+        validators: [],
+        /**
+         * Option: validateOnBlur
+         * Determines whether the plugin will validate the field on blur.
+         * Defaults to true.
          */
-        rowHeaderWidth: 28,
-        /* Option: rowHeaderWidth
-         * default 28, the width of the row header column.
+        validateOnBlur: true,
+        /**
+         * Option: validateOnChange
+         * Determines whether the plugin will validate the field on change.
+         * Defaults to true.
          */
-        colWidth: 50,
-        /* Option: colWidth
-         * default 50, the width of columns
+        validateOnChange: true
+    },
+    /**
+     * Property: valid
+     * tells whether this field passed validation or not.
+     */
+    valid: null,
+    /**
+     * Property: errors
+     * array of errors found on this field
+     */
+    errors: null,
+    validators : null,
+    /**
+     * APIMethod: init
+     * construct a new instance of the plugin.  The plugin must be attached
+     * to a Jx.Grid instance to be useful though.
+     */
+    init: function () {
+        this.parent();
+        this.errors = [];
+        this.validators = new Hash();
+        this.bound.validate = this.validate.bind(this);
+        this.bound.reset = this.reset.bind(this);
+    },
+    /**
+     * APIMethod: attach
+     * Sets up the plugin and connects it to the field
+     */
+    attach: function (field) {
+        if (!$defined(field) && !(field instanceof Jx.Field)) {
+            return;
+        }
+        this.field = field;
+        if (this.field.options.required && !this.options.validators.contains('required')) {
+            //would have used unshift() but reading tells me it may not work in IE.
+            this.options.validators.reverse().push('required');
+            this.options.validators.reverse();
+        }
+        //add validation classes
+        this.options.validators.each(function (v) {
+            var t = Jx.type(v);
+            if (t === 'string') {
+                this.field.field.addClass(v);
+            } else if (t === 'object') {
+                this.validators.set(v.validator.name, new InputValidator(v.validator.name, v.validator.options));
+                this.field.field.addClass(v.validatorClass);
+            }
+        }, this);
+        if (this.options.validateOnBlur) {
+            this.field.field.addEvent('blur', this.bound.validate);
+        }
+        if (this.options.validateOnChange) {
+            this.field.field.addEvent('change', this.bound.validate);
+        }
+        this.field.addEvent('reset', this.bound.reset);
+    },
+    /**
+     * APIMethod: detach
+     */
+    detach: function () {
+        if (this.field) {
+            this.field.field.removeEvent('blur', this.bound.validate);
+            this.field.field.removeEvent('change', this.bound.validate);
+        }
+        this.field.removeEvent('reset', this.bound.reset);
+        this.field = null;
+        this.validators = null;
+    },
+
+    validate: function () {
+        $clear(this.timer);
+        this.timer = this.validateField.delay(50, this);
+    },
+
+    validateField: function () {
+        //loop through the validators
+        this.valid = true;
+        this.errors = [];
+        this.options.validators.each(function (v) {
+            var val = (Jx.type(v) === 'string') ? Form.Validator.getValidator(v) : this.validators.get(v.validator.name);
+            if (val) {
+                if (!val.test(this.field.field)) {
+                    this.valid = false;
+                    this.errors.push(val.getError(this.field.field));
+                }
+            }
+        }, this);
+        if (!this.valid) {
+            this.field.domObj.removeClass('jxFieldSuccess').addClass('jxFieldError');
+            this.fireEvent('fieldValidationFailed', [this.field, this]);
+        } else {
+            this.field.domObj.removeClass('jxFieldError').addClass('jxFieldSuccess');
+            this.fireEvent('fieldValidationPassed', [this.field, this]);
+        }
+        return this.valid;
+    },
+
+    isValid: function () {
+        return this.validateField();
+    },
+
+    reset: function () {
+        this.valid = null;
+        this.errors = [];
+        this.field.field.removeClass('jxFieldError').removeClass('jxFieldSuccess');
+    },
+    /**
+     * APIMethod: getErrors
+     * USe this method to retrieve all of the errors noted for this field.
+     */
+    getErrors: function () {
+        return this.errors;
+    }
+
+
+});
+/*
+---
+
+name: Jx.Plugin.Form
+
+description: Namespace for Jx.Form plugins
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin
+
+provides: [Jx.Plugin.Form]
+
+...
+ */
+// $Id: plugin.form.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Plugin.Form
+ * Form plugin namespace
+ *
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Form = {};/*
+---
+
+name: Jx.Plugin.Form.Validator
+
+description: Provides validation services for Jx.Form
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin.Form
+ - Jx.Plugin.Field.Validator
+
+provides: [Jx.Plugin.Form.Validator]
+
+...
+ */
+// $Id: form.validator.js 970 2010-08-23 12:20:57Z pagameba $
+/**
+ * Class: Jx.Plugin.Form.Validator
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * Form plugin for enforcing validation on the fields in a form.
+ *
+ * License:
+ * Copyright (c) 2009, Jonathan Bomgardner.
+ * Parts inspired by mootools-more's Form.Validator class
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.Form.Validator = new Class({
+
+    Extends : Jx.Plugin,
+    name: 'Form.Validator',
+
+    options: {
+        /**
+         * Option: fields
+         * This will be key/value pairs for each of the fields as shown here:
+         * (code)
+         * {
+         *     fieldID: {
+         *          ... options for Field.Validator plugin ...
+         *     },
+         *     fieldID: {...
+         *     }
+         * }
+         * (end)
          */
-        rowHeight: 20,
-        /* Option: rowHeight
-         * default 20, the height of rows
+        fields: null,
+        /**
+         * Option: fieldDefaults
+         * {Object} contains named defaults for field validators to be
+         * triggered on blur or change.  Default is:
+         * (code)
+         * {
+         *    validateOnBlur: true
+         *    validateOnChange: false
+         * }
+         * (end)
          */
-        rowHeaders: null,
-        /* Option: columnHeaders
-         * optional column headers, defaults to null
+        fieldDefaults: {
+            validateOnBlur: true,
+            validateOnChange: true
+        },
+        /**
+         * Option: validateOnSubmit
+         * {Boolean} default true.  Trigger validation on submission of
+         * form if true.
          */
-        columnHeaders: null
+        validateOnSubmit: true,
+        /**
+         * Option: suspendSubmit
+         * {Boolean} default false.  Stop form submission when validator is
+         * attached.
+         */
+        suspendSubmit: false
     },
-    data: null,
     /**
-     * Constructor: Jx.Grid.Model
-     * create a new grid model
+     * Property: errorMessagess
+     * element holding
+     */
+    errorMessage: null,
+    /**
+     * APIMethod: init
+     * construct a new instance of the plugin.  The plugin must be attached
+     * to a Jx.Grid instance to be useful though.
+     */
+    init: function() {
+        this.parent();
+        this.bound.validate = this.validate.bind(this);
+        this.bound.failed = this.fieldFailed.bind(this);
+        this.bound.passed = this.fieldPassed.bind(this);
+    },
+    /**
+     * APIMethod: attach
+     * Sets up the plugin and connects it to the form
+     */
+    attach: function (form) {
+        if (!$defined(form) && !(form instanceof Jx.Form)) {
+            return;
+        }
+        this.form = form;
+        var plugin = this,
+            options = this.options;
+        //override the isValid function in the form
+        form.isValid = function () {
+            return plugin.isValid();
+        };
+
+        if (options.validateOnSubmit && !options.suspendSubmit) {
+            document.id(this.form).addEvent('submit', this.bound.validate);
+        } else if (options.suspendSubmit) {
+            document.id(this.form).addEvent('submit', function (ev) {
+                ev.stop();
+            });
+        }
+
+        this.plugins = $H();
+
+        //setup the fields
+        $H(options.fields).each(function (val, key) {
+            var opts = $merge(this.options.fieldDefaults, val),
+                fields = this.form.getFieldsByName(key).
+                p;
+            if (fields && fields.length) {
+                p = new Jx.Plugin.Field.Validator(opts);
+                this.plugins.set(key, p);
+                p.attach(fields[0]);
+                p.addEvent('fieldValidationFailed', this.bound.failed);
+                p.addEvent('fieldValidationPassed', this.bound.passed);
+            }
+        }, this);
+
+    },
+    /**
+     * APIMethod: detach
+     */
+    detach: function() {
+        if (this.form) {
+            document.id(this.form).removeEvent('submit');
+        }
+        this.form = null;
+        this.plugins.each(function(plugin){
+            plugin.detach();
+            plugin = null;
+        },this);
+        this.plugins = null;
+    },
+    /**
+     * APIMethod: isValid
+     * Call this to determine whether the form validates.
+     */
+    isValid: function () {
+        return this.validate();
+    },
+    /**
+     * Method: validate
+     * Method that actually does the work of validating the fields in the form.
+     */
+    validate: function () {
+        var valid = true;
+        this.errors = $H();
+        this.plugins.each(function(plugin){
+            if (!plugin.isValid()) {
+                valid = false;
+                this.errors.set(plugin.field.id,plugin.getErrors());
+            }
+        }, this);
+        if (valid) {
+            this.fireEvent('formValidationPassed', [this.form, this]);
+        } else {
+            this.fireEvent('formValidationFailed', [this.form, this]);
+        }
+        return valid;
+    },
+    /**
+     * Method: fieldFailed
+     * Refires the fieldValidationFailed event from the field validators it contains
+     */
+    fieldFailed: function (field, validator) {
+        this.fireEvent('fieldValidationFailed', [field, validator]);
+    },
+    /**
+     * Method: fieldPassed
+     * Refires the fieldValidationPassed event from the field validators it contains
+     */
+    fieldPassed: function (field, validator) {
+        this.fireEvent('fieldValidationPassed', [field, validator]);
+    },
+    /**
+     * APIMethod: getErrors
+     * Use this method to get all of the errors from all of the fields.
+     */
+    getErrors: function () {
+        if (!$defined(this.errors)) {
+           this.validate();
+        }
+        return this.errors;
+    }
+
+
+});
+/*
+---
+
+name: Jx.Plugin.ToolbarContainer
+
+description: Namespace for Jx.Toolbar.Container
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin
+
+provides: [Jx.Plugin.ToolbarContainer]
+
+...
+ */
+/**
+ * Class: Jx.Plugin.Toolbar
+ * Toolbar plugin namespace
+ *
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.ToolbarContainer = {};/*
+---
+
+name: Jx.Plugin.ToolbarContainer.TabMenu
+
+description: Adds a menu of tabs to the toolbar container for easy access to all tabs.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin.ToolbarContainer
+
+provides: [Jx.Plugin.ToolbarContainer.TabMenu]
+
+...
+ */
+/**
+ * Class: Jx.Plugin.ToolbarContainer.TabMenu
+ *
+ * Extends: <Jx.Plugin>
+ *
+ * This plugin provides a menu of tabs in a toolbar (similar to the button in firefox at the end of the row of tabs).
+ * It is designed to be used only when the toolbar contains tabs and only when the container is allowed to scroll. Also,
+ * this plugin must be added directly to the Toolbar container. You can get a reference to the container for a
+ * <Jx.TabBox> by doing
+ *
+ * (code)
+ * var tabbox = new Jx.TabBox();
+ * var toolbarContainer = document.id(tabBox.tabBar).getParent('.jxBarContainer').retrieve('jxBarContainer');
+ * (end)
+ *
+ * You can then use the attach method to connect the plugin. Otherwise, you can add it via any normal means to a
+ * directly instantiated Container.
+ *
+ * License:
+ * Copyright (c) 2010, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Plugin.ToolbarContainer.TabMenu = new Class({
+
+    Family: 'Jx.Plugin.ToolbarContainer.TabMenu',
+    Extends: Jx.Plugin,
+
+    Binds: ['addButton'],
+
+    options: {
+    },
+    /**
+     * Property: tabs
+     * holds all of the tabs that we're tracking
+     */
+    tabs: [],
+
+    init: function () {
+        this.parent();
+    },
+
+    attach: function (toolbarContainer) {
+        this.parent(toolbarContainer);
+
+        this.container = toolbarContainer;
+
+        //we will only be used if the container is allowed to scroll
+        if (!this.container.options.scroll) {
+            return;
+        }
+
+        this.menu = new Jx.Menu({},{
+            buttonTemplate: '<span class="jxButtonContainer"><a class="jxButton jxButtonMenu jxDiscloser"><span class="jxButtonContent"><span class="jxButtonLabel"></span></span></a></span>'
+        }).addTo(this.container.controls,'bottom');
+        document.id(this.menu).addClass('jxTabMenuRevealer');
+        this.container.update();
+
+        //go through all of the existing tabs and add them to the menu
+        //grab the toolbar...
+        var tb = document.id(this.container).getElement('ul').retrieve('jxToolbar');
+        tb.list.each(function(item){
+            this.addButton(item);
+        },this);
+
+        //connect to the add event of the toolbar list to monitor the addition of buttons
+        tb.list.addEvent('add',this.addButton);
+    },
+
+    detach: function () {
+        this.parent();
+    },
+
+    addButton: function (item) {
+        var tab;
+        tab = (item instanceof Jx.Tab) ? item : document.id(item).getFirst().retrieve('jxTab');
+
+
+        var l = tab.getLabel();
+        if (!$defined(l)) {
+            l = '';
+        }
+        var mi = new Jx.Menu.Item({
+            label: l,
+            image: tab.options.image,
+            onClick: function() {
+                if (tab.isActive()) {
+                    this.container.scrollIntoView(tab);
+                } else {
+                    tab.setActive(true);
+                }
+            }.bind(this)
+        });
+
+        document.id(tab).store('menuItem', mi);
+
+        tab.addEvent('close', function() {
+            this.menu.remove(mi);
+        }.bind(this));
+
+        this.menu.add([mi]);
+    }
+});/*
+---
+
+name: Jx.Adaptor
+
+description: Base class for all Adaptors.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Plugin
+
+provides: [Jx.Adaptor]
+
+...
+ */
+/**
+ * Class: Jx.Adaptor
+ * Base class for all adaptor implementations. Provides a place to locate all
+ * common code and the Jx.Adaptor namespace.  Since it extends <Jx.Plugin> all
+ * adaptors will be able to be used as plugins for their respective classes.
+ * Also as such, they must have the attach() and detach() methods.
+ *
+ * Adaptors are specifically used to conform a <Jx.Store> to any one of
+ * the different widgets (i.e. Jx.Tree, Jx.ListView, etc...) that could
+ * benefit from integration with the store. This approach was taken to minimize
+ * data access code in the widgets themselves. Widgets should have no idea where
+ * the data/items come from so that they will be usable in the broadest number
+ * of situations.
+ *
+ * Copyright 2010 by Jonathan Bomgardner
+ * License: mit-style
+ */
+Jx.Adaptor = new Class({
+
+
+  Extends: Jx.Plugin,
+  Family: 'Jx.Adaptor',
+
+  name: 'Jx.Adaptor',
+
+  options: {
+        /**
+         * Option: template
+         * The text template to use in creating the items for this adaptor
+         */
+      template: '',
+        /**
+         * Option: useTemplate
+         * Whether or not to use the text template above. Defaults to true.
+         */
+      useTemplate: true,
+        /**
+         * Option: store
+         * The store to use with the adaptor.
+         */
+      store: null
+  },
+    /**
+     * Property: columnsNeeded
+     * Will hold an array of the column names needed for processing the
+     * template
+     */
+  columnsNeeded: null,
+
+  init: function () {
+      var options = this.options;
+      this.parent();
+
+      this.store = options.store;
+
+      if (options.useTemplate && $defined(this.store.getColumns())) {
+          this.columnsNeeded = this.store.parseTemplate(options.template);
+      }
+  },
+
+  attach: function (widget) {
+    this.parent(widget);
+    this.widget = widget;
+  },
+
+  detach: function () {
+    this.parent();
+  }
+
+});/*
+---
+
+name: Jx.Adaptor.Tree
+
+description: Base class for all adaptors that fill Jx.Tree widgets. Also acts as the namespace for other Jx.Tree adaptors.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Adaptor
+
+provides: [Jx.Adaptor.Tree]
+
+...
+ */
+/**
+ * Class: Jx.Adaptor.Tree
+ * This base class is used to change a store (a flat list of records) into the
+ * data structure needed for a Jx.Tree. It will have 2 subclasses:
+ * <Jx.Adapter.Tree.Mptt> and <Jx.Adapter.Tree.Parent>.
+ *
+ * Copyright 2010 by Jonathan Bomgardner
+ * License: mit-style
+ */
+Jx.Adaptor.Tree = new Class({
+
+
+    Extends: Jx.Adaptor,
+    Family: 'Jx.Adaptor.Tree',
+
+    Binds: ['fill','checkFolder'],
+
+    options: {
+        /**
+         * Option: monitorFolders
+         * Determines if this adapter should use monitor the TreeFolder items in
+         * order to request any items they should contain if they are empty.
+         */
+        monitorFolders: false,
+        /**
+         * Option: startingNodeKey
+         * The store primary key to use as the node that we're requesting.
+         * Initially set to -1 to indicate that we're request the first set of
+         * data
+         */
+        startingNodeKey: -1,
+        /**
+         * Option: folderOptions
+         * A Hash containing the options for <Jx.TreeFolder>. Defaults to null.
+         */
+        folderOptions: null,
+        /**
+         * Option: itemOptions
+         * A Hash containing the options for <Jx.TreeItem>. Defaults to null.
+         */
+        itemOptions: null
+    },
+    /**
+     * Property: folders
+     * A Hash containing all of the <Jx.TreeFolders> in this tree.
+     */
+    folders: null,
+    /**
+     * Property: currentRecord
+     * An integer indicating the last position we were at in the store. Used to
+     * allow the adaptor to pick up rendering items after we request additional
+     * data.
+     */
+    currentRecord: -1,
+    init: function() {
+      this.folders = new Hash();
+      this.parent();
+    },
+    /**
+     * APIMethod: attach
+     * Attaches this adaptor to a specific tree instance.
      *
      * Parameters:
-     * data - array of data to display in the grid
-     * options - <Jx.Grid.Model.Options>
+     * tree - an instance of <Jx.Tree>
      */
-    initialize: function(data, options) {
-        this.data = data || [];
-        this.setOptions(options);
+    attach: function (tree) {
+        this.parent(tree);
+
+        this.tree = tree;
+
+        if (this.options.monitorFolders) {
+            this.strategy = this.store.getStrategy('progressive');
+
+            if (!$defined(this.strategy)) {
+                this.strategy = new Jx.Store.Strategy.Progressive({
+                    dropRecords: false,
+                    getPaginationParams: function () { return {}; }
+                });
+                this.store.addStrategy(this.strategy);
+            } else {
+                this.strategy.options.dropRecords = false;
+                this.strategy.options.getPaginationParams = function () { return {}; };
+            }
+
+        }
+
+        this.store.addEvent('storeDataLoaded', this.fill);
+
+
     },
-    /** 
-     * Method: getColumnCount
-     * This function returns the number of columns of data in the 
-     * model as an integer value.
-     */ 
-    getColumnCount: function() { return (this.data && this.data[0]) ? this.data[0].length : 0; },
-    /* Method: getColumnHeaderHTML
-     * This function returns an HTML string to be placed in the
-     * column header for the given column index.
-     */ 
-    getColumnHeaderHTML: function(col) { 
-        return this.options.columnHeaders?this.options.columnHeaders[col]:col+1;
-     },
-     /* Method: getColumnHeaderHeight
-      * This function returns an integer which is the height of the
-      * column header row in pixels.
-      */ 
-    getColumnHeaderHeight: function() { return this.options.colHeaderHeight; },
-    /* Method: getColumnWidth
-     * This function returns an integer which is the width of the
-     * given column in pixels.
-     */ 
-    getColumnWidth: function(col) { return this.options.colWidth; },
-    /* Method: getRowHeaderHTML
-     * This function returns an HTML string to be placed in the row
-     * header for the given row index
-     */ 
-    getRowHeaderHTML: function(row) { 
-        return this.options.rowHeaders?this.options.rowHeaders[row]:row+1; 
+    /**
+     * APIMethod: detach
+     * removes this adaptor from the current tree.
+     */
+    detach: function () {
+      this.parent();
+      this.store.removeEvent('storeDataLoaded', this.fill);
     },
-    /* Method: getRowHeaderWidth
-     * This function returns an integer which is the width of the row
-     * header column in pixels.
-     */ 
-    getRowHeaderWidth: function() { return this.options.rowHeaderWidth; },
-    /* Method: getRowHeight
-     * This function returns an integer which is the height of the
-     * given row in pixels.
-     */ 
-    getRowHeight: function(row) { return this.options.rowHeight; },
-    /* Method: getRowCount
-     * This function returns the number of rows of data in the model
-     * as an integer value.
-     */ 
-    getRowCount: function() { return this.data.length },
-    /* Method: getValueAt
-     * This function returns an HTML string which is the text to place
-     * in the cell at the given row and column.
-     */ 
-    getValueAt: function(row, col) { return (this.data && $chk(this.data[row])) ? this.data[row][col] : ''; },
-    /* Method: setColumnWidth
-     * This function is called with a column index and width in pixels
-     * when a column is resized.  This function is only required if the grid
-     * allows resizeable columns.
+    /**
+     * APIMethod: firstLoad
+     * Method used to start the first store load.
      */
-    setColumnWidth: function() {},
-    /* Method: isCellEditable
-     * This function returns a boolean value to indicate if a given
-     * cell is editable by the user.
-     */ 
-    isCellEditable: function() { return false },
-    /* Method: setValueAt
-     * This function is called with the row and column of a cell and a
-     * new value for the cell.  It is mandatory to provide this function if any of
-     * the cells in the model are editable.
-     */ 
-    setValueAt: function(row, col, value) {},
-    /* Method: rowSelected
-     * This function is called by the grid to indicate that the user
-     * has selected a row by clicking on the row header.
-     */ 
-    rowSelected: function(grid, row) {
-        if (this.selectedRow != null) {
-            grid.selectRow(this.selectedRow, false);
+    firstLoad: function () {
+      //initial store load
+      this.busy = 'tree';
+      this.tree.setBusy(true);
+        this.store.load({
+            node: this.options.startingNodeKey
+        });
+    },
+
+    /**
+     * APIMethod: fill
+     * This function will start at this.currentRecord and add the remaining
+     * items to the tree.
+     */
+    fill: function () {
+      var i,
+          template,
+          item,
+          p,
+          folder,
+          options = this.option;
+
+      if (this.busy == 'tree') {
+        this.tree.setBusy(false);
+        this.busy = 'none';
+      } else if (this.busy == 'folder') {
+        this.busyFolder.setBusy(false);
+        this.busy = 'none';
+      }
+        var l = this.store.count() - 1;
+        for (i = this.currentRecord + 1; i <= l; i++) {
+            template = this.store.fillTemplate(i,options.template,this.columnsNeeded);
+
+            if (this.hasChildren(i)) {
+                //add as folder
+                item = new Jx.TreeFolder($merge(options.folderOptions, {
+                    label: template
+                }));
+
+                if (options.monitorFolders) {
+                  item.addEvent('disclosed', this.checkFolder);
+                }
+
+                this.folders.set(i,item);
+            } else {
+                //add as item
+                item = new Jx.TreeItem($merge(options.itemOptions, {
+                    label: template
+                }));
+            }
+            document.id(item).store('index', i).store('jxAdaptor', this);
+            //check for a parent
+            if (this.hasParent(i)) {
+                //add as child of parent
+                var p = this.getParentIndex(i);
+                var folder = this.folders.get(p);
+                folder.add(item);
+            } else {
+                //otherwise add to the tree itself
+                this.tree.add(item);
+            }
         }
-        this.selectedRow = row;
-        grid.selectRow(row, true);
-        this.fireEvent('select-row', row);
+        this.currentRecord = l;
     },
-    /* Method: columnSelected
-     * This function is called by the grid to indicate that the user
-     * has selected a column by clicking on the column header.
-     */ 
-    columnSelected: function(grid, col) {
-        if (this.selectedCol != null) {
-            grid.selectColumn(this.selectedCol, false);
+    /**
+     * Method: checkFolder
+     * Called by the disclose event of the tree to determine if we need to
+     * request additional items for a branch of the tree.
+     */
+    checkFolder: function (folder) {
+        var items = folder.items(),
+            index,
+            node;
+        if (!$defined(items) || items.length === 0) {
+            //get items via the store
+          index = document.id(folder).retrieve('index');
+          node = this.store.get('primaryKey', index);
+          this.busyFolder = folder;
+          this.busyFolder.setBusy(true);
+          this.busy = 'folder';
+            this.store.load({
+                node: node
+            });
         }
-        this.selectedCol = col;
-        grid.selectColumn(col, true);
-        this.fireEvent('select-column', col);
     },
-    /* Method: cellSelected
-     * This function is called by the grid to indicate that the user
-     * has selected a cell by clicking on the cell in the grid.
+    /**
+     * Method: hasChildren
+     * Virtual method to be overridden by sublcasses. Determines if a specific
+     * node has any children.
      */
-    cellSelected: function(grid, row,col) { 
-        grid.selectCell(row, col);
-        this.fireEvent('select-cell', [row, col]);
+    hasChildren: $empty,
+    /**
+     * Method: hasParent
+     * Virtual method to be overridden by sublcasses. Determines if a specific
+     * node has a parent node.
+     */
+    hasParent: $empty,
+    /**
+     * Method: getParentIndex
+     * Virtual method to be overridden by sublcasses. Determines the store index
+     * of the parent node.
+     */
+    getParentIndex: $empty
+});/*
+---
+
+name: Jx.Adaptor.Tree.Mptt
+
+description: Fills a Jx.Tree instance from a remote table that represents an MPTT (Modified Preorder Table Traversal) data source.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Adaptor.Tree
+
+provides: [Jx.Adaptor.Tree.Mptt]
+
+...
+ */
+/**
+ * Class: Jx.Adaptor.Tree.Mptt
+ * This class adapts a table adhering to the classic Parent-style "tree table".
+ *
+ * This class requires an MPTT (Modified Preorder Tree Traversal) table. The MPTT
+ * has a 'left' and a 'right' column that indicates the order of nesting. For
+ * more details see the sitepoint.com article at
+ * http://articles.sitepoint.com/article/hierarchical-data-database
+ *
+ * if useAjax option is set to true then this adapter will send an Ajax request
+ * to the server, through the store's strategy (should be Jx.Store.Strategy.Progressive)
+ * to request additional nodes.
+ *
+ * Copyright 2010 by Jonathan Bomgardner
+ * License: mit-style
+ */
+Jx.Adaptor.Tree.Mptt = new Class({
+
+
+    Family: 'Jx.Adaptor.Tree.Mptt',
+    Extends: Jx.Adaptor.Tree,
+
+    name: 'tree.mptt',
+
+    options: {
+        left: 'left',
+        right: 'right'
+    },
+
+    /**
+     * APIMethod: hasChildren
+     *
+     * Parameters:
+     * index - {integer} the array index of the row in the store (not the
+     *          primary key).
+     */
+    hasChildren: function (index) {
+        var l = this.store.get(this.options.left, index).toInt(),
+            r = this.store.get(this.options.right, index).toInt();
+        return (l + 1 !== r);
+    },
+
+    /**
+     * APIMethod: hasParent
+     *
+     * Parameters:
+     * index - {integer} the array index of the row in the store (not the
+     *          primary key).
+     */
+    hasParent: function (index) {
+        var i = this.getParentIndex(index),
+            result = false;
+        if ($defined(i)) {
+            result = true;
+        }
+        return result;
+    },
+
+    /**
+     * APIMethod: getParentIndex
+     *
+     * Parameters:
+     * index - {integer} the array index of the row in the store (not the
+     *          primary key).
+     */
+    getParentIndex: function (index) {
+        var store = this.store,
+            options = this.options,
+            l,
+            r,
+            i,
+            pl,
+            pr;
+        l = store.get(options.left, index).toInt();
+        r = store.get(options.right, index).toInt();
+        for (i = index-1; i >= 0; i--) {
+            pl = store.get(options.left, i).toInt();
+            pr = store.get(options.right, i).toInt();
+            if (pl < l && pr > r) {
+                return i;
+            }
+        }
+        return null;
+    }
+});/*
+---
+
+name: Jx.Adaptor.Tree.Parent
+
+description: Fills a Jx.Tree instance from a standard parent/child/folder style data table.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Adaptor.Tree
+
+provides: [Jx.Adaptor.Tree.Parent]
+
+
+...
+ */
+/**
+ * Class: Jx.Adapter.Tree.Parent
+ * This class adapts a table adhering to the classic Parent-style "tree table".
+ * 
+ * Basically, the store needs to have a column that will indicate the
+ * parent of each row. The root(s) of the tree should be indicated by a "-1" 
+ * in this column. The name of the "parent" column is configurable in the 
+ * options.
+ * 
+ * if the monitorFolders option is set to true then this adapter will send
+ * an Ajax request to the server, through the store's strategy (should be
+ * Jx.Store.Strategy.Progressive) to request additional nodes. Also, a column
+ * indicating whether this is a folder needs to be set as there is no way to
+ * tell if a node has children without it.
+ *
+ * Copyright 2010 by Jonathan Bomgardner
+ * License: mit-style
+ */
+Jx.Adaptor.Tree.Parent = new Class({
     
+
+    Extends: Jx.Adaptor.Tree,
+    Family: 'Jx.Adaptor.Tree.Parent',
+    
+    options: {
+        parentColumn: 'parent',
+        folderColumn: 'folder'
+    },
+        
+    /**
+     * APIMethod: hasChildren
+     * 
+     * Parameters: 
+     * index - {integer} the array index of the row in the store (not the 
+     *          primary key).
+     */
+    hasChildren: function (index) {
+    	return this.store.get(this.options.folderColumn, index);
+    },
+    
+    /**
+     * APIMethod: hasParent
+     * 
+     * Parameters: 
+     * index - {integer} the array index of the row in the store (not the 
+     *          primary key).
+     */
+    hasParent: function (index) {
+        if (this.store.get(this.options.parentColumn, index).toInt() !== -1) {
+            return true;
+        } 
+        return false;
+    },
+    
+    /**
+     * APIMethod: getParentIndex
+     * 
+     * Parameters: 
+     * index - {integer} the array index of the row in the store (not the 
+     *          primary key).
+     */
+    getParentIndex: function (index) {
+        //get the parent based on the index
+        var pk = this.store.get(this.options.parentColumn, index);
+        return this.store.findByColumn('primaryKey', pk);
     }
-});
-// $Id: context.js 424 2009-05-12 12:51:44Z pagameba $
+});/*
+---
+
+name: Jx.Adaptor.Combo
+
+description: Namespace for all Jx.Combo adaptors.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Adaptor
+
+provides: [Jx.Adaptor.Combo]
+
+...
+*/
 /**
+ * Class: Jx.Adaptor.Combo
+ * The namespace for all combo adaptors
+ */
+Jx.Adaptor.Combo = {};/*
+---
+
+name: Jx.Adaptor.Combo.Fill
+
+description: Loads data into a Jx.Combo instance from designated column(s) of a data source.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Adaptor.Combo
+
+provides: [Jx.Adaptor.Combo.Fill]
+
+...
+ */
+Jx.Adaptor.Combo.Fill = new Class({
+
+    Family: 'Jx.Adaptor.Combo.Fill',
+    Extends: Jx.Adaptor,
+    name: 'combo.fill',
+    Binds: ['fill'],
+
+    /**
+     * Note: option.template is used for constructing the text for the label
+     */
+    options: {
+        /**
+         * Option: imagePathColumn
+         * points to a store column that holds the image information
+         * for the combo items.
+         */
+        imagePathColumn: null,
+        /**
+         * Option: imageClassColumn
+         * Points to a store column that holds the image class
+         * information for the combo items
+         */
+        imageClassColumn: null,
+        /**
+         * Option: selectedFn
+         * This should be a function that could be run to determine if
+         * an item should be selected. It will get passed the current store
+         * record as the only parameter. It should return either true or false.
+         */
+        selectedFn: null,
+        /**
+         * Option: noRepeats
+         * This option allows you to use any store even if it has duplicate
+         * values in it. With this option set to true the adaptor will keep
+         * track of all of teh labels it adds and will not add anything that's
+         * a duplicate.
+         */
+        noRepeats: false
+    },
+
+    labels: null,
+
+    init: function () {
+        this.parent();
+
+        if (this.options.noRepeat) {
+            this.labels = [];
+        }
+    },
+
+    attach: function (combo) {
+        this.parent(combo);
+
+        this.store.addEvent('storeDataLoaded', this.fill);
+        if (this.store.loaded) {
+            this.fill();
+        }
+    },
+
+    detach: function () {
+        this.parent();
+
+        this.store.removeEvent('storeDataLoaded', this.fill);
+    },
+
+    fill: function () {
+        var template,
+            items=[],
+            selected,
+            obj,
+            options = this.options,
+            noRepeat = this.options.noRepeat;
+        //empty the combo
+        this.widget.empty();
+        //reset the store and cycle through creating the objects
+        //to pass to combo.add()
+        this.store.first();
+        items = [];
+        this.store.each(function(record){
+            template = this.store.fillTemplate(record,options.template,this.columnsNeeded);
+            if (!noRepeat || (noRepeat && !this.labels.contains(template))) {
+                selected = false;
+                if ($type(options.selectedFn) == 'function') {
+                    selected = options.selectedFn.run(record);
+                }
+                obj = {
+                    label: template,
+                    image: record.get(options.imagePathColumn),
+                    imageClass: record.get(options.imageClassColumn),
+                    selected: selected
+                };
+                items.push(obj);
+
+                if (noRepeat) {
+                    this.labels.push(template);
+                }
+            }
+
+        },this);
+        //pass all of the objects at once
+        this.widget.add(items);
+    }
+});/*
+---
+
+name: Jx.Menu.Context
+
+description: A Jx.Menu that has no button but can be opened at a specific browser location to implement context menus (for instance).
+
+license: MIT-style license.
+
+requires:
+ - Jx.Menu
+
+provides: [Jx.Menu.Context]
+
+css:
+ - menu
+
+...
+ */
+// $Id: context.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
  * Class: Jx.Menu.Context
  *
  * Extends: Jx.Menu
  *
- * A <Jx.Menu> that has no button but can be opened at a specific 
+ * A <Jx.Menu> that has no button but can be opened at a specific
  * browser location to implement context menus (for instance).
  *
  * Example:
@@ -16132,32 +37688,27 @@
  * Events:
  * TODO - add open/close events?
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Menu.Context = new Class({
     Family: 'Jx.Menu.Context',
-    /** Extends:
-     * <Jx.Menu>
-     */
     Extends: Jx.Menu,
+
+    parameters: ['id'],
+
     /**
-     * Constructor: Jx.ContextMenu
+     * APIMethod: render
      * create a new context menu
-     *
-     * Parameters:
-     * id - {HTMLElement} element or id to make this the context menu
-     * for.  The menu hooks the oncontextmenu event of the element
-     * and shows itself at the mouse position where the right-click
-     * happened.
      */
-    initialize : function(id) {
+    render: function() {
+        this.id = document.id(this.options.id);
+        if (this.id) {
+            this.id.addEvent('contextmenu', this.show.bindWithEvent(this));
+        }
         this.parent();
-        if ($(id)) {
-            $(id).addEvent('contextmenu', this.show.bindWithEvent(this));
-        }
     },
     /**
      * Method: show
@@ -16167,19 +37718,25 @@
      * e - {Event} the mouse event
      */
     show : function(e) {
-        if (this.items.length ==0) {
+        if (this.list.count() ==0) {
             return;
         }
         
+        this.target = e.target;
+
         this.contentContainer.setStyle('visibility','hidden');
         this.contentContainer.setStyle('display','block');
-        $(document.body).adopt(this.contentContainer);            
+        document.id(document.body).adopt(this.contentContainer);
         /* we have to size the container for IE to render the chrome correctly
          * but just in the menu/sub menu case - there is some horrible peekaboo
          * bug in IE related to ULs that we just couldn't figure out
          */
+         this.contentContainer.setStyles({
+           width: null,
+           height: null
+         });
         this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
-        
+
         this.position(this.contentContainer, document.body, {
             horizontal: [e.page.x + ' left'],
             vertical: [e.page.y + ' top', e.page.y + ' bottom'],
@@ -16188,17 +37745,36 @@
 
         this.contentContainer.setStyle('visibility','');
         this.showChrome(this.contentContainer);
-                
-        document.addEvent('mousedown', this.hideWatcher);
-        document.addEvent('keyup', this.keypressWatcher);
 
+        document.addEvent('mousedown', this.bound.hide);
+        document.addEvent('keyup', this.bound.keypress);
+
         e.stop();
-    }    
-});// $Id: menu.separator.js 424 2009-05-12 12:51:44Z pagameba $
+    }
+});/*
+---
+
+name: Jx.Menu.Separator
+
+description: Convenience class to create a visual separator in a menu.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Menu
+
+provides: [Jx.Menu.Separator]
+
+images:
+ - toolbar_separator_v.png
+
+...
+ */
+// $Id: menu.separator.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.Menu.Separator
  *
- * Extends: Object
+ * Extends: <Jx.Object>
  *
  * A convenience class to create a visual separator in a menu.
  *
@@ -16206,13 +37782,14 @@
  * (code)
  * (end)
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Menu.Separator = new Class({
     Family: 'Jx.Menu.Separator',
+    Extends: Jx.Widget,
     /**
      * Property: domObj
      * {HTMLElement} the HTML element that the separator is contained
@@ -16224,15 +37801,25 @@
      * {<Jx.Menu>, <Jx.Menu.SubMenu>} the menu that the separator is in.
      */
     owner: null,
+    options: {
+        template: "<li class='jxMenuItemContainer jxMenuItem'><span class='jxMenuSeparator'>&nbsp;</span></li>"
+    },
+    classes: new Hash({
+        domObj: 'jxMenuItem'
+    }),
     /**
-     * Constructor: Jx.Menu.Separator
+     * APIMethod: render
      * Create a new instance of a menu separator
      */
-    initialize: function() {
-        this.domObj = new Element('li',{'class':'jxMenuItem'});
-        var span = new Element('span', {'class':'jxMenuSeparator','html':'&nbsp;'});
-        this.domObj.appendChild(span);
+    render: function() {
+        this.parent();
+        this.domObj.store('jxMenuItem', this);
     },
+    cleanup: function() {
+      this.domObj.eliminate('jxMenuItem');
+      this.owner = null;
+      this.parent();
+    },
     /**
      * Method: setOwner
      * Set the ownder of this menu item
@@ -16253,7 +37840,24 @@
      * Show the menu item
      */
     show: $empty
-});// $Id: submenu.js 424 2009-05-12 12:51:44Z pagameba $
+});/*
+---
+
+name: Jx.Menu.SubMenu
+
+description: A sub menu contains menu items within a main menu or another sub menu.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Menu.Item
+ - Jx.Menu
+
+provides: [Jx.Menu.SubMenu]
+
+...
+ */
+// $Id: submenu.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.Menu.SubMenu
  *
@@ -16271,15 +37875,14 @@
  * (code)
  * (end)
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Menu.SubMenu = new Class({
     Family: 'Jx.Menu.SubMenu',
     Extends: Jx.Menu.Item,
-    Implements: [Jx.AutoPosition, Jx.Chrome],
     /**
      * Property: subDomObj
      * {HTMLElement} the HTML container for the sub menu.
@@ -16297,31 +37900,37 @@
      */
     visibleItem: null,
     /**
-     * Property: items
-     * {Array} the menu items that are in this sub menu.
+     * Property: list
+     * {<Jx.List>} a list to manage menu items
      */
-    items: null,
+    list: null,
+    options: {
+        template: '<li class="jxMenuItemContainer"><a class="jxMenuItem jxButtonSubMenu"><span class="jxMenuItemContent"><img class="jxMenuItemIcon" src="'+Jx.aPixel.src+'"><span class="jxMenuItemLabel"></span></span></a></li>',
+        position: {
+            horizontal: ['right left', 'left right'],
+            vertical: ['top top']
+        }
+    },
+
     /**
-     * Constructor: Jx.SubMenu
+     * APIMethod: render
      * Create a new instance of Jx.SubMenu
-     *
-     * Parameters:
-     * options - see <Jx.Button.Options>
      */
-    initialize: function(options) { 
+    render: function() {
+        this.parent();
         this.open = false;
-        this.items = [];
-        this.parent(options);
-        this.domA.addClass('jxButtonSubMenu');
-        
-        this.contentContainer = new Element('div', {
-            'class': 'jxMenuContainer'
+
+        this.menu = new Jx.Menu(null, {
+            position: this.options.position
         });
-        this.subDomObj = new Element('ul', {
-            'class':'jxSubMenu'
-        });
-        this.contentContainer.adopt(this.subDomObj);
+        this.menu.domObj = this.domObj;
     },
+    cleanup: function() {
+      this.menu.domObj = null;
+      this.menu.destroy();
+      this.menu = null;
+      this.parent();
+    },
     /**
      * Method: setOwner
      * Set the owner of this sub menu
@@ -16337,48 +37946,24 @@
      * Show the sub menu
      */
     show: function() {
-        if (this.open || this.items.length == 0) {
+        if (this.open || this.menu.list.count() == 0) {
             return;
         }
-        
-        this.contentContainer.setStyle('visibility','hidden');
-        this.contentContainer.setStyle('display','block');
-        $(document.body).adopt(this.contentContainer);            
-        /* we have to size the container for IE to render the chrome correctly
-         * but just in the menu/sub menu case - there is some horrible peekaboo
-         * bug in IE related to ULs that we just couldn't figure out
-         */
-        this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
-        this.showChrome(this.contentContainer);
-        
-        this.position(this.contentContainer, this.domObj, {
-            horizontal: ['right left', 'left right'],
-            vertical: ['top top'],
-            offsets: this.chromeOffsets
-        });
-        
+        this.menu.show();
         this.open = true;
-        this.contentContainer.setStyle('visibility','');
-        
-        this.setActive(true);
+        // this.setActive(true);
     },
-    
+
     eventInMenu: function(e) {
-        if (this.visibleItem && 
-            this.visibleItem.eventInMenu && 
+        if (this.visibleItem &&
+            this.visibleItem.eventInMenu &&
             this.visibleItem.eventInMenu(e)) {
             return true;
         }
-        return $(e.target).descendantOf(this.domObj) ||
-               $(e.target).descendantOf(this.subDomObj) ||
-               this.items.some(
-                   function(item) {
-                       return item instanceof Jx.Menu.SubMenu && 
-                              item.eventInMenu(e);
-                   }
-               );
+        return document.id(e.target).descendantOf(this.domObj) ||
+               this.menu.eventInMenu(e);
     },
-    
+
     /**
      * Method: hide
      * Hide the sub menu
@@ -16388,8 +37973,7 @@
             return;
         }
         this.open = false;
-        this.items.each(function(item){item.hide();});
-        this.contentContainer.setStyle('display','none');
+        this.menu.hide();
         this.visibleItem = null;
     },
     /**
@@ -16400,54 +37984,41 @@
      * item - {<Jx.MenuItem>} the menu item to add.  Multiple menu items
      * can be added by passing multiple arguments to this function.
      */
-    add : function() { /* menu */
-        var that = this;
-        $A(arguments).each(function(item){
-            that.items.push(item);
-            item.setOwner(that);
-            that.subDomObj.adopt(item.domObj);
-        });
+    add: function(item, position) {
+        this.menu.add(item, position, this);
         return this;
     },
     /**
-     * Method: insertBefore
-     * Insert a menu item before another menu item.
+     * Method: remove
+     * Remove a menu item from the menu
      *
      * Parameters:
-     * newItem - {<Jx.MenuItem>} the menu item to insert
-     * targetItem - {<Jx.MenuItem>} the menu item to insert before
+     * item - {<Jx.MenuItem>} the menu item to remove
      */
-    insertBefore: function(newItem, targetItem) {
-        var bInserted = false;
-        for (var i=0; i<this.items.length; i++) {
-            if (this.items[i] == targetItem) {
-                this.items.splice(i, 0, newItem);
-                this.subDomObj.insertBefore(newItem.domObj, targetItem.domObj);
-                bInserted = true;
-                break;
-            }
-        }
-        if (!bInserted) {
-            this.add(newItem);
-        }
+    remove: function(item) {
+        this.menu.remove(item);
+        return this;
     },
     /**
-     * Method: remove
-     * Remove a single menu item from the menu.
+     * Method: replace
+     * Replace a menu item with another menu item
      *
      * Parameters:
-     * item - {<Jx.MenuItem} the menu item to remove.
+     * what - {<Jx.MenuItem>} the menu item to replace
+     * withWhat - {<Jx.MenuItem>} the menu item to replace it with
      */
-    remove: function(item) {
-        for (var i=0; i<this.items.length; i++) {
-            if (this.items[i] == item) {
-                this.items.splice(i,1);
-                this.subDomObj.removeChild(item.domObj);
-                break;
-            }
-        }
+    replace: function(item, withItem) {
+        this.menu.replace(item, withItem);
+        return this;
     },
     /**
+     * APIMethod: empty
+     * remove all items from the sub menu
+     */
+    empty: function() {
+      this.menu.empty();
+    },
+    /**
      * Method: deactivate
      * Deactivate the sub menu
      *
@@ -16457,7 +38028,7 @@
      */
     deactivate: function(e) {
         if (this.owner) {
-            this.owner.deactivate(e);            
+            this.owner.deactivate(e);
         }
     },
     /**
@@ -16468,7 +38039,7 @@
      * {Boolean} true if the <Jx.Menu> that ultimately contains
      * this sub menu is active, false otherwise.
      */
-    isActive: function() { 
+    isActive: function() {
         if (this.owner) {
             return this.owner.isActive();
         } else {
@@ -16482,7 +38053,7 @@
      * Parameters:
      * isActive - {Boolean} the new active state
      */
-    setActive: function(isActive) { 
+    setActive: function(isActive) {
         if (this.owner && this.owner.setActive) {
             this.owner.setActive(isActive);
         }
@@ -16492,7 +38063,7 @@
      * Set a sub menu of this menu to be visible and hide the previously
      * visible one.
      *
-     * Parameters: 
+     * Parameters:
      * obj - {<Jx.SubMenu>} the sub menu that should be visible
      */
     setVisibleItem: function(obj) {
@@ -16504,11 +38075,27 @@
             this.visibleItem.show();
         }
     }
-});// $Id: snap.js 484 2009-07-17 20:06:27Z pagameba $
+});/*
+---
+
+name: Jx.Splitter.Snap
+
+description: A helper class to create an element that can snap a split panel open or closed.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Splitter
+
+provides: [Jx.Splitter.Snap]
+
+...
+ */
+// $Id: snap.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.Splitter.Snap
  *
- * Extends: Object
+ * Extends: <Jx.Object>
  *
  * A helper class to create an element that can snap a split panel open or
  * closed.
@@ -16517,13 +38104,14 @@
  * (code)
  * (end)
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.Splitter.Snap = new Class({
     Family: 'Jx.Splitter.Snap',
+    Extends: Jx.Object,
     /**
      * Property: snap
      * {HTMLElement} the DOM element of the snap (the thing that gets
@@ -16547,22 +38135,26 @@
      */
     layout: 'vertical',
     /**
-     * Constructor: Jx.Splitter.Snap
-     * Create a new Jx.Splitter.Snap
-     *
      * Parameters:
      * snap - {HTMLElement} the clickable thing that snaps the element
      *           open and closed
      * element - {HTMLElement} the element that gets controlled by the snap
      * splitter - {<Jx.Splitter>} the splitter that this all happens inside of.
      */
-    initialize: function(snap, element, splitter, events) {
-        this.snap = snap;
-        this.element = element;
-        var jxl = element.retrieve('jxLayout');
+    parameters: ['snap','element','splitter','events'],
+
+    /**
+     * APIMethod: init
+     * Create a new Jx.Splitter.Snap
+     */
+    init: function() {
+        this.snap = this.options.snap;
+        this.element = this.options.element;
+        this.splitter = this.options.splitter;
+        this.events = this.options.events;
+        var jxl = this.element.retrieve('jxLayout');
         jxl.addEvent('sizeChange', this.sizeChange.bind(this));
-        this.splitter = splitter;
-        this.layout = splitter.options.layout; 
+        this.layout = this.splitter.options.layout;
         var jxo = jxl.options;
         var size = this.element.getContentBoxSize();
         if (this.layout == 'vertical') {
@@ -16572,36 +38164,37 @@
             this.originalSize = size.width;
             this.minimumSize = jxo.minWidth ? jxo.minWidth : 0;
         }
-        events.each(function(eventName) {
-            snap.addEvent(eventName, this.toggleElement.bind(this));
+        this.events.each(function(eventName) {
+            this.snap.addEvent(eventName, this.toggleElement.bind(this));
         }, this);
     },
-    
+
     /**
      * Method: toggleElement
      * Snap the element open or closed.
      */
     toggleElement: function() {
+        var size = this.element.getContentBoxSize();
         var newSize = {};
         if (this.layout == 'vertical') {
-            if (this.element.clientHeight <= this.minimumSize) {
+            if (size.height == this.minimumSize) {
                 newSize.height = this.originalSize;
             } else {
-                this.originalSize = this.element.clientHeight;
+                this.originalSize = size.height;
                 newSize.height = this.minimumSize;
             }
         } else {
-            if (this.element.clientWidth <= this.minimumSize) {
+            if (size.width == this.minimumSize) {
                 newSize.width = this.originalSize;
             } else {
-                this.originalSize = this.element.clientWidth;
+                this.originalSize = size.width;
                 newSize.width = this.minimumSize;
             }
         }
         this.element.resize(newSize);
         this.splitter.sizeChanged();
     },
-    
+
     /**
      * Method: sizeChanged
      * Handle the size of the element changing to see if the
@@ -16610,7 +38203,7 @@
     sizeChange: function() {
         var size = this.element.getContentBoxSize();
         if (this.layout == 'vertical') {
-            if (this.element.clientHeight == this.minimumSize) {
+            if (size.height == this.minimumSize) {
                 this.snap.addClass('jxSnapClosed');
                 this.snap.removeClass('jxSnapOpened');
             } else {
@@ -16618,7 +38211,7 @@
                 this.snap.removeClass('jxSnapClosed');
             }
         } else {
-            if (this.element.clientWidth == this.minimumSize) {
+            if (size.width == this.minimumSize) {
                 this.snap.addClass('jxSnapClosed');
                 this.snap.removeClass('jxSnapOpened');
             } else {
@@ -16627,286 +38220,276 @@
             }
         }
     }
-});// $Id: toolbar.js 451 2009-05-31 21:21:30Z pagameba $
+});/*
+---
+
+name: Jx.Tab
+
+description: A single tab in a tab set.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Button
+ - Jx.Layout
+
+provides: [Jx.Tab]
+
+css:
+ - tab
+
+images:
+ - tab_top.png
+ - tab_bottom.png
+ - tab_left.png
+ - tab_right.png
+ - tab_close.png
+
+...
+ */
+// $Id: tab.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.Toolbar
+ * Class: Jx.Tab
  *
- * Extends: Object
+ * Extends: <Jx.Button>
  *
- * Implements: Options, Events
+ * A single tab in a tab set.  A tab has a label (displayed in the tab) and a
+ * content area that is displayed when the tab is active.  A tab has to be
+ * added to both a <Jx.TabSet> (for the content) and <Jx.Toolbar> (for the
+ * actual tab itself) in order to be useful.  Alternately, you can use
+ * a <Jx.TabBox> which combines both into a single control at the cost of
+ * some flexibility in layout options.
  *
- * A toolbar is a container object that contains other objects such as
- * buttons.  The toolbar organizes the objects it contains automatically,
- * wrapping them as necessary.  Multiple toolbars may be placed within
- * the same containing object.
+ * A tab is a <Jx.ContentLoader> and you can specify the initial content of
+ * the tab using any of the methods supported by
+ * <Jx.ContentLoader::loadContent>.  You can acccess the actual DOM element
+ * that contains the content (if you want to dynamically insert content
+ * for instance) via the <Jx.Tab::content> property.
  *
- * Jx.Toolbar includes CSS classes for styling the appearance of a
- * toolbar to be similar to traditional desktop application toolbars.
+ * A tab is a button of type *toggle* which means that it emits the *up*
+ * and *down* events.
  *
- * There is one special object, Jx.ToolbarSeparator, that provides
- * a visual separation between objects in a toolbar.
- *
- * While a toolbar is generally a *dumb* container, it serves a special
- * purpose for menus by providing some infrastructure so that menus can behave
- * properly.
- *
- * In general, almost anything can be placed in a Toolbar, and mixed with 
- * anything else.
- *
  * Example:
- * The following example shows how to create a Jx.Toolbar instance and place
- * two objects in it.
- *
  * (code)
- * //myToolbarContainer is the id of a <div> in the HTML page.
- * function myFunction() {}
- * var myToolbar = new Jx.Toolbar('myToolbarContainer');
- * 
- * var myButton = new Jx.Button(buttonOptions);
- *
- * var myElement = document.createElement('select');
- *
- * myToolbar.add(myButton, new Jx.ToolbarSeparator(), myElement);
+ * var tab1 = new Jx.Tab({
+ *     label: 'tab 1',
+ *     content: 'content1',
+ *     onDown: function(tab) {
+ *         console.log('tab became active');
+ *     },
+ *     onUp: function(tab) {
+ *         console.log('tab became inactive');
+ *     }
+ * });
  * (end)
  *
- * Events:
- * add - fired when one or more buttons are added to a toolbar
- * remove - fired when on eor more buttons are removed from a toolbar
  *
- * Implements: 
- * Options
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
-Jx.Toolbar = new Class({
-    Family: 'Jx.Toolbar',
-    Implements: [Options,Events],
+Jx.Tab = new Class({
+    Family: 'Jx.Tab',
+    Extends: Jx.Button,
     /**
-     * Property: items
-     * {Array} an array of the things in the toolbar.
+     * Property: content
+     * {HTMLElement} The content area that is displayed when the tab is
+     * active.
      */
-    items : null,
-    /**
-     * Property: domObj
-     * {HTMLElement} the HTML element that the toolbar lives in
-     */
-    domObj : null,
-    /**
-     * Property: isActive
-     * When a toolbar contains <Jx.Menu> instances, they want to know
-     * if any menu in the toolbar is active and this is how they
-     * find out.
-     */
-    isActive : false,
+    content: null,
+
     options: {
-        type: 'Toolbar',
-        /* Option: position
-         * the position of this toolbar in the container.  The position
-         * affects some items in the toolbar, such as menus and flyouts, which
-         * need to open in a manner sensitive to the position.  May be one of
-         * 'top', 'right', 'bottom' or 'left'.  Default is 'top'.
+        /* Option: toggleClass
+         * the CSS class to use for the button, 'jxTabToggle' by default
          */
-        position: 'top',
-        /* Option: parent
-         * a DOM element to add this toolbar to
+        toggleClass: 'jxTabToggle',
+        /* Option: pressedClass
+         * the CSS class to use when the tab is pressed, 'jxTabPressed' by
+         * default
          */
-        parent: null,
-        /* Option: autoSize
-         * if true, the toolbar will attempt to set its size based on the
-         * things it contains.  Default is false.
+        pressedClass: 'jxTabPressed',
+        /* Option: activeClass
+         * the CSS class to use when the tab is active, 'jxTabActive' by 
+         * default.
          */
-        autoSize: false,
-        /* Option: scroll
-         * if true, the toolbar may scroll if the contents are wider than
-         * the size of the toolbar
+        activeClass: 'jxTabActive',
+        /* Option: activeTabClass
+         * the CSS class to use on the content area of the active tab,
+         * 'tabContentActive' by default.
          */
-        scroll: true
+        activeTabClass: 'tabContentActive',
+        /* Option: template
+         * the HTML template for a tab
+         */
+        template: '<span class="jxTabContainer"><a class="jxTab"><span class="jxTabContent"><img class="jxTabIcon" src="'+Jx.aPixel.src+'"><span class="jxTabLabel"></span></span></a><a class="jxTabClose"></a></span>',
+        /* Option: contentTemplate
+         * the HTML template for a tab's content area
+         */
+        contentTemplate: '<div class="tabContent"></div>',
+        /* Option: close
+         * {Boolean} can the tab be closed by the user?  False by default.
+         */
+        close: false,
+        /* Option: shouldClose
+         * {Mixed} when a tab is closeable, the shouldClose option is checked
+         * first to see if the tab should close.  You can provide a function
+         * for this option that can be used to return a boolean value.  This
+         * is useful if your tab contains something the user can edit and you
+         * want to see if they want to discard the changes before closing.
+         * The default value is true, meaning the tab will close immediately.
+         * (code)
+         * new Jx.Tab({
+         *   label: 'test close',
+         *   close: true,
+         *   shouldClose: function() {
+         *     return window.confirm('Are you sure?');
+         *   }
+         * });
+         * (end)
+         */
+        shouldClose: true
     },
     /**
-     * Constructor: Jx.Toolbar
-     * Create a new instance of Jx.Toolbar.
-     *
-     * Parameters:
-     * options - <Jx.Toolbar.Options>
+     * Property: classes
+     * {<Hash>} a hash of object properties to CSS class names used to
+     * automatically extract references to important DOM elements when
+     * processing a widget template.  This allows developers to provide custom
+     * HTML structures without affecting the functionality of widgets.
      */
-    initialize : function(options) {
-        this.setOptions(options);
-        this.items = [];
-        
-        this.domObj = new Element('ul', {
-            id: this.options.id,
-            'class':'jx'+this.options.type
-        });
-        
-        if (this.options.parent) {
-            this.addTo(this.options.parent);
-        }
-        this.deactivateWatcher = this.deactivate.bindWithEvent(this);
-        if (this.options.items) {
-            this.add(this.options.items);
-        }
-    },
-    
+    classes: new Hash({
+        domObj: 'jxTabContainer',
+        domA: 'jxTab',
+        domImg: 'jxTabIcon',
+        domLabel: 'jxTabLabel',
+        domClose: 'jxTabClose',
+        content: 'tabContent'
+    }),
+
     /**
-     * Method: addTo
-     * add this toolbar to a DOM element automatically creating a toolbar
-     * container if necessary
-     *
-     * Parameters:
-     * parent - the DOM element or toolbar container to add this toolbar to.
+     * Method: render
+     * Create a new instance of Jx.Tab.  Any layout options passed are used
+     * to create a <Jx.Layout> for the tab content area.
      */
-    addTo: function(parent) {
-        var tbc = $(parent).retrieve('jxBarContainer');
-        if (!tbc) {
-            tbc = new Jx.Toolbar.Container({
-                parent: parent, 
-                position: this.options.position, 
-                autoSize: this.options.autoSize,
-                scroll: this.options.scroll
-            });
+    render : function( ) {
+        this.options = $merge(this.options, {toggle:true});
+        this.parent();
+        this.domObj.store('jxTab', this);
+        this.processElements(this.options.contentTemplate, this.classes);
+        new Jx.Layout(this.content, this.options);
+        
+        // load content onDemand if needed
+        if(!this.options.loadOnDemand || this.options.active) {
+          this.loadContent(this.content);
+          // set active if needed
+          if(this.options.active) {
+            this.clicked();
+          }
+        }else{
+          this.addEvent('contentLoaded', function(ev) {
+            this.setActive(true);
+          }.bind(this));
         }
-        tbc.add(this);
-        return this;
-    },
-    
-    /**
-     * Method: add
-     * Add an item to the toolbar.  If the item being added is a Jx component
-     * with a domObj property, the domObj is added.  If the item being added
-     * is an LI element, then it is given a CSS class of *jxToolItem*.
-     * Otherwise, the thing is wrapped in a <Jx.ToolbarItem>.
-     *
-     * Parameters:
-     * thing - {Object} the thing to add.  More than one thing can be added
-     * by passing multiple arguments.
-     */
-    add: function( ) {
-        $A(arguments).flatten().each(function(thing) {
-            if (thing.domObj) {
-                thing = thing.domObj;
+        this.addEvent('down', function(){
+            this.content.addClass(this.options.activeTabClass);
+        }.bind(this));
+        this.addEvent('up', function(){
+            this.content.removeClass(this.options.activeTabClass);
+        }.bind(this));
+
+        //remove the close button if necessary
+        if (this.domClose) {
+            if (this.options.close) {
+                this.domObj.addClass('jxTabClose');
+                this.domClose.addEvent('click', (function(){
+                  var shouldClose = true;
+                  if ($defined(this.options.shouldClose)) {
+                    if (typeof this.options.shouldClose == 'function') {
+                      shouldClose = this.options.shouldClose();
+                    } else {
+                      shouldClose = this.options.shouldClose;
+                    }
+                  }
+                  if (shouldClose) {
+                    this.fireEvent('close');
+                  }
+                }).bind(this));
+            } else {
+                this.domClose.dispose();
             }
-            if (thing.tagName == 'LI') {
-                if (!thing.hasClass('jxToolItem')) {
-                    thing.addClass('jxToolItem');
-                }
-                this.domObj.appendChild(thing);
-            } else {
-                var item = new Jx.Toolbar.Item(thing);
-                this.domObj.appendChild(item.domObj);
-            }            
-        }, this);
-
-        if (arguments.length > 0) {
-            this.fireEvent('add', this);
         }
-        return this;
     },
     /**
-     * Method: remove
-     * remove an item from a toolbar.  If the item is not in this toolbar
-     * nothing happens
-     *
-     * Parameters:
-     * item - {Object} the object to remove
-     *
-     * Returns:
-     * {Object} the item that was removed, or null if the item was not
-     * removed.
+     * APIMethod: clicked
+     * triggered when the user clicks the button, processes the
+     * actionPerformed event
      */
-    remove: function(item) {
-        if (item.domObj) {
-            item = item.domObj;
+    clicked : function(evt) {
+      if(this.options.enabled) {
+        // just set active when caching is enabled
+        if(this.contentIsLoaded && this.options.cacheContent) {
+          this.setActive(true);
+        // load on demand or reload content if caching is disabled
+        }else if(this.options.loadOnDemand || !this.options.cacheContent){
+          this.loadContent(this.content);
+        }else{
+          this.setActive(true);
         }
-        var li = item.findElement('LI');
-        if (li && li.parentNode == this.domObj) {
-            item.dispose();
-            li.dispose();
-            this.fireEvent('remove', this);
-        } else {
-            return null;
-        }
-    },
-    /**
-     * Method: deactivate
-     * Deactivate the Toolbar (when it is acting as a menu bar).
-     */
-    deactivate: function() {
-        this.items.each(function(o){o.hide();});
-        this.setActive(false);
-    },
-    /**
-     * Method: isActive
-     * Indicate if the toolbar is currently active (as a menu bar)
-     *
-     * Returns:
-     * {Boolean}
-     */
-    isActive: function() { 
-        return this.isActive; 
-    },
-    /**
-     * Method: setActive
-     * Set the active state of the toolbar (for menus)
-     *
-     * Parameters: 
-     * b - {Boolean} the new state
-     */
-    setActive: function(b) { 
-        this.isActive = b;
-        if (this.isActive) {
-            document.addEvent('click', this.deactivateWatcher);
-        } else {
-            document.removeEvent('click', this.deactivateWatcher);
-        }
-    },
-    /**
-     * Method: setVisibleItem
-     * For menus, they want to know which menu is currently open.
-     *
-     * Parameters:
-     * obj - {<Jx.Menu>} the menu that just opened.
-     */
-    setVisibleItem: function(obj) {
-        if (this.visibleItem && this.visibleItem.hide && this.visibleItem != obj) {
-            this.visibleItem.hide();
-        }
-        this.visibleItem = obj;
-        if (this.isActive()) {
-            this.visibleItem.show();
-        }
-    },
-    showItem: function(item) {
-        this.fireEvent('show', item);
+      }
     }
 });
-// $Id: tabset.js 424 2009-05-12 12:51:44Z pagameba $
+
+/* keep the old location temporarily */
+Jx.Button.Tab = new Class({
+  Extends: Jx.Tab,
+  init: function() {
+    if (console.warn) {
+      console.warn('WARNING: Jx.Button.Tab has been renamed to Jx.Tab');
+    } else {
+      console.log('WARNING: Jx.Button.Tab has been renamed to Jx.Tab');
+    }
+    this.parent();
+  }
+});/*
+---
+
+name: Jx.TabSet
+
+description: A TabSet manages a set of Jx.Tab content areas by ensuring that only one of the content areas is visible (i.e. the active tab).
+
+license: MIT-style license.
+
+requires:
+ - Jx.Tab
+
+provides: [Jx.TabSet]
+
+...
+ */
+// $Id: tabset.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.TabSet
  *
- * Extends: Object
+ * Extends: <Jx.Object>
  *
- * Implements: Options, Events
- *
- * A TabSet manages a set of <Jx.Button.Tab> content areas by ensuring that only one
+ * A TabSet manages a set of <Jx.Tab> content areas by ensuring that only one
  * of the content areas is visible (i.e. the active tab).  TabSet does not
- * manage the actual tabs.  The instances of <Jx.Button.Tab> that are to be managed
+ * manage the actual tabs.  The instances of <Jx.Tab> that are to be managed
  * as a set have to be added to both a TabSet and a <Jx.Toolbar>.  The content
- * areas of the <Jx.Button.Tab>s are sized to fit the content area that the TabSet
+ * areas of the <Jx.Tab>s are sized to fit the content area that the TabSet
  * is managing.
  *
  * Example:
  * (code)
  * var tabBar = new Jx.Toolbar('tabBar');
  * var tabSet = new Jx.TabSet('tabArea');
- * 
- * var tab1 = new Jx.Button.Tab('tab 1', {contentID: 'content1'});
- * var tab2 = new Jx.Button.Tab('tab 2', {contentID: 'content2'});
- * var tab3 = new Jx.Button.Tab('tab 3', {contentID: 'content3'});
- * var tab4 = new Jx.Button.Tab('tab 4', {contentURL: 'test_content.html'});
- * 
+ *
+ * var tab1 = new Jx.Tab('tab 1', {contentID: 'content1'});
+ * var tab2 = new Jx.Tab('tab 2', {contentID: 'content2'});
+ * var tab3 = new Jx.Tab('tab 3', {contentID: 'content3'});
+ * var tab4 = new Jx.Tab('tab 4', {contentURL: 'test_content.html'});
+ *
  * tabSet.add(t1, t2, t3, t4);
  * tabBar.add(t1, t2, t3, t4);
  * (end)
@@ -16914,14 +38497,14 @@
  * Events:
  * tabChange - the current tab has changed
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.TabSet = new Class({
     Family: 'Jx.TabSet',
-    Implements: [Options,Events],
+    Extends: Jx.Object,
     /**
      * Property: tabs
      * {Array} array of tabs that are managed by this tab set
@@ -16934,20 +38517,22 @@
      */
     domObj : null,
     /**
-     * Constructor: Jx.TabSet
-     * Create a new instance of <Jx.TabSet> within a specific element of
-     * the DOM.
-     *
      * Parameters:
      * domObj - {HTMLElement} an element or id of an element to put the
      * content of the tabs into.
      * options - an options object, only event handlers are supported
      * as options at this time.
      */
-    initialize: function(domObj, options) {
-        this.setOptions(options);
+    parameters: ['domObj','options'],
+
+    /**
+     * APIMethod: init
+     * Create a new instance of <Jx.TabSet> within a specific element of
+     * the DOM.
+     */
+    init: function() {
         this.tabs = [];
-        this.domObj = $(domObj);
+        this.domObj = document.id(this.options.domObj);
         if (!this.domObj.hasClass('jxTabSetContainer')) {
             this.domObj.addClass('jxTabSetContainer');
         }
@@ -16963,18 +38548,18 @@
             this.activeTab.content.resize({forceResize: true});
         }
     },
-    
+
     /**
      * Method: add
-     * Add one or more <Jx.Button.Tab>s to the TabSet.
+     * Add one or more <Jx.Tab>s to the TabSet.
      *
      * Parameters:
      * tab - {<Jx.Tab>} an instance of <Jx.Tab> to add to the tab set.  More
      * than one tab can be added by passing extra parameters to this method.
      */
     add: function() {
-        $A(arguments).each(function(tab) {
-            if (tab instanceof Jx.Button.Tab) {
+        $A(arguments).flatten().each(function(tab) {
+            if (tab instanceof Jx.Tab) {
                 tab.addEvent('down',this.setActiveTabFn);
                 tab.tabSet = this;
                 this.domObj.appendChild(tab.content);
@@ -16996,7 +38581,7 @@
      * tab - {<Jx.Tab>} the tab to remove.
      */
     remove: function(tab) {
-        if (tab instanceof Jx.Button.Tab && this.tabs.indexOf(tab) != -1) {
+        if (tab instanceof Jx.Tab && this.tabs.indexOf(tab) != -1) {
             this.tabs.erase(tab);
             if (this.activeTab == tab) {
                 if (this.tabs.length) {
@@ -17012,7 +38597,7 @@
      * Set the active tab to the one passed to this method
      *
      * Parameters:
-     * tab - {<Jx.Button.Tab>} the tab to make active.
+     * tab - {<Jx.Tab>} the tab to make active.
      */
     setActiveTab: function(tab) {
         if (this.activeTab && this.activeTab != tab) {
@@ -17028,13 +38613,35 @@
 
 
 
-// $Id: tabbox.js 426 2009-05-12 15:29:00Z pagameba $
+/*
+---
+
+name: Jx.TabBox
+
+description: A convenience class to handle the common case of a single toolbar directly attached to the content area of the tabs.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Toolbar
+ - Jx.Panel
+ - Jx.TabSet
+
+provides: [Jx.TabBox]
+
+images:
+ - tabbar.png
+ - tabbar_bottom.png
+ - tabbar_left.png
+ - tabbar_right.png
+
+...
+ */
+// $Id: tabbox.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.TabBox
- * 
- * Extends: Object
  *
- * Implements: Options, Events, <Jx.Addable>
+ * Extends: <Jx.Widget>
  *
  * A convenience class to handle the common case of a single toolbar
  * directly attached to the content area of the tabs.  It manages both a
@@ -17045,21 +38652,21 @@
  * Example:
  * (code)
  * var tabBox = new Jx.TabBox('subTabArea', 'top');
- * 
+ *
  * var tab1 = new Jx.Button.Tab('Tab 1', {contentID: 'content4'});
  * var tab2 = new Jx.Button.Tab('Tab 2', {contentID: 'content5'});
- * 
+ *
  * tabBox.add(tab1, tab2);
  * (end)
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.TabBox = new Class({
     Family: 'Jx.TabBox',
-    Implements: [Options, Events, Jx.Addable],
+    Extends: Jx.Widget,
     options: {
         /* Option: parent
          * a DOM element to add the tab box to
@@ -17086,7 +38693,7 @@
          */
         scroll:true
     },
-    
+
     /**
      * Property: tabBar
      * {<Jx.Toolbar>} the toolbar for this tab box.
@@ -17098,16 +38705,12 @@
      */
     tabSet: null,
     /**
-     * Constructor: Jx.TabBox
+     * APIMethod: render
      * Create a new instance of a TabBox.
-     *
-     * Parameters:
-     * options - <Jx.TabBox.Options>
      */
-    initialize : function(options) {
-        this.setOptions(options);
+    render : function() {
+        this.parent();
         this.tabBar = new Jx.Toolbar({
-            type: 'TabBar', 
             position: this.options.position,
             scroll: this.options.scroll
         });
@@ -17123,12 +38726,13 @@
             this.showItem(tab);
         }.bind(this.tabBar));
         this.domObj = this.panel.domObj;
-        /* when the panel changes size, the tab set needs to update 
+        /* when the panel changes size, the tab set needs to update
          * the content areas.
          */
          this.panel.addEvent('sizeChange', (function() {
              this.tabSet.resizeTabBox();
              this.tabBar.domObj.getParent('.jxBarContainer').retrieve('jxBarContainer').update();
+             this.tabBar.domObj.getParent('.jxBarContainer').addClass('jxTabBar'+this.options.position.capitalize());
          }).bind(this));
         /* when tabs are added or removed, we might need to layout
          * the panel if the toolbar is or becomes empty
@@ -17156,11 +38760,11 @@
      * Parameters:
      * tab - {<Jx.Tab>} an instance of <Jx.Tab> to add to the tab box.  More
      * than one tab can be added by passing extra parameters to this method.
-     * Unlike <Jx.TabSet>, tabs do not have to be added to a separate 
+     * Unlike <Jx.TabSet>, tabs do not have to be added to a separate
      * <Jx.Toolbar>.
      */
-    add : function() { 
-        this.tabBar.add.apply(this.tabBar, arguments); 
+    add : function() {
+        this.tabBar.add.apply(this.tabBar, arguments);
         this.tabSet.add.apply(this.tabSet, arguments);
         $A(arguments).flatten().each(function(tab){
             tab.addEvents({
@@ -17184,465 +38788,475 @@
         this.tabSet.remove(tab);
     }
 });
-// $Id: container.js 424 2009-05-12 12:51:44Z pagameba $
+/*
+---
+
+name: Jx.Toolbar.Separator
+
+description:  A helper class that represents a visual separator in a Jx.Toolbar.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Toolbar
+
+provides: [Jx.Toolbar.Separator]
+
+images:
+ - toolbar_separator_h.png
+ - toolbar_separator_v.png
+
+...
+ */
+// $Id: toolbar.separator.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.Toolbar.Container
+ * Class: Jx.Toolbar.Separator
  *
- * Extends: Object
+ * Extends: <Jx.Object>
  *
- * Implements: Options, Events, <Jx.Addable>
+ * A helper class that represents a visual separator in a <Jx.Toolbar>
  *
- * A toolbar container contains toolbars.  A single toolbar container fills
- * the available space horizontally.  Toolbars placed in a toolbar container
- * do not wrap when they exceed the available space.
+ * Example:
+ * (code)
+ * (end)
  *
- * Events:
- * add - fired when one or more toolbars are added to a container
- * remove - fired when one or more toolbars are removed from a container
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
  *
- * Implements: 
- * Options
- * Events
- * {<Jx.Addable>}
+ * This file is licensed under an MIT style license
+ */
+Jx.Toolbar.Separator = new Class({
+    Family: 'Jx.Toolbar.Separator',
+    Extends: Jx.Widget,
+    /**
+     * APIMethod: render
+     * Create a new Jx.Toolbar.Separator
+     */
+    render: function() {
+        this.domObj = new Element('li', {'class':'jxToolItem'});
+        this.domSpan = new Element('span', {'class':'jxBarSeparator'});
+        this.domObj.appendChild(this.domSpan);
+    }
+});
+/*
+---
+
+name: Jx.Tree
+
+description: Jx.Tree displays hierarchical data in a tree structure of folders and nodes.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+ - Jx.List
+
+provides: [Jx.Tree]
+
+css:
+ - tree
+
+images:
+ - tree.png
+ - tree_vert_line.png
+...
+ */
+// $Id: tree.js 990 2010-10-07 17:22:02Z pagameba $
+/**
+ * Class: Jx.Tree
  *
- * License: 
+ * Jx.Tree displays hierarchical data in a tree structure of folders and nodes.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * Extends: <Jx.Widget>
+ *
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
-Jx.Toolbar.Container = new Class({
-    Family: 'Jx.Toolbar.Container',
-    Implements: [Options,Events, Jx.Addable],
+Jx.Tree = new Class({
+    Family: 'Jx.Tree',
+    Extends: Jx.Widget,
+    parameters: ['options','container', 'selection'],
+    pluginNamespace: 'Tree',
     /**
-     * Property: domObj
-     * {HTMLElement} the HTML element that the container lives in
+     * APIProperty: selection
+     * {<Jx.Selection>} the selection object for this tree.
      */
-    domObj : null,
+    selection: null,
+    /**
+     * Property: ownsSelection
+     * {Boolean} indicates if this object created the <Jx.Selection> object
+     * or not.  If true then the selection object will be destroyed when the
+     * tree is destroyed, otherwise the selection object will not be
+     * destroyed.
+     */
+    ownsSelection: false,
+    /**
+     * Property: list
+     * {<Jx.List>} the list object is used to manage the DOM elements of the
+     * items added to the tree.
+     */
+    list: null,
+    dirty: true,
+    /**
+     * APIProperty: domObj
+     * {HTMLElement} the DOM element that contains the visual representation
+     * of the tree.
+     */
+    domObj: null,
     options: {
-        /* Option: parent
-         * a DOM element to add this to
+        /**
+         * Option: select
+         * {Boolean} are items in the tree selectable?  See <Jx.Selection>
+         * for other options relating to selections that can be set here.
          */
-        parent: null,
-        /* Option: position
-         * the position of the toolbar container in its parent, one of 'top',
-         * 'right', 'bottom', or 'left'.  Default is 'top'
+        select: true,
+        /**
+         * Option: template
+         * the default HTML template for a tree can be overridden
          */
-        position: 'top',
-        /* Option: autoSize
-         * automatically size the toolbar container to fill its container.
-         * Default is false
-         */
-        autoSize: false,
-        /* Option: scroll
-         * Control whether the user can scroll of the content of the
-         * container if the content exceeds the size of the container.  
-         * Default is true.
-         */
-        scroll: true
+        template: '<ul class="jxTreeRoot"></ul>'
     },
     /**
-     * Constructor: Jx.Toolbar.Container
-     * Create a new instance of Jx.Toolbar.Container
-     *
-     * Parameters:
-     * options - <Jx.Toolbar.Options>
+     * APIProperty: classes
+     * {Hash} a hash of property to CSS class names for extracting references
+     * to DOM elements from the supplied templates.  Requires
+     * domObj element, anything else is optional.
      */
-    initialize : function(options) {
-        this.setOptions(options);
-        
-        var d = $(this.options.parent);
-        this.domObj = d || new Element('div');
-        this.domObj.addClass('jxBarContainer');
-        
-        if (this.options.scroll) {
-            this.scroller = new Element('div', {'class':'jxBarScroller'});
-            this.domObj.adopt(this.scroller);
+    classes: new Hash({domObj: 'jxTreeRoot'}),
+    
+    frozen: false,
+    
+    /**
+     * APIMethod: render
+     * Render the Jx.Tree.
+     */
+    render: function() {
+        this.parent();
+        if ($defined(this.options.container) &&
+            document.id(this.options.container)) {
+            this.domObj = this.options.container;
         }
 
-        /* this allows toolbars to add themselves to this bar container
-         * once it already exists without requiring an explicit reference
-         * to the toolbar container
-         */
-        this.domObj.store('jxBarContainer', this);
-        
-        if (['top','right','bottom','left'].contains(this.options.position)) {
-            this.domObj.addClass('jxBar' +
-                           this.options.position.capitalize());            
-        } else {
-            this.domObj.addClass('jxBarTop');
-            this.options.position = 'top';
+        if (this.options.selection) {
+            this.selection = this.options.selection;
+        } else if (this.options.select) {
+            this.selection = new Jx.Selection(this.options);
+            this.ownsSelection = true;
         }
 
-        if (this.options.scroll && ['top','bottom'].contains(this.options.position)) {
-            // make sure we update our size when we get added to the DOM
-            this.addEvent('addTo', this.update.bind(this));
-            
-            //making Fx.Tween optional
-            if (typeof Fx != 'undefined' && typeof Fx.Tween != 'undefined'){
-                this.scrollFx = scrollFx = new Fx.Tween(this.scroller, {
-                    link: 'chain'
-                });
-            }
+        this.bound.select = function(item) {
+            this.fireEvent('select', item.retrieve('jxTreeItem'));
+        }.bind(this);
+        this.bound.unselect = function(item) {
+            this.fireEvent('unselect', item.retrieve('jxTreeItem'));
+        }.bind(this);
+        this.bound.onAdd = function(item) {this.update();}.bind(this);
+        this.bound.onRemove = function(item) {this.update();}.bind(this);
 
-            this.scrollLeft = new Jx.Button({
-                image: Jx.aPixel.src
-            }).addTo(this.domObj);
-            this.scrollLeft.domObj.addClass('jxBarScrollLeft');
-            this.scrollLeft.addEvents({
-               click: (function(){
-                   var from = 0;
-                   var leftStyle = this.scroller.getStyle('left');
-                   if(leftStyle)
-                   {
-                       from = leftStyle.toInt();
-                   }
-                   if (isNaN(from)) { from = 0; }
-                   var to = Math.min(from+100, 0);
-                   if (to >= 0) {
-                       this.scrollLeft.domObj.setStyle('visibility', 'hidden');
-                   }
-                   this.scrollRight.domObj.setStyle('visibility', '');
-                   if ($defined(this.scrollFx)){
-                       this.scrollFx.start('left', from, to);
-                   } else {
-                       this.scroller.setStyle('left',to);
-                   }
-               }).bind(this)
+        if (this.selection && this.ownsSelection) {
+            this.selection.addEvents({
+                select: this.bound.select,
+                unselect: this.bound.unselect
             });
-            
-            this.scrollRight = new Jx.Button({
-                image: Jx.aPixel.src
-            }).addTo(this.domObj);
-            this.scrollRight.domObj.addClass('jxBarScrollRight');
-            this.scrollRight.addEvents({
-                click: (function(){
-                   var from = 0;
-                   var leftStyle = this.scroller.getStyle('left');
-                   if(leftStyle)
-                   {
-                       from = leftStyle.toInt();
-                   }
-                   if (isNaN(from)) { from = 0; }
-                   var to = Math.max(from - 100, this.scrollWidth);
-                   if (to == this.scrollWidth) {
-                       this.scrollRight.domObj.setStyle('visibility', 'hidden');
-                   }
-                   this.scrollLeft.domObj.setStyle('visibility', '');
-                   if ($defined(this.scrollFx)){
-                       this.scrollFx.start('left', from, to);
-                   } else {
-                       this.scroller.setStyle('left',to);
-                   }
-               }).bind(this)
-            });         
-            
-        } else {
-            this.options.scroll = false;
         }
 
-        if (this.options.toolbars) {
-            this.add(this.options.toolbars);
+        this.list = new Jx.List(this.domObj, {
+                hover: true,
+                press: true,
+                select: true,
+                onAdd: this.bound.onAdd,
+                onRemove: this.bound.onRemove
+            }, this.selection);
+        if (this.options.parent) {
+            this.addTo(this.options.parent);
         }
     },
+    /**
+     * APIMethod: freeze
+     * stop the tree from processing updates every time something is added or
+     * removed.  Used for bulk changes, call thaw() when done updating.  Note
+     * the tree will still display the changes but it will delay potentially
+     * expensive recursion across the entire tree on every change just to
+     * update visual styles.
+     */
+    freeze: function() { this.frozen = true; },
+    /**
+     * APIMethod: thaw
+     * unfreeze the tree and recursively update styles
+     */
+    thaw: function() { this.frozen = false; this.update(true); },
     
-    update: function() {
-        if (this.options.autoSize) {
-            /* delay the size update a very small amount so it happens
-             * after the current thread of execution finishes.  If the
-             * current thread is part of a window load event handler,
-             * rendering hasn't quite finished yet and the sizes are
-             * all wrong
-             */
-            (function(){
-                var x = 0;
-                this.scroller.getChildren().each(function(child){
-                    x+= child.getSize().x;
-                });
-                this.domObj.setStyles({width:x});
-                this.measure();
-            }).delay(1,this);
-        } else {
-            this.measure();
-        }
+    setDirty: function(state) {
+      this.dirty = state;
+      if (state && this.owner && this.owner.setDirty) {
+        this.owner.setDirty(state);
+      }
     },
-    
-    measure: function() {
-        if ((!this.scrollLeftSize || !this.scrollLeftSize.x) && this.domObj.parentNode) {
-            this.scrollLeftSize = this.scrollLeft.domObj.getSize();
-            this.scrollRightSize = this.scrollRight.domObj.getSize();
-        }
-        /* decide if we need to show the scroller buttons and
-         * do some calculations that will make it faster
-         */
-        this.scrollWidth = this.domObj.getSize().x;
-        this.scroller.getChildren().each(function(child){
-            this.scrollWidth -= child.getSize().x;
-        }, this);
-        if (this.scrollWidth < 0) {
-            /* we need to show scrollers on at least one side */
-            var l = 0;
-            var leftStyle = this.scroller.getStyle('left');
-            if(leftStyle)
-            {
-                l = leftStyle.toInt();
-            }
-            if (l < 0) {
-                this.scrollLeft.domObj.setStyle('visibility','');
-            } else {
-                this.scrollLeft.domObj.setStyle('visibility','hidden');
-            }
-            if (l <= this.scrollWidth) {
-                this.scrollRight.domObj.setStyle('visibility', 'hidden');
-                if (l < this.scrollWidth) {
-                    if ($defined(this.scrollFx)){
-                        this.scrollFx.start('left', l, this.scrollWidth);
-                    } else {
-                        this.scroller.setStyle('left',this.scrollWidth);
-                    }
-                }
-            } else {
-                this.scrollRight.domObj.setStyle('visibility', '');                
-            }
-            
-        } else {
-            /* don't need any scrollers but we might need to scroll
-             * the toolbar into view
-             */
-            this.scrollLeft.domObj.setStyle('visibility','hidden');
-            this.scrollRight.domObj.setStyle('visibility','hidden');
-            var from = 0;
-            var leftStyle = this.scroller.getStyle('left');
-            if(leftStyle)
-            {
-               from = leftStyle.toInt();
-            }            
-            if (!isNaN(from) && from !== 0) {
-                if ($defined(this.scrollFx)) {
-                    this.scrollFx.start('left', 0);
-                } else {
-                    this.scroller.setStyle('left',0);
-                }
-            }
-        }            
-    },
-    
+
     /**
-     * Method: add
-     * Add a toolbar to the container.
+     * APIMethod: add
+     * add one or more items to the tree at a particular position in the tree
      *
      * Parameters:
-     * toolbar - {Object} the toolbar to add.  More than one toolbar
-     *    can be added by passing multiple arguments.
+     * item - {<Jx.TreeItem>} or an array of items to be added
+     * position - {mixed} optional location to add the items.  By default,
+     * this is 'bottom' meaning the items are added at the end of the list.
+     * See <Jx.List::add> for options
+     *
+     * Returns:
+     * {<Jx.Tree>} a reference to this object for chaining calls
      */
-    add: function( ) {
-        $A(arguments).flatten().each(function(thing) {
-            if (this.options.scroll) {
-                /* we potentially need to show or hide scroller buttons
-                 * when the toolbar contents change
-                 */
-                thing.addEvent('add', this.update.bind(this));
-                thing.addEvent('remove', this.update.bind(this));                
-                thing.addEvent('show', this.scrollIntoView.bind(this));                
-            }
-            if (this.scroller) {
-                this.scroller.adopt(thing.domObj);
-            } else {
-                this.domObj.adopt(thing.domObj);
-            }
-            this.domObj.addClass('jx'+thing.options.type+this.options.position.capitalize());
-        }, this);
-        if (this.options.scroll) {
-            this.update();            
+    add: function(item, position) {
+        if ($type(item) == 'array') {
+            item.each(function(what){ this.add(what, position); }.bind(this) );
+            return;
         }
-        if (arguments.length > 0) {
-            this.fireEvent('add', this);
-        }
+        item.addEvents({
+            add: function(what) { this.fireEvent('add', what).bind(this); },
+            remove: function(what) { this.fireEvent('remove', what).bind(this); },
+            disclose: function(what) { this.fireEvent('disclose', what).bind(this); }
+        });
+        item.setSelection(this.selection);
+        item.owner = this;
+        this.list.add(item, position);
+        this.setDirty(true);
         return this;
     },
     /**
-     * Method: remove
-     * remove an item from a toolbar.  If the item is not in this toolbar
-     * nothing happens
+     * APIMethod: remove
+     * remove an item from the tree
      *
      * Parameters:
-     * item - {Object} the object to remove
+     * item - {<Jx.TreeItem>} the tree item to remove
      *
      * Returns:
-     * {Object} the item that was removed, or null if the item was not
-     * removed.
+     * {<Jx.Tree>} a reference to this object for chaining calls
      */
     remove: function(item) {
-        
+        item.removeEvents('add');
+        item.removeEvents('remove');
+        item.removeEvents('disclose');
+        item.owner = null;
+        this.list.remove(item);
+        item.setSelection(null);
+        this.setDirty(true);
+        return this;
     },
     /**
-     * Method: scrollIntoView
-     * scrolls an item in one of the toolbars into the currently visible
-     * area of the container if it is not already fully visible
+     * APIMethod: replace
+     * replaces one item with another
      *
      * Parameters:
-     * item - the item to scroll.
+     * item - {<Jx.TreeItem>} the tree item to remove
+     * withItem - {<Jx.TreeItem>} the tree item to insert
+     *
+     * Returns:
+     * {<Jx.Tree>} a reference to this object for chaining calls
      */
-    scrollIntoView: function(item) {
-        var width = this.domObj.getSize().x;
-        var coords = item.domObj.getCoordinates(this.scroller);
-        
-        //left may be set to auto or even a zero length string. 
-        //In the previous version, in air, this would evaluate to
-        //NaN which would cause the right hand scroller to show when 
-        //the component was first created.
-        
-        //So, get the left value first
-        var l = 0;
-        var leftStyle = this.scroller.getStyle('left');
-        if(leftStyle)
-        {
-            l = leftStyle.toInt();
+    replace: function(item, withItem) {
+        item.owner = null;
+        withItem.owner = this;
+        this.list.replace(item, withItem);
+        withItem.setSelection(this.selection);
+        item.setSelection(null);
+        this.setDirty(true);
+        return this;
+    },
+
+    /**
+     * Method: cleanup
+     * Clean up a Jx.Tree instance
+     */
+    cleanup: function() {
+        // stop updates when removing existing items.
+        this.freeze();
+        if (this.selection && this.ownsSelection) {
+            this.selection.removeEvents({
+                select: this.bound.select,
+                unselect: this.bound.unselect
+            });
+            this.selection.destroy();
+            this.selection = null;
         }
-        //then check to see if it's auto or a zero length string 
-        if (l === 'auto' || l.length <= 0) {
-            //If so, set to 0.
-            l = 0;
-        } else {
-            //otherwise, convert to int
-            l = l.toInt();
-        }
-        var slSize = this.scrollLeftSize ? this.scrollLeftSize.x : 0;
-        var srSize = this.scrollRightSize ? this.scrollRightSize.x : 0;
+        this.list.removeEvents({
+          remove: this.bound.onRemove,
+          add: this.bound.onAdd
+        });
+        this.list.destroy();
+        this.list = null;
+        this.bound.select = null;
+        this.bound.unselect = null;
+        this.bound.onRemove = null;
+        this.bound.onAdd = null;
+        this.parent();
+    },
+    
+    /**
+     * Method: update
+     * Update the CSS of the Tree's DOM element in case it has changed
+     * position
+     *
+     * Parameters:
+     * shouldDescend - {Boolean} propagate changes to child nodes?
+     */
+    update: function(shouldDescend, isLast) {
+        // since the memory leak cleanup, it seems that update gets called
+        // from the bound onRemove event after the list has been cleaned
+        // up.  I suspect that there is a delayed function call for IE in
+        // event handling (or some such thing) PS
+        if (!this.list) return;
+        if (this.frozen) return;
         
-        var left = l;
-        if (l < -coords.left + slSize) {
-            /* the left edge of the item is not visible */
-            left = -coords.left + slSize;
-            if (left >= 0) {
-                left = 0;
+        if ($defined(isLast)) {
+            if (isLast) {
+                this.domObj.removeClass('jxTreeNest');
+            } else {
+                this.domObj.addClass('jxTreeNest');
             }
-        } else if (width - coords.right - srSize< l) {
-            /* the right edge of the item is not visible */
-            left =  width - coords.right - srSize;
-            if (left < this.scrollWidth) {
-                left = this.scrollWidth;
-            }
         }
-                
-        if (left < 0) {
-            this.scrollLeft.domObj.setStyle('visibility','');                
-        } else {
-            this.scrollLeft.domObj.setStyle('visibility','hidden');
-        }
-        if (left <= this.scrollWidth) {
-            this.scrollRight.domObj.setStyle('visibility', 'hidden');
-        } else {
-            this.scrollRight.domObj.setStyle('visibility', '');                
-        }
-        if (left != l) {
-            if ($defined(this.scrollFx)) {
-                this.scrollFx.start('left', left);
-            } else {
-                this.scroller.setStyle('left',left);
+        var last = this.list.count() - 1;
+        this.list.each(function(item, idx){
+            var lastItem = idx == last;
+            if (item.retrieve('jxTreeFolder')) {
+                item.retrieve('jxTreeFolder').update(shouldDescend, lastItem);
             }
-        }
-    }
-});
-// $Id: toolbar.item.js 424 2009-05-12 12:51:44Z pagameba $
-/**
- * Class: Jx.Toolbar.Item
- * 
- * Extends: Object
- *
- * Implements: Options
- *
- * A helper class to provide a container for something to go into 
- * a <Jx.Toolbar>.
- *
- * License: 
- * Copyright (c) 2008, DM Solutions Group Inc.
- * 
- * This file is licensed under an MIT style license
- */
-Jx.Toolbar.Item = new Class( {
-    Family: 'Jx.Toolbar.Item',
-    Implements: [Options],
-    options: {
-        /* Option: active
-         * is this item active or not?  Default is true.
-         */
-        active: true
+            if (item.retrieve('jxTreeItem')) {
+                item.retrieve('jxTreeItem').update(lastItem);
+            }
+        });
+        this.setDirty(false);
     },
+
     /**
-     * Property: domObj
-     * {HTMLElement} an element to contain the thing to be placed in the
-     * toolbar.
+     * APIMethod: items
+     * return an array of tree item instances contained in this tree.
+     * Does not descend into folders but does return a reference to the
+     * folders
      */
-    domObj: null,
+    items: function() {
+        return this.list.items().map(function(item) {
+            return item.retrieve('jxTreeItem');
+        });
+    },
     /**
-     * Constructor: Jx.Toolbar.Item
-     * Create a new instance of Jx.Toolbar.Item.
+     * APIMethod: empty
+     * recursively empty this tree and any folders in it
+     */
+    empty: function() {
+        this.list.items().each(function(item){
+          var f = item.retrieve('jxTreeItem');
+          if (f && f instanceof Jx.TreeFolder) {
+            f.empty();
+          }
+          if (f && f instanceof Jx.TreeItem) {
+            this.remove(f);
+            f.destroy();
+          }
+        }, this);
+        this.setDirty(true);
+    },
+
+    /**
+     * APIMethod: findChild
+     * Get a reference to a child node by recursively searching the tree
      *
      * Parameters:
-     * jxThing - {Object} the thing to be contained.
+     * path - {Array} an array of labels of nodes to search for
+     *
+     * Returns:
+     * {Object} the node or null if the path was not found
      */
-    initialize : function( jxThing ) {
-        this.al = [];
-        this.domObj = new Element('li', {'class':'jxToolItem'});
-        if (jxThing) {
-            if (jxThing.domObj) {
-                this.domObj.appendChild(jxThing.domObj);
-                if (jxThing instanceof Jx.Button.Tab) {
-                    this.domObj.addClass('jxTabItem');
+    findChild : function(path) {
+        //path is empty - we are asking for this node
+        if (path.length == 0) {
+            return false;
+        }
+        //path has more than one thing in it, find a folder and descend into it
+        var name = path[0];
+        var result = false;
+        this.list.items().some(function(item) {
+            var treeItem = item.retrieve('jxTreeItem');
+            if (treeItem && treeItem.getLabel() == name) {
+                if (path.length > 0) {
+                    var folder = item.retrieve('jxTreeFolder');
+                    if (folder && path.length > 1) {
+                        result = folder.findChild(path.slice(1, path.length));
+                    } else {
+                      result = treeItem;
+                    }
+                } else {
+                    result = treeItem;
                 }
-            } else {
-                this.domObj.appendChild(jxThing);
-                if (jxThing.hasClass('jxTab')) {
-                    this.domObj.addClass('jxTabItem');
-                }
             }
-        }
-    }
-});// $Id: toolbar.separator.js 424 2009-05-12 12:51:44Z pagameba $
-/**
- * Class: Jx.Toolbar.Separator
- *
- * Extends: Object
- *
- * A helper class that represents a visual separator in a <Jx.Toolbar>
- *
- * Example:
- * (code)
- * (end)
- *
- * License: 
- * Copyright (c) 2008, DM Solutions Group Inc.
- * 
- * This file is licensed under an MIT style license
- */
-Jx.Toolbar.Separator = new Class({
-    Family: 'Jx.Toolbar.Separator',
+            return result;
+        });
+        return result;
+    },
+    
     /**
-     * Property: domObj
-     * {HTMLElement} The DOM element that goes in the <Jx.Toolbar>
+     * APIMethod: setSelection
+     * sets the <Jx.Selection> object to be used by this tree.  Used primarily
+     * by <Jx.TreeFolder> to propogate a single selection object throughout a
+     * tree.
+     *
+     * Parameters:
+     * selection - {<Jx.Selection>} the new selection object to use
+     *
+     * Returns:
+     * {<Jx.Tree>} a reference to this object for chaining
      */
-    domObj: null,
-    /**
-     * Constructor: Jx.Toolbar.Separator
-     * Create a new Jx.Toolbar.Separator
-     */
-    initialize: function() {
-        this.domObj = new Element('li', {'class':'jxToolItem'});
-        this.domSpan = new Element('span', {'class':'jxBarSeparator'});
-        this.domObj.appendChild(this.domSpan);
+    setSelection: function(selection) {
+        if (this.selection && this.ownsSelection) {
+            this.selection.removeEvents(this.bound);
+            this.selection.destroy();
+            this.ownsSelection = false;
+        }
+        this.selection = selection;
+        this.list.setSelection(selection);
+        this.list.each(function(item) {
+            item.retrieve('jxTreeItem').setSelection(selection);
+        });
+        return this;
     }
 });
-// $Id: treeitem.js 424 2009-05-12 12:51:44Z pagameba $
+
+/*
+---
+
+name: Jx.TreeItem
+
+description: An item in a tree.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+
+optional:
+ - More/Drag
+
+provides: [Jx.TreeItem]
+
+images:
+ - tree_hover.png
+
+...
+ */
+// $Id: treeitem.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.TreeItem 
+ * Class: Jx.TreeItem
  *
- * Extends: Object
+ * Extends: <Jx.Widget>
  *
- * Implements: Options, Events
- *
  * An item in a tree.  An item is a leaf node that has no children.
  *
- * Jx.TreeItem supports selection via the click event.  The application 
+ * Jx.TreeItem supports selection via the click event.  The application
  * is responsible for changing the style of the selected item in the tree
  * and for tracking selection if that is important.
  *
@@ -17657,14 +39271,15 @@
  * Events - MooTools Class.Extras
  * Options - MooTools Class.Extras
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.TreeItem = new Class ({
     Family: 'Jx.TreeItem',
-    Implements: [Options,Events],
+    Extends: Jx.Widget,
+    selection: null,
     /**
      * Property: domObj
      * {HTMLElement} a reference to the HTML element that is the TreeItem
@@ -17679,23 +39294,19 @@
     options: {
         /* Option: label
          * {String} the label to display for the TreeItem
-         */        
+         */
         label: '',
-        /* Option: data
-         * {Object} any arbitrary data to be associated with the TreeItem
-         */
-        data: null,
         /* Option: contextMenu
          * {<Jx.ContextMenu>} the context menu to trigger if there
          * is a right click on the node
          */
         contextMenu: null,
         /* Option: enabled
-         * {Boolean} the initial state of the TreeItem.  If the 
+         * {Boolean} the initial state of the TreeItem.  If the
          * TreeItem is not enabled, it cannot be clicked.
          */
         enabled: true,
-        type: 'Item',
+        selectable: true,
         /* Option: image
          * {String} URL to an image to use as the icon next to the
          * label of this TreeItem
@@ -17705,179 +39316,194 @@
          * {String} CSS class to apply to the image, useful for using CSS
          * sprites
          */
-        imageClass: ''
+        imageClass: '',
+        lastLeafClass: 'jxTreeLeafLast',
+        template: '<li class="jxTreeContainer jxTreeLeaf"><img class="jxTreeImage" src="'+Jx.aPixel.src+'" alt="" title=""><a class="jxTreeItem" href="javascript:void(0);"><img class="jxTreeIcon" src="'+Jx.aPixel.src+'" alt="" title=""><span class="jxTreeLabel"></span></a></li>',
+        busyMask: {
+          message: null
+        }
     },
+    classes: new Hash({
+        domObj: 'jxTreeContainer',
+        domA: 'jxTreeItem',
+        domImg: 'jxTreeImage',
+        domIcon: 'jxTreeIcon',
+        domLabel: 'jxTreeLabel'
+    }),
+
     /**
-     * Constructor: Jx.TreeItem
+     * APIMethod: render
      * Create a new instance of Jx.TreeItem with the associated options
-     *
-     * Parameters:
-     * options - <Jx.TreeItem.Options>
      */
-    initialize : function( options ) {
-        this.setOptions(options);
+    render : function() {
+        this.parent();
 
-        this.domObj = new Element('li', {'class':'jxTree'+this.options.type});
+        this.domObj = this.elements.get('jxTreeContainer');
+        this.domObj.store('jxTreeItem', this);
+        this.domA.store('jxTreeItem', this);
+        if (this.options.contextMenu) {
+          this.domA.store('jxContextMenu', this.options.contextMenu);
+        }
+        /* the target for jxPressed, jxSelected, jxHover classes */
+        this.domObj.store('jxListTarget', this.domA);
+
+        if (!this.options.selectable) {
+            this.domObj.addClass('jxUnselectable');
+        }
+
         if (this.options.id) {
             this.domObj.id = this.options.id;
         }
-      
-        this.domNode = new Element('img',{
-            'class': 'jxTreeImage', 
-            src: Jx.aPixel.src,
-            alt: '',
-            title: ''
-        });
-        this.domObj.appendChild(this.domNode);
-        
-        this.domLabel = (this.options.draw) ? 
-            this.options.draw.apply(this) : 
-            this.draw();
-
-        this.domObj.appendChild(this.domLabel);
-        this.domObj.store('jxTreeItem', this);
-        
         if (!this.options.enabled) {
             this.domObj.addClass('jxDisabled');
         }
-    },
-    draw: function() {
-        var domImg = new Element('img',{
-            'class':'jxTreeIcon', 
-            src: Jx.aPixel.src,
-            alt: '',
-            title: ''
-        });
-        if (this.options.image) {
-            domImg.setStyle('backgroundImage', 'url('+this.options.image+')');
+
+        if (this.options.image && this.domIcon) {
+            this.domIcon.setStyle('backgroundImage', 'url('+this.options.image+')');
+            if (this.options.imageClass) {
+                this.domIcon.addClass(this.options.imageClass);
+            }
+
         }
-        if (this.options.imageClass) {
-            domImg.addClass(this.options.imageClass);
+
+        if (this.options.label && this.domLabel) {
+            this.setLabel(this.options.label);
         }
-        // the clickable part of the button
-        var hasFocus;
-        var mouseDown;
-        
-        var domA = new Element('a',{
-            href:'javascript:void(0)',
-            html: this.options.label
-        });
-        domA.addEvents({
-            click: this.selected.bind(this),
-            dblclick: this.selected.bind(this),
-            drag: function(e) {e.stop();},
-            contextmenu: function(e) { e.stop(); },
-            mousedown: (function(e) {
-               domA.addClass('jxTreeItemPressed');
-               hasFocus = true;
-               mouseDown = true;
-               domA.focus();
-               if (e.rightClick && this.options.contextMenu) {
-                   this.options.contextMenu.show(e);
-               }
-            }).bind(this),
-            mouseup: function(e) {
-                domA.removeClass('jxTreeItemPressed');
-                mouseDown = false;
-            },
-            mouseleave: function(e) {
-                domA.removeClass('jxTreeItemPressed');
-            },
-            mouseenter: function(e) {
-                if (hasFocus && mouseDown) {
-                    domA.addClass('jxTreeItemPressed');
-                }
-            },
-            keydown: function(e) {
-                if (e.key == 'enter') {
-                    domA.addClass('jxTreeItemPressed');
-                }
-            },
-            keyup: function(e) {
-                if (e.key == 'enter') {
-                    domA.removeClass('jxTreeItemPressed');
-                }
-            },
-            blur: function() { hasFocus = false; }
-        });
-        domA.appendChild(domImg);
-        if (typeof Drag != 'undefined') {
-            new Drag(domA, {
-                onStart: function() {this.stop();}
+
+        if (this.domA) {
+            this.domA.addEvents({
+                click: this.click.bind(this),
+                dblclick: this.dblclick.bind(this),
+                drag: function(e) { e.stop(); }
             });
+            if (typeof Drag != 'undefined') {
+                new Drag(this.domA, {
+                    onStart: function() {this.stop();}
+                });
+            }
         }
-        return domA;
+
+        if ($defined(this.options.enabled)) {
+            this.enable(this.options.enabled, true);
+        }
     },
+    
+    setDirty: function(state) {
+      if (state && this.owner && this.owner.setDirty) {
+        this.owner.setDirty(state);
+      }
+    },
+    
     /**
      * Method: finalize
      * Clean up the TreeItem and remove all DOM references
      */
-    finalize: function() { this.finalizeItem(); },
+    finalize: function() { this.destroy(); },
     /**
      * Method: finalizeItem
      * Clean up the TreeItem and remove all DOM references
      */
-    finalizeItem: function() {  
-        if (!this.domObj) {
-            return;
-        }
-        //this.domA.removeEvents();
-        this.options = null;
-        this.domObj.dispose();
-        this.domObj = null;
-        this.owner = null;
+    cleanup: function() {
+      this.domObj.eliminate('jxTreeItem');
+      this.domA.eliminate('jxTreeItem');
+      this.domA.eliminate('jxContextMenu');
+      this.domObj.eliminate('jxListTarget');
+      this.domObj.eliminate('jxListTargetItem');
+      this.domA.removeEvents();
+      this.owner = null;
+      this.selection = null;
+      this.parent();
     },
     /**
-     * Method: clone
-     * Create a clone of the TreeItem
-     * 
-     * Returns: 
-     * {<Jx.TreeItem>} a copy of the TreeItem
-     */
-    clone : function() {
-        return new Jx.TreeItem(this.options);
-    },
-    /**
      * Method: update
      * Update the CSS of the TreeItem's DOM element in case it has changed
      * position
      *
      * Parameters:
-     * shouldDescend - {Boolean} propagate changes to child nodes?
+     * isLast - {Boolean} is the item the last one or not?
      */
-    update : function(shouldDescend) {
-        var isLast = (arguments.length > 1) ? arguments[1] : 
-                     (this.owner && this.owner.isLastNode(this));
+    update : function(isLast) {
         if (isLast) {
-            this.domObj.removeClass('jxTree'+this.options.type);
-            this.domObj.addClass('jxTree'+this.options.type+'Last');
+            this.domObj.addClass(this.options.lastLeafClass);
         } else {
-            this.domObj.removeClass('jxTree'+this.options.type+'Last');
-            this.domObj.addClass('jxTree'+this.options.type);
+            this.domObj.removeClass(this.options.lastLeafClass);
         }
     },
+    click: function() {
+        if (this.options.enabled) {
+            this.fireEvent('click', this);
+        }
+    },
+    dblclick: function() {
+        if (this.options.enabled) {
+            this.fireEvent('dblclick', this);
+        }
+    },
     /**
-     * Method: selected
-     * Called when the DOM element for the TreeItem is clicked, the
-     * node is selected.
-     *
-     * Parameters:
-     * e - {Event} the DOM event
+     * Method: select
+     * Select a tree node.
      */
-    selected : function(e) {
-        this.fireEvent('click', this);
+    select: function() {
+        if (this.selection && this.options.enabled) {
+            this.selection.select(this.domA);
+        }
     },
+
     /**
-     * Method: getName
+     * Method: getLabel
      * Get the label associated with a TreeItem
      *
-     * Returns: 
+     * Returns:
      * {String} the name
      */
-    getName : function() { return this.options.label; },
+    getLabel: function() {
+        return this.options.label;
+    },
+
     /**
+     * Method: setLabel
+     * set the label of a tree item
+     */
+    setLabel: function(label) {
+        this.options.label = label;
+        if (this.domLabel) {
+            this.domLabel.set('html',this.getText(label));
+            this.setDirty(true);
+        }
+    },
+
+    setImage: function(url, imageClass) {
+        if (this.domIcon && $defined(url)) {
+            this.options.image = url;
+            this.domIcon.setStyle('backgroundImage', 'url('+this.options.image+')');
+            this.setDirty(true);
+        }
+        if (this.domIcon && $defined(imageClass)) {
+            this.domIcon.removeClass(this.options.imageClass);
+            this.options.imageClass = imageClass;
+            this.domIcon.addClass(imageClass);
+            this.setDirty(true);
+        }
+    },
+    enable: function(state, force) {
+        if (this.options.enabled != state || force) {
+            this.options.enabled = state;
+            if (this.options.enabled) {
+                this.domObj.removeClass('jxDisabled');
+                this.fireEvent('enabled', this);
+            } else {
+                this.domObj.addClass('jxDisabled');
+                this.fireEvent('disabled', this);
+                if (this.selection) {
+                    this.selection.unselect(document.id(this));
+                }
+            }
+        }
+    },
+
+    /**
      * Method: propertyChanged
-     * A property of an object has changed, synchronize the state of the 
+     * A property of an object has changed, synchronize the state of the
      * TreeItem with the state of the object
      *
      * Parameters:
@@ -17890,13 +39516,58 @@
         } else {
             this.domObj.addClass('jxDisabled');
         }
+    },
+    setSelection: function(selection){
+        this.selection = selection;
+    },
+    
+    /**
+     * APIMethod: setBusy
+     * set the busy state of the widget
+     *
+     * Parameters:
+     * busy - {Boolean} true to set the widget as busy, false to set it as
+     *    idle.
+     */
+    setBusy: function(state) {
+      if (this.busy == state) {
+        return;
+      }
+      this.busy = state;
+      this.fireEvent('busy', this.busy);
+      if (this.busy) {
+        this.domImg.addClass(this.options.busyClass)
+      } else {
+        if (this.options.busyClass) {
+          this.domImg.removeClass(this.options.busyClass);
+        }
+      }
+    },
+    changeText : function(lang) {
+      this.parent();
+      this.setLabel(this.options.label);
     }
 });
-// $Id: treefolder.js 424 2009-05-12 12:51:44Z pagameba $
+/*
+---
+
+name: Jx.TreeFolder
+
+description: A Jx.TreeFolder is an item in a tree that can contain other items. It is expandable and collapsible.
+
+license: MIT-style license.
+
+requires:
+ - Jx.TreeItem
+ - Jx.Tree
+
+provides: [Jx.TreeFolder]
+
+...
+ */
+// $Id: treefolder.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
  * Class: Jx.TreeFolder
- * 
- * Extends: <Jx.TreeItem>
  *
  * A Jx.TreeFolder is an item in a tree that can contain other items.  It is
  * expandable and collapsible.
@@ -17908,421 +39579,2838 @@
  * Extends:
  * <Jx.TreeItem>
  *
- * License: 
+ * License:
  * Copyright (c) 2008, DM Solutions Group Inc.
- * 
+ *
  * This file is licensed under an MIT style license
  */
 Jx.TreeFolder = new Class({
     Family: 'Jx.TreeFolder',
     Extends: Jx.TreeItem,
     /**
-     * Property: subDomObj
-     * {HTMLElement} an HTML container for the things inside the folder
+     * Property: tree
+     * {<Jx.Tree>} a Jx.Tree instance for managing the folder contents
      */
-    subDomObj : null,
-    /**
-     * Property: nodes
-     * {Array} an array of references to the javascript objects that are
-     * children of this folder
-     */
-    nodes : null,
-
+    tree : null,
+    
     options: {
         /* Option: open
          * is the folder open?  false by default.
          */
-        open : false
+        open: false,
+        /* folders will share a selection with the tree they are in */
+        select: false,
+        template: '<li class="jxTreeContainer jxTreeBranch"><img class="jxTreeImage" src="'+Jx.aPixel.src+'" alt="" title=""><a class="jxTreeItem" href="javascript:void(0);"><img class="jxTreeIcon" src="'+Jx.aPixel.src+'" alt="" title=""><span class="jxTreeLabel"></span></a><ul class="jxTree"></ul></li>'
     },
+    classes: new Hash({
+        domObj: 'jxTreeContainer',
+        domA: 'jxTreeItem',
+        domImg: 'jxTreeImage',
+        domIcon: 'jxTreeIcon',
+        domLabel: 'jxTreeLabel',
+        domTree: 'jxTree'
+    }),
     /**
-     * Constructor: Jx.TreeFolder
+     * APIMethod: render
      * Create a new instance of Jx.TreeFolder
-     *
-     * Parameters:
-     * options - <Jx.TreeFolder.Options> and <Jx.TreeItem.Options>
      */
-    initialize : function( options ) {
-        this.parent($merge(options,{type:'Branch'}));
+    render : function() {
+        this.parent();
+        this.domObj.store('jxTreeFolder', this);
 
-        $(this.domNode).addEvent('click', this.clicked.bindWithEvent(this));
-        this.addEvent('click', this.clicked.bindWithEvent(this));
-                
-        this.nodes = [];
-        this.subDomObj = new Element('ul', {'class':'jxTree'});
-        this.domObj.appendChild(this.subDomObj);
+        this.bound.toggle = this.toggle.bind(this);
+
+        this.addEvents({
+            click: this.bound.toggle,
+            dblclick: this.bound.toggle
+        });
+
+        if (this.domImg) {
+            this.domImg.addEvent('click', this.bound.toggle);
+        }
+
+        this.tree = new Jx.Tree({
+            template: this.options.template,
+            onAdd: function(item) {
+                this.update();
+                this.fireEvent('add', item);
+            }.bind(this),
+            onRemove: function(item) {
+                this.update();
+                this.fireEvent('remove', item);
+            }.bind(this)
+        }, this.domTree);
         if (this.options.open) {
             this.expand();
         } else {
             this.collapse();
         }
+
     },
-    /**
-     * Method: finalize
-     * Clean up a TreeFolder.
-     */
-    finalize: function() {
-        this.finalizeFolder();
-        this.finalizeItem();
-        this.subDomObj.dispose();
-        this.subDomObj = null;
+    cleanup: function() {
+      this.domObj.eliminate('jxTreeFolder');
+      this.removeEvents({
+        click: this.bound.toggle,
+        dblclick: this.bound.toggle
+      });
+      if (this.domImg) {
+        this.domImg.removeEvent('click', this.bound.toggle);
+      }
+      this.bound.toggle = null;
+      this.tree.destroy();
+      this.tree = null;
+      this.parent();
     },
     /**
-     * Method: finalizeFolder
-     * Internal method to clean up folder-related stuff.
+     * APIMethod: add
+     * add one or more items to the folder at a particular position in the
+     * folder
+     *
+     * Parameters:
+     * item - {<Jx.TreeItem>} or an array of items to be added
+     * position - {mixed} optional location to add the items.  By default,
+     * this is 'bottom' meaning the items are added at the end of the list.
+     * See <Jx.List::add> for options
+     *
+     * Returns:
+     * {<Jx.TreeFolder>} a reference to this object for chaining calls
      */
-    finalizeFolder: function() {
-        this.domObj.childNodes[0].removeEvents();
-        for (var i=this.nodes.length-1; i>=0; i--) {
-            this.nodes[i].finalize();
-            this.nodes.pop();
-        }
-        
+    add: function(item, position) {
+        this.tree.add(item, position);
+        return this;
     },
-    
     /**
-     * Method: clone
-     * Create a clone of the TreeFolder
-     * 
-     * Returns: 
-     * {<Jx.TreeFolder>} a copy of the TreeFolder
+     * APIMethod: remove
+     * remove an item from the folder
+     *
+     * Parameters:
+     * item - {<Jx.TreeItem>} the folder item to remove
+     *
+     * Returns:
+     * {<Jx.Tree>} a reference to this object for chaining calls
      */
-    clone : function() {
-        var node = new Jx.TreeFolder(this.options);
-        this.nodes.each(function(n){node.append(n.clone());});
-        return node;
+    remove: function(item) {
+        this.tree.remove(item);
+        return this;
     },
     /**
-     * Method: isLastNode
-     * Indicates if a node is the last thing in the folder.
+     * APIMethod: replace
+     * replaces one item with another
      *
      * Parameters:
-     * node - {Jx.TreeItem} the node to check
+     * item - {<Jx.TreeItem>} the tree item to remove
+     * withItem - {<Jx.TreeItem>} the tree item to insert
      *
      * Returns:
-     *
-     * {Boolean}
+     * {<Jx.Tree>} a reference to this object for chaining calls
      */
-    isLastNode : function(node) {
-        if (this.nodes.length == 0) {
-            return false;
-        } else {
-            return this.nodes[this.nodes.length-1] == node;
-        }
+    replace: function(item, withItem) {
+        this.tree.replace(item, withItem);
+        return this;
     },
     /**
+     * APIMethod: items
+     * return an array of tree item instances contained in this tree.
+     * Does not descend into folders but does return a reference to the
+     * folders
+     */
+    items: function() {
+        return this.tree.items();
+    },
+    /**
+     * APIMethod: empty
+     * recursively empty this folder and any folders in it
+     */
+    empty: function() {
+        this.tree.empty();
+    },
+    /**
      * Method: update
      * Update the CSS of the TreeFolder's DOM element in case it has changed
      * position.
      *
      * Parameters:
      * shouldDescend - {Boolean} propagate changes to child nodes?
+     * isLast - {Boolean} is this the last item in the list?
+     *
+     * Returns:
+     * {<Jx.TreeFolder>} a reference to this for chaining
      */
-    update : function(shouldDescend) {
+    update: function(shouldDescend,isLast) {
         /* avoid update if not attached to tree yet */
-        if (!this.parent) return;
-        var isLast = false;
-        if (arguments.length > 1) {
-            isLast = arguments[1];
+        if (!this.domObj.parentNode) return;
+        
+        if (this.tree.dirty) {
+          if (!$defined(isLast)) {
+              isLast = this.domObj.hasClass('jxTreeBranchLastOpen') ||
+                       this.domObj.hasClass('jxTreeBranchLastClosed');
+          }
+
+          ['jxTreeBranchOpen','jxTreeBranchLastOpen','jxTreeBranchClosed',
+          'jxTreeBranchLastClosed'].each(function(c){
+              this.removeClass(c);
+          }, this.domObj);
+
+          var c = 'jxTreeBranch';
+          c += isLast ? 'Last' : '';
+          c += this.options.open ? 'Open' : 'Closed';
+          this.domObj.addClass(c);
+        }
+
+        this.tree.update(shouldDescend, isLast);
+    },
+    /**
+     * APIMethod: toggle
+     * toggle the state of the folder between open and closed
+     *
+     * Returns:
+     * {<Jx.TreeFolder>} a reference to this for chaining
+     */
+    toggle: function() {
+        if (this.options.enabled) {
+            if (this.options.open) {
+                this.collapse();
+            } else {
+                this.expand();
+            }
+        }
+        return this;
+    },
+    /**
+     * APIMethod: expand
+     * Expands the folder
+     *
+     * Returns:
+     * {<Jx.TreeFolder>} a reference to this for chaining
+     */
+    expand : function() {
+        this.options.open = true;
+        document.id(this.tree).setStyle('display', 'block');
+        this.setDirty(true);
+        this.update(true);
+        this.fireEvent('disclosed', this);
+        return this;
+    },
+    /**
+     * APIMethod: collapse
+     * Collapses the folder
+     *
+     * Returns:
+     * {<Jx.TreeFolder>} a reference to this for chaining
+     */
+    collapse : function() {
+        this.options.open = false;
+        document.id(this.tree).setStyle('display', 'none');
+        this.setDirty(true);
+        this.update(true);
+        this.fireEvent('disclosed', this);
+        return this;
+    },
+    /**
+     * APIMethod: findChild
+     * Get a reference to a child node by recursively searching the tree
+     *
+     * Parameters:
+     * path - {Array} an array of labels of nodes to search for
+     *
+     * Returns:
+     * {Object} the node or null if the path was not found
+     */
+    findChild : function(path) {
+        //path is empty - we are asking for this node
+        if (path.length == 0) {
+            return this;
         } else {
-            isLast = (this.owner && this.owner.isLastNode(this));
+            return this.tree.findChild(path);
         }
+    },
+    /**
+     * Method: setSelection
+     * sets the <Jx.Selection> object to be used by this folder.  Used
+     * to propogate a single selection object throughout a tree.
+     *
+     * Parameters:
+     * selection - {<Jx.Selection>} the new selection object to use
+     *
+     * Returns:
+     * {<Jx.TreeFolder>} a reference to this for chaining
+     */
+    setSelection: function(selection) {
+        this.tree.setSelection(selection);
+        return this;
+    }
+});/*
+---
+
+name: Jx.Slider
+
+description: A wrapper for mootools' slider class to make it more Jx Friendly.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Widget
+ - More/Slider
+
+provides: [Jx.Slider]
+
+css:
+ - slider
+
+...
+ */
+// $Id: slider.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Slider
+ * This class wraps the mootools-more slider class to make it more Jx friendly
+ *
+ * Copyright 2009 by Jonathan Bomgardner
+ * License: MIT-style
+ */
+Jx.Slider = new Class({
+    Family: 'Jx.Slider',
+    Extends: Jx.Widget,
+
+    options: {
+        /**
+         * Option: template
+         * The template used to render the slider
+         */
+        template: '<div class="jxSliderContainer"><div class="jxSliderKnob"></div></div>',
+        /**
+         * Option: max
+         * The maximum value the slider should have
+         */
+        max: 100,
+        /**
+         * Option: min
+         * The minimum value the slider should ever have
+         */
+        min: 0,
+        /**
+         * Option: step
+         * The distance between adjacent steps. For example, the default (1)
+         * with min of 0 and max of 100, provides 100 steps between the min
+         * and max values
+         */
+        step: 1,
+        /**
+         * Option: mode
+         * Whether this is a vertical or horizontal slider
+         */
+        mode: 'horizontal',
+        /**
+         * Option: wheel
+         * Whether the slider reacts to the scroll wheel.
+         */
+        wheel: true,
+        /**
+         * Option: snap
+         * whether to snap to each step
+         */
+        snap: true,
+        /**
+         * Option: startAt
+         * The value, or step, to put the slider at initially
+         */
+        startAt: 0,
+        /**
+         * Option: offset
+         *
+         */
+        offset: 0,
+        onChange: $empty,
+        onComplete: $empty
+    },
+    classes: new Hash({
+        domObj: 'jxSliderContainer',
+        knob: 'jxSliderKnob'
+    }),
+    slider: null,
+    knob: null,
+    sliderOpts: null,
+    /**
+     * APIMethod: render
+     * Create the slider but does not start it up due to issues with it
+     * having to be visible before it will work properly.
+     */
+    render: function () {
+        this.parent();
         
-        var c = 'jxTree'+this.options.type;
-        c += isLast ? 'Last' : '';
-        c += this.options.open ? 'Open' : 'Closed';
-        this.domObj.className = c;
+        /** 
+         * Not sure why this is here...
+         */
+        /**
+        if (this.domObj) {
+            return;
+        }
+        **/
+
+        this.sliderOpts = {
+            range: [this.options.min, this.options.max],
+            snap: this.options.snap,
+            mode: this.options.mode,
+            wheel: this.options.wheel,
+            steps: (this.options.max - this.options.min) / this.options.step,
+            offset: this.options.offset,
+            onChange: this.change.bind(this),
+            onComplete: this.complete.bind(this)
+        };
+
+    },
+    /**
+     * Method: change
+     * Called when the slider moves
+     */
+    change: function (step) {
+        this.fireEvent('change', [step, this]);
+    },
+    /**
+     * Method: complete
+     * Called when the slider stops moving and the mouse button is released.
+     */
+    complete: function (step) {
+        this.fireEvent('complete', [step, this]);
+    },
+    /**
+     * APIMethod: start
+     * Call this method after the slider has been rendered in the DOM to start
+     * it up and position the slider at the startAt poisition.
+     */
+    start: function () {
+        if (!$defined(this.slider)) {
+            this.slider = new Slider(this.domObj, this.knob, this.sliderOpts);
+        }
+        this.slider.set(this.options.startAt);
+    },
+    /**
+     * APIMethod: set
+     * set the value of the slider
+     */
+    set: function(value) {
+      this.slider.set(value);
+    }
+});/*
+---
+
+name: Jx.Notice
+
+description: Represents a single item used in a notifier.
+
+license: MIT-style license.
+
+requires:
+ - Jx.ListItem
+
+provides: [Jx.Notice]
+
+images:
+ - notice.png
+ - notice_error.png
+ - notice_warning.png
+ - notice_success.png
+ - icons.png
+
+
+...
+ */
+// $Id: notice.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Notice
+ *
+ * Extends: <Jx.ListItem>
+ *
+ * Events:
+ * 
+ * MooTools.lang Keys:
+ * - notice.closeTip
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Notice = new Class({
+
+    Family: 'Jx.Notice',
+    Extends: Jx.ListItem,
+
+    options: {
+        /**
+         * Option: fx
+         * the effect to use on the notice when it is shown and hidden,
+         * 'fade' by default
+         */
+        fx: 'fade',
+        /**
+         * Option: chrome
+         * {Boolean} should the notice be displayed with chrome or not,
+         * default is false
+         */
+        chrome: false,
+        /**
+         * Option: enabled
+         * {Boolean} default is false
+         */
+        enabled: true,
+        /**
+         * Option: template
+         * {String} the HTML template of a notice
+         */
+        template: '<li class="jxNoticeItemContainer"><div class="jxNoticeItem"><span class="jxNotice"></span><a class="jxNoticeClose" href="javascript:void(0);" title="' + MooTools.lang.get('Jx','notice').closeTip + '"></a></div></li>',
+        /**
+         * Option: klass
+         * {String} css class to add to the notice
+         */
+        klass: ''
+    },
+
+    classes: new Hash({
+        domObj: 'jxNoticeItemContainer',
+        domItem: 'jxNoticeItem',
+        domContent: 'jxNotice',
+        domClose: 'jxNoticeClose'
+    }),
+
+    /**
+     * Method: render
+     */
+    render: function () {
+        this.parent();
         
-        if (isLast) {
-            this.subDomObj.className = 'jxTree';
+        if (this.options.klass) {
+            this.domObj.addClass(this.options.klass);
+        }
+        if (this.domClose) {
+            this.domClose.addEvent('click', this.close.bind(this));
+        }
+    },
+    /**
+     * APIMethod: close
+     * close the notice
+     */
+    close: function() {
+        this.fireEvent('close', this);
+    },
+    /**
+     * APIMethod: show
+     * show the notice
+     */
+    show: function(el, onComplete) {
+        if (this.options.chrome) {
+            this.showChrome();
+        }
+        if (this.options.fx) {
+            document.id(el).adopt(this);
+            if (onComplete) onComplete();
         } else {
-            this.subDomObj.className = 'jxTree jxTreeNest';
+            document.id(el).adopt(this);
+            if (onComplete) onComplete();
         }
+    },
+    /**
+     * APIMethod: hide
+     * hide the notice
+     */
+    hide: function(onComplete) {
+        if (this.options.chrome) {
+            this.hideChrome();
+        }
+        if (this.options.fx) {
+            document.id(this).dispose();
+            if (onComplete) onComplete();
+        } else {
+            document.id(this).dispose();
+            if (onComplete) onComplete();
+        }
+    },
+
+    changeText : function(lang) {
+        this.parent();
+        //this.render();
+        //this.processElements(this.options.template, this.classes);
+    }
+});
+/**
+ * Class: Jx.Notice.Information
+ * A <Jx.Notice> subclass useful for displaying informational messages
+ */
+Jx.Notice.Information = new Class({
+    Extends: Jx.Notice,
+    options: {
+        template: '<li class="jxNoticeItemContainer"><div class="jxNoticeItem"><img class="jxNoticeIcon" src="'+Jx.aPixel.src+'" title="Success"><span class="jxNotice"></span><a class="jxNoticeClose" href="javascript:void(0);" title="' + MooTools.lang.get('Jx','notice').closeTip + '"></a></div></li>',
+        klass: 'jxNoticeInformation'
+    }
+});
+/**
+ * Class: Jx.Notice.Success
+ * A <Jx.Notice> subclass useful for displaying success messages
+ */
+Jx.Notice.Success = new Class({
+    Extends: Jx.Notice,
+    options: {
+        template: '<li class="jxNoticeItemContainer"><div class="jxNoticeItem"><img class="jxNoticeIcon" src="'+Jx.aPixel.src+'" title="Success"><span class="jxNotice"></span><a class="jxNoticeClose" href="javascript:void(0);" title="' + MooTools.lang.get('Jx','notice').closeTip + '"></a></div></li>',
+        klass: 'jxNoticeSuccess'
+    }
+});
+/**
+ * Class: Jx.Notice.Success
+ * A <Jx.Notice> subclass useful for displaying warning messages
+ */
+Jx.Notice.Warning = new Class({
+    Extends: Jx.Notice,
+    options: {
+        template: '<li class="jxNoticeItemContainer"><div class="jxNoticeItem"><img class="jxNoticeIcon" src="'+Jx.aPixel.src+'" title="Warning"><span class="jxNotice"></span><a class="jxNoticeClose" href="javascript:void(0);" title="' + MooTools.lang.get('Jx','notice').closeTip + '"></a></div></li>',
+        klass: 'jxNoticeWarning'
+    }
+});
+/**
+ * Class: Jx.Notice.Error
+ * A <Jx.Notice> subclass useful for displaying error messages
+ */
+Jx.Notice.Error = new Class({
+    Extends: Jx.Notice,
+    options: {
+        template: '<li class="jxNoticeItemContainer"><div class="jxNoticeItem"><img class="jxNoticeIcon" src="'+Jx.aPixel.src+'" title="Error"><span class="jxNotice"></span><a class="jxNoticeClose" href="javascript:void(0);" title="' + MooTools.lang.get('Jx','notice').closeTip + '"></a></div></li>',
+        klass: 'jxNoticeError'
+    }
+});
+/*
+---
+
+name: Jx.Notifier
+
+description: Base class for notification areas that can hold temporary notices.
+
+license: MIT-style license.
+
+requires:
+ - Jx.ListView
+ - Jx.Notice
+ - Core/Fx.Tween
+
+provides: [Jx.Notifier]
+
+css:
+ - notification
+
+
+...
+ */
+// $Id: notifier.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Notifier
+ *
+ * Extends: <Jx.ListView>
+ *
+ * Events:
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Notifier = new Class({
+    
+    Family: 'Jx.Notifier',
+    Extends: Jx.ListView,
+    
+    options: {
+        /**
+         * Option: parent
+         * The parent this notifier is to be placed in. If not specified, it
+         * will be placed in the body of the document.
+         */
+        parent: null,
+        /**
+         * Option: template
+         * This is the template for the notification container itself, not the
+         * actual notice. The actual notice is below in the class property 
+         * noticeTemplate.
+         */
+        template: '<div class="jxNoticeListContainer"><ul class="jxNoticeList"></ul></div>',
+        /**
+         * Option: listOptions
+         * An object holding custom options for the internal Jx.List instance.
+         */
+        listOptions: { }
+    },
+
+    classes: new Hash({
+        domObj: 'jxNoticeListContainer',
+        listObj: 'jxNoticeList'
+    }),
+    
+    /**
+     * Method: render
+     * render the widget
+     */
+    render: function () {
+        this.parent();
         
-        if (this.nodes && shouldDescend) {
-            var that = this;
-            this.nodes.each(function(n,i){
-                n.update(false, i==that.nodes.length-1);
-            });
+        if (!$defined(this.options.parent)) {
+            this.options.parent = document.body;
         }
+        document.id(this.options.parent).adopt(this.domObj);
+        
+        this.addEvent('postRender', function() {
+            if (Jx.type(this.options.items) == 'array') {
+                this.options.items.each(function(item){
+                    this.add(item);
+                },this);
+            }
+        }.bind(this));
     },
+    
     /**
-     * Method: append
-     * append a node at the end of the sub-tree
+     * APIMethod: add
+     * Add a new notice to the notifier
      *
      * Parameters:
-     * node - {Object} the node to append.
+     * notice - {<Jx.Notice>} the notice to add
      */
-    append : function( node ) {
-        node.owner = this;
-        this.nodes.push(node);
-        this.subDomObj.appendChild( node.domObj );
-        this.update(true);
-        return this;
+    add: function (notice) {
+        if (!(notice instanceof Jx.Notice)) {
+            notice = new Jx.Notice({content: notice});
+        }
+        notice.addEvent('close', this.remove.bind(this));
+        notice.show(this.listObj);
     },
+    
     /**
-     * Method: insert
-     * insert a node after refNode.  If refNode is null, insert at beginning
+     * APIMethod: remove
+     * Add a new notice to the notifier
      *
      * Parameters:
-     * node - {Object} the node to insert
-     * refNode - {Object} the node to insert before
+     * notice - {<Jx.Notice>} the notice to remove
      */
-    insert : function( node, refNode ) {
-        node.owner = this;
-        //if refNode is not supplied, insert at the beginning.
-        if (!refNode) {
-            this.nodes.unshift(node);
-            //sanity check to make sure there is actually something there
-            if (this.subDomObj.childNodes.length ==0) {
-                this.subDomObj.appendChild(node.domObj);
+    remove: function (notice) {
+        if (this.domObj.hasChild(notice)) {
+            notice.removeEvents('close');
+            notice.hide();
+        }
+    }
+});/*
+---
+
+name: Jx.Notifier.Float
+
+description: A notification area that floats in a container above other content.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Notifier
+
+provides: [Jx.Notifier.Float]
+
+...
+ */
+// $Id: notifier.float.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Notifier.Float
+ * A floating notice area for displaying notices, notices get chrome if
+ * the notifier has chrome
+ *
+ * Extends: <Jx.Notifier>
+ *
+ * Events:
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Notifier.Float = new Class({
+    
+    Family: 'Jx.Notifier.Float',
+    Extends: Jx.Notifier,
+    
+    options: {
+        /**
+         * Option: chrome
+         * {Boolean} should the notifier have chrome - default true
+         */
+        chrome: true,
+        /**
+         * Option: fx
+         * {String} the effect to use when showing and hiding the notifier,
+         * default is null
+         */
+        fx: null,
+        /**
+         * Option: width
+         * {Integer} the width in pixels of the notifier, default is 250
+         */
+        width: 250,
+        /**
+         * Option: position
+         * {Object} position options to use with <Jx.Widget::position>
+         * for positioning the Notifier
+         */
+        position: {
+            horizontal: 'center center',
+            vertical: 'top top'
+        }
+    },
+
+    /**
+     * Method: render
+     * render the widget
+     */
+    render: function () {
+        this.parent();
+        this.domObj.setStyle('position','absolute');
+        if ($defined(this.options.width)) {
+            this.domObj.setStyle('width',this.options.width);
+        }
+        this.position(this.domObj, 
+                      this.options.parent,
+                      this.options.position);
+    },
+    
+    /**
+     * APIMethod: add
+     * Add a new notice to the notifier
+     *
+     * Parameters:
+     * notice - {<Jx.Notice>} the notice to add
+     */
+    add: function(notice) {
+        if (!(notice instanceof Jx.Notice)) {
+            notice = new Jx.Notice({content: notice});
+        }
+        notice.options.chrome = this.options.chrome;
+        this.parent(notice);
+    }
+});/*
+---
+
+name: Jx.Scrollbar
+
+description: An implementation of a custom CSS-styled scrollbar.
+
+license: MIT-style license.
+
+requires:
+ - Jx.Slider
+
+provides: [Jx.Scrollbar]
+
+css:
+ - scrollbar
+
+...
+ */
+// $Id: scrollbar.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Scrollbar
+ * Creates a custom scrollbar either vertically or horizontally (determined by
+ * options). These scrollbars are designed to be styled entirely through CSS.
+ * 
+ * Copyright 2009 by Jonathan Bomgardner
+ * License: MIT-style
+ * 
+ * Based in part on 'Mootools CSS Styled Scrollbar' on
+ * http://solutoire.com/2008/03/10/mootools-css-styled-scrollbar/
+ */
+Jx.Scrollbar = new Class({
+    
+    Family: 'Jx.Scrollbar',
+    
+    Extends: Jx.Widget,
+    
+    Binds: ['scrollIt'],
+    
+    options: {
+        /**
+         * Option: direction
+         * Determines which bars are visible. Valid options are 'horizontal'
+         * or 'vertical'
+         */
+        direction: 'vertical',
+        /**
+         * Option: useMouseWheel
+         * Whether to allow the mouse wheel to move the content. Defaults 
+         * to true.
+         */
+        useMouseWheel: true,
+        /**
+         * Option: useScrollers
+         * Whether to show the scrollers. Defaults to true.
+         */
+        useScrollers: true,
+        /**
+         * Option: scrollerInterval
+         * The amount to scroll the content when using the scrollers. 
+         * useScrollers option must be true. Default is 50 (px).
+         */
+        scrollerInterval: 50,
+        /**
+         * Option: template
+         * the HTML template for a scrollbar
+         */
+        template: '<div class="jxScrollbarContainer"><div class="jxScrollLeft"></div><div class="jxSlider"></div><div class="jxScrollRight"></div></div>'
+    },
+    
+    classes: new Hash({
+        domObj: 'jxScrollbarContainer',
+        scrollLeft: 'jxScrollLeft',
+        scrollRight: 'jxScrollRight',
+        sliderHolder: 'jxSlider'
+    }),
+    
+    el: null,
+    //element is the element we want to scroll. 
+    parameters: ['element', 'options'],
+    
+    /**
+     * Method: render
+     * render the widget
+     */
+    render: function () {
+        this.parent();
+        this.el = document.id(this.options.element);
+        if (this.el) {
+            this.el.addClass('jxHas'+this.options.direction.capitalize()+'Scrollbar');
+            
+            //wrap content to make scroll work correctly
+            var children = this.el.getChildren();
+            this.wrapper = new Element('div',{
+                'class': 'jxScrollbarChildWrapper'
+            });
+            
+            /**
+             * the wrapper needs the same settings as the original container
+             * specifically, the width and height
+             */ 
+            this.wrapper.setStyles({
+                width: this.el.getStyle('width'),
+                height: this.el.getStyle('height')
+            });
+            
+            children.inject(this.wrapper);
+            this.wrapper.inject(this.el);
+            
+            this.domObj.inject(this.el);
+            
+            var scrollSize = this.wrapper.getScrollSize();
+            var size = this.wrapper.getContentBoxSize();
+            this.steps = this.options.direction==='horizontal'?scrollSize.x-size.width:scrollSize.y-size.height;
+            this.slider = new Jx.Slider({
+                snap: false,
+                min: 0,
+                max: this.steps,
+                step: 1,
+                mode: this.options.direction,
+                onChange: this.scrollIt
+                
+            });
+            
+            if (!this.options.useScrollers) {
+                this.scrollLeft.dispose();
+                this.scrollRight.dispose();
+                //set size of the sliderHolder
+                if (this.options.direction === 'horizontal') {
+                    this.sliderHolder.setStyle('width','100%');
+                } else {
+                    this.sliderHolder.setStyle('height', '100%');
+                }
+                
             } else {
-                this.subDomObj.insertBefore(node.domObj, this.subDomObj.childNodes[0]);                
+                this.scrollLeft.addEvents({
+                    mousedown: function () {
+                        this.slider.slider.set(this.slider.slider.step - this.options.scrollerInterval);
+                        this.pid = function () {
+                            this.slider.slider.set(this.slider.slider.step - this.options.scrollerInterval);
+                        }.periodical(1000, this);
+                    }.bind(this),
+                    mouseup: function () {
+                        $clear(this.pid);
+                    }.bind(this)
+                });
+                this.scrollRight.addEvents({
+                    mousedown: function () {
+                        this.slider.slider.set(this.slider.slider.step + this.options.scrollerInterval);
+                        this.pid = function () {
+                            this.slider.slider.set(this.slider.slider.step + this.options.scrollerInterval);
+                        }.periodical(1000, this);
+                    }.bind(this),
+                    mouseup: function () {
+                        $clear(this.pid);
+                    }.bind(this)
+                });
+                //set size of the sliderHolder
+                var holderSize, scrollerRightSize, scrollerLeftSize;
+                if (this.options.direction === 'horizontal') {
+                    scrollerRightSize = this.scrollRight.getMarginBoxSize().width;
+                    scrollerLeftSize = this.scrollLeft.getMarginBoxSize().width;
+                    holderSize = size.width - scrollerRightSize - scrollerLeftSize;
+                    this.sliderHolder.setStyle('width', holderSize + 'px');
+                } else {
+                    scrollerRightSize = this.scrollRight.getMarginBoxSize().height;
+                    scrollerLeftSize = this.scrollLeft.getMarginBoxSize().height;
+                    holderSize = size.height - scrollerRightSize - scrollerLeftSize;
+                    this.sliderHolder.setStyle('height', holderSize + 'px');
+                }
             }
+            document.id(this.slider).inject(this.sliderHolder);
+            
+            //allows mouse wheel to function
+            if (this.options.useMouseWheel) {
+                $$(this.el, this.domObj).addEvent('mousewheel', function(e){
+                    e = new Event(e).stop();
+                    var step = this.slider.slider.step - e.wheel * 30;
+                    this.slider.slider.set(step);
+                }.bind(this));
+            }
+            
+            //stop slider if we leave the window
+            document.id(document.body).addEvent('mouseleave', function(){ 
+                this.slider.slider.drag.stop();
+            }.bind(this));
+
+            this.slider.start();
+        }
+    },
+    
+    /**
+     * Method: scrollIt
+     * scroll the content in response to the slider being moved.
+     */
+    scrollIt: function (step) {
+        var x = this.options.direction==='horizontal'?step:0;
+        var y = this.options.direction==='horizontal'?0:step;
+        this.wrapper.scrollTo(x,y);
+    }
+});/*
+---
+
+name: Jx.Formatter
+
+description: Base formatter object
+
+license: MIT-style license.
+
+requires:
+ - Jx.Object
+
+provides: [Jx.Formatter]
+
+...
+ */
+ // $Id: formatter.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Formatter
+ *
+ * Extends: <Jx.Object>
+ *
+ * Base class used for specific implementations to coerce data into specific formats
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter = new Class({
+    Family: 'Jx.Formatter',
+    Extends: Jx.Object,
+
+    /**
+     * APIMethod: format
+     * Empty method that must be overridden by subclasses to provide
+     * the needed formatting functionality.
+     */
+    format: $empty
+});/*
+---
+
+name: Jx.Formatter.Number
+
+description: Formats numbers including negative and floats
+
+license: MIT-style license.
+
+requires:
+ - Jx.Formatter
+
+provides: [Jx.Formatter.Number]
+
+...
+ */
+// $Id: number.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Formatter.Number
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * This class formats numbers. You can have it do the following
+ *
+ * o replace the decimal separator
+ * o use/add a thousands separator
+ * o change the precision (number of decimal places)
+ * o format negative numbers with parenthesis
+ *
+ * Example:
+ * (code)
+ * (end)
+ * 
+ * MooTools.lang Keys:
+ * - 'formatter.number'.decimalSeparator
+ * - 'formatter.number'.thousandsSeparator
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Number = new Class({
+
+    Extends: Jx.Formatter,
+
+    options: {
+        /**
+         * Option: precision
+         * The number of decimal places to round to
+         */
+        precision: 2,
+        /**
+         * Option: useParens
+         * Whether negative numbers should be done with parenthesis
+         */
+        useParens: true,
+        /**
+         * Option: useThousands
+         * Whether to use the thousands separator
+         */
+        useThousands: true
+    },
+    /**
+     * APIMethod: format
+     * Formats the provided number
+     *
+     * Parameters:
+     * value - the raw number to format
+     */
+    format : function (value) {
+            //first set the decimal
+        if (Jx.type(value) === 'string') {
+                //remove commas from the string
+            var p = value.split(',');
+            value = p.join('');
+            value = value.toFloat();
+        }
+        value = value.toFixed(this.options.precision);
+
+        //split on the decimalSeparator
+        var parts = value.split('.');
+        var dec = true;
+        if (parts.length === 1) {
+            dec = false;
+        }
+        //check for negative
+        var neg = false;
+        var main;
+        var ret = '';
+        if (parts[0].contains('-')) {
+            neg = true;
+            main = parts[0].substring(1, parts[0].length);
         } else {
-            //walk all nodes looking for the ref node.  Track if it actually
-            //happens so we can append if it fails.
-            var b = false;
-            for(var i=0;i<this.nodes.length;i++) {
-                if (this.nodes[i] == refNode) {
-                    //increment to append after ref node.  If this pushes us
-                    //past the end, it'll get appended below anyway
-                    i = i + 1;
-                    if (i < this.nodes.length) {
-                        this.nodes.splice(i, 0, node);
-                        this.subDomObj.insertBefore(node.domObj, this.subDomObj.childNodes[i]);
-                        b = true;
-                        break;
+            main = parts[0];
+        }
+
+        if (this.options.useThousands) {
+            var l = main.length;
+            var left = l % 3;
+            var j = 0;
+            for (var i = 0; i < l; i++) {
+                ret = ret + main.charAt(i);
+                if (i === left - 1 && i !== l - 1) {
+                    ret = ret + this.getText({set:'Jx',key:'formatter.number',value:'thousandsSeparator'});
+                } else if (i >= left) {
+                    j++;
+                    if (j === 3 && i !== l - 1) {
+                        ret = ret + this.getText({set:'Jx',key:'formatter.number',value:'thousandsSeparator'});
+                        j = 0;
                     }
                 }
+
             }
-            //if the node wasn't inserted, it is because refNode didn't exist
-            //and so the fallback is to just append the node.
-            if (!b) {
-                this.nodes.push(node); 
-                this.subDomObj.appendChild(node.domObj); 
-            }
+        } else {
+            ret = parts[0];
         }
-        this.update(true);
-        return this;
+
+        if (dec) {
+            ret = ret + this.getText({set:'Jx',key:'formatter.number',value:'decimalSeparator'}) + parts[1];
+        }
+        if (neg && this.options.useParens) {
+            ret = "(" + ret + ")";
+        } else if (neg && !this.options.useParens) {
+            ret = "-" + ret;
+        }
+
+        return ret;
     },
+    
     /**
-     * Method: remove
-     * remove the specified node from the tree
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     * 
+     * Parameters:
+     * lang - the language being changed to or that had it's data set of 
+     * 		translations changed.
+     */
+    changeText: function (lang) {
+    	this.parent();
+    }
+});/*
+---
+
+name: Jx.Formatter.Currency
+
+description: Formats input as currency. Currently only US currency is supported
+
+license: MIT-style license.
+
+requires:
+ - Jx.Formatter.Number
+
+provides: [Jx.Formatter.Currency]
+
+...
+ */
+// $Id: currency.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Formatter.Currency
+ *
+ * Extends: <Jx.Formatter.Number>
+ *
+ * This class formats numbers as US currency. It actually
+ * runs the value through Jx.Formatter.Number first and then
+ * updates the returned value as currency.
+ *
+ * Example:
+ * (code)
+ * (end)
+ * 
+ * MooTools.lang Keys:
+ * - 'formatter.currency'.sign
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Currency = new Class({
+
+    Extends: Jx.Formatter.Number,
+
+    options: {},
+    /**
+     * APIMethod: format
+     * Takes a number and formats it as currency.
      *
      * Parameters:
-     * node - {Object} the node to remove
+     * value - the number to format
      */
-    remove : function(node) {
-        node.owner = null;
-        for(var i=0;i<this.nodes.length;i++) {
-            if (this.nodes[i] == node) {
-                this.nodes.splice(i, 1);
-                this.subDomObj.removeChild(this.subDomObj.childNodes[i]);
-                break;
-            }
+    format: function (value) {
+
+        this.options.precision = 2;
+
+        value = this.parent(value);
+        //check for negative
+        var neg = false;
+        if (value.contains('(') || value.contains('-')) {
+            neg = true;
         }
-        this.update(true);
-        return this;
+
+        var ret;
+        if (neg && !this.options.useParens) {
+            ret = "-" + this.getText({set:'Jx',key:'formatter.currency',value:'sign'}) + value.substring(1, value.length);
+        } else {
+            ret = this.getText({set:'Jx',key:'formatter.currency',value:'sign'}) + value;
+        }
+        return ret;
     },
+    
     /**
-     * Method: replace
-     * Replace a node with another node
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     * 
+     * Parameters:
+     * lang - the language being changed to or that had it's data set of 
+     * 		translations changed.
+     */
+    changeText: function (lang) {
+    	this.parent();
+    }
+});/*
+---
+
+name: Jx.Formatter.Date
+
+description: Formats dates using the mootools-more Date extensions
+
+license: MIT-style license.
+
+requires:
+ - More/Date.Extras
+ - Jx.Formatter
+
+provides: [Jx.Formatter.Date]
+...
+ */
+// $Id: date.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Formatter.Date
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * This class formats dates using the mootools-more's
+ * Date extensions. See the -more docs for details of
+ * supported formats for parsing and formatting.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Date = new Class({
+
+    Extends: Jx.Formatter,
+
+    options: {
+        /**
+         * Option: format
+         * The format to use. See the mootools-more Date
+         * extension documentation for details on supported
+         * formats
+         */
+        format: '%B %d, %Y'
+    },
+    /**
+     * APIMethod: format
+     * Does the work of formatting dates
      *
      * Parameters:
-     * newNode - {Object} the node to put into the tree
-     * refNode - {Object} the node to replace
+     * value - the text to format
+     */
+    format: function (value) {
+        var d = Date.parse(value);
+        return d.format(this.options.format);
+    }
+});/*
+---
+
+name: Jx.Formatter.URI
+
+description: Formats uris using the mootools-more URI extensions
+
+license: MIT-style license.
+
+requires:
+ - More/String.Extras
+ - Jx.Formatter
+ - More/URI
+
+provides: [Jx.Formatter.URI]
+
+...
+ */
+// $Id: uri.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Formatter.URI
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * This class formats URIs using the mootools-more's
+ * URI extensions. See the -more docs for details of
+ * supported formats for parsing and formatting.
+ * 
+ * @url http://mootools.net/docs/more/Native/URI
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Uri = new Class({
+
+    Extends: Jx.Formatter,
+
+    options: {
+        /**
+         * Option: format
+         * The format to use. See the mootools-more URI options
+         * to use within a {pattern}
+         *   {string} will call the URI.toString() method
+         */
+        format: '<a href="{string}" target="_blank">{host}</a>'
+    },
+    /**
+     * APIMethod: format
+     * Does the work of formatting dates
      *
-     * Returns:
-     * {Boolean} true if the replacement was successful.
+     * Parameters:
+     * value - the text to format
      */
-    replace: function( newNode, refNode ) {
-        //walk all nodes looking for the ref node. 
+    format: function (value) {
+      var uri        = new URI(value),
+          uriContent = {},
+          pattern    = new Array(),
+          patternTmp = this.options.format.match(/\\?\{([^{}]+)\}/g);
+
+      // remove bracktes
+      patternTmp.each(function(e) {
+        pattern.push(e.slice(1, e.length-1));
+      });
+
+      // build object that contains replacements
+      for(var i = 0, j = pattern.length; i < j; i++) {
+        switch(pattern[i]) {
+          case 'string':
+            uriContent[pattern[i]] = uri.toString();
+            break;
+          default:
+            uriContent[pattern[i]] = uri.get(pattern[i]);
+            break;
+        }
+      }
+      return this.options.format.substitute(uriContent);
+    }
+});/*
+---
+
+name: Jx.Formatter.Boolean
+
+description: Formats boolean input
+
+license: MIT-style license.
+
+requires:
+ - Jx.Formatter
+
+provides: [Jx.Formatter.Boolean]
+...
+ */
+// $Id: boolean.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Formatter.Boolean
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * This class formats boolean values. You supply the
+ * text values for true and false in the options.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * MooTools.lang Keys:
+ * - 'formatter.boolean'.true
+ * - 'formatter.boolean'.false
+ * 
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Boolean = new Class({
+
+    Extends: Jx.Formatter,
+
+    options: {},
+    /**
+     * APIMethod: format
+     * Takes a value, determines boolean equivalent and
+     * displays the appropriate text value.
+     *
+     * Parameters:
+     * value - the text to format
+     */
+    format : function (value) {
         var b = false;
-        for(var i=0;i<this.nodes.length;i++) {
-            if (this.nodes[i] == refNode) {
-                if (i < this.nodes.length) {
-                    newNode.owner = this;
-                    this.nodes.splice(i, 1, newNode);
-                    this.subDomObj.replaceChild(newNode.domObj, refNode.domObj);
-                    return true;
-                }
+        var t = Jx.type(value);
+        switch (t) {
+        case 'string':
+            if (value === 'true') {
+                b = true;
             }
+            break;
+        case 'number':
+            if (value !== 0) {
+                b = true;
+            }
+            break;
+        case 'boolean':
+            b = value;
+            break;
+        default:
+            b = true;
         }
-        return false;
+        return b ? this.getText({set:'Jx',key:'formatter.boolean',value:'true'}) : this.getText({set:'Jx',key:'formatter.boolean',value:'false'});
     },
     
     /**
-     * Method: clicked
-     * handle the user clicking on this folder by expanding or
-     * collapsing it.
+     * APIMethod: changeText
+     * This method should be overridden by subclasses. It should be used
+     * to change any language specific default text that is used by the widget.
+     * 
+     * Parameters:
+     * lang - the language being changed to or that had it's data set of 
+     * 		translations changed.
+     */
+    changeText: function (lang) {
+    	this.parent();
+    }
+
+});/*
+---
+
+name: Jx.Formatter.Phone
+
+description: Formats phone numbers in US format including area code
+
+license: MIT-style license.
+
+requires:
+ - Jx.Formatter
+
+
+provides: [Jx.Formatter.Phone]
+
+...
+ */
+// $Id: phone.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Formatter.Phone
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * Formats data as phone numbers. Currently only US-style phone numbers
+ * are supported.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Phone = new Class({
+
+    Extends: Jx.Formatter,
+
+    options: {
+        /**
+         * Option: useParens
+         * Whether to use parenthesis () around the area code.
+         * Defaults to true
+         */
+        useParens: true,
+        /**
+         * Option: separator
+         * The character to use as a separator in the phone number.
+         * Defaults to a dash '-'.
+         */
+        separator: "-"
+    },
+    /**
+     * APIMethod: format
+     * Format the input as a phone number. This will strip all non-numeric
+     * characters and apply the current default formatting
      *
-     * Parameters: 
-     * e - {Event} the event object
+     * Parameters:
+     * value - the text to format
      */
-    clicked : function(e) {
-        if (this.options.open) {
-            this.collapse();
+    format : function (value) {
+        //first strip any non-numeric characters
+        var sep = this.options.separator;
+        var v = '' + value;
+        v = v.replace(/[^0-9]/g, '');
+
+        //now check the length. For right now, we only do US phone numbers
+        var ret = '';
+        if (v.length === 11) {
+            //do everything including the leading 1
+            ret = v.charAt(0);
+            v = v.substring(1);
+        }
+        if (v.length === 10) {
+            //do the area code
+            if (this.options.useParens) {
+                ret = ret + "(" + v.substring(0, 3) + ")";
+            } else {
+                ret = ret + sep + v.substring(0, 3) + sep;
+            }
+            v = v.substring(3);
+        }
+        //do the rest of the number
+        ret = ret + v.substring(0, 3) + sep + v.substring(3);
+        return ret;
+    }
+});/*
+---
+
+name: Jx.Formatter.Text
+
+description: Formats strings by limiting to a max length
+
+license: MIT-style license.
+
+requires:
+ - Jx.Formatter
+
+provides: [Jx.Formatter.Text]
+
+...
+ */
+// $Id: $
+/**
+ * Class: Jx.Formatter.Text
+ *
+ * Extends: <Jx.Formatter>
+ *
+ * This class formats strings by limiting them to a maximum length
+ * and replacing the remainder with an ellipsis.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2010, Hughes Gauthier.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Formatter.Text = new Class({
+
+  Extends: Jx.Formatter,
+
+  options: {
+    /**
+     * Option: length
+     * {Integer} default null, if set to an integer value greater than
+     * 0 then the value will be truncated to length characters and
+     * the remaining characters will be replaced by an ellipsis (...)
+     */
+    length: null,
+    /**
+     * Option: ellipsis
+     * {String} the text to use as the ellipsis when truncating a string
+     * default is three periods (...)
+     */
+    ellipsis: '...'
+  },
+
+  format : function (value) {
+    var text = '' + value,
+        max = this.options.length,
+        ellipsis = this.options.ellipsis;
+
+    if (max && text.length > max) {
+      text = text.substr(0,max-ellipsis.length) + ellipsis;
+    }
+
+    return text;
+  }
+});/*
+---
+
+name: Jx.Field.Check
+
+description: Represents a checkbox input
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field
+
+provides: [Jx.Field.Checkbox]
+
+...
+ */
+// $Id: checkbox.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Field.Check
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a radio input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ *
+ */
+Jx.Field.Checkbox = new Class({
+
+    Extends : Jx.Field,
+
+    options : {
+        /**
+         * Option: template
+         * The template used for rendering this field
+         */
+        template : '<span class="jxInputContainer"><input class="jxInputCheck" type="checkbox" name="{name}"/><label class="jxInputLabel"></label><span class="jxInputTag"></span></span>',
+        /**
+         * Option: checked
+         * Whether this field is checked or not
+         */
+        checked : false,
+
+        labelSeparator: ''
+    },
+    /**
+     * Property: type
+     * The type of this field
+     */
+    type : 'Check',
+
+    /**
+     * APIMethod: render
+     * Creates a checkbox input field.
+    */
+    render : function () {
+        this.parent();
+
+        if ($defined(this.options.checked) && this.options.checked) {
+            if (Browser.Engine.trident) {
+                var parent = this.field.getParent();
+                var sibling;
+                if (parent) {
+                    sibling = this.field.getPrevious();
+                }
+                this.field.setStyle('visibility','hidden');
+                this.field.inject(document.id(document.body));
+                this.field.checked = true;
+                this.field.defaultChecked = true;
+                this.field.dispose();
+                this.field.setStyle('visibility','visible');
+                if (sibling) {
+                    this.field.inject(sibling, 'after');
+                } else if (parent) {
+                    this.field.inject(parent, 'top');
+                }
+            } else {
+                this.field.set("checked", "checked");
+                this.field.set("defaultChecked", "checked");
+            }
+        }
+
+        // add click event to the label to toggle the checkbox
+        if(this.label) {
+          this.label.addEvent('click', function(ev) {
+            this.setValue(this.getValue() != null ? false : true)
+          }.bind(this));
+        }
+    },
+
+    /**
+     * APIMethod: setValue
+     * Sets the value property of the field
+     *
+     * Parameters:
+     * v - Whether the box shouldbe checked or not. "checked" or "true" if it should be checked.
+     */
+    setValue : function (v) {
+        if (!this.options.readonly) {
+            if (v === 'checked' || v === 'true' || v === true) {
+                this.field.set('checked', "checked");
+            } else {
+                this.field.erase('checked');
+            }
+        }
+    },
+
+    /**
+     * APIMethod: getValue
+     * Returns the current value of the field. The field must be
+     * "checked" in order to return a value. Otherwise it returns null.
+     */
+    getValue : function () {
+        if (this.field.get("checked")) {
+            return this.field.get("value");
         } else {
-            this.expand();
+            return null;
         }
     },
+
     /**
-     * Method: expand
-     * Expands the folder
+     * APIMethod: reset
+     * Sets the field back to the value passed in the original
+     * options. no IE hack is implemented because the field should
+     * already be in the DOM when this is called.
      */
-    expand : function() {
-        this.options.open = true;
-        this.subDomObj.setStyle('display', 'block');
-        this.update(true);
-        this.fireEvent('disclosed', this);    
+    reset : function () {
+        if (this.options.checked) {
+            this.field.set('checked', "checked");
+        } else {
+            this.field.erase('checked');
+        }
     },
+
+    getChecked: function () {
+        return this.field.get("checked");
+    }
+
+});
+/*
+---
+
+name: Jx.Field.Radio
+
+description: Represents a radio button input
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field
+
+provides: [Jx.Field.Radio]
+
+...
+ */
+// $Id: radio.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Field.Radio
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a radio input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Radio = new Class({
+
+    Extends: Jx.Field,
+
+    options: {
+        /**
+         * Option: template
+         * The template used to create this field
+         */
+        template: '<span class="jxInputContainer"><input class="jxInputRadio" type="radio" name="{name}"/><label class="jxInputLabel"></label><span class="jxInputTag"></span></span>',
+        /**
+         * Option: checked
+         * whether this radio button is checked or not
+         */
+        checked: false,
+
+        labelSeparator: ''
+    },
     /**
-     * Method: collapse
-     * Collapses the folder
+     * Property: type
+     * What kind of field this is
      */
-    collapse : function() {
-        this.options.open = false;
-        this.subDomObj.setStyle('display', 'none');
-        this.update(true);
-        this.fireEvent('disclosed', this);
+    type: 'Radio',
+
+    /**
+     * APIMethod: render
+     * Creates a radiobutton input field.
+     */
+    render: function () {
+        this.parent();
+
+        if ($defined(this.options.checked) && this.options.checked) {
+            if (Browser.Engine.trident) {
+                var parent = this.field.getParent();
+                var sibling;
+                if (parent) {
+                    sibling = this.field.getPrevious();
+                }
+                this.field.setStyle('visibility','hidden');
+                this.field.inject(document.id(document.body));
+                this.field.checked = true;
+                this.field.defaultChecked = true;
+                this.field.dispose();
+                this.field.setStyle('visibility','visible');
+                if (sibling) {
+                    this.field.inject(sibling, 'after');
+                } else if (parent) {
+                    this.field.inject(parent, 'top');
+                }
+            } else {
+                this.field.set("checked", "checked");
+                this.field.set("defaultChecked", "checked");
+            }
+        }
+
+        // add click event to toggle the radio buttons
+        this.label.addEvent('click', function(ev) {
+          this.field.checked ? this.setValue(false) : this.setValue(true);
+        }.bind(this));
+
     },
+
     /**
-     * Method: findChild
-     * Get a reference to a child node by recursively searching the tree
-     * 
+     * APIMethod: setValue
+     * Sets the value property of the field
+     *
      * Parameters:
-     * path - {Array} an array of labels of nodes to search for
-     *
-     * Returns:
-     * {Object} the node or null if the path was not found
+     * v - The value to set the field to, "checked" it should be checked.
      */
-    findChild : function(path) {
-        //path is empty - we are asking for this node
-        if (path.length == 0)
-            return this;
-        
-        //path has only one thing in it - looking for something in this folder
-        if (path.length == 1)
-        {
-            for (var i=0; i<this.nodes.length; i++)
-            {
-                if (this.nodes[i].getName() == path[0])
-                    return this.nodes[i];
+    setValue: function (v) {
+        if (!this.options.readonly) {
+            if (v === 'checked' || v === 'true' || v === true) {
+                this.field.set('checked', "checked");
+            } else {
+                this.field.erase('checked');
             }
+        }
+    },
+
+    /**
+     * APIMethod: getValue
+     * Returns the current value of the field. The field must be "checked"
+     * in order to return a value. Otherwise it returns null.
+     */
+    getValue: function () {
+        if (this.field.get("checked")) {
+            return this.field.get("value");
+        } else {
             return null;
         }
-        //path has more than one thing in it, find a folder and descend into it    
-        var childName = path.shift();
-        for (var i=0; i<this.nodes.length; i++)
-        {
-            if (this.nodes[i].getName() == childName && this.nodes[i].findChild)
-                return this.nodes[i].findChild(path);
+    },
+
+    /**
+     * Method: reset
+     * Sets the field back to the value passed in the original
+     * options
+     */
+    reset: function () {
+        if (this.options.checked) {
+            this.field.set('checked', "checked");
+        } else {
+            this.field.erase('checked');
         }
-        return null;
     }
-});// $Id: tree.js 424 2009-05-12 12:51:44Z pagameba $
+
+});
+
+
+
+
+/*
+---
+
+name: Jx.Field.Select
+
+description: Represents a select, or drop down, input
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field
+
+provides: [Jx.Field.Select]
+
+...
+ */
+// $Id: select.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
 /**
- * Class: Jx.Tree
+ * Class: Jx.Field.Select
  *
- * Extends: Jx.TreeFolder
+ * Extends: <Jx.Field>
  *
- * Implements: <Jx.Addable>
+ * This class represents a form select field.
  *
- * Jx.Tree displays hierarchical data in a tree structure of folders and nodes.
+ * These fields are rendered as below.
  *
+ * (code)
+ * <div id='' class=''>
+ *    <label for=''>A label for the field</label>
+ *    <select id='' name=''>
+ *      <option value='' selected=''>text</option>
+ *    </select>
+ * </div>
+ * (end)
+ *
  * Example:
  * (code)
  * (end)
  *
- * Extends: <Jx.TreeFolder>
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
  *
- * License: 
- * Copyright (c) 2008, DM Solutions Group Inc.
- * 
  * This file is licensed under an MIT style license
+ *
  */
-Jx.Tree = new Class({
-    Extends: Jx.TreeFolder,
-    Implements: [Jx.Addable],
-    Family: 'Jx.Tree',
+
+Jx.Field.Select = new Class({
+
+    Extends: Jx.Field,
+
+    options: {
+        /**
+         * Option: multiple
+         * {Boolean} optional, defaults to false.  If true, then the select
+         * will support multi-select
+         */
+        mulitple: false,
+        /**
+         * Option: size
+         * {Integer} optional, defaults to 1.  If set, then this specifies
+         * the number of rows of the select that are visible
+         */
+        size: 1,
+        /**
+         * Option: comboOpts
+         * Optional, defaults to null. if not null, this should be an array of
+         * objects formated like [{value:'', selected: true|false,
+         * text:''},...]
+         */
+        comboOpts: null,
+        /**
+         * Option: optGroups
+         * Optional, defaults to null. if not null this should be an array of
+         * objects defining option groups for this select. The comboOpts and
+         * optGroups options are mutually exclusive. optGroups will always be
+         * shown if defined.
+         *
+         * define them like [{name: '', options: [{value:'', selected: '',
+         * text: ''}...]},...]
+         */
+        optGroups: null,
+        /**
+         * Option: template
+         * The template for creating this select input
+         */
+        template: '<span class="jxInputContainer"><label class="jxInputLabel"></label><select class="jxInputSelect" name="{name}"></select><span class="jxInputTag"></span></span>'
+    },
     /**
-     * Constructor: Jx.Tree
-     * Create a new instance of Jx.Tree
+     * Property: type
+     * Indictes this type of field.
+     */
+    type: 'Select',
+
+    /**
+     * APIMethod: render
+     * Creates a select field.
+     */
+    render: function () {
+        this.parent();
+        this.field.addEvent('change', function() {this.fireEvent('change', this);}.bind(this));
+        if ($defined(this.options.multiple)) {
+          this.field.set('multiple', this.options.multiple);
+        }
+        if ($defined(this.options.size)) {
+          this.field.set('size', this.options.size);
+        }
+        if ($defined(this.options.optGroups)) {
+            this.options.optGroups.each(function(group){
+                var gr = new Element('optGroup');
+                gr.set('label',group.name);
+                group.options.each(function(option){
+                    var opt = new Element('option', {
+                        'value': option.value,
+                        'html': this.getText(option.text)
+                    });
+                    if ($defined(option.selected) && option.selected) {
+                        opt.set("selected", "selected");
+                    }
+                    gr.grab(opt);
+                },this);
+                this.field.grab(gr);
+            },this);
+        } else if ($defined(this.options.comboOpts)) {
+            this.options.comboOpts.each(function (item) {
+                this.addOption(item);
+            }, this);
+        }
+    },
+
+    /**
+     * Method: addOption
+     * add an option to the select list
      *
      * Parameters:
-     * options: options for <Jx.Addable>
+     * item - The option to add.
+     * position (optional) - an integer index or the string 'top'.
+     *                     - default is to add at the bottom.
      */
-    initialize : function( options ) {
-        this.parent(options);
-        this.subDomObj = new Element('ul',{
-            'class':'jxTreeRoot'
+    addOption: function (item, position) {
+        var opt = new Element('option', {
+            'value': item.value,
+            'html': this.getText(item.text)
         });
+        if ($defined(item.selected) && item.selected) {
+            opt.set("selected", "selected");
+        }
+        var where = 'bottom';
+        var field = this.field;
+        if ($defined(position)) {
+            if (Jx.type(position) == 'integer' &&
+                (position >= 0  && position < field.options.length)) {
+                field = this.field.options[position];
+                where = 'before';
+            } else if (position == 'top') {
+                where = 'top';
+            }
+
+        }
+        opt.inject(field, where);
+    },
+
+    /**
+     * Method: removeOption
+     * removes an option from the select list
+     *
+     * Parameters:
+     *  item - The option to remove.
+     */
+    removeOption: function (item) {
+        //TBD
+    },
+    /**
+     * Method: setValue
+     * Sets the value property of the field
+     *
+     * Parameters:
+     * v - The value to set the field to.
+     */
+    setValue: function (v) {
+        if (!this.options.readonly) {
+            //loop through the options and set the one that matches v
+            $$(this.field.options).each(function (opt) {
+                if (opt.get('value') === v) {
+                    document.id(opt).set("selected", true);
+                }
+            }, this);
+        }
+    },
+
+    /**
+     * Method: getValue
+     * Returns the current value of the field.
+     */
+    getValue: function () {
+        var index = this.field.selectedIndex;
+        //check for a set "value" attribute. If not there return the text
+        if (index > -1) {
+            var ret = this.field.options[index].get("value");
+            if (!$defined(ret)) {
+                ret = this.field.options[index].get("text");
+            }
+            return ret;
+        }
+    },
+    
+    /**
+     * APIMethod: empty
+     * Empties all options from this select
+     */
+    empty: function () {
+        if ($defined(this.field.options)) {
+            $A(this.field.options).each(function (option) {
+                this.field.remove(option);
+            }, this);
+        }
+    }
+});/*
+---
+
+name: Jx.Field.Textarea
+
+description: Represents a textarea input
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field
+
+provides: [Jx.Field.Textarea]
+
+...
+ */
+// $Id: textarea.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Field.Textarea
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a textarea field.
+ *
+ * These fields are rendered as below.
+ *
+ * (code)
+ * <div id='' class=''>
+ *    <label for=''>A label for the field</label>
+ *    <textarea id='' name='' rows='' cols=''>
+ *      value/ext
+ *    </textarea>
+ * </div>
+ * (end)
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ *
+ */
+Jx.Field.Textarea = new Class({
+
+    Extends: Jx.Field,
+
+    options: {
+        /**
+         * Option: rows
+         * the number of rows to show
+         */
+        rows: null,
+        /**
+         * Option: columns
+         * the number of columns to show
+         */
+        columns: null,
+        /**
+         * Option: template
+         * the template used to render this field
+         */
+        template: '<span class="jxInputContainer"><label class="jxInputLabel"></label><textarea class="jxInputTextarea" name="{name}"></textarea><span class="jxInputTag"></span></span>'
+    },
+    /**
+     * Property: type
+     * The type of field this is.
+     */
+    type: 'Textarea',
+    /**
+     * Property: errorClass
+     * The class applied to error elements
+     */
+    errorClass: 'jxFormErrorTextarea',
+
+    /**
+     * APIMethod: render
+     * Creates the input.
+    */
+    render: function () {
+        this.parent();
+
+        if ($defined(this.options.rows)) {
+            this.field.set('rows', this.options.rows);
+        }
+        if ($defined(this.options.columns)) {
+            this.field.set('cols', this.options.columns);
+        }
+
+        //TODO: Do we need to use OverText here as well??
+
+    }
+});/*
+---
+
+name: Jx.Field.Button
+
+description: Represents a button input
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field
+ - Jx.Button
+
+provides: [Jx.Field.Button]
+
+...
+ */
+/**
+ * Class: Jx.Field.Button
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class represents a button.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, DM Solutions Group
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Button = new Class({
+
+    Extends: Jx.Field,
+
+    options: {
+        /**
+         * Option: buttonClass
+         * choose the actual Jx.Button subclass to create for this form
+         * field.  The default is to create a basic Jx.Button.  To create
+         * a different kind of button, pass the class to this option, for
+         * instance:
+         * (code)
+         * buttonClass: Jx.Button.Color
+         * (end)
+         */
+        buttonClass: Jx.Button,
         
-        this.nodes = [];
-        this.isOpen = true;
+        /**
+         * Option: buttonOptions
+         */
+        buttonOptions: {},
+        /**
+         * Option: template
+         * The template used to render this field
+         */
+        template: '<span class="jxInputContainer"><label class="jxInputLabel"></label><div class="jxInputButton"></div><span class="jxInputTag"></span></span>'
+    },
+    
+    button: null,
+    
+    /**
+     * Property: type
+     * The type of this field
+     */
+    type: 'Button',
+
+    processTemplate: function(template, classes, container) {
+        var h = this.parent(template, classes, container);
+        this.button = new this.options.buttonClass(this.options.buttonOptions);
+        this.button.addEvent('click', function(){
+          this.fireEvent('click');
+        }.bind(this));
+        var c = h.get('jxInputButton');
+        if (c) {
+            this.button.domObj.replaces(c);
+        }
+        this.button.setEnabled(!this.options.disabled);
+        return h;
+    },
+    
+    click: function() {
+        this.button.clicked();
+    },
+    
+    enable: function() {
+      this.parent();
+      this.button.setEnabled(true);
+    },
+    
+    disable: function() {
+      this.parent();
+      this.button.setEnabled(false);
+    }
+});/*
+---
+
+name: Jx.Field.Combo
+
+description: Represents an editable combo
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field
+ - Jx.Button
+ - Jx.Menu
+ - Jx.Menu.Item
+ - Jx.ButtonSet
+
+provides: [Jx.Field.Combo]
+
+...
+ */
+// $Id: jxcombo.js 993 2010-10-07 19:29:08Z pagameba $
+/**
+ * Class: Jx.Field.Combo
+ *
+ * Extends: <Jx.Field>
+ *
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * Events:
+ * change - 
+ *
+ * License:
+ * Copyright (c) 2008, DM Solutions Group Inc.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Combo = new Class({
+    Family: 'Jx.Field.Combo',
+    Extends: Jx.Field,
+    pluginNamespace: 'Combo',
+
+    options: {
+        buttonTemplate: '<a class="jxButtonContainer jxButton" href="javascript:void(0);"><img class="jxButtonIcon" src="'+Jx.aPixel.src+'"></a>',
+        /* Option: template
+         */
+         template: '<span class="jxInputContainer"><label class="jxInputLabel"></label><span class="jxInputWrapper"><input type="text" class="jxInputCombo"  name="{name}"><img class="jxInputIcon" src="'+Jx.aPixel.src+'"><span class="jxInputRevealer"></span></span><span class="jxInputTag"></span></span>'
+     },
+     
+     type: 'Combo',
+     
+    /**
+     * APIMethod: render
+     * create a new instance of Jx.Field.Combo
+     */
+    render: function() {
+        this.classes.combine({
+          wrapper: 'jxInputWrapper',
+          revealer: 'jxInputRevealer',
+          icon: 'jxInputIcon'
+        });
+        this.parent();
         
-        this.addable = this.subDomObj;
-        
-        if (this.options.parent) {
-            this.addTo(this.options.parent);
+        var button = new Jx.Button({
+          template: this.options.buttonTemplate,
+          imageClass: 'jxInputRevealerIcon'
+        }).addTo(this.revealer);
+
+        this.menu = new Jx.Menu();
+        this.menu.button = button;
+        this.buttonSet = new Jx.ButtonSet();
+
+        this.buttonSet = new Jx.ButtonSet({
+            onChange: (function(set) {
+                var button = set.activeButton;
+                var l = button.options.label;
+                if (l == '&nbsp;') {
+                    l = '';
+                }
+                this.setLabel(l);
+                var img = button.options.image;
+                if (img.indexOf('a_pixel') != -1) {
+                    img = '';
+                }
+                this.setImage(img, button.options.imageClass);
+
+                this.fireEvent('change', this);
+            }).bind(this)
+        });
+        if (this.options.items) {
+            this.add(this.options.items);
         }
+        var that = this;
+        button.addEvent('click', function(e) {
+            if (this.list.count() === 0) {
+                return;
+            }
+            if (!button.options.enabled) {
+                return;
+            }
+            this.contentContainer.setStyle('visibility','hidden');
+            this.contentContainer.setStyle('display','block');
+            document.id(document.body).adopt(this.contentContainer);
+            /* we have to size the container for IE to render the chrome correctly
+             * but just in the menu/sub menu case - there is some horrible peekaboo
+             * bug in IE related to ULs that we just couldn't figure out
+             */
+            this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
+
+            this.showChrome(this.contentContainer);
+
+            this.position(this.contentContainer, that.field, {
+                horizontal: ['left left', 'right right'],
+                vertical: ['bottom top', 'top bottom'],
+                offsets: this.chromeOffsets
+            });
+
+            this.contentContainer.setStyle('visibility','');
+
+            document.addEvent('mousedown', this.bound.hide);
+            document.addEvent('keyup', this.bound.keypress);
+
+            this.fireEvent('show', this);
+        }.bindWithEvent(this.menu));
+
+        this.menu.addEvents({
+            'show': (function() {
+                //this.setActive(true);
+            }).bind(this),
+            'hide': (function() {
+                //this.setActive(false);
+            }).bind(this)
+        });
     },
     
+    setLabel: function(label) {
+      if ($defined(this.field)) {
+        this.field.value = this.getText(label);
+      }
+    },
+    
+    setImage: function(url, imageClass) {
+      if ($defined(this.icon)) {
+        this.icon.setStyle('background-image', 'url('+url+')');
+        this.icon.setStyle('background-repeat', 'no-repeat');
+
+        if (this.options.imageClass) {
+            this.icon.removeClass(this.options.imageClass);
+        }
+        if (imageClass) {
+            this.options.imageClass = imageClass;
+            this.icon.addClass(imageClass);
+            this.icon.setStyle('background-position','');
+        } else {
+            this.options.imageClass = null;
+            this.icon.setStyle('background-position','center center');
+        }
+      }
+      if (!url) {
+        this.wrapper.addClass('jxInputIconHidden');
+      } else {
+        this.wrapper.removeClass('jxInputIconHidden');
+      }
+    },
+
     /**
-     * Method: finalize
-     * Clean up a Jx.Tree instance
+     * Method: valueChanged
+     * invoked when the current value is changed
      */
-    finalize: function() { 
-        this.clear(); 
-        this.subDomObj.parentNode.removeChild(this.subDomObj); 
+    valueChanged: function() {
+        this.fireEvent('change', this);
     },
+
+    setValue: function(value) {
+        this.field.set('value', value);
+        this.buttonSet.buttons.each(function(button){
+          button.setActive(button.options.label === value);
+        },this);
+    },
+
     /**
-     * Method: clear
-     * Clear the tree of all child nodes
+     * Method: onKeyPress
+     * Handle the user pressing a key by looking for an ENTER key to set the
+     * value.
+     *
+     * Parameters:
+     * e - {Event} the keypress event
      */
-    clear: function() {
-        for (var i=this.nodes.length-1; i>=0; i--) {
-            this.subDomObj.removeChild(this.nodes[i].domObj);
-            this.nodes[i].finalize();
-            this.nodes.pop();
+    onKeyPress: function(e) {
+        if (e.key == 'enter') {
+            this.valueChanged();
         }
     },
+
     /**
-     * Method: update
-     * Update the CSS of the Tree's DOM element in case it has changed
-     * position
+     * Method: add
+     * add a new item to the pick list
      *
      * Parameters:
-     * shouldDescend - {Boolean} propagate changes to child nodes?
+     * options - {Object} object with properties suitable to be passed to
+     * a <Jx.Menu.Item.Options> object.  More than one options object can be
+     * passed, comma separated or in an array.
      */
-    update: function(shouldDescend) {
-        var bLast = true;
-        if (this.subDomObj)
-        {
-            if (bLast) {
-                this.subDomObj.removeClass('jxTreeNest');
-            } else {
-                this.subDomObj.addClass('jxTreeNest');
+    add: function() {
+        $A(arguments).flatten().each(function(opt) {
+            var button = new Jx.Menu.Item($merge(opt,{
+                toggle: true
+            }));
+            this.menu.add(button);
+            this.buttonSet.add(button);
+            if (opt.selected) {
+              this.buttonSet.setActiveButton(button);
             }
-        }
-        if (this.nodes && shouldDescend) {
-            this.nodes.each(function(n){n.update(false);});
-        }
+        }, this);
     },
+
     /**
-     * Method: append
-     * Append a node at the end of the sub-tree
-     * 
+     * Method: remove
+     * Remove the item at the given index.  Not implemented.
+     *
      * Parameters:
-     * node - {Object} the node to append.
+     * idx - {Mixed} the item to remove by reference or by index.
      */
-    append: function( node ) {
-        node.owner = this;
-        this.nodes.push(node);
-        this.subDomObj.appendChild( node.domObj );
-        this.update(true);
-        return this;    
+    remove: function(idx) {
+      var item;
+      if ($type(idx) == 'number' && idx < this.buttonSet.buttons.length) {
+        item = this.buttonSet.buttons[idx];
+      } else if ($type(idx) == 'string'){
+        this.buttonSet.buttons.some(function(button){
+            if (button.options.label === idx) {
+                item = button;
+                return true;
+            }
+            return false;
+        },this);
+      }
+      if (item) {
+        this.buttonSet.remove(item);
+        this.menu.remove(item);
+      }
+    },
+    /**
+     * APIMethod: empty
+     * remove all values from the combo
+     */
+    empty: function() {
+      this.menu.empty();
+      this.buttonSet.empty();
+      this.setLabel('');
+      this.setImage(Jx.aPixel.src);
+    },
+    
+    enable: function() {
+      this.parent();
+      this.menu.setEnabled(true);
+    },
+    
+    disable: function() {
+      this.parent();
+      this.menu.setEnabled(false);
     }
-});
+    
+});/*
+---
 
+name: Jx.Field.Password
 
+description: Represents a password input
+
+license: MIT-style license.
+
+requires:
+ - Jx.Field.Text
+
+provides: [Jx.Field.Password]
+
+...
+ */
+// $Id: password.js 960 2010-06-06 22:23:16Z jonlb at comcast.net $
+/**
+ * Class: Jx.Field.Password
+ *
+ * Extends: <Jx.Field.Text>
+ *
+ * This class represents a password input field.
+ *
+ * Example:
+ * (code)
+ * (end)
+ *
+ * License:
+ * Copyright (c) 2009, Jon Bomgardner.
+ *
+ * This file is licensed under an MIT style license
+ */
+Jx.Field.Password = new Class({
+
+    Extends: Jx.Field,
+
+    options: {
+        template: '<span class="jxInputContainer"><label class="jxInputLabel" ></label><input class="jxInputPassword" type="password" name="{name}"/><span class="jxInputTag"></span></span>'
+    },
+
+    type: 'Password'
+});/*
+---
+
+name: Jx.Field.Color
+
+description: Represents an input field with a jx.button.color
+
+license: MIT-style license.
+
+requires:
+ - Jx.Text
+ - Jx.Button.Color
+ - Jx.Form
+ - Jx.Plugin.Field.Validator
+
+provides: [Jx.Field.Color]
+
+...
+ */
+/**
+ * Class: Jx.Field.Color
+ *
+ * Extends: <Jx.Field>
+ *
+ * This class provides a Jx.Field.Text in combination with a Jx.Button.Color
+ * to have a Colorpicker with an input field.
+ *
+ * License:
+ * Copyright (c) 2010, Paul Spener, Fred Warnock, Conrad Barthelmes
+ *
+ * This file is licensed under an MIT style license
+ */
+  Jx.Field.Color = new Class({
+    Extends: Jx.Field,
+    Binds: ['changed','hide','keyup','changeText'],
+    type: 'Color',
+    options: {
+      buttonTemplate: '<a class="jxButtonContainer jxButton" href="javascript:void(0);"><img class="jxButtonIcon" src="'+Jx.aPixel.src+'"></a>',
+      /**
+       * Option: template
+       * The template used to render this field
+       */
+      template: '<span class="jxInputContainer"><label class="jxInputLabel"></label><span class="jxInputWrapper"><input type="text" class="jxInputColor"  name="{name}"><img class="jxInputIcon" src="'+Jx.aPixel.src+'"><span class="jxInputRevealer"></span></span><span class="jxInputTag"></span></span>',
+      /**
+       * Option: showOnHover
+       * {Boolean} show the color palette when hovering over the input, default 
+       * is false
+       */
+      showOnHover: false,
+      /**
+       *  Option: showDelay
+       *  set time in milliseconds when to show the color field on mouseenter
+       */
+      showDelay: 250,
+      /**
+       * Option: errorMsg
+       * error message for the validator.
+       */
+      errorMsg: 'Invalid Web-Color',
+      /**
+       * Option: color
+       * a color to initialize the field with, defaults to #000000
+       * (black) if not specified.
+       */
+      color: '#000000'
+
+    },
+    button: null,
+    validator: null,
+    render: function() {
+        this.classes.combine({
+          wrapper: 'jxInputWrapper',
+          revealer: 'jxInputRevealer',
+          icon: 'jxInputIcon'
+        });
+        this.parent();
+
+      var self = this;
+      if (!Jx.Field.Color.ColorPalette) {
+          Jx.Field.Color.ColorPalette = new Jx.ColorPalette(this.options);
+      }
+      this.button = new Jx.Button.Flyout({
+          template: this.options.buttonTemplate,
+          imageClass: 'jxInputRevealerIcon',
+          positionElement: this.field,
+          onBeforeOpen: function() {
+            if (Jx.Field.Color.ColorPalette.currentButton) {
+                Jx.Field.Color.ColorPalette.currentButton.hide();
+            }
+            Jx.Field.Color.ColorPalette.currentButton = this;
+            Jx.Field.Color.ColorPalette.addEvent('change', self.changed);
+            Jx.Field.Color.ColorPalette.addEvent('click', self.hide);
+            this.content.appendChild(Jx.Field.Color.ColorPalette.domObj);
+            Jx.Field.Color.ColorPalette.domObj.setStyle('display', 'block');
+          },
+          onOpen: function() {
+            /* setting these before causes an update problem when clicking on
+             * a second color button when another one is open - the color
+             * wasn't updating properly
+             */
+            Jx.Field.Color.ColorPalette.options.color = self.options.color;
+            Jx.Field.Color.ColorPalette.updateSelected();
+          }
+        }).addTo(this.revealer);
+
+      this.validator = new Jx.Plugin.Field.Validator({
+        validators: [{
+            validatorClass: 'colorHex',
+            validator: {
+              name: 'colorValidator',
+              options: {
+                validateOnChange: false,
+                errorMsg: self.options.errorMsg,
+                test: function(field,props) {
+                  try {
+                    var c = field.get('value').hexToRgb(true);
+                    if(c == null) return false;
+                    for(var i = 0; i < 3; i++) {
+                      if(c[i].toString() == 'NaN') {
+                        return false;
+                      }
+                    }
+                  }catch(e) {
+                    return false;
+                  }
+                  c = c.rgbToHex().toUpperCase();
+                  self.setColor(c);
+                  return true;
+                }
+              }
+            }
+        }],
+        validateOnBlur: true,
+        validateOnChange: true
+      });
+      this.validator.attach(this);
+      this.field.addEvent('keyup', this.onKeyUp.bind(this));
+      if (this.options.showOnHover) {
+        this.field.addEvent('mouseenter', function(ev) {
+          self.button.clicked.delay(self.options.showDelay, self.button);
+        });
+      }
+      this.setValue(this.options.color);
+      this.icon.setStyle('background-color', this.options.color);
+      //this.addEvent('change', self.changed);
+    },
+    /*
+     * Method: onKeyUp
+     *
+     * listens to the keyup event and validates the input for a hex color
+     *
+     */
+    onKeyUp : function(ev) {
+      var color = this.getValue();
+      if (color.substring(0,1) == '#') {
+          color = color.substring(1);
+      }
+      if (color.toLowerCase().match(/^[0-9a-f]{6}$/)) {
+          this.options.color = '#' +color.toUpperCase();
+          this.setColor(this.options.color);
+      }
+    },
+    setColor: function(c) {
+        this.options.color = c;
+        this.setValue(c);
+        this.icon.setStyle('background-color', c);
+    },
+    changed: function() {
+        var c = Jx.Field.Color.ColorPalette.options.color;
+        this.setColor(c);
+    },
+    hide: function() {
+        this.button.setActive(false);
+        Jx.Field.Color.ColorPalette.removeEvent('change', this.changed);
+        Jx.Field.Color.ColorPalette.removeEvent('click', this.hide);
+
+        this.button.hide();
+        Jx.Field.Color.ColorPalette.currentButton = null;
+    },
+    changeText: function(lang) {
+      this.parent();
+    }
+  });

Modified: trunk/templates/mapguide/standard/index.html
===================================================================
--- trunk/templates/mapguide/standard/index.html	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapguide/standard/index.html	2010-10-19 14:31:05 UTC (rev 2249)
@@ -94,18 +94,27 @@
 
 <div id="thePage" style="visibility: hidden">
 
-  <div id="PanelPane">
-      <div id="Legend"></div>
-      <div id="SelectionPanel"></div>
-      <div id="OverviewMap"></div>
-  </div>
-  <div id="Map"><div id="Navigator"></div></div>
-  <div id="Statusbar"></div>
-  <div id="Statusbar"></div>
-  <div id="MapTip"></div>
-  <div id="TaskPane"></div>
+<div id="PanelPane">
+    <div id="Legend"></div>
+    <div id="SelectionPanel"></div>
+    <div id="OverviewMap"></div>
+</div>
+<div id="Map"><div id="Navigator"></div></div>
+<div id="Statusbar"></div>
+<div id="Statusbar"></div>
+<div id="MapTip"></div>
+<div id="TaskPane"></div>
+<div id="PoweredBy" class="statusBarItem">
+<a href="http://mapserver.gis.umn.edu.org/" target="_blank">
+  <img src="images/PoweredBy_MapServer.gif" width="137" height="18" border="0">
+</a>
+</div>
 
 </div>
 
+<div id="BusyIndicator" style="visibility: hidden;">
+  <img src="images/icon_loading.gif" width="30" height="14">
+</div>
+
 </body>
 </html>

Modified: trunk/templates/mapguide/standard/themes/crispin/ie6.css
===================================================================
--- trunk/templates/mapguide/standard/themes/crispin/ie6.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapguide/standard/themes/crispin/ie6.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,6 +1,6 @@
 /**
  * @project         Jx
- * @revision        $Id: ie6.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: ie6.css 321 2009-04-06 18:23:39Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */

Modified: trunk/templates/mapguide/standard/themes/crispin/ie7.css
===================================================================
--- trunk/templates/mapguide/standard/themes/crispin/ie7.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapguide/standard/themes/crispin/ie7.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,6 +1,6 @@
 /**
  * @project         Jx
- * @revision        $Id: ie7.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: ie7.css 321 2009-04-06 18:23:39Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */

Copied: trunk/templates/mapguide/standard/themes/crispin/images/delete.gif (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/delete.gif)
===================================================================
(Binary files differ)

Modified: trunk/templates/mapguide/standard/themes/crispin/images/dialog_chrome.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapguide/standard/themes/crispin/images/emblems.png
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/crispin/images/error.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/error.png)
===================================================================
(Binary files differ)

Modified: trunk/templates/mapguide/standard/themes/crispin/images/flyout_chrome.png
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/crispin/images/green_tick.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/green_tick.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/crispin/images/icons.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/icons.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/crispin/images/listitem.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/listitem.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/crispin/images/menuitem.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/menuitem.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/crispin/images/notice.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/notice.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/crispin/images/notice_error.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/notice_error.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/crispin/images/notice_success.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/notice_success.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/crispin/images/notice_warning.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/crispin/images/notice_warning.png)
===================================================================
(Binary files differ)

Modified: trunk/templates/mapguide/standard/themes/crispin/jxtheme.css
===================================================================
--- trunk/templates/mapguide/standard/themes/crispin/jxtheme.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapguide/standard/themes/crispin/jxtheme.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -21,4 +21,4 @@
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
- */body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;}q:before,q:after{content:'';}div.jxButtonContainer{display:block;position:relative;float:left;margin:0;padding:2px;border:none;}.jxButton{display:block;position:relative;float:left;margin:0;padding:0 0 0 4px;border:none;background-image:url(images/button.png);background-position:left -24px;background-repeat:no-repeat;text-decoration:none;outline:none;}a.jxButton{cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;}ul.jxToolbar .jxButton{background-position:left top;}span.jxButtonContent{display:block;position:relative;float:left;font-size:0;line-height:0;margin:0;padding:4px 4px 4px 0;border:none;backgr
 ound-image:url(images/button.png);background-position:right -24px;background-repeat:no-repeat;}ul.jxToolbar span.jxButtonContent{background-position:right top;}ul.jxToolbar .jxButtonActive,.jxButtonActive{background-position:left -72px;}ul.jxToolbar .jxButtonActive span.jxButtonContent,.jxButtonActive span.jxButtonContent{background-position:right -72px;}ul.jxToolbar .jxButton:focus,.jxButton:focus{background-position:left -96px;}ul.jxToolbar .jxButton:focus span.jxButtonContent,.jxButton:focus span.jxButtonContent{background-position:right -96px;}ul.jxToolbar .jxButtonActive:focus,.jxButtonActive:focus{background-position:left -144px;}ul.jxToolbar .jxButtonActive:focus span.jxButtonContent,.jxButtonActive:focus span.jxButtonContent{background-position:right -144px;}ul.jxToolbar .jxButton:hover,ul.jxToolbar .jxButtonActive:hover,.jxButton:hover,.jxButtonActive:hover{background-position:left -48px;}ul.jxToolbar .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxButtonActive
 :hover span.jxButtonContent,.jxButton:hover span.jxButtonContent,.jxButtonActive:hover span.jxButtonContent{background-position:right -48px;}ul.jxToolbar .jxButtonPressed,ul.jxToolbar .jxButtonPressed:focus,.jxButtonPressed,.jxButtonPressed:focus{background-position:left -120px;}ul.jxToolbar .jxButtonPressed span.jxButtonContent,ul.jxToolbar .jxButtonPressed:focus span.jxButtonContent,.jxButtonPressed span.jxButtonContent,.jxButtonPressed:focus span.jxButtonContent{background-position:right -120px;}.jxDisabled .jxButton,.jxDisabled span.jxButtonContent span{cursor:default;}ul.jxToolbar .jxDisabled .jxButton:focus,ul.jxToolbar .jxDisabled .jxButton:active,ul.jxToolbar .jxDisabled .jxButton:hover,ul.jxToolbar .jxDisabled .jxButtonPressed{background-position:left top;}.jxDisabled .jxButton:focus,.jxDisabled .jxButton:active,.jxDisabled .jxButton:hover,.jxDisabled .jxButtonPressed{background-position:left -24px;}ul.jxToolbar .jxDisabled .jxButton:focus span.jxButtonContent,ul.jx
 Toolbar .jxDisabled .jxButton:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right top;}.jxDisabled .jxButton:focus span.jxButtonContent,.jxDisabled .jxButton:active span.jxButtonContent,.jxDisabled .jxButton:hover span.jxButtonContent,.jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right -24px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus,ul.jxToolbar .jxDisabled .jxButtonActive:active,ul.jxToolbar .jxDisabled .jxButtonActive:hover,.jxDisabled .jxButtonActive:focus,.jxDisabled .jxButtonActive:active,.jxDisabled .jxButtonActive:hover{background-position:left -72px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:hover span.jxButtonContent,.jxDisabled .jxButtonActive:focus span.jxButtonContent,.jxDisa
 bled .jxButtonActive:active span.jxButtonContent,.jxDisabled .jxButtonActive:hover span.jxButtonContent{background-position:right -72px;}img.jxButtonIcon{position:relative;float:left;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxButtonContent span{display:block;position:relative;float:left;cursor:pointer;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;height:16px;white-space:nowrap;}span.jxButtonContent span.jxButtonLabel{margin:0;padding:0 4px 0 4px;color:#000;font-size:11px;}.jxButtonMenu span.jxButtonContent,.jxButtonMulti span.jxButtonContent,.jxButtonFlyout span.jxButtonContent,.jxButtonCombo span.jxButtonContent,.jxButtonEditCombo span.jxButtonContent{padding-right:0;}.jxButtonMenu span.jxButtonContent span,.jxButtonFlyout span.jxButtonContent span,.jxButtonMulti span.jxButtonContent span,.jxButtonCombo span.jxButtonContent span,.jxButtonEditCombo span.jxButtonContent span{padding-right:16px;backgrou
 nd-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose{position:absolute;display:block;padding:4px 0;font-size:0;line-height:0;right:2px;top:2px;background-image:url(images/button_multi_disclose.png);background-position:right 0;background-repeat:no-repeat;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxButtonDisclose img{width:16px;height:16px;margin:0;padding:0;border:0;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose:focus,a.jxButtonDisclose:active{background-position:right -96px;}a.jxButtonDisclose:hover{background-position:right -48px;}a.jxButtonDisclosePressed{background-position:right -120px;}.jxDisabled a.jxButtonDisclose,.jxDisabled a.jxButtonDisclose:focus,.jxDisabled a.jxButtonDisclose:active,.jxDisabled a.jxButtonDisclose:hover,.jxDisabled a.jxButtonDisclosePressed{cursor:default;background-position:righ
 t 0;}ul.jxToolbar .jxButtonHover{background-position:left -24px!important;}ul.jxToolbar .jxButtonHover span.jxButtonContent{background-position:right -24px!important;}.jxFlyout .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxFlyout{position:absolute;display:block;z-index:100;margin:0;padding:0;}.jxFlyoutContent{position:relative;display:block;overflow:auto;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}.jxButtonMulti,.jxButtonMulti span.jxButtonContent{background-image:url(images/button_multi.png);}.jxButtonEditCombo,.jxButtonEditCombo span.jxButtonContent{background-image:url(images/button_combo.png);}.jxButtonMulti span.jxButtonContent span{padding-right:21px;}.jxButtonEditCombo span.jxButtonContent span{font-size:0;}.jxButtonComboDefault span.jxButtonContent span,.jxButtonComboDefault input{font-style:italic;color:#999;}.jxButtonEditCombo input{float:left;line-height:16px;height:16px;padding:0 4px;margin:0;border:n
 one;font-size:11px;font-family:Arial,Helvetica,sans-serif;background-color:transparent;}.jxChrome{position:absolute;display:block;font-size:0;line-height:0;z-index:-1;width:100%;height:100%;top:0;left:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxChromeDrag{opacity:.5;-ms-filter:"Alpha(opacity=50)";}.jxChromeTL{position:absolute;overflow:hidden;left:0;top:0;width:50%;height:50%;}.jxChromeTR{position:absolute;overflow:hidden;left:50%;top:0;width:50%;height:50%;}.jxChromeBL{position:absolute;overflow:hidden;left:0;top:50%;width:50%;height:50%;}.jxChromeBR{position:absolute;overflow:hidden;left:50%;top:50%;width:50%;height:50%;}.jxChromeTL img{position:absolute;top:0;left:0;width:800px;height:500px;}.jxChromeTR img{position:absolute;top:0;right:0;width:800px;height:500px;}.jxChromeBL img{position:absolute;bottom:0;left:0;width:800px;height:500px;}.jxChromeBR img{position:absolute;bottom:0;right:0;width:800px;height:500px;}.jxColorBar{position:relative;ove
 rflow:hidden;}table.jxColorGrid{position:relative;border-collapse:collapse;empty-cells:show;clear:both;padding:0;margin:0;}.jxColorGrid tr{padding:0;margin:0;}.jxColorGrid td{border:1px solid #000;padding:0;margin:0;}.jxColorGrid td.emptyCell{border:0 solid #000;}.jxColorGrid td.emptyCell span{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;padding:1px;margin:0;}.jxColorGrid a.colorSwatch{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;margin:0;padding:1px;}.jxColorGrid a.borderWhite:hover{border:1px solid #fff;padding:0;}.jxColorGrid a.borderBlack:hover{border:1px solid #000;padding:0;}input.jxHexInput{width:55px;vertical-align:middle;}input.jxAlphaInput{width:30px;vertical-align:middle;}div.jxColorPreview{float:left;position:relative;width:20px;height:20px;border:1px solid #000;margin:2px;vertical-align:middle;background-image:url('images/grid.png');overflow:hidden;}.jxButtonFlyout span.jxButtonContent span.
 jxButtonSwatch{display:block;float:left;width:14px;height:14px;border:1px solid #000;background-image:url('images/grid.png');background-position:0 0;background-repeat:repeat;padding-right:0!important;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch span{display:block;width:14px;height:14px;position:absolute;padding-right:0;background:none;}div.jxColorPreview img{position:absolute;z-index:0;}div.jxColorPreview div{width:20px;height:10px;position:absolute;display:block;left:0;z-index:1;font-size:10px;line-height:0;}div.jxColorPreview div.jxColorSelected{top:0;}div.jxColorPreview div.jxColorHover{bottom:0;}label.jxColorLabel,label.jxAlphaLabel{width:auto;font-family:Arial,sans-serif;font-size:11px;line-height:24px;padding:2px;vertical-align:middle;}a.jxColorClose{position:absolute;top:0;right:0;width:16px;height:16px;}a.jxColorClose img{width:16px;height:16px;}.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;ma
 rgin:0;padding:0;}.jxDisabled{opacity:.4;-ms-filter:"Alpha(opacity=40)";}.jxDisabled *{-ms-filter:"Alpha(opacity=40)";}iframe.jxIframeShim{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;-ms-filter:"Alpha(opacity=0)";z-index:-1;}.jxDialog .jxChrome{background-image:url(images/dialog_chrome.png);}.jxDialog{display:block;z-index:1000;overflow:hidden;}.jxDialogContentContainer{z-index:1;margin:0 11px 13px 12px;border:1px solid #b7b7b7;background-color:#f0f0f0;}.jxDialogModal{position:absolute;display:block;top:0;left:0;width:100%;height:100%;background-color:#000;opacity:.2;-ms-filter:"Alpha(opacity=20)";}.jxDialogContent{display:block;position:relative;overflow:auto;padding:0;z-index:1;}.jxDialogTitle{display:block;position:relative;background-image:url(images/a_pixel.png);text-align:center;height:24px;line-height:24px;z-index:1;margin:6px 6px 0 7px;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxDialogMin .jxDialogTitle{margin-bottom:8px;}.j
 xDialogMoveable,.jxDialogMoveable .jxDialogLabel{cursor:move;}.jxDialogIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxDialogLabel{font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;cursor:default;}.jxDialogResize{position:absolute;bottom:7px;right:6px;width:16px;height:16px;z-index:2;border:0;cursor:se-resize;background-image:url(images/dialog_resize.png);}.jxDialogControls{position:absolute;top:3px;right:2px;height:16px;width:80px;}.jxDialogControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxDialogClose img{background-position:0 -32px;}.jxDialogMenu img{background-position:0 -48px;}.jxDialogHelp img{background-position:0 -64px;}.jxDialogCollapse img{background-position:0 -16px;}.jxDialogMin .jxDialogCollapse img{background-position:0 0;}.jxDialogMax .jxDialogCollapse img{back
 ground-position:0 -16px;}.jxDialogLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxDialogControls .jxButtonContainer,.jxDialogControls span.jxButtonContent,.jxDialogControls .jxButton:hover span.jxButtonContent,.jxDialogControls .jxButton:active span.jxButtonContent,.jxDialogControls .jxButtonActive span.jxButtonContent,.jxDialogControls .jxButtonActive:hover span.jxButtonContent,.jxDialogControls .jxButtonActive:active span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:hover span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:active span.jxButtonContent,.jxDialogControls .jxButton,.jxDialogControls .jxButton:hover,.jxDialogControls .jxButton:active,.jxDialogControls .jxButtonActive,.jxDialogControls .jxButtonActive:hover,.jxDialogControls .jxButtonActive:active,.jxDialogControls .jxDisabled .jxButton,.jxDialogControls .jxDisabled .jxBu
 tton:hover,.jxDialogControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent,.jxDialogControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent span,.jxDialogControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxDialogControls .jxBarContainer{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxDialogControls .jxBarScroller{left:auto;right:0;}.jxDialogControls ul.jxToolbar{float:right;}.jxDialogControls ul.jxToolbar,.jxDialogControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxForm{display:block;position:relative;overflow:hidden;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;}.jxForm label{background-color:transparent;color:#000;}.j
 xForm select,.jxForm input{color:#000;}.jxFormFloat .radioGroup{display:block;overflow:hidden;}.jxFormFloat .radioGroup input,.jxFormFloat .radioGroup label{width:auto!important;vertical-align:middle;}.jxForm .tipArea{display:block;padding:5px 0;background-color:transparent;color:#999;}.jxFormInline label{display:inline;}.jxFormFloat label{display:block;float:left;clear:left;width:75px;}.jxFormFloat select,.jxFormFloat input{float:left;}.jxFormFloat .radioGroup{float:left;}.jxFormFloat .radioGroup input,.jxFormFloat .radioGroup label{float:none;}.jxFormFloat .tipArea{clear:left;}.jxFormBlock label{display:block;}.jxGridContainer{position:absolute;top:0;left:0;border-left:0 solid #d8d8d8;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;overflow:hidden;}.jxGridTable{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;width:0;cursor:default;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal
 ;}.jxGridColHeadHide{height:0;line-height:0;font-size:0;background-color:#fff;white-space:normal;}.jxGridColHeadHide p,.jxGridRowHeadHide p{font-size:0;line-height:0;height:0;margin:0;padding:0;}.jxGridRowHeadHide{width:0;white-space:normal;}.jxGridCell{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;overflow:hidden;padding-left:3px;padding-right:3px;overflow:hidden;white-space:nowrap;cursor:cell;text-overflow:ellipsis;}.jxGridColHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_col.png');background-position:0 0;background-repeat:repeat-x;text-align:center;font-weight:bold;color:#333;cursor:default;}.jxGridRowHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url(
 'images/table_row.png');background-position:0 0;background-repeat:repeat-y;text-align:center;font-weight:bold;color:#333;cursor:default;}.jxGridRowAll{background-color:#fff;}.jxGridColumnHeaderSelected{background-color:#e1e1e1;background-position:0 -200px;}.jxGridRowHeaderSelected{background-color:#e1e1e1;background-position:-400px 0;}.jxGridColumnSelected{background-color:#f7f7f7;}.jxGridRowSelected td{background-color:#f7f7f7;}td.jxGridCellSelected{background-color:#ebebeb;}.jxGridColumnHeaderPrelight{background-color:#cee5ff;background-position:0 -300px;}.jxGridRowHeaderPrelight{background-color:#cee5ff;background-position:-600px 0;}.jxGridColumnPrelight{background-color:#e5f1ff;}.jxGridRowPrelight td{background-color:#e5f1ff;}td.jxGridCellPrelight{background-color:#cce3ff;}.jxMenuContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxButtonMenu span.jxMenuItemSpan{padding-right:16px;}.jxMenuContainer{position:absolute;display:block
 ;z-index:2000;padding:0;}ul.jxMenu,ul.jxSubMenu{display:block;position:relative;list-style-type:none;padding:1px;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}li.jxMenuItemContainer{display:block;position:relative;font-size:0;line-height:0;margin:0;padding:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxMenuItem{display:block;position:relative;overflow:hidden;text-decoration:none;cursor:pointer;outline:none;border:1px solid #fff;background-image:url(images/menuitem.png);background-repeat:no-repeat;background-position:left top;font-family:Arial,Helvetica,sans-serif;font-size:11px;text-decoration:none;margin:0;padding:0;color:#000;}a.jxMenuItemActive{background-position:left -98px;}a.jxMenuItem:focus{background-position:left -74px;}a.jxMenuItem:focus span.jxMenuItemContent{border-right:1px dotted #75ADFF;}a.jxMenuItemActive:focus{background-position:left -170px;}a.jxMenuItem:hover{background-color:#CDE5FF;background-position:left -26
 px;}a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #C5E0FF;}a.jxMenuItemActive:hover{background-position:left -122px;}a.jxMenuItemPressed,a.jxMenuItemPressed:hover{background-color:#CDE5FF;background-position:left -50px;}.jxDisabled a.jxMenuItem,.jxDisabled span.jxMenuItemContent span{cursor:default;}.jxDisabled a.jxMenuItem:focus,.jxDisabled a.jxMenuItemPressed,.jxDisabled a.jxMenuItemPressed:hover,.jxDisabled a.jxMenuItem:hover{background-color:#fff;background-position:left top;border-right:1px solid #fff;}.jxDisabled a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #fff;}span.jxMenuItemContent{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:0;line-height:0;white-space:nowrap;padding:0 20px 0 0;margin:0;border-right:1px solid #fff;}.jxButtonSubMenu span.jxMenuItemContent,.jxButtonSubMenu:hover span.jxMenuItemContent{background-image:url(images/emblems.png);background-position:right -30px;background-repeat:n
 o-repeat;}img.jxMenuItemIcon{position:absolute;top:2px;left:2px;width:16px;height:16px;background-position:left center;background-repeat:no-repeat;}span.jxMenuItemContent span{display:block;position:relative;cursor:pointer;margin:0;padding:2px 0 2px 22px;font-size:16px;line-height:16px;color:#000;}span.jxMenuItemContent span.jxMenuItemLabel{color:#000;font-size:11px;}.jxMenuItemToggle img.jxMenuItemIcon,.jxMenuItemSet img.jxMenuItemIcon{background-image:url(images/emblems.png);background-position:2px 0;background-repeat:no-repeat;}.jxMenuItemToggle a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -48px;}.jxMenuItemSet a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -64px;}ul.jxMenu span.jxMenuSeparator,ul.jxSubMenu span.jxMenuSeparator{display:block;font-size:10px;line-height:10px;background-image:url(images/toolbar_separator_v.png);background-repeat:repeat-x;background-position:left center;}.jxPanel{display:block;position:relative;}.jxPanelContent
 Container{overflow:hidden;background-color:#f0f0f0;}.jxPanelContent{position:relative;display:block;overflow:auto;background-color:#fff;margin:0;padding:0;}.jxPanelTitle{display:block;position:relative;background-image:url(images/panelbar.png);background-repeat:repeat-x;background-position:left top;height:22px;margin:0;padding:0;text-align:center;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxPanelBar{position:absolute;line-height:1px;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxPanelIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxPanelLabel{padding-left:25px;font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;}.jxPanelControls{position:absolute;top:3px;right:2px;height:16px;width:80px;overflow:hidden;}.jxPanelControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0
 ;margin:0;width:16px;height:16px;}.jxPanelClose img{background-position:0 -32px;}.jxPanelMenu img{background-position:0 -48px;}.jxPanelHelp img{background-position:0 -64px;}.jxPanelCollapse img{background-position:0 -16px;}.jxPanelMin .jxPanelCollapse img{background-position:0 0;}.jxPanelMax .jxPanelCollapse img{background-position:0 -16px;}.jxPanelMaximize img{background-position:0 0;}.jxPanelLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxPanelControls .jxButtonContainer,.jxPanelControls span.jxButtonContent,.jxPanelControls .jxButton:hover span.jxButtonContent,.jxPanelControls .jxButton:active span.jxButtonContent,.jxPanelControls .jxButtonActive span.jxButtonContent,.jxPanelControls .jxButtonActive:hover span.jxButtonContent,.jxPanelControls .jxButtonActive:active span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton:hover span.jxButtonContent,.
 jxPanelControls .jxDisabled .jxButton:active span.jxButtonContent,.jxPanelControls .jxButton,.jxPanelControls .jxButton:hover,.jxPanelControls .jxButton:active,.jxPanelControls .jxButtonActive,.jxPanelControls .jxButtonActive:hover,.jxPanelControls .jxButtonActive:active,.jxPanelControls .jxDisabled .jxButton,.jxPanelControls .jxDisabled .jxButton:hover,.jxPanelControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent,.jxPanelControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent span,.jxPanelControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxPanelControls div.jxBarTop{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxPanelControls .jxBarScroller{left:auto;right:0;}.jxPanelControls ul.jxToolbar{float:righ
 t;}.jxPanelControls ul.jxToolbar,.jxPanelControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxSplitterMask{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background-image:url(images/a_pixel.png);z-index:1;}.jxSplitBarHorizontal{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:5px;height:100%;cursor:col-resize;background-color:#f0f0f0;z-index:2;}.jxSplitBarVertical{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxSplitContainer{display:block;position:relative;margin:0;padding:0;border:none;overflow:hidden;}.jxSplitArea{display:block;position:absolute;margin:0;padding:0;border:none;z-index:0;}.jxSplitBarDrag{background-color:#eee;}.jxSnapHorizontalBefore{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxSnapHo
 rizontalAfter{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxTabSetContainer{position:relative;display:block;overflow:hidden;width:200px;height:200px;margin:0;padding:0;background-color:#fff;}.jxTabSetContainer .jxToolbarContainer{z-index:auto;}.tabContent{display:none;position:relative;width:100%;height:100%;overflow:auto;}.tabContentActive{display:block;}div.jxTabContainer{display:block;position:relative;margin:0;padding:2px;border:none;}a.jxTab{display:block;position:relative;cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;margin:0;padding:0;border:none;background-repeat:no-repeat;text-decoration:none;outline:none;}span.jxTabContent{display:block;font-size:0;line-height:0;margin:0;padding:0;border:none;background-repeat:no-repeat;}img.jxTabIcon{position:relative;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxTabLabel{display:block;position:relative;cursor:pointer;mar
 gin:0;padding:0;color:#000;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;}a.jxTabClose{display:block;position:absolute;cursor:pointer;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxTabClose img{width:16px;height:16px;background-image:url(images/tab_close.png);}.jxDisabled a.jxTab,.jxDisabled span.jxTabContent span,.jxDisabled a.jxTabClose{cursor:default;}.jxBarTop div.jxTabContainer,.jxBarBottom div.jxTabContainer{float:left;}.jxBarTop a.jxTab,.jxBarTop span.jxTabContent{background-image:url(images/tab_top.png);}.jxBarBottom a.jxTab,.jxBarBottom span.jxTabContent{background-image:url(images/tab_bottom.png);}.jxBarTop a.jxTabClose,.jxBarBottom a.jxTabClose{top:3px;right:3px;}.jxBarTop .jxTabClose span.jxTabContent,.jxBarBottom .jxTabClose span.jxTabContent{padding-right:16px;}.jxBarTop a.jxTab,.jxBarBottom a.jxTab{float:left;padding-left:4px;background-position:left -24px;}.jxBarTop span.jxTabContent,.jxBarBottom span
 .jxTabContent{float:left;padding:4px 4px 4px 0;background-position:right -24px;}.jxBarTop a.jxTabActive,.jxBarBottom a.jxTabActive{background-position:left -72px;}.jxBarTop a.jxTabActive span.jxTabContent,.jxBarBottom a.jxTabActive span.jxTabContent{background-position:right -72px;}.jxBarTop a.jxTab:focus,.jxBarBottom a.jxTab:focus{background-position:left -96px;}.jxBarTop a.jxTab:focus span.jxTabContent,.jxBarBottom a.jxTab:focus span.jxTabContent{background-position:right -96px;}.jxBarTop a.jxTabActive:focus,.jxBarBottom a.jxTabActive:focus{background-position:left -144px;}.jxBarTop a.jxTabActive:focus span.jxTabContent,.jxBarBottom a.jxTabActive:focus span.jxTabContent{background-position:right -144px;}.jxBarTop a.jxTab:hover,.jxBarTop a.jxTabActive:hover,.jxBarBottom a.jxTab:hover,.jxBarBottom a.jxTabActive:hover{background-position:left -48px;}.jxBarTop a.jxTab:hover span.jxTabContent,.jxBarTop a.jxTabActive:hover span.jxTabContent,.jxBarBottom a.jxTab:hover span.jxTabC
 ontent,.jxBarBottom a.jxTabActive:hover span.jxTabContent{background-position:right -48px;}.jxBarTop a.jxTabPressed,.jxBarTop a.jxTabPressed:focus,.jxBarBottom a.jxTabPressed,.jxBarBottom a.jxTabPressed:focus{background-position:left -120px;}.jxBarTop a.jxTabPressed span.jxTabContent,.jxBarTop a.jxTabPressed:focus span.jxTabContent,.jxBarBottom a.jxTabPressed span.jxTabContent,.jxBarBottom a.jxTabPressed:focus span.jxTabContent{background-position:right -120px;}.jxBarTop .jxDisabled a.jxTab:focus,.jxBarTop .jxDisabled a.jxTab:active,.jxBarTop .jxDisabled a.jxTab:hover,.jxBarTop .jxDisabled a.jxTabPressed,.jxBarBottom .jxDisabled a.jxTab:focus,.jxBarBottom .jxDisabled a.jxTab:active,.jxBarBottom .jxDisabled a.jxTab:hover,.jxBarBottom .jxDisabled a.jxTabPressed{background-position:left -24px;}.jxBarTop .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarTop .jxDisabled a.
 jxTabPressed span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabPressed span.jxTabContent{background-position:right -24px;}.jxBarTop .jxDisabled a.jxTabActive:focus,.jxBarTop .jxDisabled a.jxTabActive:active,.jxBarTop .jxDisabled a.jxTabActive:hover,.jxBarBottom .jxDisabled a.jxTabActive:focus,.jxBarBottom .jxDisabled a.jxTabActive:active,.jxBarBottom .jxDisabled a.jxTabActive:hover{background-position:left -72px;}.jxBarTop .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:right -72
 px;}.jxBarTop img.jxTabIcon,.jxBarBottom img.jxTabIcon{float:left;}.jxBarTop span.jxTabLabel,.jxBarBottom span.jxTabLabel{float:left;height:16px;padding:0 4px 0 4px;}.jxBarLeft a.jxTab,.jxBarLeft span.jxTabContent{background-image:url(images/tab_left.png);}.jxBarRight a.jxTab,.jxBarRight span.jxTabContent{background-image:url(images/tab_right.png);}.jxBarLeft a.jxTabClose,.jxBarRight a.jxTabClose{top:3px;left:3px;}.jxBarLeft .jxTabClose span.jxTabContent,.jxBarRight .jxTabClose span.jxTabContent{padding-top:16px;}.jxBarLeft a.jxTab,.jxBarRight a.jxTab{padding-top:4px;background-position:-24px top;}.jxBarLeft span.jxTabContent,.jxBarRight span.jxTabContent{padding:0 4px 4px 4px;background-position:-24px bottom;}.jxBarLeft a.jxTabActive,.jxBarRight a.jxTabActive{background-position:-72px top;}.jxBarLeft a.jxTabActive span.jxTabContent,.jxBarRight a.jxTabActive span.jxTabContent{background-position:-72px bottom;}.jxBarLeft a.jxTab:focus,.jxBarRight a.jxTab:focus{background-posi
 tion:-96px top;}.jxBarLeft a.jxTab:focus span.jxTabContent,.jxBarRight a.jxTab:focus span.jxTabContent{background-position:-96px bottom;}.jxBarLeft a.jxTabActive:focus,.jxBarRight a.jxTabActive:focus{background-position:-144px top;}.jxBarLeft a.jxTabActive:focus span.jxTabContent,.jxBarRight a.jxTabActive:focus span.jxTabContent{background-position:-144px bottom;}.jxBarLeft a.jxTab:hover,.jxBarLeft a.jxTabActive:hover,.jxBarRight a.jxTab:hover,.jxBarRight a.jxTabActive:hover{background-position:-48px top;}.jxBarLeft a.jxTab:hover span.jxTabContent,.jxBarLeft a.jxTabActive:hover span.jxTabContent,.jxBarRight a.jxTab:hover span.jxTabContent,.jxBarRight a.jxTabActive:hover span.jxTabContent{background-position:-48px bottom;}.jxBarLeft a.jxTabPressed,.jxBarLeft a.jxTabPressed:focus,.jxBarRight a.jxTabPressed,.jxBarRight a.jxTabPressed:focus{background-position:-120px top;}.jxBarLeft a.jxTabPressed span.jxTabContent,.jxBarLeft a.jxTabPressed:focus span.jxTabContent,.jxBarRight a.
 jxTabPressed span.jxTabContent,.jxBarRight a.jxTabPressed:focus span.jxTabContent{background-position:-120px bottom;}.jxBarLeft .jxDisabled a.jxTab:focus,.jxBarLeft .jxDisabled a.jxTab:active,.jxBarLeft .jxDisabled a.jxTab:hover,.jxBarLeft .jxDisabled a.jxTabPressed,.jxBarRight .jxDisabled a.jxTab:focus,.jxBarRight .jxDisabled a.jxTab:active,.jxBarRight .jxDisabled a.jxTab:hover,.jxBarRight .jxDisabled a.jxTabPressed{background-position:-24px top;}.jxBarLeft .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabPressed span.jxTabContent{background-position:-24px bottom;}.jxBarLeft .jxDisabled a.jxTabActive:focus,.jxBarLeft .
 jxDisabled a.jxTabActive:active,.jxBarLeft .jxDisabled a.jxTabActive:hover,.jxBarRight .jxDisabled a.jxTabActive:focus,.jxBarRight .jxDisabled a.jxTabActive:active,.jxBarRight .jxDisabled a.jxTabActive:hover{background-position:-72px top;}.jxBarLeft .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:-72px bottom;}.jxBarLeft span.jxTabLabel,.jxBarRight span.jxTabLabel{padding:4px 0 4px 0;}.jxBarContainer{display:block;position:relative;z-index:1;overflow:hidden;margin:0;padding:0;border:0;background-color:#f0f0f0;}.jxBarTop,.jxBarBottom{width:100%;height:28px;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;ove
 rflow:hidden;}.jxTabBox .jxTabBarTop{background-image:url(images/tabbar.png);background-position:0 bottom;}.jxTabBox .jxTabBarBottom{background-image:url(images/tabbar_bottom.png);background-position:0 top;}.jxBarLeft,.jxBarRight{width:auto;height:100%;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;float:left;overflow:hidden;}.jxTabBox .jxTabBarLeft{background-image:url(images/tabbar_left.png);background-repeat:repeat-y;background-position:right 0;}.jxTabBox .jxTabBarRight{background-image:url(images/tabbar_right.png);background-repeat:repeat-y;background-position:left 0;}.jxBarTop .jxBarScroller,.jxBarBottom .jxBarScroller{position:absolute;width:1000%;overflow:hidden;}.jxBarTop .jxBarScrollLeft,.jxBarBottom .jxBarScrollLeft{position:absolute;top:0;left:0;}.jxBarTop .jxBarScrollRight,.jxBarBottom .jxBarScrollRight{position:absolute;top:0;right:0;}.jxBarTop .jxBarScrollLeft img.jxButtonIcon,.jxBarBottom .jxBarScrollLeft img.jxButt
 onIcon{background-image:url(images/emblems.png);background-position:0 -80px;}.jxBarTop .jxBarScrollRight img.jxButtonIcon,.jxBarBottom .jxBarScrollRight img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -96px;}ul.jxToolbar,ul.jxTabBar{display:block;position:relative;float:left;list-style-type:none;margin:0;padding:0;border:none;}li.jxToolItem{display:block;position:relative;float:left;font-size:0;line-height:0;padding:0;margin:0;border:none;}li.jxToolItem span.jxBarSeparator{display:block;position:relative;float:left;font-size:0;line-height:0;border:0;margin:0;padding:4px;background-repeat:no-repeat;background-position:center center;}.jxBarTop li.jxToolItem span.jxBarSeparator,.jxBarBottom li.jxToolItem span.jxBarSeparator{width:8px;height:20px;background-image:url(images/toolbar_separator_h.png);}.jxBarLeft li.jxToolItem span.jxBarSeparator,.jxBarRight li.jxToolItem span.jxBarSeparator{width:20px;height:8px;background-image:url(images/toolbar_s
 eparator_v.png);}.jxBarLeft ul.jxToolbar,.jxBarLeft ul.jxTabBar,.jxBarLeft li.jxToolItem,.jxBarRight ul.jxToolbar,.jxBarRight ul.jxTabBar,.jxBarRight li.jxToolItem{clear:both;}.jxTree,.jxTreeRoot{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxTreeNest{list-style:none;margin:0;padding:0;background-repeat:repeat-y;background-position:left top;}.jxTree li,.jxTreeRoot li{position:relative;display:block;margin:0;padding:0;background-repeat:no-repeat;background-position:left top;white-space:nowrap;font-size:0;line-height:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxTree li{margin-left:16px;}.jxTree a,.jxTreeRoot a{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;background-image:url(images/tree_hover.png);background-repeat:repeat-x;background-position:left top;border:none;margin:0 1px 0 15px;padding:0 0 0 20px;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;li
 ne-height:20px;height:20px;}.jxTree a:focus,.jxTreeRoot a:focus{border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;margin:0 0 0 14px;background-position:left -72px;}.jxTree a:hover,.jxTreeRoot a:hover{border-left:1px solid #C5E0FF;border-right:1px solid #C5E0FF;margin:0 0 0 14px;background-color:#CDE5FF;background-position:left -24px;}.jxTree a.jxTreeItemPressed,.jxTree a.jxTreeItemPressed:hover,.jxTreeRoot a.jxTreeItemPressed,.jxTreeRoot a.jxTreeItemPressed:hover{border-left:1px solid #C5E0FF;border-right:1px solid #C5E0FF;margin:0 0 0 14px;background-color:#CDE5FF;background-position:left -48px;}.jxTree .jxDisabled a,.jxTreeRoot.jxDisabled a{cursor:default;}.jxTree .jxDisabled a:focus,.jxTree .jxDisabled a.jxTreeItemPressed,.jxTree .jxDisabled a.jxTreeItemPressed:hover,.jxTree .jxDisabled a:hover,.jxTreeRoot .jxDisabled a:focus,.jxTreeRoot .jxDisabled a.jxTreeItemPressed,.jxTreeRoot .jxDisabled a.jxTreeItemPressed:hover,.jxTreeRoot .jxDisabled a:hover{backgroun
 d-position:left top;border:none;margin:0 1px 0 15px;}.jxTreeNest{background-image:url(images/tree_vert_line.png);}.jxTreeImage,.jxTreeIcon{position:absolute;display:inline;left:0;top:0;width:16px;height:20px;z-index:1;background-image:url(images/tree.png);background-repeat:no-repeat;border:0;margin:0;}.jxTreeIcon{height:16px;top:1px;left:1px;}.jxTreeBranchOpen .jxTreeIcon,.jxTreeBranchLastOpen .jxTreeIcon{background-position:left -40px;}.jxTreeBranchOpen .jxTreeImage{background-position:left -100px;}.jxTreeBranchLastOpen .jxTreeImage{background-position:left -160px;}.jxTreeBranchClosed .jxTreeIcon,.jxTreeBranchLastClosed .jxTreeIcon{background-position:left -20px;}.jxTreeBranchClosed .jxTreeImage{background-position:left -80px;}.jxTreeBranchLastClosed .jxTreeImage{background-position:left -140px;}.jxTreeItem .jxTreeIcon,.jxTreeItemLast .jxTreeIcon{background-position:left 0;}.jxTreeItem .jxTreeImage{background-position:left -60px;}.jxTreeItemLast .jxTreeImage{background-posi
 tion:left -120px;}.jxTreeItemSelected{background-color:#AFD4FA;font-weight:bold;}.jxTreeItem a,.jxTreeItem img,.jxTreeItem input,.jxTreeItemLast a,.jxTreeItemLast img,.jxTreeItemLast input,.jxTreeBranch a,.jxTreeBranch img,.jxTreeBranch input,.jxTreeBranchLast a,.jxTreeBranchLast img,.jxTreeBranchLast input{vertical-align:middle;}
\ No newline at end of file
+ */body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;}q:before,q:after{content:'';}.jxButtonContainer{display:-moz-inline-box;display:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:2px;border:none;}.jxButton{display:-moz-inline-box;display:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:0 0 0 4px;border:none;background-image:url(images/button.png);background-position:left -24px;background-repeat:no-repeat;text-decoration:none;outline:none;}a.jxButton{cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;}ul.jxToolbar .jxButton{background-position:left top;}span.jxButtonContent{display:-moz-inline-box;displ
 ay:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:4px 4px 4px 0;border:none;background-image:url(images/button.png);background-position:right -24px;background-repeat:no-repeat;}ul.jxToolbar span.jxButtonContent{background-position:right top;}ul.jxToolbar .jxButtonActive,.jxButtonActive{background-position:left -72px;}ul.jxToolbar .jxButtonActive span.jxButtonContent,.jxButtonActive span.jxButtonContent{background-position:right -72px;}ul.jxToolbar .jxButton:focus,.jxButton:focus{background-position:left -96px;}ul.jxToolbar .jxButton:focus span.jxButtonContent,.jxButton:focus span.jxButtonContent{background-position:right -96px;}ul.jxToolbar .jxButtonActive:focus,.jxButtonActive:focus{background-position:left -144px;}ul.jxToolbar .jxButtonActive:focus span.jxButtonContent,.jxButtonActive:focus span.jxButtonContent{background-position:right -144px;}ul.jxToolbar .jxButton:hover,ul.jxToolbar .jxButtonActive:hover,.jxButton:hover,.jxButtonActive:hover{b
 ackground-position:left -48px;}ul.jxToolbar .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxButtonActive:hover span.jxButtonContent,.jxButton:hover span.jxButtonContent,.jxButtonActive:hover span.jxButtonContent{background-position:right -48px;}ul.jxToolbar .jxButtonPressed,ul.jxToolbar .jxButtonPressed:focus,.jxButtonPressed,.jxButtonPressed:focus{background-position:left -120px;}ul.jxToolbar .jxButtonPressed span.jxButtonContent,ul.jxToolbar .jxButtonPressed:focus span.jxButtonContent,.jxButtonPressed span.jxButtonContent,.jxButtonPressed:focus span.jxButtonContent{background-position:right -120px;}.jxDisabled .jxButton,.jxDisabled span.jxButtonContent span{cursor:default;}ul.jxToolbar .jxDisabled .jxButton:focus,ul.jxToolbar .jxDisabled .jxButton:active,ul.jxToolbar .jxDisabled .jxButton:hover,ul.jxToolbar .jxDisabled .jxButtonPressed{background-position:left top;}.jxDisabled .jxButton:focus,.jxDisabled .jxButton:active,.jxDisabled .jxButton:hover,.jxDisabled .jxButt
 onPressed{background-position:left -24px;}ul.jxToolbar .jxDisabled .jxButton:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right top;}.jxDisabled .jxButton:focus span.jxButtonContent,.jxDisabled .jxButton:active span.jxButtonContent,.jxDisabled .jxButton:hover span.jxButtonContent,.jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right -24px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus,ul.jxToolbar .jxDisabled .jxButtonActive:active,ul.jxToolbar .jxDisabled .jxButtonActive:hover,.jxDisabled .jxButtonActive:focus,.jxDisabled .jxButtonActive:active,.jxDisabled .jxButtonActive:hover{background-position:left -72px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:active span.jxButtonContent,ul.jxToolbar .jxDisab
 led .jxButtonActive:hover span.jxButtonContent,.jxDisabled .jxButtonActive:focus span.jxButtonContent,.jxDisabled .jxButtonActive:active span.jxButtonContent,.jxDisabled .jxButtonActive:hover span.jxButtonContent{background-position:right -72px;}img.jxButtonIcon{display:-moz-inline-box;display:inline-block;position:relative;vertical-align:middle;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxButtonContent span{display:-moz-inline-box;display:inline-block;position:relative;vertical-align:middle;cursor:pointer;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;height:16px;white-space:nowrap;}span.jxButtonContent span.jxButtonLabel{margin:0;padding:0 4px 0 4px;color:#000;font-size:11px;}.jxDiscloser span.jxButtonContent{padding-right:0;}.jxDiscloser span.jxButtonContent span{padding-right:16px;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose{
 position:absolute;display:-moz-inline-box;display:inline-block;padding:4px 0;font-size:0;line-height:0;right:2px;top:2px;background-image:url(images/button_multi_disclose.png);background-position:right 0;background-repeat:no-repeat;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxButtonDisclose img{width:16px;height:16px;margin:0;padding:0;border:0;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose:focus,a.jxButtonDisclose:active{background-position:right -96px;}a.jxButtonDisclose:hover{background-position:right -48px;}a.jxButtonDisclosePressed{background-position:right -120px;}.jxDisabled a.jxButtonDisclose,.jxDisabled a.jxButtonDisclose:focus,.jxDisabled a.jxButtonDisclose:active,.jxDisabled a.jxButtonDisclose:hover,.jxDisabled a.jxButtonDisclosePressed{cursor:default;background-position:right 0;}ul.jxToolbar .jxButtonHover{background-position:left -24px!important;}ul.jxTo
 olbar .jxButtonHover span.jxButtonContent{background-position:right -24px!important;}.jxFlyout .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxFlyout{position:absolute;display:block;z-index:100;margin:0;padding:0;}.jxFlyoutContent{position:relative;display:block;overflow:auto;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}.jxButtonMulti,.jxButtonMulti span.jxButtonContent{background-image:url(images/button_multi.png);}.jxButtonEditCombo,.jxButtonEditCombo span.jxButtonContent{background-image:url(images/button_combo.png);}.jxButtonMulti span.jxButtonContent span{padding-right:21px;}.jxButtonEditCombo span.jxButtonContent span{font-size:0;}.jxButtonComboDefault span.jxButtonContent span,.jxButtonComboDefault input{font-style:italic;color:#999;}.jxButtonEditCombo input{float:left;line-height:16px;height:16px;padding:0 4px;margin:0;border:none;font-size:11px;font-family:Arial,Helvetica,sans-serif;background-color:transpa
 rent;}.jxChrome{position:absolute;display:block;font-size:0;line-height:0;z-index:-1;width:100%;height:100%;top:0;left:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxChromeDrag{opacity:.5;-ms-filter:"Alpha(opacity=50)";}.jxChromeTL{position:absolute;overflow:hidden;left:0;top:0;width:50%;height:50%;}.jxChromeTR{position:absolute;overflow:hidden;left:50%;top:0;width:50%;height:50%;}.jxChromeBL{position:absolute;overflow:hidden;left:0;top:50%;width:50%;height:50%;}.jxChromeBR{position:absolute;overflow:hidden;left:50%;top:50%;width:50%;height:50%;}.jxChromeTL img{position:absolute;top:0;left:0;width:1000px;height:600px;}.jxChromeTR img{position:absolute;top:0;right:0;width:1000px;height:600px;}.jxChromeBL img{position:absolute;bottom:0;left:0;width:1000px;height:600px;}.jxChromeBR img{position:absolute;bottom:0;right:0;width:1000px;height:600px;}.jxColorBar{position:relative;overflow:hidden;}table.jxColorGrid{position:relative;border-collapse:collapse;emp
 ty-cells:show;clear:both;padding:0;margin:0;}.jxColorGrid tr{padding:0;margin:0;}.jxColorGrid td{border:1px solid #000;padding:0;margin:0;}.jxColorGrid td.emptyCell{border:0 solid #000;}.jxColorGrid td.emptyCell span{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;padding:1px;margin:0;}.jxColorGrid a.colorSwatch{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;margin:0;padding:1px;}.jxColorGrid a.borderWhite:hover{border:1px solid #fff;padding:0;}.jxColorGrid a.borderBlack:hover{border:1px solid #000;padding:0;}input.jxHexInput{width:55px;vertical-align:middle;}input.jxAlphaInput{width:30px;vertical-align:middle;}div.jxColorPreview{float:left;position:relative;width:20px;height:20px;border:1px solid #000;margin:2px;vertical-align:middle;background-image:url('images/grid.png');overflow:hidden;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch{display:block;float:left;width:14px;height:14px;border:1px soli
 d #000;background-image:url('images/grid.png');background-position:0 0;background-repeat:repeat;padding-right:0!important;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch span{display:block;width:14px;height:14px;position:absolute;padding-right:0;background:none;}div.jxColorPreview img{position:absolute;z-index:0;}div.jxColorPreview div{width:20px;height:10px;position:absolute;display:block;left:0;z-index:1;font-size:10px;line-height:0;}div.jxColorPreview div.jxColorSelected{top:0;}div.jxColorPreview div.jxColorHover{bottom:0;}label.jxColorLabel,label.jxAlphaLabel{width:auto;font-family:Arial,sans-serif;font-size:11px;line-height:24px;padding:2px;vertical-align:middle;}a.jxColorClose{position:absolute;top:0;right:0;width:16px;height:16px;}a.jxColorClose img{width:16px;height:16px;}.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;margin:0;padding:0;}.jxDisabled{opacity:.4;-ms-filter:"Alpha(opacity=40)";}.jxDi
 sabled *{-ms-filter:"Alpha(opacity=40)";}iframe.jxIframeShim{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;-ms-filter:"Alpha(opacity=0)";z-index:-1;}.jxConfirmQuestion{text-align:center;padding:10px;margin-top:10px;}.jxDialog .jxChrome{background-image:url(images/dialog_chrome.png);}.jxDialog{display:block;z-index:1000;overflow:hidden;}.jxDialogContentContainer{z-index:1;margin:0 11px 13px 12px;border:1px solid #b7b7b7;background-color:#f0f0f0;}.jxDialogModal{position:absolute;display:block;top:0;left:0;width:100%;height:100%;background-color:#000;opacity:.2;-ms-filter:"Alpha(opacity=20)";}.jxDialogContent{display:block;position:relative;overflow:auto;padding:0;z-index:1;}.jxDialogTitle{display:block;position:relative;background-image:url(images/a_pixel.png);text-align:center;height:24px;line-height:24px;z-index:1;margin:6px 6px 0 7px;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxDialogMin .jxDialogTitle{margin-bottom:8px;}.jxDialogMove
 able,.jxDialogMoveable .jxDialogLabel{cursor:move;}.jxDialogIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxDialogLabel{font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;cursor:default;}.jxDialogResize{position:absolute;bottom:7px;right:6px;width:16px;height:16px;z-index:2;border:0;cursor:se-resize;background-image:url(images/dialog_resize.png);}.jxDialogControls{position:absolute;top:3px;right:2px;height:16px;width:80px;}.jxDialogControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxDialogClose img{background-position:0 -32px;}.jxDialogMenu img{background-position:0 -48px;}.jxDialogHelp img{background-position:0 -64px;}.jxDialogCollapse img{background-position:0 -16px;}.jxDialogMin .jxDialogCollapse img{background-position:0 0;}.jxDialogMax .jxDialogCollapse img{background-posi
 tion:0 -16px;}.jxDialogLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxDialogControls .jxButtonContainer,.jxDialogControls span.jxButtonContent,.jxDialogControls .jxButton:hover span.jxButtonContent,.jxDialogControls .jxButton:active span.jxButtonContent,.jxDialogControls .jxButtonActive span.jxButtonContent,.jxDialogControls .jxButtonActive:hover span.jxButtonContent,.jxDialogControls .jxButtonActive:active span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:hover span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:active span.jxButtonContent,.jxDialogControls .jxButton,.jxDialogControls .jxButton:hover,.jxDialogControls .jxButton:active,.jxDialogControls .jxButtonActive,.jxDialogControls .jxButtonActive:hover,.jxDialogControls .jxButtonActive:active,.jxDialogControls .jxDisabled .jxButton,.jxDialogControls .jxDisabled .jxButton:hover,
 .jxDialogControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent,.jxDialogControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent span,.jxDialogControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxDialogControls .jxBarContainer{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxDialogControls .jxBarScroller{left:auto;right:0;}.jxDialogControls ul.jxToolbar{float:right;}.jxDialogControls ul.jxToolbar,.jxDialogControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}div.jxFileInputs{position:relative;}div.jxFileFake{position:absolute;top:0;left:0;z-index:1;}div.jxFileFake span{float:left;display:block;}div.jxFileFake .jxInputContainer{width:150px;}div.jxFileF
 ake .jxInputText{width:135px;}div.jxFileFake .jxButtonContainer{margin-top:2px;float:left;}.jxInputFile{position:relative;text-align:right;-moz-opacity:0;filter:alpha(opacity:0);opacity:0;z-index:2;margin-top:-5px;height:35px;}.jxForm{display:block;position:relative;overflow:hidden;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxFieldset{display:block;position:relative;overflow:hidden;border:1px solid #ccc;border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:10px;-khtml-border-radius:10px;margin:10px;padding:5px;}.jxFieldsetLegend,.jxFieldset legend{position:relative;margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:26px;color:#000;}.jxInputContainer{display:block;position:relative;padding:0;margin:2px;border:none;}.jxInputLabel,.jxInputTag{display:-moz-inline-box;display:inline-block;margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:26px;color:#000;}.jxInp
 utLabel{vertical-align:top;}.jxInputTag{vertical-align:bottom;}.jxInputText,.jxInputPassword,.jxInputTextarea{margin:0 4px;padding:4px;border:1px solid #bbb;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputSelect{margin:0 4px;padding:3px 4px 3px 1px;border:1px solid #bbb;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputText:focus,.jxInputPassword:focus,.jxInputTextarea:focus,.jxInputSelect:focus{border:1px solid #000;}.jxInputRadio,.jxInputCheck{margin:5px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputContainer .jxButtonContainer{padding:0;margin:0 4px;}.jxInputGroup{border:none;padding:0;margin:2px;}.jxInputGroup legend{font-size:0;line-height:0;padding:0;margin:0;border:none;}.jxInputGroup .jxFieldsetLegend{font-size:12px;}.jxInputGroup .jxInputLabel{width:auto;}.jxFieldError .jxInputText,.jxFieldError .jxInputPassword,.jxFieldError .jxInputTexta
 rea,.jxFieldError .jxInputSelect{background-color:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;}.jxFieldSuccess .jxInputText,.jxFieldSuccess .jxInputPassword,.jxFieldSuccess .jxInputTextarea,.jxFieldSuccess .jxInputSelect{background-color:#E6EFC2;color:#264409;border-color:#C6D880;}.jxFieldError .jxInputText:focus,.jxFieldError .jxInputPassword:focus,.jxFieldError .jxInputTextarea:focus,.jxFieldError .jxInputSelect:focus{border-color:#8a1f11;}.jxFieldSuccess .jxInputText:focus,.jxFieldSuccess .jxInputPassword:focus,.jxFieldSuccess .jxInputTextarea:focus,.jxFieldSuccess .jxInputSelect:focus{border-color:#264409;}.jxFieldError .jxInputLabel,.jxFieldError .jxInputTag{color:#8a1f11;}.jxFieldSuccess .jxInputLabel,.jxFieldSuccess .jxInputTag{color:#264409;}.jxFormInline .jxInputContainer,form .jxFormInline .jxInputContainer,form .jxFieldset span.jxFormInline,form.jxForm span.jxFormInline{display:inline;}.jxFormInline .jxInputLabel,form .jxFormInline .jxInputLabel,form span.jxFormInl
 ine .jxInputLabel{display:inline;width:auto;}.jxFormInline .jxInputTag,form .jxFormInline .jxInputTag,form span.jxFormInline .jxInputTag{display:inline;}.jxFormInline .jxInputGroup,form .jxFormInline .jxInputGroup{padding-left:0;}.jxFormInline .jxInputGroup .jxFieldsetLegend,form .jxFormInline .jxInputGroup .jxFieldsetLegend{position:relative;left:auto;}.jxFormInline .jxInputGroup .jxInputLabel,form .jxFormInline .jxInputGroup .jxInputLabel{display:inline;width:auto;}.jxFormBlock .jxInputContainer,form .jxFormBlock .jxInputContainer,form .jxFieldset span.jxFormBlock,form.jxform span.jxFormBlock{display:block;}.jxFormBlock .jxInputLabel,form .jxFormBlock .jxInputLabel,form span.jxFormBlock .jxInputLabel{display:block;width:auto;margin-left:4px;}.jxFormBlock .jxInputContainerCheck .jxInputLabel,.jxFormBlock .jxInputContainerRadio .jxInputLabel,form .jxFormBlock .jxInputContainerCheck .jxInputLabel,form .jxFormBlock .jxInputContainerRadio .jxInputLabel,form span.jxFormBlock .jx
 InputContainerCheck .jxInputLabel,form span.jxFormBlock .jxInputContainerRadio .jxInputLabel{display:-moz-inline-box;display:inline-block;}.jxFormBlock .jxInputGroup,form .jxFormBlock .jxInputGroup{padding-left:0;}.jxFormBlock .jxInputGroup .jxFieldsetLegend,form .jxFormBlock .jxInputGroup .jxFieldsetLegend{position:relative;left:auto;}.jxFormBlock .jxInputGroup .jxInputLabel,form .jxFormBlock .jxInputGroup .jxInputLabel{display:-moz-inline-box;display:inline-block;width:auto;}.jxFormInlineblock .jxInputContainer,form .jxFormInlineblock .jxInputContainer,form .jxFieldset span.jxFormInlineblock,form.jxForm span.jxFormInlineblock{display:block;}.jxFormInlineblock .jxInputLabel,form .jxFormInlineblock .jxInputLabel,form span.jxFormInlineblock .jxInputLabel{display:-moz-inline-box;display:inline-block;width:200px;}.jxFormInlineblock .jxInputGroup,form .jxFormInlineblock .jxInputGroup{padding-left:200px;}.jxFormInlineblock .jxInputGroup .jxFieldsetLegend,form .jxFormInlineblock .
 jxInputGroup .jxFieldsetLegend{position:absolute;left:-200px;width:200px;}.jxFormInlineblock .jxInputGroup .jxInputLabel,form .jxFormInlineblock .jxInputGroup .jxInputLabel{display:-moz-inline-box;display:inline-block;width:auto;}.jxGridContainer{position:absolute;top:0;left:0;border-left:0 solid #d8d8d8;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;overflow:hidden;}.jxGridTable{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;cursor:default;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal;}.jxGridHeader{width:100%;}.jxGridTableBody{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;cursor:default;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal;}.jxGridColHeadHide{height:0;line-height:0;font-size:0;background-color:#fff;white-space:normal;}.jxGridColHeadHide p,.jxGridRowHeadHide p{font-size:0;line-height:0;height:0;margin:0;padd
 ing:0;}.jxGridRowHeadHide{width:0;white-space:normal;}.jxGridCell{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;overflow:hidden;}.jxGridCellContent{position:relative;display:-moz-inline-box;display:inline-block;overflow:hidden;padding-left:3px;padding-right:3px;overflow:hidden;white-space:nowrap;cursor:cell;text-overflow:ellipsis;}.jxGridColHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_col.png');background-position:0 0;background-repeat:repeat-x;text-align:center;font-weight:bold;color:#333;cursor:default;padding-left:3px;padding-right:3px;white-space:nowrap;}.jxGridRowHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_row.png');background-
 position:0 0;background-repeat:repeat-y;text-align:center;font-weight:bold;color:#333;cursor:default;overflow:hidden;white-space:nowrap;}.jxGridRowAll{background-color:#fff;}.jxGridColumnHeaderSelected{background-color:#e1e1e1;background-position:0 -200px;}.jxGridRowHeaderSelected{background-color:#e1e1e1;background-position:-400px 0;}.jxGridColumnSelected{background-color:#f7f7f7;}.jxGridRowSelected td{background-color:#f7f7f7;}td.jxGridCellSelected{background-color:#ebebeb;}.jxGridColumnHeaderPrelight{background-color:#cee5ff;background-position:0 -300px;}.jxGridRowHeaderPrelight{background-color:#cee5ff;background-position:-600px 0;}.jxGridColumnPrelight{background-color:#e5f1ff;}.jxGridRowPrelight td{background-color:#e5f1ff;}td.jxGridCellPrelight{background-color:#cce3ff;}.jxColSortable{padding-right:20px;}.jxGridCell.jxColSortable{padding-right:0;}.jxColSortable span{background-image:url('images/emblems.png');padding-right:20px;background-repeat:no-repeat;background-po
 sition:right top;}.jxGridColumnSortedAsc span{background-position:right -160px;}.jxGridColumnSortedDesc span{background-position:right -16px;}.jxGridColumnResize,.jxGridRowResize{display:block;position:absolute;background-image:url(images/a_pixel.png);z-index:1;}.jxGridColumnResize{right:0;top:0;height:100%;width:4px;cursor:col-resize;}.jxGridRowResize{left:0;bottom:0;height:4px;width:100%;cursor:row-resize;}.jxListView .jxListItemContainer{position:relative;display:block;outline:none;overflow:hidden;border:none;margin:0 1px;padding:0;}.jxListItem{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;border:none;margin:0 1px;padding:0;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;line-height:20px;height:20px;}.jxListView .jxHover{margin:0;border-left:1px solid #CDDFFD;border-right:1px solid #CDDFFD;background-image:url(images/listitem.png);background-repeat:repeat-x;background-color:#CDE5FF;background-po
 sition:left -24px;}.jxListItem:focus{margin:0;border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;background-image:url(images/listitem.png);background-repeat:repeat-x;background-position:left -72px;}.jxListView .jxPressed,.jxListView .jxSelected{margin:0;border-left:1px solid #8AABFB;border-right:1px solid #8AABFB;background-color:#CDE5FF;background-image:url(images/listitem.png);background-repeat:repeat-x;background-position:left -48px;}.jxMenuContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxButtonMenu span.jxMenuItemSpan{padding-right:16px;}.jxMenuContainer{position:absolute;top:0;left:-10000px;display:none;z-index:2000;padding:0;}ul.jxMenu{display:block;position:relative;list-style-type:none;padding:1px;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}li.jxMenuItemContainer{display:block;position:relative;font-size:0;line-height:0;margin:0;padding:0;user-select:none;-moz-user-select:none;-khtml-us
 er-select:none;}a.jxMenuItem{display:block;position:relative;overflow:hidden;text-decoration:none;cursor:pointer;outline:none;border:1px solid #fff;background-image:url(images/menuitem.png);background-repeat:no-repeat;background-position:left top;font-family:Arial,Helvetica,sans-serif;font-size:11px;text-decoration:none;margin:0;padding:0;color:#000;}a.jxMenuItemActive{background-position:left -98px;}a.jxMenuItem:focus{background-position:left -74px;}a.jxMenuItem:focus span.jxMenuItemContent{border-right:1px dotted #75ADFF;}a.jxMenuItemActive:focus{background-position:left -170px;}a.jxMenuItem:hover{background-color:#CDE5FF;background-position:left -26px;}a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #C5E0FF;}a.jxMenuItemActive:hover{background-position:left -122px;}a.jxMenuItemPressed,a.jxMenuItemPressed:hover{background-color:#CDE5FF;background-position:left -50px;}.jxDisabled a.jxMenuItem,.jxDisabled span.jxMenuItemContent span{cursor:default;}.jxDisabl
 ed a.jxMenuItem:focus,.jxDisabled a.jxMenuItemPressed,.jxDisabled a.jxMenuItemPressed:hover,.jxDisabled a.jxMenuItem:hover{background-color:#fff;background-position:left top;border-right:1px solid #fff;}.jxDisabled a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #fff;}span.jxMenuItemContent{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:0;line-height:0;white-space:nowrap;padding:0 20px 0 0;margin:0;border-right:1px solid #fff;}.jxButtonSubMenu span.jxMenuItemContent,.jxButtonSubMenu:hover span.jxMenuItemContent{background-image:url(images/emblems.png);background-position:right -30px;background-repeat:no-repeat;}img.jxMenuItemIcon{position:absolute;top:2px;left:2px;width:16px;height:16px;background-position:left center;background-repeat:no-repeat;}span.jxMenuItemContent span{display:block;position:relative;cursor:pointer;margin:0;padding:2px 0 2px 22px;font-size:16px;line-height:16px;color:#000;}span.jxMenuItemContent span.jx
 MenuItemLabel{color:#000;font-size:11px;}.jxMenuItemToggle img.jxMenuItemIcon,.jxMenuItemToggleSet img.jxMenuItemIcon{background-image:url(images/emblems.png);background-position:2px 0;background-repeat:no-repeat;}.jxMenuItemToggle a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -48px;}.jxMenuItemToggleSet a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -64px;}ul.jxMenu span.jxMenuSeparator{display:block;font-size:10px;line-height:10px;background-image:url(images/toolbar_separator_v.png);background-repeat:repeat-x;background-position:left center;}.jxMessage{text-align:center;padding:10px;margin-top:10px;}.jxNoticeListContainer{border:none;padding:0;margin:0;font-size:0;line-height:0;z-index:500;}.jxNoticeList{position:relative;}.jxNoticeItemContainer{position:relative;overflow:hidden;}.jxNoticeItemContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxHasChrome .jxNoticeItem{margin:6px 6px 8px 7px;}.jxNotice
 Item{position:relative;border:2px solid #ccc;margin:0;padding:0;background-color:#f8f8f8;background-image:url(images/notice.png);background-repeat:repeat-x;background-position:left bottom;}.jxNoticeIcon{position:absolute;top:0;left:0;margin:6px;width:16px;height:16px;background-image:url(images/icons.png);background-repeat:no-repeat;background-position:0 0;}.jxNotice{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;color:#666;margin:0;border:none;padding:6px 26px 6px 10px;}.jxNoticeError .jxNoticeItem{background-color:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;background-image:url(images/notice_error.png);}.jxNoticeWarning .jxNoticeItem{background-color:#FFF6BF;color:#514721;border-color:#FFD324;background-image:url(images/notice_warning.png);}.jxNoticeSuccess .jxNoticeItem{background-color:#E6EFC2;color:#264409;border-color:#C6D880;background-image:url(images/notice_success.png);}.jxNoticeError .jxNotice{color:#8a1f11;pa
 dding-left:28px;}.jxNoticeWarning .jxNotice{color:#514721;padding-left:28px;}.jxNoticeSuccess .jxNotice{color:#264409;padding-left:28px;}.jxNoticeInfo .jxNotice{padding-left:28px;}.jxNoticeError .jxNoticeIcon{background-position:0 -32px;}.jxNoticeWarning .jxNoticeIcon{background-position:0 -16px;}.jxNoticeSuccess .jxNoticeIcon{background-position:0 -48px;}.jxNoticeInfo .jxNoticeIcon{background-position:0 -64px;}.jxNoticeClose{position:absolute;top:0;right:0;margin:6px;width:16px;height:16px;background-image:url(images/tab_close.png);background-position:0 0;background-repeat:no-repeat;}.jxPanel{display:block;position:relative;}.jxPanelContentContainer{overflow:hidden;background-color:#f0f0f0;}.jxPanelContent{position:relative;display:block;overflow:auto;background-color:#fff;margin:0;padding:0;}.jxPanelTitle{display:block;position:relative;background-image:url(images/panelbar.png);background-repeat:repeat-x;background-position:left top;height:22px;margin:0;padding:0;text-alig
 n:center;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxPanelBar{position:absolute;line-height:1px;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxPanelIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxPanelLabel{padding-left:25px;font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;}.jxPanelControls{position:absolute;top:3px;right:2px;height:16px;width:80px;overflow:hidden;}.jxPanelControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxPanelClose img{background-position:0 -32px;}.jxPanelMenu img{background-position:0 -48px;}.jxPanelHelp img{background-position:0 -64px;}.jxPanelCollapse img{background-position:0 -16px;}.jxPanelMin .jxPanelCollapse img{background-position:0 0;}.jxPanelMax .jxPanelCollapse img{background-position:0 -16p
 x;}.jxPanelMaximize img{background-position:0 0;}.jxPanelLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxPanelControls .jxButtonContainer,.jxPanelControls span.jxButtonContent,.jxPanelControls .jxButton:hover span.jxButtonContent,.jxPanelControls .jxButton:active span.jxButtonContent,.jxPanelControls .jxButtonActive span.jxButtonContent,.jxPanelControls .jxButtonActive:hover span.jxButtonContent,.jxPanelControls .jxButtonActive:active span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton:hover span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton:active span.jxButtonContent,.jxPanelControls .jxButton,.jxPanelControls .jxButton:hover,.jxPanelControls .jxButton:active,.jxPanelControls .jxButtonActive,.jxPanelControls .jxButtonActive:hover,.jxPanelControls .jxButtonActive:active,.jxPanelControls .jxDisabled .jxButton,.jxPanelControls .jxDisabled 
 .jxButton:hover,.jxPanelControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent,.jxPanelControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent span,.jxPanelControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxPanelControls div.jxBarTop{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxPanelControls .jxBarScroller{left:auto;right:0;}.jxPanelControls ul.jxToolbar{float:right;}.jxPanelControls ul.jxToolbar,.jxPanelControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxProgressBar-container{width:100%;display:block;clear:both;}.jxProgressBar-message{color:black;}.jxProgressBar-container .jxProgressBar{width:100%;clear:both;border-top:none;}.jxProgressBar-c
 ontainer .jxProgressBar .jxProgressBar-outline{border:1px solid #360;position:absolute;top:0;left:0;z-index:10;}.jxProgressBar-container .jxProgressBar .jxProgressBar-fill{background-color:#9c6;position:absolute;top:1px;left:1px;z-index:20;}.jxProgressBar-container .jxProgressBar .jxProgressBar-text{color:#360;margin:0;padding:2px;position:absolute;top:1px;left:1px;width:auto;z-index:30;border:none;}.jxSliderContainer{width:100%;height:10px;border:1px solid black;}.jxSliderKnob{width:10px;height:10px;background-color:black;cursor:pointer;}.jxSplitterMask{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background-image:url(images/a_pixel.png);z-index:1;}.jxSplitBarHorizontal{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:5px;height:100%;cursor:col-resize;background-color:#f0f0f0;z-index:2;}.jxSplitBarVertical{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:100%
 ;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxSplitContainer{display:block;position:relative;margin:0;padding:0;border:none;overflow:hidden;}.jxSplitArea{display:block;position:absolute;margin:0;padding:0;border:none;z-index:0;}.jxSplitBarDrag{background-color:#eee;}.jxSnapHorizontalBefore{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxSnapHorizontalAfter{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxTabSetContainer{position:relative;display:-moz-inline-box;display:inline-block;overflow:hidden;width:200px;height:200px;margin:0;padding:0;background-color:#fff;}.jxTabSetContainer .jxToolbarContainer{z-index:auto;}.tabContent{display:none;position:relative;width:100%;height:100%;overflow:auto;}.tabContentActive{display:block;}span.jxTabContainer{display:block;position:relative;margin:0;padding:2px;border:none;}a.jxTab{display:-moz-inline-box;display:inline-block;position:relative;cursor:pointe
 r;user-select:none;-moz-user-select:none;-khtml-user-select:none;margin:0;padding:0;border:none;background-repeat:no-repeat;text-decoration:none;outline:none;}span.jxTabContent{display:-moz-inline-box;display:inline-block;font-size:0;line-height:0;margin:0;padding:0;border:none;background-repeat:no-repeat;}img.jxTabIcon{display:-moz-inline-box;display:inline-block;position:relative;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxTabLabel{display:-moz-inline-box;display:inline-block;position:relative;cursor:pointer;margin:0;padding:0;color:#000;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;}a.jxTabClose{display:block;position:absolute;cursor:pointer;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;width:16px;height:16px;background-image:url(images/tab_close.png);background-position:0 0;background-repeat:no-repeat;}.jxDisabled a.jxTab,.jxDisabled span.jxTabContent span,.jxDisabled 
 a.jxTabClose{cursor:default;}.jxBarTop a.jxTab,.jxBarTop span.jxTabContent{background-image:url(images/tab_top.png);}.jxBarBottom a.jxTab,.jxBarBottom span.jxTabContent{background-image:url(images/tab_bottom.png);}.jxBarTop a.jxTabClose,.jxBarBottom a.jxTabClose{top:5px;right:5px;}.jxBarTop .jxTabClose span.jxTabContent,.jxBarBottom .jxTabClose span.jxTabContent{padding-right:16px;}.jxBarTop a.jxTab,.jxBarBottom a.jxTab{padding-left:4px;background-position:left -24px;}.jxBarTop span.jxTabContent,.jxBarBottom span.jxTabContent{padding:4px 4px 4px 0;background-position:right -24px;}.jxBarTop a.jxTabActive,.jxBarBottom a.jxTabActive{background-position:left -72px;}.jxBarTop a.jxTabActive span.jxTabContent,.jxBarBottom a.jxTabActive span.jxTabContent{background-position:right -72px;}.jxBarTop a.jxTab:focus,.jxBarBottom a.jxTab:focus{background-position:left -96px;}.jxBarTop a.jxTab:focus span.jxTabContent,.jxBarBottom a.jxTab:focus span.jxTabContent{background-position:right -96
 px;}.jxBarTop a.jxTabActive:focus,.jxBarBottom a.jxTabActive:focus{background-position:left -144px;}.jxBarTop a.jxTabActive:focus span.jxTabContent,.jxBarBottom a.jxTabActive:focus span.jxTabContent{background-position:right -144px;}.jxBarTop a.jxTab:hover,.jxBarTop a.jxTabActive:hover,.jxBarBottom a.jxTab:hover,.jxBarBottom a.jxTabActive:hover{background-position:left -48px;}.jxBarTop a.jxTab:hover span.jxTabContent,.jxBarTop a.jxTabActive:hover span.jxTabContent,.jxBarBottom a.jxTab:hover span.jxTabContent,.jxBarBottom a.jxTabActive:hover span.jxTabContent{background-position:right -48px;}.jxBarTop a.jxTabPressed,.jxBarTop a.jxTabPressed:focus,.jxBarBottom a.jxTabPressed,.jxBarBottom a.jxTabPressed:focus{background-position:left -120px;}.jxBarTop a.jxTabPressed span.jxTabContent,.jxBarTop a.jxTabPressed:focus span.jxTabContent,.jxBarBottom a.jxTabPressed span.jxTabContent,.jxBarBottom a.jxTabPressed:focus span.jxTabContent{background-position:right -120px;}.jxBarTop .jxDis
 abled a.jxTab:focus,.jxBarTop .jxDisabled a.jxTab:active,.jxBarTop .jxDisabled a.jxTab:hover,.jxBarTop .jxDisabled a.jxTabPressed,.jxBarBottom .jxDisabled a.jxTab:focus,.jxBarBottom .jxDisabled a.jxTab:active,.jxBarBottom .jxDisabled a.jxTab:hover,.jxBarBottom .jxDisabled a.jxTabPressed{background-position:left -24px;}.jxBarTop .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarTop .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabPressed span.jxTabContent{background-position:right -24px;}.jxBarTop .jxDisabled a.jxTabActive:focus,.jxBarTop .jxDisabled a.jxTabActive:active,.jxBarTop .jxDisabled a.jxTabActive:hover,.jxBarBottom .jxDisabled a.jxTabActive:focus,.jxBarBottom .jx
 Disabled a.jxTabActive:active,.jxBarBottom .jxDisabled a.jxTabActive:hover{background-position:left -72px;}.jxBarTop .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:right -72px;}.jxBarTop img.jxTabIcon,.jxBarBottom img.jxTabIcon{vertical-align:middle;}.jxBarTop span.jxTabLabel,.jxBarBottom span.jxTabLabel{vertical-align:middle;height:16px;padding:0 4px 0 4px;}.jxBarLeft a.jxTab,.jxBarLeft span.jxTabContent{background-image:url(images/tab_left.png);}.jxBarRight a.jxTab,.jxBarRight span.jxTabContent{background-image:url(images/tab_right.png);}.jxBarLeft a.jxTabClose,.jxBarRight a.jxTabClose{top:5px;left:5px;}.jxBarLeft .jxTabClose span.jxTabContent,.
 jxBarRight .jxTabClose span.jxTabContent{padding-top:16px;}.jxBarLeft a.jxTab,.jxBarRight a.jxTab{padding-top:4px;background-position:-24px top;}.jxBarLeft span.jxTabContent,.jxBarRight span.jxTabContent{padding:0 4px 4px 4px;background-position:-24px bottom;}.jxBarLeft a.jxTabActive,.jxBarRight a.jxTabActive{background-position:-72px top;}.jxBarLeft a.jxTabActive span.jxTabContent,.jxBarRight a.jxTabActive span.jxTabContent{background-position:-72px bottom;}.jxBarLeft a.jxTab:focus,.jxBarRight a.jxTab:focus{background-position:-96px top;}.jxBarLeft a.jxTab:focus span.jxTabContent,.jxBarRight a.jxTab:focus span.jxTabContent{background-position:-96px bottom;}.jxBarLeft a.jxTabActive:focus,.jxBarRight a.jxTabActive:focus{background-position:-144px top;}.jxBarLeft a.jxTabActive:focus span.jxTabContent,.jxBarRight a.jxTabActive:focus span.jxTabContent{background-position:-144px bottom;}.jxBarLeft a.jxTab:hover,.jxBarLeft a.jxTabActive:hover,.jxBarRight a.jxTab:hover,.jxBarRight 
 a.jxTabActive:hover{background-position:-48px top;}.jxBarLeft a.jxTab:hover span.jxTabContent,.jxBarLeft a.jxTabActive:hover span.jxTabContent,.jxBarRight a.jxTab:hover span.jxTabContent,.jxBarRight a.jxTabActive:hover span.jxTabContent{background-position:-48px bottom;}.jxBarLeft a.jxTabPressed,.jxBarLeft a.jxTabPressed:focus,.jxBarRight a.jxTabPressed,.jxBarRight a.jxTabPressed:focus{background-position:-120px top;}.jxBarLeft a.jxTabPressed span.jxTabContent,.jxBarLeft a.jxTabPressed:focus span.jxTabContent,.jxBarRight a.jxTabPressed span.jxTabContent,.jxBarRight a.jxTabPressed:focus span.jxTabContent{background-position:-120px bottom;}.jxBarLeft .jxDisabled a.jxTab:focus,.jxBarLeft .jxDisabled a.jxTab:active,.jxBarLeft .jxDisabled a.jxTab:hover,.jxBarLeft .jxDisabled a.jxTabPressed,.jxBarRight .jxDisabled a.jxTab:focus,.jxBarRight .jxDisabled a.jxTab:active,.jxBarRight .jxDisabled a.jxTab:hover,.jxBarRight .jxDisabled a.jxTabPressed{background-position:-24px top;}.jxBarLe
 ft .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabPressed span.jxTabContent{background-position:-24px bottom;}.jxBarLeft .jxDisabled a.jxTabActive:focus,.jxBarLeft .jxDisabled a.jxTabActive:active,.jxBarLeft .jxDisabled a.jxTabActive:hover,.jxBarRight .jxDisabled a.jxTabActive:focus,.jxBarRight .jxDisabled a.jxTabActive:active,.jxBarRight .jxDisabled a.jxTabActive:hover{background-position:-72px top;}.jxBarLeft .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive
 :focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:-72px bottom;}.jxBarLeft span.jxTabLabel,.jxBarRight span.jxTabLabel{padding:4px 0 4px 0;}.jxBarContainer{display:block;position:relative;z-index:1;overflow:hidden;margin:0;padding:0;border:0;background-color:#f0f0f0;}.jxBarTop,.jxBarBottom{width:100%;height:28px;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;overflow:hidden;}.jxTabBox .jxTabBarTop{background-image:url(images/tabbar.png);background-position:0 bottom;}.jxTabBox .jxTabBarBottom{background-image:url(images/tabbar_bottom.png);background-position:0 top;}.jxBarLeft,.jxBarRight{width:auto;height:100%;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;float:left;overflow:hidden;}.jxTabBox .jxTabBarLeft{background-image:url(images/tabbar_left.png);background-repeat:
 repeat-y;background-position:right 0;}.jxTabBox .jxTabBarRight{background-image:url(images/tabbar_right.png);background-repeat:repeat-y;background-position:left 0;}.jxBarTop .jxBarScroller,.jxBarBottom .jxBarScroller{position:absolute;width:10000%;overflow:hidden;}.jxBarTop .jxBarScrollLeft,.jxBarBottom .jxBarScrollLeft{position:absolute;top:0;left:0;}.jxBarTop .jxBarScrollRight,.jxBarBottom .jxBarScrollRight{position:absolute;top:0;right:0;}.jxBarTop .jxBarScrollLeft img.jxButtonIcon,.jxBarBottom .jxBarScrollLeft img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -80px;}.jxBarTop .jxBarScrollRight img.jxButtonIcon,.jxBarBottom .jxBarScrollRight img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -96px;}ul.jxToolbar,ul.jxTabBar{display:block;position:relative;float:left;list-style-type:none;margin:0;padding:0;border:none;}li.jxToolItem{display:block;position:relative;float:left;font-size:0;line-height:0;padding:0;margi
 n:0;border:none;}li.jxToolItem span.jxBarSeparator{display:block;position:relative;float:left;font-size:0;line-height:0;border:0;margin:0;padding:4px;background-repeat:no-repeat;background-position:center center;}.jxBarTop li.jxToolItem span.jxBarSeparator,.jxBarBottom li.jxToolItem span.jxBarSeparator{width:8px;height:20px;background-image:url(images/toolbar_separator_h.png);}.jxBarLeft li.jxToolItem span.jxBarSeparator,.jxBarRight li.jxToolItem span.jxBarSeparator{width:20px;height:8px;background-image:url(images/toolbar_separator_v.png);}.jxBarLeft ul.jxToolbar,.jxBarLeft ul.jxTabBar,.jxBarLeft li.jxToolItem,.jxBarRight ul.jxToolbar,.jxBarRight ul.jxTabBar,.jxBarRight li.jxToolItem{clear:both;}.jxTooltip{width:auto;height:auto;background-color:black;color:white;padding:5px;z-index:65536;}.jxTree,.jxTreeRoot{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxTreeNest{list-style:none;margin:0;padding:0;background-repeat:repeat-y;background-position:left 
 top;}li.jxTreeContainer{position:relative;display:block;margin:0;padding:0;background-repeat:no-repeat;background-position:left top;white-space:nowrap;font-size:0;line-height:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxTree li.jxTreeContainer{margin-left:16px;}a.jxTreeItem{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;background-image:url(images/tree_hover.png);background-repeat:repeat-x;background-position:left top;border:none;margin:0 1px 0 17px;padding:0 0 0 20px;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;line-height:20px;height:20px;}a.jxTreeItem:focus{border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;margin:0 0 0 16px;background-position:left -72px;}a.jxTreeItem:hover,li.jxTreeContainer a.jxHover{border-left:1px solid #CDDFFD;border-right:1px solid #CDDFFD;margin:0 0 0 16px;background-color:#CDE5FF;background-position:left -24px;}li.jxTreeContainer a.
 jxSelected,li.jxTreeContainer a.jxSelected:hover,li.jxTreeContainer a.jxPressed,li.jxTreeContainer a.jxPressed:hover{border-left:1px solid #8AABFB;border-right:1px solid #8AABFB;margin:0 0 0 16px;background-color:#CDE5FF;background-position:left -48px;}li.jxDisabled a.jxTreeItem{cursor:default;}li.jxDisabled a.jxTreeItem:focus,li.jxDisabled a.jxTreeItem:hover{background-position:left top;background-color:transparent;border:none;margin:0 1px 0 17px;}.jxTreeNest{background-image:url(images/tree_vert_line.png);}img.jxTreeImage,img.jxTreeIcon{position:absolute;display:inline;left:0;top:0;width:16px;height:20px;z-index:1;background-image:url(images/tree.png);background-repeat:no-repeat;border:0;margin:0;}img.jxTreeIcon{height:16px;top:2px;left:1px;}.jxTreeBranchOpen .jxTreeIcon,.jxTreeBranchLastOpen .jxTreeIcon{background-position:left -40px;}.jxTreeBranchOpen .jxTreeImage{background-position:left -100px;}.jxTreeBranchLastOpen .jxTreeImage{background-position:left -160px;}.jxTree
 BranchClosed .jxTreeIcon,.jxTreeBranchLastClosed .jxTreeIcon{background-position:left -20px;}.jxTreeBranchClosed .jxTreeImage{background-position:left -80px;}.jxTreeBranchLastClosed .jxTreeImage{background-position:left -140px;}.jxTreeLeaf .jxTreeIcon,.jxTreeLeafLast .jxTreeIcon{background-position:left 0;}.jxTreeLeaf .jxTreeImage{background-position:left -60px;}.jxTreeLeafLast .jxTreeImage{background-position:left -120px;}a.jxTreeItem,img.jxTreeImage,img.jxTreeIcon,span.jxTreeLabel,.jxTreeItemContainer input{vertical-align:middle;}.jxFileUploadPanel{padding:5px;}.jxFileUploadPanel .jxInputContainer{width:300px;}.jxUploadQueue{width:100%;margin-top:10px;}.jxUploadQueue div{position:relative;width:95%;clear:both;border-top:1px solid black;padding:2px;}.jxUploadQueue div span{display:block;}.jxUploadQueue div span.jxUploadFileName{float:left;font-size:16px;}.jxUploadQueue div span.jxUploadFileDelete,.jxUploadQueue div span.jxUploadFileProgress,.jxUploadQueue div span.jxUploadF
 ileComplete,.jxUploadQueue div span.jxUploadFileError{float:right;width:16px;height:16px;background-repeat:no-repeat;background-position:top left;cursor:pointer;}.jxUploadQueue div span.jxUploadFileDelete{background-image:url('images/delete.gif');}.jxUploadQueue div span.jxUploadFileProgress{background-image:url('images/loading.gif');}.jxUploadQueue div span.jxUploadFileComplete{background-image:url('images/green_tick.png');}.jxUploadQueue div span.jxUploadFileError{background-image:url('images/error.png');}.jxUploadFileErrorTip{padding:4px 4px 4px 20px;border:2px solid #ddd;background:url("images/error.png") no-repeat left top;color:black;width:100px;}
\ No newline at end of file

Modified: trunk/templates/mapguide/standard/themes/crispin/jxtheme.uncompressed.css
===================================================================
--- trunk/templates/mapguide/standard/themes/crispin/jxtheme.uncompressed.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapguide/standard/themes/crispin/jxtheme.uncompressed.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -13,7 +13,7 @@
 h1,h2,h3,h4,h5,h6{font-size:100%;}
 q:before,q:after{content:'';}/**
  * @project         Jx
- * @revision        $Id: button.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: button.css 593 2009-11-09 20:29:54Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -25,11 +25,14 @@
    Buttons can use the sliding door technique with background images to horizontally
    accomodate icons with labels. */
 
-div.jxButtonContainer {
+.jxButtonContainer {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  font-size: 0px;
+  line-height: 0px;
+  /* float: left; */
 
   margin: 0px;
   padding: 2px;
@@ -39,9 +42,12 @@
 /* normal button */
 .jxButton {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  font-size: 0px;
+  line-height: 0px;
+  /* float: left; */
 
   /* Using background images, the A contains the left side of the background */
   margin: 0px; /* margins don't seem to work properly in IE */
@@ -69,9 +75,10 @@
 
 span.jxButtonContent {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  /* float: left; */
   font-size: 0px;
   line-height: 0px;
 
@@ -207,8 +214,11 @@
 
 img.jxButtonIcon {
   /* Base setup */
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  vertical-align: middle;
+  /* float: left; */
 
   width: 16px;
   height: 16px;
@@ -218,9 +228,11 @@
 
 span.jxButtonContent span {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  vertical-align: middle;
+  /* float: left; */
   cursor: pointer;
 
   font-family: Arial, Helvetica, sans-serif;
@@ -241,19 +253,11 @@
 /* JX BUTTON EXTENSION STYLES */
 /* ========================== */
 
-.jxButtonMenu span.jxButtonContent,
-.jxButtonMulti span.jxButtonContent,
-.jxButtonFlyout span.jxButtonContent,
-.jxButtonCombo span.jxButtonContent,
-.jxButtonEditCombo span.jxButtonContent {
+.jxDiscloser span.jxButtonContent {
   padding-right: 0px;
 }
 
-.jxButtonMenu span.jxButtonContent span,
-.jxButtonFlyout span.jxButtonContent span,
-.jxButtonMulti span.jxButtonContent span,
-.jxButtonCombo span.jxButtonContent span,
-.jxButtonEditCombo span.jxButtonContent span {
+.jxDiscloser span.jxButtonContent span {
   padding-right: 16px;
   background-image: url(images/emblems.png);
   background-position: right -16px;
@@ -262,7 +266,8 @@
 
 a.jxButtonDisclose {
   position: absolute;
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   padding: 4px 0px;
   font-size: 0px;
   line-height: 0px;
@@ -390,7 +395,7 @@
   background-color: transparent;
 }/**
  * @project         Jx
- * @revision        $Id: chrome.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: chrome.css 454 2009-06-03 14:50:22Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -463,37 +468,37 @@
   position: absolute; 
   top: 0px; 
   left: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeTR img { 
   position: absolute; 
   top: 0px; 
   right: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeBL img { 
   position: absolute; 
   bottom: 0px; 
   left: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeBR img { 
   position: absolute; 
   bottom: 0px; 
   right: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 /**
  * @project         Jx
- * @revision        $Id: color.css 424 2009-05-12 12:51:44Z pagameba $
+ * @revision        $Id: color.css 423 2009-05-12 12:37:56Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -664,7 +669,7 @@
     height: 16px;
 }/**
  * @project         Jx
- * @revision        $Id: common.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: common.css 267 2009-03-31 20:49:16Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -676,9 +681,16 @@
 .jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;margin:0;padding:0;}
 .jxDisabled{opacity:0.4;-ms-filter: "Alpha(opacity=40)";}
 .jxDisabled * {-ms-filter: "Alpha(opacity=40)";}
-iframe.jxIframeShim{position:absolute;top:0px;left:0px;width:100%;height:100%;opacity:0;-ms-filter: "Alpha(opacity=0)";z-index:-1;}/**
+iframe.jxIframeShim{position:absolute;top:0px;left:0px;width:100%;height:100%;opacity:0;-ms-filter: "Alpha(opacity=0)";z-index:-1;}/**
+ * Confirm dialog classes
+ */
+.jxConfirmQuestion {
+    text-align: center;
+    padding: 10px;
+    margin-top: 10px;
+}/**
  * @project         Jx
- * @revision        $Id: dialog.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: dialog.css 302 2009-04-02 18:42:56Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -929,9 +941,50 @@
   margin: 0px;
   padding: 0px;
   border: none;
+}/**
+ * File Input classes
+ */
+div.jxFileInputs {
+    position: relative;
+}
+
+div.jxFileFake {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    z-index: 1;
+}
+
+div.jxFileFake span {
+    float: left;
+    display: block;
+}
+
+div.jxFileFake .jxInputContainer {
+    width: 150px;
+}
+
+div.jxFileFake .jxInputText {
+    width: 135px;
+} 
+
+div.jxFileFake .jxButtonContainer {
+    margin-top: 2px;
+    float: left;
+}
+
+.jxInputFile {
+    position: relative;
+    text-align: right;
+    -moz-opacity: 0;
+    filter: alpha(opacity:0);
+    opacity: 0;
+    z-index: 2;
+    margin-top: -5px;
+    height: 35px;
 }/**
  * @project         Jx
- * @revision        $Id: form.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: form.css 615 2009-11-19 22:01:38Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -939,98 +992,356 @@
 /* =========== */
 /* FORM STYLES */
 /* =========== */
-/* JxForm classes are a set of styles that can be used forlaying out forms 
- * There are three different types of layouts: Inline, Float and Block.
- * Each can be usedto layout an entire form, or a portion of the form as is needed
+/* JxForm classes are a set of styles that can be used for laying out forms 
+ * There are three different types of layouts: Inline, Inlineblock and Block.
+ * Each can be used to layout an entire form, a fieldset or an individual input.
  */
 
- .jxForm {
+/* debuggiong styles */
+/*
+.jxForm           { background-color: yellow; }
+.jxFieldset       { background-color: khaki; }
+.jxInputGroup     { background-color: tan; }
+.jxFieldsetLegend { background-color: orange; }
+.jxInputContainer { background-color: pink; }
+.jxInputLabel     { background-color: plum; }
+.jxInputTag       { background-color: lime; }
+*/
+
+ /* Base and Typography Styles */
+ 
+.jxForm {
   display: block;
   position: relative;
   overflow: hidden; /* ensures that floated elements are contained */
   font-family: Arial, Helvetica, sans-serif;
   font-size: 12px;
-  line-height: 18px;
+  line-height: 16px;
+  color: #000;
 }
 
-.jxForm label {
-  background-color: transparent;
-  color: #000;
+.jxFieldset {
+  display: block;
+  position: relative;
+  overflow: hidden; /* ensures that floated elements are contained */
+  border: 1px solid #ccc;
+  border-radius: 10px;
+  -moz-border-radius: 10px;
+  -webkit-border-radius: 10px;
+  -khtml-border-radius: 10px;
+  margin: 10px;
+  padding: 5px;
 }
 
-.jxForm select, 
-.jxForm input {
+.jxFieldsetLegend,
+.jxFieldset legend {
+  position: relative;
+  margin: 0px;
+  padding: 0px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 14px;
+  line-height: 26px;
   color: #000;
 }
 
-.jxFormFloat .radioGroup {
+.jxInputContainer {
   display: block;
-  overflow: hidden;
+  position: relative;
+  padding: 0px;
+  margin: 2px;
+  border: none;
 }
 
-/* wrap the radio button in a label */
-.jxFormFloat .radioGroup input,
-.jxFormFloat .radioGroup label {
-  width: auto !important;
-  vertical-align: middle;
+.jxInputLabel,
+.jxInputTag {
+  display: -moz-inline-box;
+  display: inline-block;
+  margin: 0px;
+  padding: 0px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 26px;
+  color: #000;
 }
 
-.jxForm .tipArea {
-  display: block;
-  padding: 5px 0px;
-  background-color: transparent;
-  color: #999;
+.jxInputLabel {
+  vertical-align: top;
 }
 
+.jxInputTag {
+  vertical-align: bottom;
+}
+
+.jxInputText,
+.jxInputPassword,
+.jxInputTextarea {
+  margin: 0px 4px;
+  padding: 4px;
+  border: 1px solid #bbb;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+
+.jxInputSelect {
+  margin: 0px 4px;
+  padding: 3px 4px 3px 1px;
+  border: 1px solid #bbb;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputText:focus,
+.jxInputPassword:focus,
+.jxInputTextarea:focus,
+.jxInputSelect:focus {
+  border: 1px solid #000;
+}
+
+.jxInputRadio,
+.jxInputCheck {
+  margin: 5px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputContainer .jxButtonContainer {
+  padding: 0px;
+  margin: 0px 4px;
+}
+
+/* Input Group */
+
+.jxInputGroup {
+  border: none;
+  padding: 0px;
+  margin: 2px;
+}
+
+.jxInputGroup legend {
+  font-size: 0px;
+  line-height: 0px;
+  padding: 0px;
+  margin: 0px;
+  border: none;
+}
+
+.jxInputGroup .jxFieldsetLegend {
+  font-size: 12px;
+}
+
+.jxInputGroup .jxInputLabel {
+  width: auto;
+}
+
+/* Field Validation */
+
+.jxFieldError .jxInputText,
+.jxFieldError .jxInputPassword,
+.jxFieldError .jxInputTextarea,
+.jxFieldError .jxInputSelect {
+  background-color: #FBE3E4; 
+  color: #8a1f11; 
+  border-color: #FBC2C4;
+}
+
+.jxFieldSuccess .jxInputText,
+.jxFieldSuccess .jxInputPassword,
+.jxFieldSuccess .jxInputTextarea,
+.jxFieldSuccess .jxInputSelect {
+  background-color: #E6EFC2; 
+  color: #264409; 
+  border-color: #C6D880;
+}
+
+.jxFieldError .jxInputText:focus,
+.jxFieldError .jxInputPassword:focus,
+.jxFieldError .jxInputTextarea:focus,
+.jxFieldError .jxInputSelect:focus {
+  border-color: #8a1f11;
+}
+
+.jxFieldSuccess .jxInputText:focus,
+.jxFieldSuccess .jxInputPassword:focus,
+.jxFieldSuccess .jxInputTextarea:focus,
+.jxFieldSuccess .jxInputSelect:focus {
+  border-color: #264409;
+}
+
+.jxFieldError .jxInputLabel,
+.jxFieldError .jxInputTag {
+  color: #8a1f11; 
+}
+
+.jxFieldSuccess .jxInputLabel,
+.jxFieldSuccess .jxInputTag {
+  color: #264409; 
+}
+
+
+/* For Reference
+   Success, notice and error boxes from Blueprint */
+
+/* 
+.error      { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
+.notice     { background: #FFF6BF; color: #514721; border-color: #FFD324; }
+.success    { background: #E6EFC2; color: #264409; border-color: #C6D880; }
+.error a    { color: #8a1f11; }
+.notice a   { color: #514721; }
+.success a  { color: #264409; }
+*/
+
+
+
 /* INLINE FORM 
- * Wrap an Inline Form with .jxFormInline to sets up form elements to work as 
- * inline objects like they do by default */
+ * Sets up form elements to work as inline objects like they do by default
+ * These styles rely on increasing specificity to provide overrides */
 
-.jxFormInline label {
+/* Inline Input Container */
+.jxFormInline .jxInputContainer,
+form .jxFormInline .jxInputContainer,
+form .jxFieldset span.jxFormInline,
+form.jxForm span.jxFormInline {
   display: inline;
 }
 
-/* FLOAT FORM 
- * Wrap a Floating Form with .jxFormFloat to sets up form elements to work as 
- * block objects so labels can have set widths to simulate a 2 column display 
- * for label / input pairs. */
+/* Inline Label */
+.jxFormInline .jxInputLabel,
+form .jxFormInline .jxInputLabel,
+form span.jxFormInline .jxInputLabel {
+  display: inline;
+  width: auto;
+}
 
-.jxFormFloat label {
+/* Inline Tag */
+.jxFormInline .jxInputTag,
+form .jxFormInline .jxInputTag,
+form span.jxFormInline .jxInputTag {
+  display: inline;
+}
+
+/* Inline Input Group */
+.jxFormInline .jxInputGroup,
+form .jxFormInline .jxInputGroup {
+  padding-left: 0px;
+}
+
+.jxFormInline .jxInputGroup .jxFieldsetLegend,
+form .jxFormInline .jxInputGroup .jxFieldsetLegend {
+  position: relative;
+  left: auto;
+}
+
+.jxFormInline .jxInputGroup .jxInputLabel,
+form .jxFormInline .jxInputGroup .jxInputLabel {
+  display: inline;
+  width: auto;
+}
+
+
+/* BLOCK FORM 
+ *  Sets up form elements to work as block objects so they can appear stacked */
+
+/* Block Input Container */
+.jxFormBlock .jxInputContainer,
+form .jxFormBlock .jxInputContainer,
+form .jxFieldset span.jxFormBlock,
+form.jxform span.jxFormBlock {
   display: block;
-  float: left;
-  clear: left;
-  width: 75px; /* Overide the width depending on the form layout */
 }
+ 
+/* Block Label */
+.jxFormBlock .jxInputLabel,
+form .jxFormBlock .jxInputLabel,
+form span.jxFormBlock .jxInputLabel {
+  display: block;
+  width: auto;
+  margin-left: 4px;
+}
 
-.jxFormFloat select, 
-.jxFormFloat input {
-  float: left;
+/* Checks and Radios Label
+ * Most inputs are preceeded by their labels, but in the case of radio buttons
+ * and checkboxes, the inputs are followed by their labels */
+.jxFormBlock .jxInputContainerCheck .jxInputLabel,
+.jxFormBlock .jxInputContainerRadio .jxInputLabel,
+form .jxFormBlock .jxInputContainerCheck .jxInputLabel,
+form .jxFormBlock .jxInputContainerRadio .jxInputLabel,
+form span.jxFormBlock .jxInputContainerCheck .jxInputLabel,
+form span.jxFormBlock .jxInputContainerRadio .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
 }
 
-.jxFormFloat .radioGroup {
-  float: left;
+/* Block Input Group */
+.jxFormBlock .jxInputGroup,
+form .jxFormBlock .jxInputGroup {
+  padding-left: 0px;
 }
 
-/* wrap the radio button in a label */
-.jxFormFloat .radioGroup input,
-.jxFormFloat .radioGroup label {
-  float: none;
+.jxFormBlock .jxInputGroup .jxFieldsetLegend,
+form .jxFormBlock .jxInputGroup .jxFieldsetLegend {
+  position: relative;
+  left: auto;
 }
 
-.jxFormFloat .tipArea {
-  clear: left;
+.jxFormBlock .jxInputGroup .jxInputLabel,
+form .jxFormBlock .jxInputGroup .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: auto;
 }
 
-/* BLOCK FORM 
- *  Wrap a Block Form with .jxFormBlock to sets up form elements to work as 
- * block objects so they can label / input pairs */
 
-.jxFormBlock label {
+/* INLINE-BLOCK FORM 
+ * Sets up form elements to work as inline-block objects so labels can have set 
+ * widths to simulate a 2 column display for label / input pairs. */
+
+/* Inline-Block Input Container */
+.jxFormInlineblock .jxInputContainer,
+form .jxFormInlineblock .jxInputContainer,
+form .jxFieldset span.jxFormInlineblock,
+form.jxForm span.jxFormInlineblock {
   display: block;
 }
+
+/* Inline-Block Label */
+.jxFormInlineblock .jxInputLabel,
+form .jxFormInlineblock .jxInputLabel,
+form span.jxFormInlineblock .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: 200px;
+}
+
+/* Inline-Block Input Group */
+.jxFormInlineblock .jxInputGroup,
+form .jxFormInlineblock .jxInputGroup {
+  padding-left: 200px;
+}
+
+.jxFormInlineblock .jxInputGroup .jxFieldsetLegend,
+form .jxFormInlineblock .jxInputGroup .jxFieldsetLegend {
+  position: absolute;
+  left: -200px; /* for ie? */
+  width: 200px;
+}
+
+.jxFormInlineblock .jxInputGroup .jxInputLabel,
+form .jxFormInlineblock .jxInputGroup .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: auto;
+}
+
 /**
  * @project         Jx
- * @revision        $Id: grid.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: grid.css 572 2009-10-29 05:53:36Z jonlb at comcast.net $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1055,13 +1366,30 @@
     table-layout: fixed;
     border-collapse: collapse;
     border-style: none;
-    width: 0px;
+
     cursor: default;
     font-family: Arial, Verdana, sans-serif;
     font-size: 11px;
     font-weight: normal;
 }
 
+.jxGridHeader {
+    width: 100%;
+}
+
+.jxGridTableBody {
+    position: relative;
+    table-layout: fixed;
+    border-collapse: collapse;
+    border-style: none;
+   /* width: 100%;*/
+    cursor: default;
+    font-family: Arial, Verdana, sans-serif;
+    font-size: 11px;
+    font-weight: normal;
+}
+
+
 .jxGridColHeadHide {
     height: 0px;
     line-height: 0px;
@@ -1089,6 +1417,13 @@
     border-bottom: 1px solid #d8d8d8;
     border-left: 0px solid #d8d8d8;
     overflow: hidden;
+}
+
+.jxGridCellContent {
+    position: relative;
+    display: -moz-inline-box;
+    display: inline-block;
+    overflow: hidden;
     padding-left: 3px;
     padding-right: 3px;
     overflow: hidden;
@@ -1099,7 +1434,7 @@
     text-overflow: ellipsis;
 }
 
-/* Nornal Styles */
+/* Normal Styles */
 
 .jxGridColHead {
     border-top: 0px solid  #d8d8d8;
@@ -1115,6 +1450,9 @@
     font-weight: bold;
     color: #333;
     cursor: default;
+    padding-left: 3px;
+    padding-right: 3px;
+    white-space: nowrap;
 }
 
 .jxGridRowHead {
@@ -1131,6 +1469,8 @@
     font-weight: bold;
     color: #333;
     cursor: default;
+    overflow: hidden;
+    white-space: nowrap;
 }
 
 /* Alternating Row Styles */
@@ -1192,9 +1532,119 @@
   background-color: #cce3ff;
 }
 
+/* Sort styles */
+.jxColSortable {
+    padding-right: 20px;
+}
+
+.jxGridCell.jxColSortable {
+    padding-right: 0;
+}
+
+.jxColSortable span {
+    background-image: url('images/emblems.png');
+    padding-right: 20px;
+    background-repeat: no-repeat;
+    background-position: right top;
+}
+ 
+.jxGridColumnSortedAsc span {
+    background-position: right -160px;
+}
+ 
+.jxGridColumnSortedDesc span {
+    background-position: right -16px;
+}
+ 
+.jxGridColumnResize,
+.jxGridRowResize {
+    display: block;
+    position: absolute;
+    background-image: url(images/a_pixel.png);
+    z-index: 1;
+    
+}
+.jxGridColumnResize {
+    right: 0px;
+    top: 0px;
+    height: 100%;
+    width: 4px;
+    cursor: col-resize;
+}
+
+.jxGridRowResize {
+    left: 0px;
+    bottom: 0px;
+    height: 4px;
+    width: 100%;
+    cursor: row-resize;
+}.jxListView {
+}
+
+.jxListView .jxListItemContainer {
+    position: relative;
+    display: block;
+    outline: none;
+    overflow: hidden;
+    
+    border: none;
+    margin: 0px 1px;
+    padding: 0px;
+}
+
+.jxListItem {
+    position: relative;
+    display: block;
+    cursor: pointer;
+    outline: none;
+    overflow: hidden;
+
+    border: none;
+    margin: 0px 1px;
+    padding: 0px;
+    z-index: 0;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 11px;
+    color: #000;
+    text-decoration: none;
+    /* Line Height needs to be an even number so branches line up properly */
+    line-height: 20px;
+    height: 20px;
+}
+
+.jxListView .jxHover {
+    margin: 0px;
+    border-left: 1px solid #CDDFFD;
+    border-right: 1px solid #CDDFFD;
+    background-image: url(images/listitem.png);
+    background-repeat: repeat-x;
+    background-color: #CDE5FF;
+    background-position: left -24px;
+    
+}
+
+.jxListItem:focus {
+    margin: 0px;
+    border-left: 1px dotted #75ADFF;
+    border-right: 1px dotted #75ADFF;
+    background-image: url(images/listitem.png);
+    background-repeat: repeat-x;
+    background-position: left -72px;
+}
+
+.jxListView .jxPressed,
+.jxListView .jxSelected {
+  margin: 0px;
+  border-left: 1px solid #8AABFB;
+  border-right: 1px solid #8AABFB;
+  background-color: #CDE5FF;
+  background-image: url(images/listitem.png);
+  background-repeat: repeat-x;
+  background-position: left -48px;
+}
 /**
  * @project         Jx
- * @revision        $Id: menu.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: menu.css 601 2009-11-10 18:44:35Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1223,13 +1673,14 @@
 
 .jxMenuContainer {
   position: absolute;
-  display: block;
+  top: 0;
+  left: -10000px;
+  display: none;
   z-index: 2000;
   padding: 0px;
 }
 
-ul.jxMenu,
-ul.jxSubMenu {
+ul.jxMenu {
   /* Base setup */
   display: block;
   position: relative;
@@ -1387,7 +1838,7 @@
 }
 
 .jxMenuItemToggle img.jxMenuItemIcon,
-.jxMenuItemSet img.jxMenuItemIcon {
+.jxMenuItemToggleSet img.jxMenuItemIcon {
   background-image: url(images/emblems.png);
   background-position: 2px 0px;
   background-repeat: no-repeat;
@@ -1397,12 +1848,11 @@
   background-position: 2px -48px;
 }
 
-.jxMenuItemSet a.jxMenuItemActive img.jxMenuItemIcon {
+.jxMenuItemToggleSet a.jxMenuItemActive img.jxMenuItemIcon {
   background-position: 2px -64px;
 }
 
-ul.jxMenu span.jxMenuSeparator,
-ul.jxSubMenu span.jxMenuSeparator {
+ul.jxMenu span.jxMenuSeparator {
   /* Base setup */
   display: block;
 
@@ -1411,9 +1861,116 @@
   background-image: url(images/toolbar_separator_v.png);
   background-repeat: repeat-x;
   background-position: left center;
+}
+/**
+ * Confirm dialog classes
+ */
+.jxMessage {
+    text-align: center;
+    padding: 10px;
+    margin-top: 10px;
+}/* For Reference
+   Success, notice and error boxes from Blueprint */
+
+/* 
+.error      { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
+.notice     { background: #FFF6BF; color: #514721; border-color: #FFD324; }
+.success    { background: #E6EFC2; color: #264409; border-color: #C6D880; }
+.error a    { color: #8a1f11; }
+.notice a   { color: #514721; }
+.success a  { color: #264409; }
+*/
+
+
+.jxNoticeListContainer {
+  border: none;
+  padding: 0px;
+  margin: 0px;
+  font-size: 0px;
+  line-height: 0px;
+  z-index: 500;
+}
+
+.jxNoticeList {
+    position: relative;
+}
+
+.jxNoticeItemContainer {
+    position: relative;
+    overflow: hidden;
+}
+
+.jxNoticeItemContainer .jxChrome {
+  background-image: url(images/flyout_chrome.png);
+  padding: 5px 5px 7px 6px;
+}
+
+.jxHasChrome .jxNoticeItem  {
+  margin: 6px 6px 8px 7px;
+}
+
+.jxNoticeItem {
+    position: relative;
+    border: 2px solid #ccc;
+    margin: 0px;
+    padding: 0px;
+    background-color: #f8f8f8;
+    background-image: url(images/notice.png);
+    background-repeat: repeat-x;
+    background-position: left bottom;
+}
+
+.jxNoticeIcon {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  margin: 6px;
+  width: 16px;
+  height: 16px;
+  background-image: url(images/icons.png);
+  background-repeat: no-repeat;
+  background-position: 0px 0px;
+}
+
+.jxNotice {
+    display: block;
+    position: relative;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    line-height: 18px;
+    color: #666;
+    margin: 0px;
+    border: none;
+    padding: 6px 26px 6px 10px; 
+}                          
+
+.jxNoticeError .jxNoticeItem {   background-color: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; background-image: url(images/notice_error.png);}
+.jxNoticeWarning .jxNoticeItem { background-color: #FFF6BF; color: #514721; border-color: #FFD324; background-image: url(images/notice_warning.png);}
+.jxNoticeSuccess .jxNoticeItem { background-color: #E6EFC2; color: #264409; border-color: #C6D880; background-image: url(images/notice_success.png);}
+
+.jxNoticeError .jxNotice {   color: #8a1f11; padding-left: 28px; }
+.jxNoticeWarning .jxNotice { color: #514721; padding-left: 28px; }
+.jxNoticeSuccess .jxNotice { color: #264409; padding-left: 28px; }
+.jxNoticeInfo .jxNotice { padding-left: 28px; }
+
+.jxNoticeError .jxNoticeIcon {   background-position: 0px -32px; }
+.jxNoticeWarning .jxNoticeIcon { background-position: 0px -16px; }
+.jxNoticeSuccess .jxNoticeIcon { background-position: 0px -48px; }            
+.jxNoticeInfo .jxNoticeIcon {    background-position: 0px -64px; }
+
+.jxNoticeClose {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    margin: 6px;
+    width: 16px;
+    height: 16px;
+    background-image: url(images/tab_close.png);
+    background-position: 0px 0px;
+    background-repeat: no-repeat;
 }/**
  * @project         Jx
- * @revision        $Id: panel.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: panel.css 302 2009-04-02 18:42:56Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1644,9 +2201,71 @@
   margin: 0px;
   padding: 0px;
   border: none;
-}/**
+}/**
+ * progress bar classes
+ */
+.jxProgressBar-container {
+    width: 100%;
+    display: block;
+    clear: both;
+    
+}
+
+.jxProgressBar-message {
+    color: black;
+}
+
+.jxProgressBar-container .jxProgressBar {
+    width: 100%;
+    clear: both;
+    border-top: none;
+}
+
+.jxProgressBar-container .jxProgressBar .jxProgressBar-outline {
+    border: 1px solid #336600;
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    z-index: 10;
+}
+
+.jxProgressBar-container .jxProgressBar .jxProgressBar-fill {
+    background-color: #99cc66;
+    position: absolute;
+    top: 1px;
+    left: 1px;
+    z-index: 20;
+}
+
+.jxProgressBar-container .jxProgressBar .jxProgressBar-text {
+    color: #336600;
+    margin: 0px;
+    padding: 2px;
+    position: absolute;
+    top: 1px;
+    left: 1px;
+    width: auto;
+    z-index: 30;
+    border: none;
+}/**
+ * Slider classes
+ */
+.jxSliderContainer {
+    width: 100%;
+    height: 10px;
+    border: 1px solid black;
+}
+
+.jxSliderKnob {
+    width: 10px;
+    height: 10px;
+    background-color: black;
+    cursor: pointer;
+}
+
+/**
  * @project         Jx
- * @revision        $Id: splitter.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: splitter.css 294 2009-04-02 12:26:26Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1738,7 +2357,7 @@
   background-color: #aaa;
 }/**
  * @project         Jx
- * @revision        $Id: tab.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: tab.css 615 2009-11-19 22:01:38Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1756,7 +2375,8 @@
      the position need to be explicitly set, as well as the width and height. */
   /* Base setup */
   position: relative;
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   overflow: hidden;
 
   width: 200px;
@@ -1792,7 +2412,7 @@
 /* BASE TAB (BUTTON) STYLES */
 /* ======================== */
 
-div.jxTabContainer {
+span.jxTabContainer {
   /* Base setup */
   display: block;
   position: relative;
@@ -1804,7 +2424,8 @@
 
 a.jxTab {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
   cursor: pointer;
   user-select: none;
@@ -1822,7 +2443,8 @@
 
 span.jxTabContent {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   font-size: 0px;
   line-height: 0px;
 
@@ -1836,6 +2458,8 @@
 
 img.jxTabIcon {
   /* Base setup */
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
 
   width: 16px;
@@ -1846,7 +2470,8 @@
 
 span.jxTabLabel {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
   cursor: pointer;
 
@@ -1867,12 +2492,11 @@
   user-select: none;
   -moz-user-select: none;
   -khtml-user-select: none;
-}
-
-a.jxTabClose img {
   width: 16px;
   height: 16px;
-  background-image: url(images/tab_close.png);  
+  background-image: url(images/tab_close.png);
+  background-position: 0px 0px;
+  background-repeat: no-repeat;
 }
 
 .jxDisabled a.jxTab,
@@ -1891,7 +2515,6 @@
 .jxBarTop div.jxTabContainer,
 .jxBarBottom div.jxTabContainer {
   /* Base setup */
-  float: left;
 }
 
 .jxBarTop a.jxTab,
@@ -1907,8 +2530,8 @@
 /* Closeable Tab */
 .jxBarTop a.jxTabClose,
 .jxBarBottom a.jxTabClose {
-  top: 3px;
-  right: 3px;
+  top: 5px;
+  right: 5px;
 }
 
 .jxBarTop .jxTabClose span.jxTabContent,
@@ -1920,7 +2543,6 @@
 .jxBarTop a.jxTab,
 .jxBarBottom a.jxTab {
   /* Base setup */
-  float: left;
 
   padding-left: 4px; /* makes room for the left of the tab bg */
   background-position: left -24px; 
@@ -1929,7 +2551,6 @@
 .jxBarTop span.jxTabContent,
 .jxBarBottom span.jxTabContent {
   /* Base setup */
-  float: left;
 
   padding: 4px 4px 4px 0px; /* makes space around the label */
   background-position: right -24px; 
@@ -2044,14 +2665,14 @@
 
 .jxBarTop img.jxTabIcon,
 .jxBarBottom img.jxTabIcon {
+  vertical-align: middle;
   /* Base setup */
-  float: left;
 }
 
 .jxBarTop span.jxTabLabel,
 .jxBarBottom span.jxTabLabel {
   /* Base setup */
-  float: left;
+  vertical-align: middle;
   height: 16px;
 
   padding: 0px 4px 0px 4px;
@@ -2062,8 +2683,8 @@
 /* VERTICAL TAB BAR - LEFT and RIGHT */
 /* ================================= */
 
-.jxBarLeft div.jxTabContainer,
-.jxBarRight div.jxTabContainer {
+.jxBarLeft span.jxTabContainer,
+.jxBarRight span.jxTabContainer {
   /* Base setup */
 }
 
@@ -2080,8 +2701,8 @@
 /* Closeable Tab */
 .jxBarLeft a.jxTabClose,
 .jxBarRight a.jxTabClose {
-  top: 3px;
-  left: 3px;
+  top: 5px;
+  left: 5px;
 }
 
 .jxBarLeft .jxTabClose span.jxTabContent,
@@ -2215,7 +2836,7 @@
 
 /**
  * @project         Jx
- * @revision        $Id: toolbar.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: toolbar.css 289 2009-04-01 18:09:24Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -2292,7 +2913,7 @@
 .jxBarBottom .jxBarScroller {
   position: absolute;
   /* much bigger than its container */
-  width: 1000%;
+  width: 10000%;
   overflow: hidden;
 }
 
@@ -2417,9 +3038,21 @@
   clear: both;
 }
 
+/*
+ * Tooltip classes
+ */
+.jxTooltip {
+	width: auto;
+	height: auto;
+	background-color: black;
+	color: white;
+	padding: 5px;
+	z-index: 65536;
+}
+
 /**
  * @project         Jx
- * @revision        $Id: tree.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: tree.css 602 2009-11-10 19:41:36Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -2434,7 +3067,7 @@
 */
 
 
-.jxTree, 
+.jxTree,
 .jxTreeRoot {
   /* relative positioning is required for IE to fix the peek-a-boo bug */
   position:relative;
@@ -2454,8 +3087,7 @@
 
 /* Node Classes */
 
-.jxTree li, 
-.jxTreeRoot li {
+li.jxTreeContainer {
   /* relative positioning is required for IE to fix the peek-a-boo bug */
   position:relative;
   display: block;
@@ -2472,12 +3104,11 @@
   -khtml-user-select: none;
 }
 
-.jxTree li {
+.jxTree li.jxTreeContainer {
   margin-left: 16px;
 }
 
-.jxTree a, 
-.jxTreeRoot a {
+a.jxTreeItem {
   position: relative;
   display: block;
   cursor: pointer;
@@ -2489,7 +3120,7 @@
   background-position: left top;
   border: none;
 
-  margin: 0px 1px 0px 15px;
+  margin: 0px 1px 0px 17px;
   padding: 0px 0px 0px 20px;
   z-index: 0;
   font-family: Arial, Helvetica, sans-serif;
@@ -2502,59 +3133,52 @@
 }
 
 
-.jxTree a:focus,
-.jxTreeRoot a:focus {
+a.jxTreeItem:focus {
   border-left: 1px dotted #75ADFF;
   border-right: 1px dotted #75ADFF;
-  margin: 0px 0px 0px 14px;
+  margin: 0px 0px 0px 16px;
   background-position: left -72px;
 }
 
-.jxTree a:hover, 
-.jxTreeRoot a:hover {
+a.jxTreeItem:hover,
+li.jxTreeContainer a.jxHover {
   /*border: 1px solid #C5E0FF;*/
-  border-left: 1px solid #C5E0FF;
-  border-right: 1px solid #C5E0FF;
-  margin: 0px 0px 0px 14px;
+  border-left: 1px solid #CDDFFD;
+  border-right: 1px solid #CDDFFD;
+  margin: 0px 0px 0px 16px;
   background-color: #CDE5FF;
   background-position: left -24px;
 }
 
-.jxTree a.jxTreeItemPressed,
-.jxTree a.jxTreeItemPressed:hover,
-.jxTreeRoot a.jxTreeItemPressed,
-.jxTreeRoot a.jxTreeItemPressed:hover {
-  border-left: 1px solid #C5E0FF;
-  border-right: 1px solid #C5E0FF;
-  margin: 0px 0px 0px 14px;
+li.jxTreeContainer a.jxSelected,
+li.jxTreeContainer a.jxSelected:hover,
+li.jxTreeContainer a.jxPressed,
+li.jxTreeContainer a.jxPressed:hover {
+  border-left: 1px solid #8AABFB;
+  border-right: 1px solid #8AABFB;
+  margin: 0px 0px 0px 16px;
   background-color: #CDE5FF;
   background-position: left -48px;
 }
 
-.jxTree .jxDisabled a,
-.jxTreeRoot.jxDisabled a {
+li.jxDisabled a.jxTreeItem {
   cursor: default;
 }
-
-.jxTree .jxDisabled a:focus,
-.jxTree .jxDisabled a.jxTreeItemPressed,
-.jxTree .jxDisabled a.jxTreeItemPressed:hover,
-.jxTree .jxDisabled a:hover,
-.jxTreeRoot .jxDisabled a:focus,
-.jxTreeRoot .jxDisabled a.jxTreeItemPressed,
-.jxTreeRoot .jxDisabled a.jxTreeItemPressed:hover,
-.jxTreeRoot .jxDisabled a:hover {
+            
+li.jxDisabled a.jxTreeItem:focus,
+li.jxDisabled a.jxTreeItem:hover {
   background-position: left top;
+  background-color: transparent;
   border: none;
-  margin: 0px 1px 0px 15px;
+  margin: 0px 1px 0px 17px;
 }
 
 .jxTreeNest {
   background-image: url(images/tree_vert_line.png);
 }
 
-.jxTreeImage,
-.jxTreeIcon {
+img.jxTreeImage,
+img.jxTreeIcon {
   position: absolute;
   display: inline;
 
@@ -2572,9 +3196,9 @@
   margin: 0px;
 }
 
-.jxTreeIcon { 
+img.jxTreeIcon { 
   height: 16px;
-  top: 1px;
+  top: 2px;
   left: 1px;
 }
 
@@ -2606,43 +3230,91 @@
   background-position: left -140px; /* plus last image */
 }
 
-.jxTreeItem .jxTreeIcon,
-.jxTreeItemLast .jxTreeIcon {
+.jxTreeLeaf .jxTreeIcon,
+.jxTreeLeafLast .jxTreeIcon {
   background-position: left 0px; /* page image */
 }
 
-.jxTreeItem .jxTreeImage {
+.jxTreeLeaf .jxTreeImage {
   background-position: left -60px; /* node image */
 }
 
-.jxTreeItemLast .jxTreeImage {
+.jxTreeLeafLast .jxTreeImage {
   background-position: left -120px; /* last node image */
 }
 
-.jxTreeItemSelected {
-  background-color: #AFD4FA;
-  font-weight:bold;
-}
 
-.jxTreeItem a,
-.jxTreeItem img,
-.jxTreeItem input,
-.jxTreeItemLast a,
-.jxTreeItemLast img,
-.jxTreeItemLast input,
-.jxTreeBranch a,
-.jxTreeBranch img,
-.jxTreeBranch input,
-.jxTreeBranchLast a,
-.jxTreeBranchLast img,
-.jxTreeBranchLast input {
+a.jxTreeItem,
+img.jxTreeImage,
+img.jxTreeIcon,
+span.jxTreeLabel,
+.jxTreeItemContainer input {
     vertical-align: middle;
 }
 
-#pbmg {
-  position: absolute;
-  right: 0px;
-  padding-top: 2px;
-}
-
-
+/* FileUploadPanel */
+.jxFileUploadPanel {
+    padding: 5px;
+}
+
+.jxFileUploadPanel .jxInputContainer {
+    width: 300px;
+}
+ 
+.jxUploadQueue {
+    width: 100%;  
+    margin-top: 10px;  
+}
+
+.jxUploadQueue div {
+    position: relative;
+    width: 95%;
+    clear: both;
+    border-top: 1px solid black;
+    padding: 2px;
+}
+
+.jxUploadQueue div span {
+    display: block;
+}
+
+.jxUploadQueue div span.jxUploadFileName {
+    float: left;
+    font-size: 16px;
+}
+
+.jxUploadQueue div span.jxUploadFileDelete,
+.jxUploadQueue div span.jxUploadFileProgress,
+.jxUploadQueue div span.jxUploadFileComplete,
+.jxUploadQueue div span.jxUploadFileError {
+    float: right;
+    width: 16px;
+    height: 16px;
+    background-repeat: no-repeat;
+    background-position: top left;
+    cursor: pointer;
+}
+
+.jxUploadQueue div span.jxUploadFileDelete {
+    background-image: url('images/delete.gif');
+}
+
+.jxUploadQueue div span.jxUploadFileProgress {
+    background-image: url('images/loading.gif');
+}
+
+.jxUploadQueue div span.jxUploadFileComplete {
+    background-image: url('images/green_tick.png');
+}
+
+.jxUploadQueue div span.jxUploadFileError {
+    background-image: url('images/error.png');
+}
+
+.jxUploadFileErrorTip {
+  padding: 4px 4px 4px 20px; 
+  border: 2px solid #ddd;
+  background: url("images/error.png") no-repeat left top;
+  color: black;
+  width: 100px;
+}

Modified: trunk/templates/mapguide/standard/themes/delicious/ie6.css
===================================================================
--- trunk/templates/mapguide/standard/themes/delicious/ie6.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapguide/standard/themes/delicious/ie6.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,6 +1,6 @@
 /**
  * @project         Jx
- * @revision        $Id: ie6.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: ie6.css 321 2009-04-06 18:23:39Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */

Modified: trunk/templates/mapguide/standard/themes/delicious/ie7.css
===================================================================
--- trunk/templates/mapguide/standard/themes/delicious/ie7.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapguide/standard/themes/delicious/ie7.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,6 +1,6 @@
 /**
  * @project         Jx
- * @revision        $Id: ie7.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: ie7.css 321 2009-04-06 18:23:39Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */

Copied: trunk/templates/mapguide/standard/themes/delicious/images/delete.gif (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/delete.gif)
===================================================================
(Binary files differ)

Modified: trunk/templates/mapguide/standard/themes/delicious/images/dialog_chrome.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapguide/standard/themes/delicious/images/emblems.png
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/delicious/images/error.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/error.png)
===================================================================
(Binary files differ)

Modified: trunk/templates/mapguide/standard/themes/delicious/images/flyout_chrome.png
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/delicious/images/green_tick.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/green_tick.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/delicious/images/icons.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/icons.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/delicious/images/listitem.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/listitem.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/delicious/images/menuitem.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/menuitem.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/delicious/images/notice.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/notice.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/delicious/images/notice_error.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/notice_error.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/delicious/images/notice_success.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/notice_success.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapguide/standard/themes/delicious/images/notice_warning.png (from rev 2248, sandbox/jxlib-3.0/templates/mapguide/standard/themes/delicious/images/notice_warning.png)
===================================================================
(Binary files differ)

Modified: trunk/templates/mapguide/standard/themes/delicious/jxtheme.css
===================================================================
--- trunk/templates/mapguide/standard/themes/delicious/jxtheme.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapguide/standard/themes/delicious/jxtheme.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -21,4 +21,4 @@
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
- */body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;}q:before,q:after{content:'';}div.jxButtonContainer{display:block;position:relative;float:left;margin:0;padding:2px;border:none;}.jxButton{display:block;position:relative;float:left;margin:0;padding:0 0 0 4px;border:none;background-image:url(images/button.png);background-position:left -24px;background-repeat:no-repeat;text-decoration:none;outline:none;}a.jxButton{cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;}ul.jxToolbar .jxButton{background-position:left top;}span.jxButtonContent{display:block;position:relative;float:left;font-size:0;line-height:0;margin:0;padding:4px 4px 4px 0;border:none;backgr
 ound-image:url(images/button.png);background-position:right -24px;background-repeat:no-repeat;}ul.jxToolbar span.jxButtonContent{background-position:right top;}ul.jxToolbar .jxButtonActive,.jxButtonActive{background-position:left -72px;}ul.jxToolbar .jxButtonActive span.jxButtonContent,.jxButtonActive span.jxButtonContent{background-position:right -72px;}ul.jxToolbar .jxButton:focus,.jxButton:focus{background-position:left -96px;}ul.jxToolbar .jxButton:focus span.jxButtonContent,.jxButton:focus span.jxButtonContent{background-position:right -96px;}ul.jxToolbar .jxButtonActive:focus,.jxButtonActive:focus{background-position:left -144px;}ul.jxToolbar .jxButtonActive:focus span.jxButtonContent,.jxButtonActive:focus span.jxButtonContent{background-position:right -144px;}ul.jxToolbar .jxButton:hover,ul.jxToolbar .jxButtonActive:hover,.jxButton:hover,.jxButtonActive:hover{background-position:left -48px;}ul.jxToolbar .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxButtonActive
 :hover span.jxButtonContent,.jxButton:hover span.jxButtonContent,.jxButtonActive:hover span.jxButtonContent{background-position:right -48px;}ul.jxToolbar .jxButtonPressed,ul.jxToolbar .jxButtonPressed:focus,.jxButtonPressed,.jxButtonPressed:focus{background-position:left -120px;}ul.jxToolbar .jxButtonPressed span.jxButtonContent,ul.jxToolbar .jxButtonPressed:focus span.jxButtonContent,.jxButtonPressed span.jxButtonContent,.jxButtonPressed:focus span.jxButtonContent{background-position:right -120px;}.jxDisabled .jxButton,.jxDisabled span.jxButtonContent span{cursor:default;}ul.jxToolbar .jxDisabled .jxButton:focus,ul.jxToolbar .jxDisabled .jxButton:active,ul.jxToolbar .jxDisabled .jxButton:hover,ul.jxToolbar .jxDisabled .jxButtonPressed{background-position:left top;}.jxDisabled .jxButton:focus,.jxDisabled .jxButton:active,.jxDisabled .jxButton:hover,.jxDisabled .jxButtonPressed{background-position:left -24px;}ul.jxToolbar .jxDisabled .jxButton:focus span.jxButtonContent,ul.jx
 Toolbar .jxDisabled .jxButton:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right top;}.jxDisabled .jxButton:focus span.jxButtonContent,.jxDisabled .jxButton:active span.jxButtonContent,.jxDisabled .jxButton:hover span.jxButtonContent,.jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right -24px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus,ul.jxToolbar .jxDisabled .jxButtonActive:active,ul.jxToolbar .jxDisabled .jxButtonActive:hover,.jxDisabled .jxButtonActive:focus,.jxDisabled .jxButtonActive:active,.jxDisabled .jxButtonActive:hover{background-position:left -72px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:hover span.jxButtonContent,.jxDisabled .jxButtonActive:focus span.jxButtonContent,.jxDisa
 bled .jxButtonActive:active span.jxButtonContent,.jxDisabled .jxButtonActive:hover span.jxButtonContent{background-position:right -72px;}img.jxButtonIcon{position:relative;float:left;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxButtonContent span{display:block;position:relative;float:left;cursor:pointer;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;height:16px;white-space:nowrap;}span.jxButtonContent span.jxButtonLabel{margin:0;padding:0 4px 0 4px;color:#000;font-size:11px;}.jxButtonMenu span.jxButtonContent,.jxButtonMulti span.jxButtonContent,.jxButtonFlyout span.jxButtonContent,.jxButtonCombo span.jxButtonContent,.jxButtonEditCombo span.jxButtonContent{padding-right:0;}.jxButtonMenu span.jxButtonContent span,.jxButtonFlyout span.jxButtonContent span,.jxButtonMulti span.jxButtonContent span,.jxButtonCombo span.jxButtonContent span,.jxButtonEditCombo span.jxButtonContent span{padding-right:16px;backgrou
 nd-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose{position:absolute;display:block;padding:4px 0;font-size:0;line-height:0;right:2px;top:2px;background-image:url(images/button_multi_disclose.png);background-position:right 0;background-repeat:no-repeat;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxButtonDisclose img{width:16px;height:16px;margin:0;padding:0;border:0;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose:focus,a.jxButtonDisclose:active{background-position:right -96px;}a.jxButtonDisclose:hover{background-position:right -48px;}a.jxButtonDisclosePressed{background-position:right -120px;}.jxDisabled a.jxButtonDisclose,.jxDisabled a.jxButtonDisclose:focus,.jxDisabled a.jxButtonDisclose:active,.jxDisabled a.jxButtonDisclose:hover,.jxDisabled a.jxButtonDisclosePressed{cursor:default;background-position:righ
 t 0;}ul.jxToolbar .jxButtonHover{background-position:left -24px!important;}ul.jxToolbar .jxButtonHover span.jxButtonContent{background-position:right -24px!important;}.jxFlyout .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxFlyout{position:absolute;display:block;z-index:100;margin:0;padding:0;}.jxFlyoutContent{position:relative;display:block;overflow:auto;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}.jxButtonMulti,.jxButtonMulti span.jxButtonContent{background-image:url(images/button_multi.png);}.jxButtonEditCombo,.jxButtonEditCombo span.jxButtonContent{background-image:url(images/button_combo.png);}.jxButtonMulti span.jxButtonContent span{padding-right:21px;}.jxButtonEditCombo span.jxButtonContent span{font-size:0;}.jxButtonComboDefault span.jxButtonContent span,.jxButtonComboDefault input{font-style:italic;color:#999;}.jxButtonEditCombo input{float:left;line-height:16px;height:16px;padding:0 4px;margin:0;border:n
 one;font-size:11px;font-family:Arial,Helvetica,sans-serif;background-color:transparent;}.jxChrome{position:absolute;display:block;font-size:0;line-height:0;z-index:-1;width:100%;height:100%;top:0;left:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxChromeDrag{opacity:.5;-ms-filter:"Alpha(opacity=50)";}.jxChromeTL{position:absolute;overflow:hidden;left:0;top:0;width:50%;height:50%;}.jxChromeTR{position:absolute;overflow:hidden;left:50%;top:0;width:50%;height:50%;}.jxChromeBL{position:absolute;overflow:hidden;left:0;top:50%;width:50%;height:50%;}.jxChromeBR{position:absolute;overflow:hidden;left:50%;top:50%;width:50%;height:50%;}.jxChromeTL img{position:absolute;top:0;left:0;width:800px;height:500px;}.jxChromeTR img{position:absolute;top:0;right:0;width:800px;height:500px;}.jxChromeBL img{position:absolute;bottom:0;left:0;width:800px;height:500px;}.jxChromeBR img{position:absolute;bottom:0;right:0;width:800px;height:500px;}.jxColorBar{position:relative;ove
 rflow:hidden;}table.jxColorGrid{position:relative;border-collapse:collapse;empty-cells:show;clear:both;padding:0;margin:0;}.jxColorGrid tr{padding:0;margin:0;}.jxColorGrid td{border:1px solid #000;padding:0;margin:0;}.jxColorGrid td.emptyCell{border:0 solid #000;}.jxColorGrid td.emptyCell span{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;padding:1px;margin:0;}.jxColorGrid a.colorSwatch{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;margin:0;padding:1px;}.jxColorGrid a.borderWhite:hover{border:1px solid #fff;padding:0;}.jxColorGrid a.borderBlack:hover{border:1px solid #000;padding:0;}input.jxHexInput{width:55px;vertical-align:middle;}input.jxAlphaInput{width:30px;vertical-align:middle;}div.jxColorPreview{float:left;position:relative;width:20px;height:20px;border:1px solid #000;margin:2px;vertical-align:middle;background-image:url('images/grid.png');overflow:hidden;}.jxButtonFlyout span.jxButtonContent span.
 jxButtonSwatch{display:block;float:left;width:14px;height:14px;border:1px solid #000;background-image:url('images/grid.png');background-position:0 0;background-repeat:repeat;padding-right:0!important;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch span{display:block;width:14px;height:14px;position:absolute;padding-right:0;background:none;}div.jxColorPreview img{position:absolute;z-index:0;}div.jxColorPreview div{width:20px;height:10px;position:absolute;display:block;left:0;z-index:1;font-size:10px;line-height:0;}div.jxColorPreview div.jxColorSelected{top:0;}div.jxColorPreview div.jxColorHover{bottom:0;}label.jxColorLabel,label.jxAlphaLabel{width:auto;font-family:Arial,sans-serif;font-size:11px;line-height:24px;padding:2px;vertical-align:middle;}a.jxColorClose{position:absolute;top:0;right:0;width:16px;height:16px;}a.jxColorClose img{width:16px;height:16px;}.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;ma
 rgin:0;padding:0;}.jxDisabled{opacity:.4;-ms-filter:"Alpha(opacity=40)";}.jxDisabled *{-ms-filter:"Alpha(opacity=40)";}iframe.jxIframeShim{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;-ms-filter:"Alpha(opacity=0)";z-index:-1;}.jxDialog .jxChrome{background-image:url(images/dialog_chrome.png);}.jxDialog{display:block;z-index:1000;overflow:hidden;}.jxDialogContentContainer{z-index:1;margin:0 11px 13px 12px;border:1px solid #b7b7b7;background-color:#f0f0f0;}.jxDialogModal{position:absolute;display:block;top:0;left:0;width:100%;height:100%;background-color:#000;opacity:.2;-ms-filter:"Alpha(opacity=20)";}.jxDialogContent{display:block;position:relative;overflow:auto;padding:0;z-index:1;}.jxDialogTitle{display:block;position:relative;background-image:url(images/a_pixel.png);text-align:center;height:24px;line-height:24px;z-index:1;margin:6px 6px 0 7px;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxDialogMin .jxDialogTitle{margin-bottom:8px;}.j
 xDialogMoveable,.jxDialogMoveable .jxDialogLabel{cursor:move;}.jxDialogIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxDialogLabel{font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;cursor:default;}.jxDialogResize{position:absolute;bottom:7px;right:6px;width:16px;height:16px;z-index:2;border:0;cursor:se-resize;background-image:url(images/dialog_resize.png);}.jxDialogControls{position:absolute;top:3px;right:2px;height:16px;width:80px;}.jxDialogControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxDialogClose img{background-position:0 -32px;}.jxDialogMenu img{background-position:0 -48px;}.jxDialogHelp img{background-position:0 -64px;}.jxDialogCollapse img{background-position:0 -16px;}.jxDialogMin .jxDialogCollapse img{background-position:0 0;}.jxDialogMax .jxDialogCollapse img{back
 ground-position:0 -16px;}.jxDialogLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxDialogControls .jxButtonContainer,.jxDialogControls span.jxButtonContent,.jxDialogControls .jxButton:hover span.jxButtonContent,.jxDialogControls .jxButton:active span.jxButtonContent,.jxDialogControls .jxButtonActive span.jxButtonContent,.jxDialogControls .jxButtonActive:hover span.jxButtonContent,.jxDialogControls .jxButtonActive:active span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:hover span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:active span.jxButtonContent,.jxDialogControls .jxButton,.jxDialogControls .jxButton:hover,.jxDialogControls .jxButton:active,.jxDialogControls .jxButtonActive,.jxDialogControls .jxButtonActive:hover,.jxDialogControls .jxButtonActive:active,.jxDialogControls .jxDisabled .jxButton,.jxDialogControls .jxDisabled .jxBu
 tton:hover,.jxDialogControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent,.jxDialogControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent span,.jxDialogControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxDialogControls .jxBarContainer{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxDialogControls .jxBarScroller{left:auto;right:0;}.jxDialogControls ul.jxToolbar{float:right;}.jxDialogControls ul.jxToolbar,.jxDialogControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxForm{display:block;position:relative;overflow:hidden;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;}.jxForm label{background-color:transparent;color:#000;}.j
 xForm select,.jxForm input{color:#000;}.jxFormFloat .radioGroup{display:block;overflow:hidden;}.jxFormFloat .radioGroup input,.jxFormFloat .radioGroup label{width:auto!important;vertical-align:middle;}.jxForm .tipArea{display:block;padding:5px 0;background-color:transparent;color:#999;}.jxFormInline label{display:inline;}.jxFormFloat label{display:block;float:left;clear:left;width:75px;}.jxFormFloat select,.jxFormFloat input{float:left;}.jxFormFloat .radioGroup{float:left;}.jxFormFloat .radioGroup input,.jxFormFloat .radioGroup label{float:none;}.jxFormFloat .tipArea{clear:left;}.jxFormBlock label{display:block;}.jxGridContainer{position:absolute;top:0;left:0;border-left:0 solid #d8d8d8;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;overflow:hidden;}.jxGridTable{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;width:0;cursor:default;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal
 ;}.jxGridColHeadHide{height:0;line-height:0;font-size:0;background-color:#fff;white-space:normal;}.jxGridColHeadHide p,.jxGridRowHeadHide p{font-size:0;line-height:0;height:0;margin:0;padding:0;}.jxGridRowHeadHide{width:0;white-space:normal;}.jxGridCell{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;overflow:hidden;padding-left:3px;padding-right:3px;overflow:hidden;white-space:nowrap;cursor:cell;text-overflow:ellipsis;}.jxGridColHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_col.png');background-position:0 0;background-repeat:repeat-x;text-align:center;font-weight:bold;color:#333;cursor:default;}.jxGridRowHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url(
 'images/table_row.png');background-position:0 0;background-repeat:repeat-y;text-align:center;font-weight:bold;color:#333;cursor:default;}.jxGridRowAll{background-color:#fff;}.jxGridColumnHeaderSelected{background-color:#e1e1e1;background-position:0 -200px;}.jxGridRowHeaderSelected{background-color:#e1e1e1;background-position:-400px 0;}.jxGridColumnSelected{background-color:#f7f7f7;}.jxGridRowSelected td{background-color:#f7f7f7;}td.jxGridCellSelected{background-color:#ebebeb;}.jxGridColumnHeaderPrelight{background-color:#cee5ff;background-position:0 -300px;}.jxGridRowHeaderPrelight{background-color:#cee5ff;background-position:-600px 0;}.jxGridColumnPrelight{background-color:#e5f1ff;}.jxGridRowPrelight td{background-color:#e5f1ff;}td.jxGridCellPrelight{background-color:#cce3ff;}.jxMenuContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxButtonMenu span.jxMenuItemSpan{padding-right:16px;}.jxMenuContainer{position:absolute;display:block
 ;z-index:2000;padding:0;}ul.jxMenu,ul.jxSubMenu{display:block;position:relative;list-style-type:none;padding:1px;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}li.jxMenuItemContainer{display:block;position:relative;font-size:0;line-height:0;margin:0;padding:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxMenuItem{display:block;position:relative;overflow:hidden;text-decoration:none;cursor:pointer;outline:none;border:1px solid #fff;background-image:url(images/menuitem.png);background-repeat:no-repeat;background-position:left top;font-family:Arial,Helvetica,sans-serif;font-size:11px;text-decoration:none;margin:0;padding:0;color:#000;}a.jxMenuItemActive{background-position:left -98px;}a.jxMenuItem:focus{background-position:left -74px;}a.jxMenuItem:focus span.jxMenuItemContent{border-right:1px dotted #75ADFF;}a.jxMenuItemActive:focus{background-position:left -170px;}a.jxMenuItem:hover{background-color:#CDE5FF;background-position:left -26
 px;}a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #C5E0FF;}a.jxMenuItemActive:hover{background-position:left -122px;}a.jxMenuItemPressed,a.jxMenuItemPressed:hover{background-color:#CDE5FF;background-position:left -50px;}.jxDisabled a.jxMenuItem,.jxDisabled span.jxMenuItemContent span{cursor:default;}.jxDisabled a.jxMenuItem:focus,.jxDisabled a.jxMenuItemPressed,.jxDisabled a.jxMenuItemPressed:hover,.jxDisabled a.jxMenuItem:hover{background-color:#fff;background-position:left top;border-right:1px solid #fff;}.jxDisabled a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #fff;}span.jxMenuItemContent{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:0;line-height:0;white-space:nowrap;padding:0 20px 0 0;margin:0;border-right:1px solid #fff;}.jxButtonSubMenu span.jxMenuItemContent,.jxButtonSubMenu:hover span.jxMenuItemContent{background-image:url(images/emblems.png);background-position:right -30px;background-repeat:n
 o-repeat;}img.jxMenuItemIcon{position:absolute;top:2px;left:2px;width:16px;height:16px;background-position:left center;background-repeat:no-repeat;}span.jxMenuItemContent span{display:block;position:relative;cursor:pointer;margin:0;padding:2px 0 2px 22px;font-size:16px;line-height:16px;color:#000;}span.jxMenuItemContent span.jxMenuItemLabel{color:#000;font-size:11px;}.jxMenuItemToggle img.jxMenuItemIcon,.jxMenuItemSet img.jxMenuItemIcon{background-image:url(images/emblems.png);background-position:2px 0;background-repeat:no-repeat;}.jxMenuItemToggle a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -48px;}.jxMenuItemSet a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -64px;}ul.jxMenu span.jxMenuSeparator,ul.jxSubMenu span.jxMenuSeparator{display:block;font-size:10px;line-height:10px;background-image:url(images/toolbar_separator_v.png);background-repeat:repeat-x;background-position:left center;}.jxPanel{display:block;position:relative;}.jxPanelContent
 Container{overflow:hidden;background-color:#f0f0f0;}.jxPanelContent{position:relative;display:block;overflow:auto;background-color:#fff;margin:0;padding:0;}.jxPanelTitle{display:block;position:relative;background-image:url(images/panelbar.png);background-repeat:repeat-x;background-position:left top;height:22px;margin:0;padding:0;text-align:center;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxPanelBar{position:absolute;line-height:1px;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxPanelIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxPanelLabel{padding-left:25px;font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;}.jxPanelControls{position:absolute;top:3px;right:2px;height:16px;width:80px;overflow:hidden;}.jxPanelControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0
 ;margin:0;width:16px;height:16px;}.jxPanelClose img{background-position:0 -32px;}.jxPanelMenu img{background-position:0 -48px;}.jxPanelHelp img{background-position:0 -64px;}.jxPanelCollapse img{background-position:0 -16px;}.jxPanelMin .jxPanelCollapse img{background-position:0 0;}.jxPanelMax .jxPanelCollapse img{background-position:0 -16px;}.jxPanelMaximize img{background-position:0 0;}.jxPanelLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxPanelControls .jxButtonContainer,.jxPanelControls span.jxButtonContent,.jxPanelControls .jxButton:hover span.jxButtonContent,.jxPanelControls .jxButton:active span.jxButtonContent,.jxPanelControls .jxButtonActive span.jxButtonContent,.jxPanelControls .jxButtonActive:hover span.jxButtonContent,.jxPanelControls .jxButtonActive:active span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton:hover span.jxButtonContent,.
 jxPanelControls .jxDisabled .jxButton:active span.jxButtonContent,.jxPanelControls .jxButton,.jxPanelControls .jxButton:hover,.jxPanelControls .jxButton:active,.jxPanelControls .jxButtonActive,.jxPanelControls .jxButtonActive:hover,.jxPanelControls .jxButtonActive:active,.jxPanelControls .jxDisabled .jxButton,.jxPanelControls .jxDisabled .jxButton:hover,.jxPanelControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent,.jxPanelControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent span,.jxPanelControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxPanelControls div.jxBarTop{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxPanelControls .jxBarScroller{left:auto;right:0;}.jxPanelControls ul.jxToolbar{float:righ
 t;}.jxPanelControls ul.jxToolbar,.jxPanelControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxSplitterMask{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background-image:url(images/a_pixel.png);z-index:1;}.jxSplitBarHorizontal{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:5px;height:100%;cursor:col-resize;background-color:#f0f0f0;z-index:2;}.jxSplitBarVertical{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxSplitContainer{display:block;position:relative;margin:0;padding:0;border:none;overflow:hidden;}.jxSplitArea{display:block;position:absolute;margin:0;padding:0;border:none;z-index:0;}.jxSplitBarDrag{background-color:#eee;}.jxSnapHorizontalBefore{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxSnapHo
 rizontalAfter{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxTabSetContainer{position:relative;display:block;overflow:hidden;width:200px;height:200px;margin:0;padding:0;background-color:#fff;}.jxTabSetContainer .jxToolbarContainer{z-index:auto;}.tabContent{display:none;position:relative;width:100%;height:100%;overflow:auto;}.tabContentActive{display:block;}div.jxTabContainer{display:block;position:relative;margin:0;padding:2px;border:none;}a.jxTab{display:block;position:relative;cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;margin:0;padding:0;border:none;background-repeat:no-repeat;text-decoration:none;outline:none;}span.jxTabContent{display:block;font-size:0;line-height:0;margin:0;padding:0;border:none;background-repeat:no-repeat;}img.jxTabIcon{position:relative;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxTabLabel{display:block;position:relative;cursor:pointer;mar
 gin:0;padding:0;color:#000;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;}a.jxTabClose{display:block;position:absolute;cursor:pointer;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxTabClose img{width:16px;height:16px;background-image:url(images/tab_close.png);}.jxDisabled a.jxTab,.jxDisabled span.jxTabContent span,.jxDisabled a.jxTabClose{cursor:default;}.jxBarTop div.jxTabContainer,.jxBarBottom div.jxTabContainer{float:left;}.jxBarTop a.jxTab,.jxBarTop span.jxTabContent{background-image:url(images/tab_top.png);}.jxBarBottom a.jxTab,.jxBarBottom span.jxTabContent{background-image:url(images/tab_bottom.png);}.jxBarTop a.jxTabClose,.jxBarBottom a.jxTabClose{top:3px;right:3px;}.jxBarTop .jxTabClose span.jxTabContent,.jxBarBottom .jxTabClose span.jxTabContent{padding-right:16px;}.jxBarTop a.jxTab,.jxBarBottom a.jxTab{float:left;padding-left:4px;background-position:left -24px;}.jxBarTop span.jxTabContent,.jxBarBottom span
 .jxTabContent{float:left;padding:4px 4px 4px 0;background-position:right -24px;}.jxBarTop a.jxTabActive,.jxBarBottom a.jxTabActive{background-position:left -72px;}.jxBarTop a.jxTabActive span.jxTabContent,.jxBarBottom a.jxTabActive span.jxTabContent{background-position:right -72px;}.jxBarTop a.jxTab:focus,.jxBarBottom a.jxTab:focus{background-position:left -96px;}.jxBarTop a.jxTab:focus span.jxTabContent,.jxBarBottom a.jxTab:focus span.jxTabContent{background-position:right -96px;}.jxBarTop a.jxTabActive:focus,.jxBarBottom a.jxTabActive:focus{background-position:left -144px;}.jxBarTop a.jxTabActive:focus span.jxTabContent,.jxBarBottom a.jxTabActive:focus span.jxTabContent{background-position:right -144px;}.jxBarTop a.jxTab:hover,.jxBarTop a.jxTabActive:hover,.jxBarBottom a.jxTab:hover,.jxBarBottom a.jxTabActive:hover{background-position:left -48px;}.jxBarTop a.jxTab:hover span.jxTabContent,.jxBarTop a.jxTabActive:hover span.jxTabContent,.jxBarBottom a.jxTab:hover span.jxTabC
 ontent,.jxBarBottom a.jxTabActive:hover span.jxTabContent{background-position:right -48px;}.jxBarTop a.jxTabPressed,.jxBarTop a.jxTabPressed:focus,.jxBarBottom a.jxTabPressed,.jxBarBottom a.jxTabPressed:focus{background-position:left -120px;}.jxBarTop a.jxTabPressed span.jxTabContent,.jxBarTop a.jxTabPressed:focus span.jxTabContent,.jxBarBottom a.jxTabPressed span.jxTabContent,.jxBarBottom a.jxTabPressed:focus span.jxTabContent{background-position:right -120px;}.jxBarTop .jxDisabled a.jxTab:focus,.jxBarTop .jxDisabled a.jxTab:active,.jxBarTop .jxDisabled a.jxTab:hover,.jxBarTop .jxDisabled a.jxTabPressed,.jxBarBottom .jxDisabled a.jxTab:focus,.jxBarBottom .jxDisabled a.jxTab:active,.jxBarBottom .jxDisabled a.jxTab:hover,.jxBarBottom .jxDisabled a.jxTabPressed{background-position:left -24px;}.jxBarTop .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarTop .jxDisabled a.
 jxTabPressed span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabPressed span.jxTabContent{background-position:right -24px;}.jxBarTop .jxDisabled a.jxTabActive:focus,.jxBarTop .jxDisabled a.jxTabActive:active,.jxBarTop .jxDisabled a.jxTabActive:hover,.jxBarBottom .jxDisabled a.jxTabActive:focus,.jxBarBottom .jxDisabled a.jxTabActive:active,.jxBarBottom .jxDisabled a.jxTabActive:hover{background-position:left -72px;}.jxBarTop .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:right -72
 px;}.jxBarTop img.jxTabIcon,.jxBarBottom img.jxTabIcon{float:left;}.jxBarTop span.jxTabLabel,.jxBarBottom span.jxTabLabel{float:left;height:16px;padding:0 4px 0 4px;}.jxBarLeft a.jxTab,.jxBarLeft span.jxTabContent{background-image:url(images/tab_left.png);}.jxBarRight a.jxTab,.jxBarRight span.jxTabContent{background-image:url(images/tab_right.png);}.jxBarLeft a.jxTabClose,.jxBarRight a.jxTabClose{top:3px;left:3px;}.jxBarLeft .jxTabClose span.jxTabContent,.jxBarRight .jxTabClose span.jxTabContent{padding-top:16px;}.jxBarLeft a.jxTab,.jxBarRight a.jxTab{padding-top:4px;background-position:-24px top;}.jxBarLeft span.jxTabContent,.jxBarRight span.jxTabContent{padding:0 4px 4px 4px;background-position:-24px bottom;}.jxBarLeft a.jxTabActive,.jxBarRight a.jxTabActive{background-position:-72px top;}.jxBarLeft a.jxTabActive span.jxTabContent,.jxBarRight a.jxTabActive span.jxTabContent{background-position:-72px bottom;}.jxBarLeft a.jxTab:focus,.jxBarRight a.jxTab:focus{background-posi
 tion:-96px top;}.jxBarLeft a.jxTab:focus span.jxTabContent,.jxBarRight a.jxTab:focus span.jxTabContent{background-position:-96px bottom;}.jxBarLeft a.jxTabActive:focus,.jxBarRight a.jxTabActive:focus{background-position:-144px top;}.jxBarLeft a.jxTabActive:focus span.jxTabContent,.jxBarRight a.jxTabActive:focus span.jxTabContent{background-position:-144px bottom;}.jxBarLeft a.jxTab:hover,.jxBarLeft a.jxTabActive:hover,.jxBarRight a.jxTab:hover,.jxBarRight a.jxTabActive:hover{background-position:-48px top;}.jxBarLeft a.jxTab:hover span.jxTabContent,.jxBarLeft a.jxTabActive:hover span.jxTabContent,.jxBarRight a.jxTab:hover span.jxTabContent,.jxBarRight a.jxTabActive:hover span.jxTabContent{background-position:-48px bottom;}.jxBarLeft a.jxTabPressed,.jxBarLeft a.jxTabPressed:focus,.jxBarRight a.jxTabPressed,.jxBarRight a.jxTabPressed:focus{background-position:-120px top;}.jxBarLeft a.jxTabPressed span.jxTabContent,.jxBarLeft a.jxTabPressed:focus span.jxTabContent,.jxBarRight a.
 jxTabPressed span.jxTabContent,.jxBarRight a.jxTabPressed:focus span.jxTabContent{background-position:-120px bottom;}.jxBarLeft .jxDisabled a.jxTab:focus,.jxBarLeft .jxDisabled a.jxTab:active,.jxBarLeft .jxDisabled a.jxTab:hover,.jxBarLeft .jxDisabled a.jxTabPressed,.jxBarRight .jxDisabled a.jxTab:focus,.jxBarRight .jxDisabled a.jxTab:active,.jxBarRight .jxDisabled a.jxTab:hover,.jxBarRight .jxDisabled a.jxTabPressed{background-position:-24px top;}.jxBarLeft .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabPressed span.jxTabContent{background-position:-24px bottom;}.jxBarLeft .jxDisabled a.jxTabActive:focus,.jxBarLeft .
 jxDisabled a.jxTabActive:active,.jxBarLeft .jxDisabled a.jxTabActive:hover,.jxBarRight .jxDisabled a.jxTabActive:focus,.jxBarRight .jxDisabled a.jxTabActive:active,.jxBarRight .jxDisabled a.jxTabActive:hover{background-position:-72px top;}.jxBarLeft .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:-72px bottom;}.jxBarLeft span.jxTabLabel,.jxBarRight span.jxTabLabel{padding:4px 0 4px 0;}.jxBarContainer{display:block;position:relative;z-index:1;overflow:hidden;margin:0;padding:0;border:0;background-color:#f0f0f0;}.jxBarTop,.jxBarBottom{width:100%;height:28px;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;ove
 rflow:hidden;}.jxTabBox .jxTabBarTop{background-image:url(images/tabbar.png);background-position:0 bottom;}.jxTabBox .jxTabBarBottom{background-image:url(images/tabbar_bottom.png);background-position:0 top;}.jxBarLeft,.jxBarRight{width:auto;height:100%;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;float:left;overflow:hidden;}.jxTabBox .jxTabBarLeft{background-image:url(images/tabbar_left.png);background-repeat:repeat-y;background-position:right 0;}.jxTabBox .jxTabBarRight{background-image:url(images/tabbar_right.png);background-repeat:repeat-y;background-position:left 0;}.jxBarTop .jxBarScroller,.jxBarBottom .jxBarScroller{position:absolute;width:1000%;overflow:hidden;}.jxBarTop .jxBarScrollLeft,.jxBarBottom .jxBarScrollLeft{position:absolute;top:0;left:0;}.jxBarTop .jxBarScrollRight,.jxBarBottom .jxBarScrollRight{position:absolute;top:0;right:0;}.jxBarTop .jxBarScrollLeft img.jxButtonIcon,.jxBarBottom .jxBarScrollLeft img.jxButt
 onIcon{background-image:url(images/emblems.png);background-position:0 -80px;}.jxBarTop .jxBarScrollRight img.jxButtonIcon,.jxBarBottom .jxBarScrollRight img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -96px;}ul.jxToolbar,ul.jxTabBar{display:block;position:relative;float:left;list-style-type:none;margin:0;padding:0;border:none;}li.jxToolItem{display:block;position:relative;float:left;font-size:0;line-height:0;padding:0;margin:0;border:none;}li.jxToolItem span.jxBarSeparator{display:block;position:relative;float:left;font-size:0;line-height:0;border:0;margin:0;padding:4px;background-repeat:no-repeat;background-position:center center;}.jxBarTop li.jxToolItem span.jxBarSeparator,.jxBarBottom li.jxToolItem span.jxBarSeparator{width:8px;height:20px;background-image:url(images/toolbar_separator_h.png);}.jxBarLeft li.jxToolItem span.jxBarSeparator,.jxBarRight li.jxToolItem span.jxBarSeparator{width:20px;height:8px;background-image:url(images/toolbar_s
 eparator_v.png);}.jxBarLeft ul.jxToolbar,.jxBarLeft ul.jxTabBar,.jxBarLeft li.jxToolItem,.jxBarRight ul.jxToolbar,.jxBarRight ul.jxTabBar,.jxBarRight li.jxToolItem{clear:both;}.jxTree,.jxTreeRoot{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxTreeNest{list-style:none;margin:0;padding:0;background-repeat:repeat-y;background-position:left top;}.jxTree li,.jxTreeRoot li{position:relative;display:block;margin:0;padding:0;background-repeat:no-repeat;background-position:left top;white-space:nowrap;font-size:0;line-height:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxTree li{margin-left:16px;}.jxTree a,.jxTreeRoot a{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;background-image:url(images/tree_hover.png);background-repeat:repeat-x;background-position:left top;border:none;margin:0 1px 0 15px;padding:0 0 0 20px;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;li
 ne-height:20px;height:20px;}.jxTree a:focus,.jxTreeRoot a:focus{border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;margin:0 0 0 14px;background-position:left -72px;}.jxTree a:hover,.jxTreeRoot a:hover{border-left:1px solid #C5E0FF;border-right:1px solid #C5E0FF;margin:0 0 0 14px;background-color:#CDE5FF;background-position:left -24px;}.jxTree a.jxTreeItemPressed,.jxTree a.jxTreeItemPressed:hover,.jxTreeRoot a.jxTreeItemPressed,.jxTreeRoot a.jxTreeItemPressed:hover{border-left:1px solid #C5E0FF;border-right:1px solid #C5E0FF;margin:0 0 0 14px;background-color:#CDE5FF;background-position:left -48px;}.jxTree .jxDisabled a,.jxTreeRoot.jxDisabled a{cursor:default;}.jxTree .jxDisabled a:focus,.jxTree .jxDisabled a.jxTreeItemPressed,.jxTree .jxDisabled a.jxTreeItemPressed:hover,.jxTree .jxDisabled a:hover,.jxTreeRoot .jxDisabled a:focus,.jxTreeRoot .jxDisabled a.jxTreeItemPressed,.jxTreeRoot .jxDisabled a.jxTreeItemPressed:hover,.jxTreeRoot .jxDisabled a:hover{backgroun
 d-position:left top;border:none;margin:0 1px 0 15px;}.jxTreeNest{background-image:url(images/tree_vert_line.png);}.jxTreeImage,.jxTreeIcon{position:absolute;display:inline;left:0;top:0;width:16px;height:20px;z-index:1;background-image:url(images/tree.png);background-repeat:no-repeat;border:0;margin:0;}.jxTreeIcon{height:16px;top:1px;left:1px;}.jxTreeBranchOpen .jxTreeIcon,.jxTreeBranchLastOpen .jxTreeIcon{background-position:left -40px;}.jxTreeBranchOpen .jxTreeImage{background-position:left -100px;}.jxTreeBranchLastOpen .jxTreeImage{background-position:left -160px;}.jxTreeBranchClosed .jxTreeIcon,.jxTreeBranchLastClosed .jxTreeIcon{background-position:left -20px;}.jxTreeBranchClosed .jxTreeImage{background-position:left -80px;}.jxTreeBranchLastClosed .jxTreeImage{background-position:left -140px;}.jxTreeItem .jxTreeIcon,.jxTreeItemLast .jxTreeIcon{background-position:left 0;}.jxTreeItem .jxTreeImage{background-position:left -60px;}.jxTreeItemLast .jxTreeImage{background-posi
 tion:left -120px;}.jxTreeItemSelected{background-color:#AFD4FA;font-weight:bold;}.jxTreeItem a,.jxTreeItem img,.jxTreeItem input,.jxTreeItemLast a,.jxTreeItemLast img,.jxTreeItemLast input,.jxTreeBranch a,.jxTreeBranch img,.jxTreeBranch input,.jxTreeBranchLast a,.jxTreeBranchLast img,.jxTreeBranchLast input{vertical-align:middle;}
\ No newline at end of file
+ */body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;}q:before,q:after{content:'';}.jxButtonContainer{display:-moz-inline-box;display:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:2px;border:none;}.jxButton{display:-moz-inline-box;display:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:0 0 0 4px;border:none;background-image:url(images/button.png);background-position:left -24px;background-repeat:no-repeat;text-decoration:none;outline:none;}a.jxButton{cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;}ul.jxToolbar .jxButton{background-position:left top;}span.jxButtonContent{display:-moz-inline-box;displ
 ay:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:4px 4px 4px 0;border:none;background-image:url(images/button.png);background-position:right -24px;background-repeat:no-repeat;}ul.jxToolbar span.jxButtonContent{background-position:right top;}ul.jxToolbar .jxButtonActive,.jxButtonActive{background-position:left -72px;}ul.jxToolbar .jxButtonActive span.jxButtonContent,.jxButtonActive span.jxButtonContent{background-position:right -72px;}ul.jxToolbar .jxButton:focus,.jxButton:focus{background-position:left -96px;}ul.jxToolbar .jxButton:focus span.jxButtonContent,.jxButton:focus span.jxButtonContent{background-position:right -96px;}ul.jxToolbar .jxButtonActive:focus,.jxButtonActive:focus{background-position:left -144px;}ul.jxToolbar .jxButtonActive:focus span.jxButtonContent,.jxButtonActive:focus span.jxButtonContent{background-position:right -144px;}ul.jxToolbar .jxButton:hover,ul.jxToolbar .jxButtonActive:hover,.jxButton:hover,.jxButtonActive:hover{b
 ackground-position:left -48px;}ul.jxToolbar .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxButtonActive:hover span.jxButtonContent,.jxButton:hover span.jxButtonContent,.jxButtonActive:hover span.jxButtonContent{background-position:right -48px;}ul.jxToolbar .jxButtonPressed,ul.jxToolbar .jxButtonPressed:focus,.jxButtonPressed,.jxButtonPressed:focus{background-position:left -120px;}ul.jxToolbar .jxButtonPressed span.jxButtonContent,ul.jxToolbar .jxButtonPressed:focus span.jxButtonContent,.jxButtonPressed span.jxButtonContent,.jxButtonPressed:focus span.jxButtonContent{background-position:right -120px;}.jxDisabled .jxButton,.jxDisabled span.jxButtonContent span{cursor:default;}ul.jxToolbar .jxDisabled .jxButton:focus,ul.jxToolbar .jxDisabled .jxButton:active,ul.jxToolbar .jxDisabled .jxButton:hover,ul.jxToolbar .jxDisabled .jxButtonPressed{background-position:left top;}.jxDisabled .jxButton:focus,.jxDisabled .jxButton:active,.jxDisabled .jxButton:hover,.jxDisabled .jxButt
 onPressed{background-position:left -24px;}ul.jxToolbar .jxDisabled .jxButton:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right top;}.jxDisabled .jxButton:focus span.jxButtonContent,.jxDisabled .jxButton:active span.jxButtonContent,.jxDisabled .jxButton:hover span.jxButtonContent,.jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right -24px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus,ul.jxToolbar .jxDisabled .jxButtonActive:active,ul.jxToolbar .jxDisabled .jxButtonActive:hover,.jxDisabled .jxButtonActive:focus,.jxDisabled .jxButtonActive:active,.jxDisabled .jxButtonActive:hover{background-position:left -72px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:active span.jxButtonContent,ul.jxToolbar .jxDisab
 led .jxButtonActive:hover span.jxButtonContent,.jxDisabled .jxButtonActive:focus span.jxButtonContent,.jxDisabled .jxButtonActive:active span.jxButtonContent,.jxDisabled .jxButtonActive:hover span.jxButtonContent{background-position:right -72px;}img.jxButtonIcon{display:-moz-inline-box;display:inline-block;position:relative;vertical-align:middle;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxButtonContent span{display:-moz-inline-box;display:inline-block;position:relative;vertical-align:middle;cursor:pointer;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;height:16px;white-space:nowrap;}span.jxButtonContent span.jxButtonLabel{margin:0;padding:0 4px 0 4px;color:#000;font-size:11px;}.jxDiscloser span.jxButtonContent{padding-right:0;}.jxDiscloser span.jxButtonContent span{padding-right:16px;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose{
 position:absolute;display:-moz-inline-box;display:inline-block;padding:4px 0;font-size:0;line-height:0;right:2px;top:2px;background-image:url(images/button_multi_disclose.png);background-position:right 0;background-repeat:no-repeat;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxButtonDisclose img{width:16px;height:16px;margin:0;padding:0;border:0;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose:focus,a.jxButtonDisclose:active{background-position:right -96px;}a.jxButtonDisclose:hover{background-position:right -48px;}a.jxButtonDisclosePressed{background-position:right -120px;}.jxDisabled a.jxButtonDisclose,.jxDisabled a.jxButtonDisclose:focus,.jxDisabled a.jxButtonDisclose:active,.jxDisabled a.jxButtonDisclose:hover,.jxDisabled a.jxButtonDisclosePressed{cursor:default;background-position:right 0;}ul.jxToolbar .jxButtonHover{background-position:left -24px!important;}ul.jxTo
 olbar .jxButtonHover span.jxButtonContent{background-position:right -24px!important;}.jxFlyout .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxFlyout{position:absolute;display:block;z-index:100;margin:0;padding:0;}.jxFlyoutContent{position:relative;display:block;overflow:auto;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}.jxButtonMulti,.jxButtonMulti span.jxButtonContent{background-image:url(images/button_multi.png);}.jxButtonEditCombo,.jxButtonEditCombo span.jxButtonContent{background-image:url(images/button_combo.png);}.jxButtonMulti span.jxButtonContent span{padding-right:21px;}.jxButtonEditCombo span.jxButtonContent span{font-size:0;}.jxButtonComboDefault span.jxButtonContent span,.jxButtonComboDefault input{font-style:italic;color:#999;}.jxButtonEditCombo input{float:left;line-height:16px;height:16px;padding:0 4px;margin:0;border:none;font-size:11px;font-family:Arial,Helvetica,sans-serif;background-color:transpa
 rent;}.jxChrome{position:absolute;display:block;font-size:0;line-height:0;z-index:-1;width:100%;height:100%;top:0;left:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxChromeDrag{opacity:.5;-ms-filter:"Alpha(opacity=50)";}.jxChromeTL{position:absolute;overflow:hidden;left:0;top:0;width:50%;height:50%;}.jxChromeTR{position:absolute;overflow:hidden;left:50%;top:0;width:50%;height:50%;}.jxChromeBL{position:absolute;overflow:hidden;left:0;top:50%;width:50%;height:50%;}.jxChromeBR{position:absolute;overflow:hidden;left:50%;top:50%;width:50%;height:50%;}.jxChromeTL img{position:absolute;top:0;left:0;width:1000px;height:600px;}.jxChromeTR img{position:absolute;top:0;right:0;width:1000px;height:600px;}.jxChromeBL img{position:absolute;bottom:0;left:0;width:1000px;height:600px;}.jxChromeBR img{position:absolute;bottom:0;right:0;width:1000px;height:600px;}.jxColorBar{position:relative;overflow:hidden;}table.jxColorGrid{position:relative;border-collapse:collapse;emp
 ty-cells:show;clear:both;padding:0;margin:0;}.jxColorGrid tr{padding:0;margin:0;}.jxColorGrid td{border:1px solid #000;padding:0;margin:0;}.jxColorGrid td.emptyCell{border:0 solid #000;}.jxColorGrid td.emptyCell span{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;padding:1px;margin:0;}.jxColorGrid a.colorSwatch{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;margin:0;padding:1px;}.jxColorGrid a.borderWhite:hover{border:1px solid #fff;padding:0;}.jxColorGrid a.borderBlack:hover{border:1px solid #000;padding:0;}input.jxHexInput{width:55px;vertical-align:middle;}input.jxAlphaInput{width:30px;vertical-align:middle;}div.jxColorPreview{float:left;position:relative;width:20px;height:20px;border:1px solid #000;margin:2px;vertical-align:middle;background-image:url('images/grid.png');overflow:hidden;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch{display:block;float:left;width:14px;height:14px;border:1px soli
 d #000;background-image:url('images/grid.png');background-position:0 0;background-repeat:repeat;padding-right:0!important;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch span{display:block;width:14px;height:14px;position:absolute;padding-right:0;background:none;}div.jxColorPreview img{position:absolute;z-index:0;}div.jxColorPreview div{width:20px;height:10px;position:absolute;display:block;left:0;z-index:1;font-size:10px;line-height:0;}div.jxColorPreview div.jxColorSelected{top:0;}div.jxColorPreview div.jxColorHover{bottom:0;}label.jxColorLabel,label.jxAlphaLabel{width:auto;font-family:Arial,sans-serif;font-size:11px;line-height:24px;padding:2px;vertical-align:middle;}a.jxColorClose{position:absolute;top:0;right:0;width:16px;height:16px;}a.jxColorClose img{width:16px;height:16px;}.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;margin:0;padding:0;}.jxDisabled{opacity:.4;-ms-filter:"Alpha(opacity=40)";}.jxDi
 sabled *{-ms-filter:"Alpha(opacity=40)";}iframe.jxIframeShim{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;-ms-filter:"Alpha(opacity=0)";z-index:-1;}@CHARSET "ISO-8859-1";.jxConfirmQuestion{text-align:center;padding:10px;margin-top:10px;}.jxDialog .jxChrome{background-image:url(images/dialog_chrome.png);}.jxDialog{display:block;z-index:1000;overflow:hidden;}.jxDialogContentContainer{z-index:1;margin:0 11px 13px 12px;border:1px solid #b7b7b7;background-color:#f0f0f0;}.jxDialogModal{position:absolute;display:block;top:0;left:0;width:100%;height:100%;background-color:#000;opacity:.2;-ms-filter:"Alpha(opacity=20)";}.jxDialogContent{display:block;position:relative;overflow:auto;padding:0;z-index:1;}.jxDialogTitle{display:block;position:relative;background-image:url(images/a_pixel.png);text-align:center;height:24px;line-height:24px;z-index:1;margin:6px 6px 0 7px;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxDialogMin .jxDialogTitle{margin-bot
 tom:8px;}.jxDialogMoveable,.jxDialogMoveable .jxDialogLabel{cursor:move;}.jxDialogIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxDialogLabel{font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;cursor:default;}.jxDialogResize{position:absolute;bottom:7px;right:6px;width:16px;height:16px;z-index:2;border:0;cursor:se-resize;background-image:url(images/dialog_resize.png);}.jxDialogControls{position:absolute;top:3px;right:2px;height:16px;width:80px;}.jxDialogControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxDialogClose img{background-position:0 -32px;}.jxDialogMenu img{background-position:0 -48px;}.jxDialogHelp img{background-position:0 -64px;}.jxDialogCollapse img{background-position:0 -16px;}.jxDialogMin .jxDialogCollapse img{background-position:0 0;}.jxDialogMax .jxDialogCollap
 se img{background-position:0 -16px;}.jxDialogLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxDialogControls .jxButtonContainer,.jxDialogControls span.jxButtonContent,.jxDialogControls .jxButton:hover span.jxButtonContent,.jxDialogControls .jxButton:active span.jxButtonContent,.jxDialogControls .jxButtonActive span.jxButtonContent,.jxDialogControls .jxButtonActive:hover span.jxButtonContent,.jxDialogControls .jxButtonActive:active span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:hover span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:active span.jxButtonContent,.jxDialogControls .jxButton,.jxDialogControls .jxButton:hover,.jxDialogControls .jxButton:active,.jxDialogControls .jxButtonActive,.jxDialogControls .jxButtonActive:hover,.jxDialogControls .jxButtonActive:active,.jxDialogControls .jxDisabled .jxButton,.jxDialogControls .jxDis
 abled .jxButton:hover,.jxDialogControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent,.jxDialogControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent span,.jxDialogControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxDialogControls .jxBarContainer{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxDialogControls .jxBarScroller{left:auto;right:0;}.jxDialogControls ul.jxToolbar{float:right;}.jxDialogControls ul.jxToolbar,.jxDialogControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}div.jxFileInputs{position:relative;}div.jxFileFake{position:absolute;top:0;left:0;z-index:1;}div.jxFileFake span{float:left;display:block;}div.jxFileFake .jxInputContainer{wi
 dth:150px;}div.jxFileFake .jxInputText{width:135px;}div.jxFileFake .jxButtonContainer{margin-top:6px;float:left;}.jxInputFile{position:relative;text-align:right;-moz-opacity:0;filter:alpha(opacity:0);opacity:0;z-index:2;margin-top:3px;height:35px;width:100%;}.jxForm{display:block;position:relative;overflow:hidden;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxFieldset{display:block;position:relative;overflow:hidden;border:1px solid #ccc;margin:10px;padding:5px;}.jxFieldsetLegend,.jxFieldset legend{position:relative;margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:26px;color:#000;}.jxInputContainer{display:block;position:relative;padding:0;margin:2px;border:none;}.jxInputLabel,.jxInputTag{display:-moz-inline-box;display:inline-block;margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:26px;color:#000;}.jxInputLabel{vertical-align:top;}.jxInputTag{vertical-align:bottom;}.
 jxInputText,.jxInputPassword,.jxInputTextarea{margin:0 4px;padding:4px;border:1px solid #bbb;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputSelect{margin:0 4px;padding:3px 4px 3px 1px;border:1px solid #bbb;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputText:focus,.jxInputPassword:focus,.jxInputTextarea:focus,.jxInputSelect:focus{border:1px solid #000;}.jxInputRadio,.jxInputCheck{margin:5px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputContainer .jxButtonContainer{padding:0;margin:0 4px;}.jxInputGroup{border:none;padding:0;margin:2px;}.jxInputGroup legend{font-size:0;line-height:0;padding:0;margin:0;border:none;}.jxInputGroup .jxFieldsetLegend{font-size:12px;}.jxInputGroup .jxInputLabel{width:auto;}.jxFieldError .jxInputText,.jxFieldError .jxInputPassword,.jxFieldError .jxInputTextarea,.jxFieldError .jxInputSelect{background-color:#FBE3E4;color:
 #8a1f11;border-color:#FBC2C4;}.jxFieldSuccess .jxInputText,.jxFieldSuccess .jxInputPassword,.jxFieldSuccess .jxInputTextarea,.jxFieldSuccess .jxInputSelect{background-color:#E6EFC2;color:#264409;border-color:#C6D880;}.jxFieldError .jxInputText:focus,.jxFieldError .jxInputPassword:focus,.jxFieldError .jxInputTextarea:focus,.jxFieldError .jxInputSelect:focus{border-color:#8a1f11;}.jxFieldSuccess .jxInputText:focus,.jxFieldSuccess .jxInputPassword:focus,.jxFieldSuccess .jxInputTextarea:focus,.jxFieldSuccess .jxInputSelect:focus{border-color:#264409;}.jxFieldError .jxInputLabel,.jxFieldError .jxInputTag{color:#8a1f11;}.jxFieldSuccess .jxInputLabel,.jxFieldSuccess .jxInputTag{color:#264409;}.jxFormInline .jxInputContainer,form .jxFormInline .jxInputContainer,form .jxFieldset span.jxFormInline,form.jxForm span.jxFormInline{display:inline;}.jxFormInline .jxInputLabel,form .jxFormInline .jxInputLabel,form span.jxFormInline .jxInputLabel{display:inline;width:auto;}.jxFormInline .jxIn
 putTag,form .jxFormInline .jxInputTag,form span.jxFormInline .jxInputTag{display:inline;}.jxFormInline .jxInputGroup,form .jxFormInline .jxInputGroup{padding-left:0;}.jxFormInline .jxInputGroup .jxFieldsetLegend,form .jxFormInline .jxInputGroup .jxFieldsetLegend{position:relative;left:auto;}.jxFormInline .jxInputGroup .jxInputLabel,form .jxFormInline .jxInputGroup .jxInputLabel{display:inline;width:auto;}.jxFormBlock .jxInputContainer,form .jxFormBlock .jxInputContainer,form .jxFieldset span.jxFormBlock,form.jxform span.jxFormBlock{display:block;}.jxFormBlock .jxInputLabel,form .jxFormBlock .jxInputLabel,form span.jxFormBlock .jxInputLabel{display:block;width:auto;margin-left:4px;}.jxFormBlock .jxInputContainerCheck .jxInputLabel,.jxFormBlock .jxInputContainerRadio .jxInputLabel,form .jxFormBlock .jxInputContainerCheck .jxInputLabel,form .jxFormBlock .jxInputContainerRadio .jxInputLabel,form span.jxFormBlock .jxInputContainerCheck .jxInputLabel,form span.jxFormBlock .jxInput
 ContainerRadio .jxInputLabel{display:-moz-inline-box;display:inline-block;}.jxFormBlock .jxInputGroup,form .jxFormBlock .jxInputGroup{padding-left:0;}.jxFormBlock .jxInputGroup .jxFieldsetLegend,form .jxFormBlock .jxInputGroup .jxFieldsetLegend{position:relative;left:auto;}.jxFormBlock .jxInputGroup .jxInputLabel,form .jxFormBlock .jxInputGroup .jxInputLabel{display:-moz-inline-box;display:inline-block;width:auto;}.jxFormInlineblock .jxInputContainer,form .jxFormInlineblock .jxInputContainer,form .jxFieldset span.jxFormInlineblock,form.jxForm span.jxFormInlineblock{display:block;}.jxFormInlineblock .jxInputLabel,form .jxFormInlineblock .jxInputLabel,form span.jxFormInlineblock .jxInputLabel{display:-moz-inline-box;display:inline-block;width:200px;}.jxFormInlineblock .jxInputGroup,form .jxFormInlineblock .jxInputGroup{padding-left:200px;}.jxFormInlineblock .jxInputGroup .jxFieldsetLegend,form .jxFormInlineblock .jxInputGroup .jxFieldsetLegend{position:absolute;left:-200px;wid
 th:200px;}.jxFormInlineblock .jxInputGroup .jxInputLabel,form .jxFormInlineblock .jxInputGroup .jxInputLabel{display:-moz-inline-box;display:inline-block;width:auto;}.jxGridContainer{position:absolute;top:0;left:0;border-left:0 solid #d8d8d8;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;overflow:hidden;}.jxGridTable{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;cursor:default;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal;}.jxGridHeader{width:100%;}.jxGridTableBody{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;cursor:default;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal;}.jxGridColHeadHide{height:0;line-height:0;font-size:0;background-color:#fff;white-space:normal;}.jxGridColHeadHide p,.jxGridRowHeadHide p{font-size:0;line-height:0;height:0;margin:0;padding:0;}.jxGridRowHeadHide{width:0;white-space:normal;}.jxGridCel
 l{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;overflow:hidden;}.jxGridCellContent{position:relative;display:-moz-inline-box;display:inline-block;overflow:hidden;padding-left:3px;padding-right:3px;overflow:hidden;white-space:nowrap;cursor:cell;text-overflow:ellipsis;}.jxGridColHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_col.png');background-position:0 0;background-repeat:repeat-x;text-align:center;font-weight:bold;color:#333;cursor:default;padding-left:3px;padding-right:3px;white-space:nowrap;}.jxGridRowHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_row.png');background-position:0 0;background-repeat:repeat-y;text-align:center;font-w
 eight:bold;color:#333;cursor:default;overflow:hidden;white-space:nowrap;}.jxGridRowAll{background-color:#fff;}.jxGridColumnHeaderSelected{background-color:#e1e1e1;background-position:0 -200px;}.jxGridRowHeaderSelected{background-color:#e1e1e1;background-position:-400px 0;}.jxGridColumnSelected{background-color:#f7f7f7;}.jxGridRowSelected td{background-color:#f7f7f7;}td.jxGridCellSelected{background-color:#ebebeb;}.jxGridColumnHeaderPrelight{background-color:#cee5ff;background-position:0 -300px;}.jxGridRowHeaderPrelight{background-color:#cee5ff;background-position:-600px 0;}.jxGridColumnPrelight{background-color:#e5f1ff;}.jxGridRowPrelight td{background-color:#e5f1ff;}td.jxGridCellPrelight{background-color:#cce3ff;}.jxColSortable{padding-right:20px;}.jxGridCell.jxColSortable{padding-right:0;}.jxColSortable span{background-image:url('images/emblems.png');padding-right:20px;background-repeat:no-repeat;background-position:right top;}.jxGridColumnSortedAsc span{background-positio
 n:right -160px;}.jxGridColumnSortedDesc span{background-position:right -16px;}.jxGridColumnResize,.jxGridRowResize{display:block;position:absolute;background-image:url(images/a_pixel.png);z-index:1;}.jxGridColumnResize{right:0;top:0;height:100%;width:4px;cursor:col-resize;}.jxGridRowResize{left:0;bottom:0;height:4px;width:100%;cursor:row-resize;}.jxListView .jxListItemContainer{position:relative;display:block;outline:none;overflow:hidden;border:none;margin:0 1px;padding:0;}.jxListItem{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;border:none;margin:0 1px;padding:0;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;line-height:20px;height:20px;}.jxListView .jxHover{margin:0;border-left:1px solid #CDDFFD;border-right:1px solid #CDDFFD;background-image:url(images/listitem.png);background-repeat:repeat-x;background-color:#CDE5FF;background-position:left -24px;}.jxListItem:focus{margin:0;border-left:1px do
 tted #75ADFF;border-right:1px dotted #75ADFF;background-image:url(images/listitem.png);background-repeat:repeat-x;background-position:left -72px;}.jxListView .jxPressed,.jxListView .jxSelected{margin:0;border-left:1px solid #8AABFB;border-right:1px solid #8AABFB;background-color:#CDE5FF;background-image:url(images/listitem.png);background-repeat:repeat-x;background-position:left -48px;}.jxMenuContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxButtonMenu span.jxMenuItemSpan{padding-right:16px;}.jxMenuContainer{position:absolute;top:0;left:-10000px;display:none;z-index:2000;padding:0;}ul.jxMenu{display:block;position:relative;list-style-type:none;padding:1px;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}li.jxMenuItemContainer{display:block;position:relative;font-size:0;line-height:0;margin:0;padding:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxMenuItem{display:block;position:relative;ove
 rflow:hidden;text-decoration:none;cursor:pointer;outline:none;border:1px solid #fff;background-image:url(images/menuitem.png);background-repeat:no-repeat;background-position:left top;font-family:Arial,Helvetica,sans-serif;font-size:11px;text-decoration:none;margin:0;padding:0;color:#000;}a.jxMenuItemActive{background-position:left -98px;}a.jxMenuItem:focus{background-position:left -74px;}a.jxMenuItem:focus span.jxMenuItemContent{border-right:1px dotted #75ADFF;}a.jxMenuItemActive:focus{background-position:left -170px;}a.jxMenuItem:hover{background-color:#CDE5FF;background-position:left -26px;}a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #C5E0FF;}a.jxMenuItemActive:hover{background-position:left -122px;}a.jxMenuItemPressed,a.jxMenuItemPressed:hover{background-color:#CDE5FF;background-position:left -50px;}.jxDisabled a.jxMenuItem,.jxDisabled span.jxMenuItemContent span{cursor:default;}.jxDisabled a.jxMenuItem:focus,.jxDisabled a.jxMenuItemPressed,.jxDisable
 d a.jxMenuItemPressed:hover,.jxDisabled a.jxMenuItem:hover{background-color:#fff;background-position:left top;border-right:1px solid #fff;}.jxDisabled a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #fff;}span.jxMenuItemContent{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:0;line-height:0;white-space:nowrap;padding:0 20px 0 0;margin:0;border-right:1px solid #fff;}.jxButtonSubMenu span.jxMenuItemContent,.jxButtonSubMenu:hover span.jxMenuItemContent{background-image:url(images/emblems.png);background-position:right -30px;background-repeat:no-repeat;}img.jxMenuItemIcon{position:absolute;top:2px;left:2px;width:16px;height:16px;background-position:left center;background-repeat:no-repeat;}span.jxMenuItemContent span{display:block;position:relative;cursor:pointer;margin:0;padding:2px 0 2px 22px;font-size:16px;line-height:16px;color:#000;}span.jxMenuItemContent span.jxMenuItemLabel{color:#000;font-size:11px;}.jxMenuItemToggle img.j
 xMenuItemIcon,.jxMenuItemToggleSet img.jxMenuItemIcon{background-image:url(images/emblems.png);background-position:2px 0;background-repeat:no-repeat;}.jxMenuItemToggle a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -48px;}.jxMenuItemToggleSet a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -64px;}ul.jxMenu span.jxMenuSeparator{display:block;font-size:10px;line-height:10px;background-image:url(images/toolbar_separator_v.png);background-repeat:repeat-x;background-position:left center;}@CHARSET "ISO-8859-1";.jxMessage{text-align:center;padding:10px;margin-top:10px;}.jxNoticeListContainer{border:none;padding:0;margin:0;font-size:0;line-height:0;z-index:500;}.jxNoticeList{position:relative;}.jxNoticeItemContainer{position:relative;overflow:hidden;}.jxNoticeItemContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxHasChrome .jxNoticeItem{margin:6px 6px 8px 7px;}.jxNoticeItem{position:relative;border:2px solid #c
 cc;margin:0;padding:0;background-color:#f8f8f8;background-image:url(images/notice.png);background-repeat:repeat-x;background-position:left bottom;}.jxNoticeIcon{position:absolute;top:0;left:0;margin:6px;width:16px;height:16px;background-image:url(images/icons.png);background-repeat:no-repeat;background-position:0 0;}.jxNotice{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;color:#666;margin:0;border:none;padding:6px 26px 6px 10px;}.jxNoticeError .jxNoticeItem{background-color:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;background-image:url(images/notice_error.png);}.jxNoticeWarning .jxNoticeItem{background-color:#FFF6BF;color:#514721;border-color:#FFD324;background-image:url(images/notice_warning.png);}.jxNoticeSuccess .jxNoticeItem{background-color:#E6EFC2;color:#264409;border-color:#C6D880;background-image:url(images/notice_success.png);}.jxNoticeError .jxNotice{color:#8a1f11;padding-left:28px;}.jxNoticeWarning .jxNotic
 e{color:#514721;padding-left:28px;}.jxNoticeSuccess .jxNotice{color:#264409;padding-left:28px;}.jxNoticeInfo .jxNotice{padding-left:28px;}.jxNoticeError .jxNoticeIcon{background-position:0 -32px;}.jxNoticeWarning .jxNoticeIcon{background-position:0 -16px;}.jxNoticeSuccess .jxNoticeIcon{background-position:0 -48px;}.jxNoticeInfo .jxNoticeIcon{background-position:0 -64px;}.jxNoticeClose{position:absolute;top:0;right:0;margin:6px;width:16px;height:16px;background-image:url(images/tab_close.png);background-position:0 0;background-repeat:no-repeat;}.jxPanel{display:block;position:relative;}.jxPanelContentContainer{overflow:hidden;background-color:#f0f0f0;}.jxPanelContent{position:relative;display:block;overflow:auto;background-color:#fff;margin:0;padding:0;}.jxPanelTitle{display:block;position:relative;background-image:url(images/panelbar.png);background-repeat:repeat-x;background-position:left top;height:22px;margin:0;padding:0;text-align:center;user-select:none;-moz-user-select
 :none;-khtml-user-select:none;}.jxPanelBar{position:absolute;line-height:1px;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxPanelIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxPanelLabel{padding-left:25px;font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;}.jxPanelControls{position:absolute;top:3px;right:2px;height:16px;width:80px;overflow:hidden;}.jxPanelControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxPanelClose img{background-position:0 -32px;}.jxPanelMenu img{background-position:0 -48px;}.jxPanelHelp img{background-position:0 -64px;}.jxPanelCollapse img{background-position:0 -16px;}.jxPanelMin .jxPanelCollapse img{background-position:0 0;}.jxPanelMax .jxPanelCollapse img{background-position:0 -16px;}.jxPanelMaximize img{background-positio
 n:0 0;}.jxPanelLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxPanelControls .jxButtonContainer,.jxPanelControls span.jxButtonContent,.jxPanelControls .jxButton:hover span.jxButtonContent,.jxPanelControls .jxButton:active span.jxButtonContent,.jxPanelControls .jxButtonActive span.jxButtonContent,.jxPanelControls .jxButtonActive:hover span.jxButtonContent,.jxPanelControls .jxButtonActive:active span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton:hover span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton:active span.jxButtonContent,.jxPanelControls .jxButton,.jxPanelControls .jxButton:hover,.jxPanelControls .jxButton:active,.jxPanelControls .jxButtonActive,.jxPanelControls .jxButtonActive:hover,.jxPanelControls .jxButtonActive:active,.jxPanelControls .jxDisabled .jxButton,.jxPanelControls .jxDisabled .jxButton:hover,.jxPanelControls .jxDisabl
 ed .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent,.jxPanelControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent span,.jxPanelControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxPanelControls div.jxBarTop{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxPanelControls .jxBarScroller{left:auto;right:0;}.jxPanelControls ul.jxToolbar{float:right;}.jxPanelControls ul.jxToolbar,.jxPanelControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}@CHARSET "ISO-8859-1";.jxProgressBar-container{width:100%;display:block;clear:both;}.jxProgressBar-message{color:black;}.jxProgressBar-container .jxProgressBar{width:100%;clear:both;border-top:none;}.jxProgressBar-container .jxProgress
 Bar .jxProgressBar-outline{border:1px solid #360;position:absolute;top:0;left:0;z-index:10;}.jxProgressBar-container .jxProgressBar .jxProgressBar-fill{background-color:#9c6;position:absolute;top:1px;left:1px;z-index:20;}.jxProgressBar-container .jxProgressBar .jxProgressBar-text{color:#360;margin:0;padding:2px;position:absolute;top:1px;left:1px;width:auto;z-index:30;border:none;}@CHARSET "ISO-8859-1";.jxSliderContainer{width:100%;height:10px;border:1px solid black;}.jxSliderKnob{width:10px;height:10px;background-color:black;cursor:pointer;}.jxSplitterMask{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background-image:url(images/a_pixel.png);z-index:1;}.jxSplitBarHorizontal{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:5px;height:100%;cursor:col-resize;background-color:#f0f0f0;z-index:2;}.jxSplitBarVertical{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:10
 0%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxSplitContainer{display:block;position:relative;margin:0;padding:0;border:none;overflow:hidden;}.jxSplitArea{display:block;position:absolute;margin:0;padding:0;border:none;z-index:0;}.jxSplitBarDrag{background-color:#eee;}.jxSnapHorizontalBefore{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxSnapHorizontalAfter{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxTabSetContainer{position:relative;display:-moz-inline-box;display:inline-block;overflow:hidden;width:200px;height:200px;margin:0;padding:0;background-color:#fff;}.jxTabSetContainer .jxToolbarContainer{z-index:auto;}.tabContent{display:none;position:relative;width:100%;height:100%;overflow:auto;}.tabContentActive{display:block;}span.jxTabContainer{display:block;position:relative;margin:0;padding:2px;border:none;}a.jxTab{display:-moz-inline-box;display:inline-block;position:relative;cursor:poin
 ter;user-select:none;-moz-user-select:none;-khtml-user-select:none;margin:0;padding:0;border:none;background-repeat:no-repeat;text-decoration:none;outline:none;}span.jxTabContent{display:-moz-inline-box;display:inline-block;font-size:0;line-height:0;margin:0;padding:0;border:none;background-repeat:no-repeat;}img.jxTabIcon{display:-moz-inline-box;display:inline-block;position:relative;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxTabLabel{display:-moz-inline-box;display:inline-block;position:relative;cursor:pointer;margin:0;padding:0;color:#000;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;}a.jxTabClose{display:block;position:absolute;cursor:pointer;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;width:16px;height:16px;background-image:url(images/tab_close.png);background-position:0 0;background-repeat:no-repeat;}.jxDisabled a.jxTab,.jxDisabled span.jxTabContent span,.jxDisable
 d a.jxTabClose{cursor:default;}.jxBarTop a.jxTab,.jxBarTop span.jxTabContent{background-image:url(images/tab_top.png);}.jxBarBottom a.jxTab,.jxBarBottom span.jxTabContent{background-image:url(images/tab_bottom.png);}.jxBarTop a.jxTabClose,.jxBarBottom a.jxTabClose{top:5px;right:5px;}.jxBarTop .jxTabClose span.jxTabContent,.jxBarBottom .jxTabClose span.jxTabContent{padding-right:16px;}.jxBarTop a.jxTab,.jxBarBottom a.jxTab{padding-left:4px;background-position:left -24px;}.jxBarTop span.jxTabContent,.jxBarBottom span.jxTabContent{padding:4px 4px 4px 0;background-position:right -24px;}.jxBarTop a.jxTabActive,.jxBarBottom a.jxTabActive{background-position:left -72px;}.jxBarTop a.jxTabActive span.jxTabContent,.jxBarBottom a.jxTabActive span.jxTabContent{background-position:right -72px;}.jxBarTop a.jxTab:focus,.jxBarBottom a.jxTab:focus{background-position:left -96px;}.jxBarTop a.jxTab:focus span.jxTabContent,.jxBarBottom a.jxTab:focus span.jxTabContent{background-position:right -
 96px;}.jxBarTop a.jxTabActive:focus,.jxBarBottom a.jxTabActive:focus{background-position:left -144px;}.jxBarTop a.jxTabActive:focus span.jxTabContent,.jxBarBottom a.jxTabActive:focus span.jxTabContent{background-position:right -144px;}.jxBarTop a.jxTab:hover,.jxBarTop a.jxTabActive:hover,.jxBarBottom a.jxTab:hover,.jxBarBottom a.jxTabActive:hover{background-position:left -48px;}.jxBarTop a.jxTab:hover span.jxTabContent,.jxBarTop a.jxTabActive:hover span.jxTabContent,.jxBarBottom a.jxTab:hover span.jxTabContent,.jxBarBottom a.jxTabActive:hover span.jxTabContent{background-position:right -48px;}.jxBarTop a.jxTabPressed,.jxBarTop a.jxTabPressed:focus,.jxBarBottom a.jxTabPressed,.jxBarBottom a.jxTabPressed:focus{background-position:left -120px;}.jxBarTop a.jxTabPressed span.jxTabContent,.jxBarTop a.jxTabPressed:focus span.jxTabContent,.jxBarBottom a.jxTabPressed span.jxTabContent,.jxBarBottom a.jxTabPressed:focus span.jxTabContent{background-position:right -120px;}.jxBarTop .jxD
 isabled a.jxTab:focus,.jxBarTop .jxDisabled a.jxTab:active,.jxBarTop .jxDisabled a.jxTab:hover,.jxBarTop .jxDisabled a.jxTabPressed,.jxBarBottom .jxDisabled a.jxTab:focus,.jxBarBottom .jxDisabled a.jxTab:active,.jxBarBottom .jxDisabled a.jxTab:hover,.jxBarBottom .jxDisabled a.jxTabPressed{background-position:left -24px;}.jxBarTop .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarTop .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabPressed span.jxTabContent{background-position:right -24px;}.jxBarTop .jxDisabled a.jxTabActive:focus,.jxBarTop .jxDisabled a.jxTabActive:active,.jxBarTop .jxDisabled a.jxTabActive:hover,.jxBarBottom .jxDisabled a.jxTabActive:focus,.jxBarBottom .
 jxDisabled a.jxTabActive:active,.jxBarBottom .jxDisabled a.jxTabActive:hover{background-position:left -72px;}.jxBarTop .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:right -72px;}.jxBarTop img.jxTabIcon,.jxBarBottom img.jxTabIcon{vertical-align:middle;}.jxBarTop span.jxTabLabel,.jxBarBottom span.jxTabLabel{vertical-align:middle;height:16px;padding:0 4px 0 4px;}.jxBarLeft a.jxTab,.jxBarLeft span.jxTabContent{background-image:url(images/tab_left.png);}.jxBarRight a.jxTab,.jxBarRight span.jxTabContent{background-image:url(images/tab_right.png);}.jxBarLeft a.jxTabClose,.jxBarRight a.jxTabClose{top:5px;left:5px;}.jxBarLeft .jxTabClose span.jxTabContent
 ,.jxBarRight .jxTabClose span.jxTabContent{padding-top:16px;}.jxBarLeft a.jxTab,.jxBarRight a.jxTab{padding-top:4px;background-position:-24px top;}.jxBarLeft span.jxTabContent,.jxBarRight span.jxTabContent{padding:0 4px 4px 4px;background-position:-24px bottom;}.jxBarLeft a.jxTabActive,.jxBarRight a.jxTabActive{background-position:-72px top;}.jxBarLeft a.jxTabActive span.jxTabContent,.jxBarRight a.jxTabActive span.jxTabContent{background-position:-72px bottom;}.jxBarLeft a.jxTab:focus,.jxBarRight a.jxTab:focus{background-position:-96px top;}.jxBarLeft a.jxTab:focus span.jxTabContent,.jxBarRight a.jxTab:focus span.jxTabContent{background-position:-96px bottom;}.jxBarLeft a.jxTabActive:focus,.jxBarRight a.jxTabActive:focus{background-position:-144px top;}.jxBarLeft a.jxTabActive:focus span.jxTabContent,.jxBarRight a.jxTabActive:focus span.jxTabContent{background-position:-144px bottom;}.jxBarLeft a.jxTab:hover,.jxBarLeft a.jxTabActive:hover,.jxBarRight a.jxTab:hover,.jxBarRigh
 t a.jxTabActive:hover{background-position:-48px top;}.jxBarLeft a.jxTab:hover span.jxTabContent,.jxBarLeft a.jxTabActive:hover span.jxTabContent,.jxBarRight a.jxTab:hover span.jxTabContent,.jxBarRight a.jxTabActive:hover span.jxTabContent{background-position:-48px bottom;}.jxBarLeft a.jxTabPressed,.jxBarLeft a.jxTabPressed:focus,.jxBarRight a.jxTabPressed,.jxBarRight a.jxTabPressed:focus{background-position:-120px top;}.jxBarLeft a.jxTabPressed span.jxTabContent,.jxBarLeft a.jxTabPressed:focus span.jxTabContent,.jxBarRight a.jxTabPressed span.jxTabContent,.jxBarRight a.jxTabPressed:focus span.jxTabContent{background-position:-120px bottom;}.jxBarLeft .jxDisabled a.jxTab:focus,.jxBarLeft .jxDisabled a.jxTab:active,.jxBarLeft .jxDisabled a.jxTab:hover,.jxBarLeft .jxDisabled a.jxTabPressed,.jxBarRight .jxDisabled a.jxTab:focus,.jxBarRight .jxDisabled a.jxTab:active,.jxBarRight .jxDisabled a.jxTab:hover,.jxBarRight .jxDisabled a.jxTabPressed{background-position:-24px top;}.jxBar
 Left .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabPressed span.jxTabContent{background-position:-24px bottom;}.jxBarLeft .jxDisabled a.jxTabActive:focus,.jxBarLeft .jxDisabled a.jxTabActive:active,.jxBarLeft .jxDisabled a.jxTabActive:hover,.jxBarRight .jxDisabled a.jxTabActive:focus,.jxBarRight .jxDisabled a.jxTabActive:active,.jxBarRight .jxDisabled a.jxTabActive:hover{background-position:-72px top;}.jxBarLeft .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActi
 ve:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:-72px bottom;}.jxBarLeft span.jxTabLabel,.jxBarRight span.jxTabLabel{padding:4px 0 4px 0;}.jxBarContainer{display:block;position:relative;z-index:1;overflow:hidden;margin:0;padding:0;border:0;background-color:#f0f0f0;}.jxBarTop,.jxBarBottom{width:100%;height:28px;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;overflow:hidden;}.jxTabBox .jxTabBarTop{background-image:url(images/tabbar.png);background-position:0 bottom;}.jxTabBox .jxTabBarBottom{background-image:url(images/tabbar_bottom.png);background-position:0 top;}.jxBarLeft,.jxBarRight{width:auto;height:100%;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;float:left;overflow:hidden;}.jxTabBox .jxTabBarLeft{background-image:url(images/tabbar_left.png);background-repea
 t:repeat-y;background-position:right 0;}.jxTabBox .jxTabBarRight{background-image:url(images/tabbar_right.png);background-repeat:repeat-y;background-position:left 0;}.jxBarTop .jxBarScroller,.jxBarBottom .jxBarScroller{position:absolute;width:10000%;overflow:hidden;}.jxBarTop .jxBarScrollLeft,.jxBarBottom .jxBarScrollLeft{position:absolute;top:0;left:0;}.jxBarTop .jxBarScrollRight,.jxBarBottom .jxBarScrollRight{position:absolute;top:0;right:0;}.jxBarTop .jxBarScrollLeft img.jxButtonIcon,.jxBarBottom .jxBarScrollLeft img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -80px;}.jxBarTop .jxBarScrollRight img.jxButtonIcon,.jxBarBottom .jxBarScrollRight img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -96px;}ul.jxToolbar,ul.jxTabBar{display:block;position:relative;float:left;list-style-type:none;margin:0;padding:0;border:none;}li.jxToolItem{display:block;position:relative;float:left;font-size:0;line-height:0;padding:0;mar
 gin:0;border:none;}li.jxToolItem span.jxBarSeparator{display:block;position:relative;float:left;font-size:0;line-height:0;border:0;margin:0;padding:4px;background-repeat:no-repeat;background-position:center center;}.jxBarTop li.jxToolItem span.jxBarSeparator,.jxBarBottom li.jxToolItem span.jxBarSeparator{width:8px;height:20px;background-image:url(images/toolbar_separator_h.png);}.jxBarLeft li.jxToolItem span.jxBarSeparator,.jxBarRight li.jxToolItem span.jxBarSeparator{width:20px;height:8px;background-image:url(images/toolbar_separator_v.png);}.jxBarLeft ul.jxToolbar,.jxBarLeft ul.jxTabBar,.jxBarLeft li.jxToolItem,.jxBarRight ul.jxToolbar,.jxBarRight ul.jxTabBar,.jxBarRight li.jxToolItem{clear:both;}.jxTooltip{width:auto;height:auto;background-color:black;color:white;padding:5px;z-index:65536;}.jxTree,.jxTreeRoot{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxTreeNest{list-style:none;margin:0;padding:0;background-repeat:repeat-y;background-position:lef
 t top;}li.jxTreeContainer{position:relative;display:block;margin:0;padding:0;background-repeat:no-repeat;background-position:left top;white-space:nowrap;font-size:0;line-height:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxTree li.jxTreeContainer{margin-left:16px;}a.jxTreeItem{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;background-image:url(images/tree_hover.png);background-repeat:repeat-x;background-position:left top;border:none;margin:0 1px 0 17px;padding:0 0 0 20px;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;line-height:20px;height:20px;}a.jxTreeItem:focus{border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;margin:0 0 0 16px;background-position:left -72px;}a.jxTreeItem:hover,li.jxTreeContainer a.jxHover{border-left:1px solid #CDDFFD;border-right:1px solid #CDDFFD;margin:0 0 0 16px;background-color:#CDE5FF;background-position:left -24px;}li.jxTreeContainer 
 a.jxSelected,li.jxTreeContainer a.jxSelected:hover,li.jxTreeContainer a.jxPressed,li.jxTreeContainer a.jxPressed:hover{border-left:1px solid #8AABFB;border-right:1px solid #8AABFB;margin:0 0 0 16px;background-color:#CDE5FF;background-position:left -48px;}li.jxDisabled a.jxTreeItem{cursor:default;}li.jxDisabled a.jxTreeItem:focus,li.jxDisabled a.jxTreeItem:hover{background-position:left top;background-color:transparent;border:none;margin:0 1px 0 17px;}.jxTreeNest{background-image:url(images/tree_vert_line.png);}img.jxTreeImage,img.jxTreeIcon{position:absolute;display:inline;left:0;top:0;width:16px;height:20px;z-index:1;background-image:url(images/tree.png);background-repeat:no-repeat;border:0;margin:0;}img.jxTreeIcon{height:16px;top:2px;left:1px;}.jxTreeBranchOpen .jxTreeIcon,.jxTreeBranchLastOpen .jxTreeIcon{background-position:left -40px;}.jxTreeBranchOpen .jxTreeImage{background-position:left -100px;}.jxTreeBranchLastOpen .jxTreeImage{background-position:left -160px;}.jxTr
 eeBranchClosed .jxTreeIcon,.jxTreeBranchLastClosed .jxTreeIcon{background-position:left -20px;}.jxTreeBranchClosed .jxTreeImage{background-position:left -80px;}.jxTreeBranchLastClosed .jxTreeImage{background-position:left -140px;}.jxTreeLeaf .jxTreeIcon,.jxTreeLeafLast .jxTreeIcon{background-position:left 0;}.jxTreeLeaf .jxTreeImage{background-position:left -60px;}.jxTreeLeafLast .jxTreeImage{background-position:left -120px;}a.jxTreeItem,img.jxTreeImage,img.jxTreeIcon,span.jxTreeLabel,.jxTreeItemContainer input{vertical-align:middle;}.jxFileUploadPanel{padding:5px;}.jxFileUploadPanel .jxInputContainer{width:300px;}.jxUploadQueue{width:100%;margin-top:10px;}.jxUploadQueue div{position:relative;width:95%;clear:both;border-top:1px solid black;padding:2px;}.jxUploadQueue div span{display:block;}.jxUploadQueue div span.jxUploadFileName{float:left;font-size:16px;}.jxUploadQueue div span.jxUploadFileDelete,.jxUploadQueue div span.jxUploadFileProgress,.jxUploadQueue div span.jxUploa
 dFileComplete,.jxUploadQueue div span.jxUploadFileError{float:right;width:16px;height:16px;background-repeat:no-repeat;background-position:top left;cursor:pointer;}.jxUploadQueue div span.jxUploadFileDelete{background-image:url('images/delete.gif');}.jxUploadQueue div span.jxUploadFileProgress{background-image:url('images/loading.gif');}.jxUploadQueue div span.jxUploadFileComplete{background-image:url('images/green_tick.png');}.jxUploadQueue div span.jxUploadFileError{background-image:url('images/error.png');}.jxUploadFileErrorTip{padding:4px 4px 4px 20px;border:2px solid #ddd;background:url("images/error.png") no-repeat left top;color:black;width:100px;}
\ No newline at end of file

Modified: trunk/templates/mapguide/standard/themes/delicious/jxtheme.uncompressed.css
===================================================================
--- trunk/templates/mapguide/standard/themes/delicious/jxtheme.uncompressed.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapguide/standard/themes/delicious/jxtheme.uncompressed.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -13,7 +13,7 @@
 h1,h2,h3,h4,h5,h6{font-size:100%;}
 q:before,q:after{content:'';}/**
  * @project         Jx
- * @revision        $Id: button.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: button.css 593 2009-11-09 20:29:54Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -25,11 +25,14 @@
    Buttons can use the sliding door technique with background images to horizontally
    accomodate icons with labels. */
 
-div.jxButtonContainer {
+.jxButtonContainer {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  font-size: 0px;
+  line-height: 0px;
+  /* float: left; */
 
   margin: 0px;
   padding: 2px;
@@ -39,9 +42,12 @@
 /* normal button */
 .jxButton {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  font-size: 0px;
+  line-height: 0px;
+  /* float: left; */
 
   /* Using background images, the A contains the left side of the background */
   margin: 0px; /* margins don't seem to work properly in IE */
@@ -69,9 +75,10 @@
 
 span.jxButtonContent {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  /* float: left; */
   font-size: 0px;
   line-height: 0px;
 
@@ -207,8 +214,11 @@
 
 img.jxButtonIcon {
   /* Base setup */
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  vertical-align: middle;
+  /* float: left; */
 
   width: 16px;
   height: 16px;
@@ -218,9 +228,11 @@
 
 span.jxButtonContent span {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  vertical-align: middle;
+  /* float: left; */
   cursor: pointer;
 
   font-family: Arial, Helvetica, sans-serif;
@@ -241,19 +253,11 @@
 /* JX BUTTON EXTENSION STYLES */
 /* ========================== */
 
-.jxButtonMenu span.jxButtonContent,
-.jxButtonMulti span.jxButtonContent,
-.jxButtonFlyout span.jxButtonContent,
-.jxButtonCombo span.jxButtonContent,
-.jxButtonEditCombo span.jxButtonContent {
+.jxDiscloser span.jxButtonContent {
   padding-right: 0px;
 }
 
-.jxButtonMenu span.jxButtonContent span,
-.jxButtonFlyout span.jxButtonContent span,
-.jxButtonMulti span.jxButtonContent span,
-.jxButtonCombo span.jxButtonContent span,
-.jxButtonEditCombo span.jxButtonContent span {
+.jxDiscloser span.jxButtonContent span {
   padding-right: 16px;
   background-image: url(images/emblems.png);
   background-position: right -16px;
@@ -262,7 +266,8 @@
 
 a.jxButtonDisclose {
   position: absolute;
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   padding: 4px 0px;
   font-size: 0px;
   line-height: 0px;
@@ -390,7 +395,7 @@
   background-color: transparent;
 }/**
  * @project         Jx
- * @revision        $Id: chrome.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: chrome.css 454 2009-06-03 14:50:22Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -463,37 +468,37 @@
   position: absolute; 
   top: 0px; 
   left: 0px;
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeTR img { 
   position: absolute; 
   top: 0px; 
   right: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeBL img { 
   position: absolute; 
   bottom: 0px; 
   left: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeBR img { 
   position: absolute; 
   bottom: 0px; 
   right: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 /**
  * @project         Jx
- * @revision        $Id: color.css 424 2009-05-12 12:51:44Z pagameba $
+ * @revision        $Id: color.css 423 2009-05-12 12:37:56Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -664,7 +669,7 @@
     height: 16px;
 }/**
  * @project         Jx
- * @revision        $Id: common.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: common.css 267 2009-03-31 20:49:16Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -676,9 +681,19 @@
 .jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;margin:0;padding:0;}
 .jxDisabled{opacity:0.4;-ms-filter: "Alpha(opacity=40)";}
 .jxDisabled * {-ms-filter: "Alpha(opacity=40)";}
-iframe.jxIframeShim{position:absolute;top:0px;left:0px;width:100%;height:100%;opacity:0;-ms-filter: "Alpha(opacity=0)";z-index:-1;}/**
+iframe.jxIframeShim{position:absolute;top:0px;left:0px;width:100%;height:100%;opacity:0;-ms-filter: "Alpha(opacity=0)";z-index:-1;}@CHARSET "ISO-8859-1";
+
+
+/**
+ * Confirm dialog classes
+ */
+.jxConfirmQuestion {
+    text-align: center;
+    padding: 10px;
+    margin-top: 10px;
+}/**
  * @project         Jx
- * @revision        $Id: dialog.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: dialog.css 302 2009-04-02 18:42:56Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -929,9 +944,51 @@
   margin: 0px;
   padding: 0px;
   border: none;
+}/**
+ * File Input classes
+ */
+div.jxFileInputs {
+    position: relative;
+}
+
+div.jxFileFake {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    z-index: 1;
+}
+
+div.jxFileFake span {
+    float: left;
+    display: block;
+}
+
+div.jxFileFake .jxInputContainer {
+    width: 150px;
+}
+
+div.jxFileFake .jxInputText {
+    width: 135px;
+} 
+
+div.jxFileFake .jxButtonContainer {
+    margin-top: 6px;
+    float: left;
+}
+
+.jxInputFile {
+    position: relative;
+    text-align: right;
+    -moz-opacity: 0;
+    filter: alpha(opacity:0);
+    opacity: 0;
+    z-index: 2;
+    margin-top: 3px;
+    height: 35px;
+    width: 100%;
 }/**
  * @project         Jx
- * @revision        $Id: form.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: form.css 615 2009-11-19 22:01:38Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -939,98 +996,351 @@
 /* =========== */
 /* FORM STYLES */
 /* =========== */
-/* JxForm classes are a set of styles that can be used forlaying out forms 
- * There are three different types of layouts: Inline, Float and Block.
- * Each can be usedto layout an entire form, or a portion of the form as is needed
+/* JxForm classes are a set of styles that can be used for laying out forms 
+ * There are three different types of layouts: Inline, Inlineblock and Block.
+ * Each can be used to layout an entire form, a fieldset or an individual input.
  */
 
- .jxForm {
+/* debuggiong styles */
+/*
+.jxForm           { background-color: yellow; }
+.jxFieldset       { background-color: khaki; }
+.jxInputGroup     { background-color: tan; }
+.jxFieldsetLegend { background-color: orange; }
+.jxInputContainer { background-color: pink; }
+.jxInputLabel     { background-color: plum; }
+.jxInputTag       { background-color: lime; }
+*/
+
+ /* Base and Typography Styles */
+ 
+.jxForm {
   display: block;
   position: relative;
   overflow: hidden; /* ensures that floated elements are contained */
   font-family: Arial, Helvetica, sans-serif;
   font-size: 12px;
-  line-height: 18px;
+  line-height: 16px;
+  color: #000;
 }
 
-.jxForm label {
-  background-color: transparent;
-  color: #000;
+.jxFieldset {
+  display: block;
+  position: relative;
+  overflow: hidden; /* ensures that floated elements are contained */
+  border: 1px solid #ccc;
+  margin: 10px;
+  padding: 5px;
 }
 
-.jxForm select, 
-.jxForm input {
+.jxFieldsetLegend,
+.jxFieldset legend {
+  position: relative;
+  margin: 0px;
+  padding: 0px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 14px;
+  line-height: 26px;
   color: #000;
 }
 
-.jxFormFloat .radioGroup {
+.jxInputContainer {
   display: block;
-  overflow: hidden;
+  position: relative;
+  padding: 0px;
+  margin: 2px;
+  border: none;
 }
 
-/* wrap the radio button in a label */
-.jxFormFloat .radioGroup input,
-.jxFormFloat .radioGroup label {
-  width: auto !important;
-  vertical-align: middle;
+.jxInputLabel,
+.jxInputTag {
+  display: -moz-inline-box;
+  display: inline-block;
+  margin: 0px;
+  padding: 0px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 26px;
+  color: #000;
 }
 
-.jxForm .tipArea {
-  display: block;
-  padding: 5px 0px;
-  background-color: transparent;
-  color: #999;
+.jxInputLabel {
+  vertical-align: top;
 }
 
+.jxInputTag {
+  vertical-align: bottom;
+}
+
+.jxInputText,
+.jxInputPassword,
+.jxInputTextarea {
+  margin: 0px 4px;
+  padding: 4px;
+  border: 1px solid #bbb;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputSelect {
+  margin: 0px 4px;
+  padding: 3px 4px 3px 1px;
+  border: 1px solid #bbb;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputText:focus,
+.jxInputPassword:focus,
+.jxInputTextarea:focus,
+.jxInputSelect:focus {
+  border: 1px solid #000;
+}
+
+.jxInputRadio,
+.jxInputCheck {
+  margin: 5px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputContainer .jxButtonContainer {
+  padding: 0px;
+  margin: 0px 4px;
+}
+
+/* Input Group */
+
+.jxInputGroup {
+  border: none;
+  padding: 0px;
+  margin: 2px;
+}
+
+.jxInputGroup legend {
+  font-size: 0px;
+  line-height: 0px;
+  padding: 0px;
+  margin: 0px;
+  border: none;
+}
+
+.jxInputGroup .jxFieldsetLegend {
+  font-size: 12px;
+}
+
+.jxInputGroup .jxInputLabel {
+  width: auto;
+}
+
+/* Field Validation */
+
+.jxFieldError .jxInputText,
+.jxFieldError .jxInputPassword,
+.jxFieldError .jxInputTextarea,
+.jxFieldError .jxInputSelect {
+  background-color: #FBE3E4; 
+  color: #8a1f11; 
+  border-color: #FBC2C4;
+}
+
+.jxFieldSuccess .jxInputText,
+.jxFieldSuccess .jxInputPassword,
+.jxFieldSuccess .jxInputTextarea,
+.jxFieldSuccess .jxInputSelect {
+  background-color: #E6EFC2; 
+  color: #264409; 
+  border-color: #C6D880;
+}
+
+.jxFieldError .jxInputText:focus,
+.jxFieldError .jxInputPassword:focus,
+.jxFieldError .jxInputTextarea:focus,
+.jxFieldError .jxInputSelect:focus {
+  border-color: #8a1f11;
+}
+
+.jxFieldSuccess .jxInputText:focus,
+.jxFieldSuccess .jxInputPassword:focus,
+.jxFieldSuccess .jxInputTextarea:focus,
+.jxFieldSuccess .jxInputSelect:focus {
+  border-color: #264409;
+}
+
+.jxFieldError .jxInputLabel,
+.jxFieldError .jxInputTag {
+  color: #8a1f11; 
+}
+
+.jxFieldSuccess .jxInputLabel,
+.jxFieldSuccess .jxInputTag {
+  color: #264409; 
+}
+
+
+/* For Reference
+   Success, notice and error boxes from Blueprint */
+
+/* 
+.error      { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
+.notice     { background: #FFF6BF; color: #514721; border-color: #FFD324; }
+.success    { background: #E6EFC2; color: #264409; border-color: #C6D880; }
+.error a    { color: #8a1f11; }
+.notice a   { color: #514721; }
+.success a  { color: #264409; }
+*/
+
+
+
 /* INLINE FORM 
- * Wrap an Inline Form with .jxFormInline to sets up form elements to work as 
- * inline objects like they do by default */
+ * Sets up form elements to work as inline objects like they do by default
+ * These styles rely on increasing specificity to provide overrides */
 
-.jxFormInline label {
+/* Inline Input Container */
+.jxFormInline .jxInputContainer,
+form .jxFormInline .jxInputContainer,
+form .jxFieldset span.jxFormInline,
+form.jxForm span.jxFormInline {
   display: inline;
 }
 
-/* FLOAT FORM 
- * Wrap a Floating Form with .jxFormFloat to sets up form elements to work as 
- * block objects so labels can have set widths to simulate a 2 column display 
- * for label / input pairs. */
+/* Inline Label */
+.jxFormInline .jxInputLabel,
+form .jxFormInline .jxInputLabel,
+form span.jxFormInline .jxInputLabel {
+  display: inline;
+  width: auto;
+}
 
-.jxFormFloat label {
+/* Inline Tag */
+.jxFormInline .jxInputTag,
+form .jxFormInline .jxInputTag,
+form span.jxFormInline .jxInputTag {
+  display: inline;
+}
+
+/* Inline Input Group */
+.jxFormInline .jxInputGroup,
+form .jxFormInline .jxInputGroup {
+  padding-left: 0px;
+}
+
+.jxFormInline .jxInputGroup .jxFieldsetLegend,
+form .jxFormInline .jxInputGroup .jxFieldsetLegend {
+  position: relative;
+  left: auto;
+}
+
+.jxFormInline .jxInputGroup .jxInputLabel,
+form .jxFormInline .jxInputGroup .jxInputLabel {
+  display: inline;
+  width: auto;
+}
+
+
+/* BLOCK FORM 
+ *  Sets up form elements to work as block objects so they can appear stacked */
+
+/* Block Input Container */
+.jxFormBlock .jxInputContainer,
+form .jxFormBlock .jxInputContainer,
+form .jxFieldset span.jxFormBlock,
+form.jxform span.jxFormBlock {
   display: block;
-  float: left;
-  clear: left;
-  width: 75px; /* Overide the width depending on the form layout */
 }
+ 
+/* Block Label */
+.jxFormBlock .jxInputLabel,
+form .jxFormBlock .jxInputLabel,
+form span.jxFormBlock .jxInputLabel {
+  display: block;
+  width: auto;
+  margin-left: 4px;
+}
 
-.jxFormFloat select, 
-.jxFormFloat input {
-  float: left;
+/* Checks and Radios Label
+ * Most inputs are preceeded by their labels, but in the case of radio buttons
+ * and checkboxes, the inputs are followed by their labels */
+.jxFormBlock .jxInputContainerCheck .jxInputLabel,
+.jxFormBlock .jxInputContainerRadio .jxInputLabel,
+form .jxFormBlock .jxInputContainerCheck .jxInputLabel,
+form .jxFormBlock .jxInputContainerRadio .jxInputLabel,
+form span.jxFormBlock .jxInputContainerCheck .jxInputLabel,
+form span.jxFormBlock .jxInputContainerRadio .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
 }
 
-.jxFormFloat .radioGroup {
-  float: left;
+/* Block Input Group */
+.jxFormBlock .jxInputGroup,
+form .jxFormBlock .jxInputGroup {
+  padding-left: 0px;
 }
 
-/* wrap the radio button in a label */
-.jxFormFloat .radioGroup input,
-.jxFormFloat .radioGroup label {
-  float: none;
+.jxFormBlock .jxInputGroup .jxFieldsetLegend,
+form .jxFormBlock .jxInputGroup .jxFieldsetLegend {
+  position: relative;
+  left: auto;
 }
 
-.jxFormFloat .tipArea {
-  clear: left;
+.jxFormBlock .jxInputGroup .jxInputLabel,
+form .jxFormBlock .jxInputGroup .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: auto;
 }
 
-/* BLOCK FORM 
- *  Wrap a Block Form with .jxFormBlock to sets up form elements to work as 
- * block objects so they can label / input pairs */
 
-.jxFormBlock label {
+/* INLINE-BLOCK FORM 
+ * Sets up form elements to work as inline-block objects so labels can have set 
+ * widths to simulate a 2 column display for label / input pairs. */
+
+/* Inline-Block Input Container */
+.jxFormInlineblock .jxInputContainer,
+form .jxFormInlineblock .jxInputContainer,
+form .jxFieldset span.jxFormInlineblock,
+form.jxForm span.jxFormInlineblock {
   display: block;
 }
+
+/* Inline-Block Label */
+.jxFormInlineblock .jxInputLabel,
+form .jxFormInlineblock .jxInputLabel,
+form span.jxFormInlineblock .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: 200px;
+}
+
+/* Inline-Block Input Group */
+.jxFormInlineblock .jxInputGroup,
+form .jxFormInlineblock .jxInputGroup {
+  padding-left: 200px;
+}
+
+.jxFormInlineblock .jxInputGroup .jxFieldsetLegend,
+form .jxFormInlineblock .jxInputGroup .jxFieldsetLegend {
+  position: absolute;
+  left: -200px; /* for ie? */
+  width: 200px;
+}
+
+.jxFormInlineblock .jxInputGroup .jxInputLabel,
+form .jxFormInlineblock .jxInputGroup .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: auto;
+}
+
 /**
  * @project         Jx
- * @revision        $Id: grid.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: grid.css 572 2009-10-29 05:53:36Z jonlb at comcast.net $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1055,13 +1365,30 @@
     table-layout: fixed;
     border-collapse: collapse;
     border-style: none;
-    width: 0px;
+
     cursor: default;
     font-family: Arial, Verdana, sans-serif;
     font-size: 11px;
     font-weight: normal;
 }
 
+.jxGridHeader {
+    width: 100%;
+}
+
+.jxGridTableBody {
+    position: relative;
+    table-layout: fixed;
+    border-collapse: collapse;
+    border-style: none;
+   /* width: 100%;*/
+    cursor: default;
+    font-family: Arial, Verdana, sans-serif;
+    font-size: 11px;
+    font-weight: normal;
+}
+
+
 .jxGridColHeadHide {
     height: 0px;
     line-height: 0px;
@@ -1089,6 +1416,13 @@
     border-bottom: 1px solid #d8d8d8;
     border-left: 0px solid #d8d8d8;
     overflow: hidden;
+}
+
+.jxGridCellContent {
+    position: relative;
+    display: -moz-inline-box;
+    display: inline-block;
+    overflow: hidden;
     padding-left: 3px;
     padding-right: 3px;
     overflow: hidden;
@@ -1099,7 +1433,7 @@
     text-overflow: ellipsis;
 }
 
-/* Nornal Styles */
+/* Normal Styles */
 
 .jxGridColHead {
     border-top: 0px solid  #d8d8d8;
@@ -1115,6 +1449,9 @@
     font-weight: bold;
     color: #333;
     cursor: default;
+    padding-left: 3px;
+    padding-right: 3px;
+    white-space: nowrap;
 }
 
 .jxGridRowHead {
@@ -1131,6 +1468,8 @@
     font-weight: bold;
     color: #333;
     cursor: default;
+    overflow: hidden;
+    white-space: nowrap;
 }
 
 /* Alternating Row Styles */
@@ -1192,9 +1531,119 @@
   background-color: #cce3ff;
 }
 
+/* Sort styles */
+.jxColSortable {
+    padding-right: 20px;
+}
+
+.jxGridCell.jxColSortable {
+    padding-right: 0;
+}
+
+.jxColSortable span {
+    background-image: url('images/emblems.png');
+    padding-right: 20px;
+    background-repeat: no-repeat;
+    background-position: right top;
+}
+ 
+.jxGridColumnSortedAsc span {
+    background-position: right -160px;
+}
+ 
+.jxGridColumnSortedDesc span {
+    background-position: right -16px;
+}
+ 
+.jxGridColumnResize,
+.jxGridRowResize {
+    display: block;
+    position: absolute;
+    background-image: url(images/a_pixel.png);
+    z-index: 1;
+    
+}
+.jxGridColumnResize {
+    right: 0px;
+    top: 0px;
+    height: 100%;
+    width: 4px;
+    cursor: col-resize;
+}
+
+.jxGridRowResize {
+    left: 0px;
+    bottom: 0px;
+    height: 4px;
+    width: 100%;
+    cursor: row-resize;
+}.jxListView {
+}
+
+.jxListView .jxListItemContainer {
+    position: relative;
+    display: block;
+    outline: none;
+    overflow: hidden;
+    
+    border: none;
+    margin: 0px 1px;
+    padding: 0px;
+}
+
+.jxListItem {
+    position: relative;
+    display: block;
+    cursor: pointer;
+    outline: none;
+    overflow: hidden;
+
+    border: none;
+    margin: 0px 1px;
+    padding: 0px;
+    z-index: 0;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 11px;
+    color: #000;
+    text-decoration: none;
+    /* Line Height needs to be an even number so branches line up properly */
+    line-height: 20px;
+    height: 20px;
+}
+
+.jxListView .jxHover {
+    margin: 0px;
+    border-left: 1px solid #CDDFFD;
+    border-right: 1px solid #CDDFFD;
+    background-image: url(images/listitem.png);
+    background-repeat: repeat-x;
+    background-color: #CDE5FF;
+    background-position: left -24px;
+    
+}
+
+.jxListItem:focus {
+    margin: 0px;
+    border-left: 1px dotted #75ADFF;
+    border-right: 1px dotted #75ADFF;
+    background-image: url(images/listitem.png);
+    background-repeat: repeat-x;
+    background-position: left -72px;
+}
+
+.jxListView .jxPressed,
+.jxListView .jxSelected {
+  margin: 0px;
+  border-left: 1px solid #8AABFB;
+  border-right: 1px solid #8AABFB;
+  background-color: #CDE5FF;
+  background-image: url(images/listitem.png);
+  background-repeat: repeat-x;
+  background-position: left -48px;
+}
 /**
  * @project         Jx
- * @revision        $Id: menu.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: menu.css 601 2009-11-10 18:44:35Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1222,14 +1671,15 @@
 */
 
 .jxMenuContainer {
-  position: absolute;
-  display: block;
-  z-index: 2000;
-  padding: 0px;
+    position: absolute;
+    top: 0;
+    left: -10000px;
+    display: none;
+    z-index: 2000;
+    padding: 0px;
 }
 
-ul.jxMenu,
-ul.jxSubMenu {
+ul.jxMenu {
   /* Base setup */
   display: block;
   position: relative;
@@ -1387,7 +1837,7 @@
 }
 
 .jxMenuItemToggle img.jxMenuItemIcon,
-.jxMenuItemSet img.jxMenuItemIcon {
+.jxMenuItemToggleSet img.jxMenuItemIcon {
   background-image: url(images/emblems.png);
   background-position: 2px 0px;
   background-repeat: no-repeat;
@@ -1397,12 +1847,11 @@
   background-position: 2px -48px;
 }
 
-.jxMenuItemSet a.jxMenuItemActive img.jxMenuItemIcon {
+.jxMenuItemToggleSet a.jxMenuItemActive img.jxMenuItemIcon {
   background-position: 2px -64px;
 }
 
-ul.jxMenu span.jxMenuSeparator,
-ul.jxSubMenu span.jxMenuSeparator {
+ul.jxMenu span.jxMenuSeparator {
   /* Base setup */
   display: block;
 
@@ -1412,9 +1861,118 @@
   background-repeat: repeat-x;
   background-position: left center;
 }
-/**
+ at CHARSET "ISO-8859-1";
+
+
+/**
+ * Confirm dialog classes
+ */
+.jxMessage {
+    text-align: center;
+    padding: 10px;
+    margin-top: 10px;
+}/* For Reference
+   Success, notice and error boxes from Blueprint */
+
+/* 
+.error      { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
+.notice     { background: #FFF6BF; color: #514721; border-color: #FFD324; }
+.success    { background: #E6EFC2; color: #264409; border-color: #C6D880; }
+.error a    { color: #8a1f11; }
+.notice a   { color: #514721; }
+.success a  { color: #264409; }
+*/
+
+
+.jxNoticeListContainer {
+  border: none;
+  padding: 0px;
+  margin: 0px;
+  font-size: 0px;
+  line-height: 0px;
+  z-index: 500;
+}
+
+.jxNoticeList {
+    position: relative;
+}
+
+.jxNoticeItemContainer {
+    position: relative;
+    overflow: hidden;
+}
+
+.jxNoticeItemContainer .jxChrome {
+  background-image: url(images/flyout_chrome.png);
+  padding: 5px 5px 7px 6px;
+}
+
+.jxHasChrome .jxNoticeItem  {
+  margin: 6px 6px 8px 7px;
+}
+
+.jxNoticeItem {
+    position: relative;
+    border: 2px solid #ccc;
+    margin: 0px;
+    padding: 0px;
+    background-color: #f8f8f8;
+    background-image: url(images/notice.png);
+    background-repeat: repeat-x;
+    background-position: left bottom;
+}
+
+.jxNoticeIcon {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  margin: 6px;
+  width: 16px;
+  height: 16px;
+  background-image: url(images/icons.png);
+  background-repeat: no-repeat;
+  background-position: 0px 0px;
+}
+
+.jxNotice {
+    display: block;
+    position: relative;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    line-height: 18px;
+    color: #666;
+    margin: 0px;
+    border: none;
+    padding: 6px 26px 6px 10px; 
+}                          
+
+.jxNoticeError .jxNoticeItem {   background-color: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; background-image: url(images/notice_error.png);}
+.jxNoticeWarning .jxNoticeItem { background-color: #FFF6BF; color: #514721; border-color: #FFD324; background-image: url(images/notice_warning.png);}
+.jxNoticeSuccess .jxNoticeItem { background-color: #E6EFC2; color: #264409; border-color: #C6D880; background-image: url(images/notice_success.png);}
+
+.jxNoticeError .jxNotice {   color: #8a1f11; padding-left: 28px; }
+.jxNoticeWarning .jxNotice { color: #514721; padding-left: 28px; }
+.jxNoticeSuccess .jxNotice { color: #264409; padding-left: 28px; }
+.jxNoticeInfo .jxNotice { padding-left: 28px; }
+
+.jxNoticeError .jxNoticeIcon {   background-position: 0px -32px; }
+.jxNoticeWarning .jxNoticeIcon { background-position: 0px -16px; }
+.jxNoticeSuccess .jxNoticeIcon { background-position: 0px -48px; }            
+.jxNoticeInfo .jxNoticeIcon {    background-position: 0px -64px; }
+
+.jxNoticeClose {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    margin: 6px;
+    width: 16px;
+    height: 16px;
+    background-image: url(images/tab_close.png);
+    background-position: 0px 0px;
+    background-repeat: no-repeat;
+}/**
  * @project         Jx
- * @revision        $Id: panel.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: panel.css 302 2009-04-02 18:42:56Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1645,9 +2203,76 @@
   margin: 0px;
   padding: 0px;
   border: none;
-}/**
+}@CHARSET "ISO-8859-1";
+
+
+/**
+ * progress bar classes
+ */
+.jxProgressBar-container {
+    width: 100%;
+    display: block;
+    clear: both;
+    
+}
+
+.jxProgressBar-message {
+    color: black;
+}
+
+.jxProgressBar-container .jxProgressBar {
+    width: 100%;
+    clear: both;
+    border-top: none;
+}
+
+.jxProgressBar-container .jxProgressBar .jxProgressBar-outline {
+    border: 1px solid #336600;
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    z-index: 10;
+}
+
+.jxProgressBar-container .jxProgressBar .jxProgressBar-fill {
+    background-color: #99cc66;
+    position: absolute;
+    top: 1px;
+    left: 1px;
+    z-index: 20;
+}
+
+.jxProgressBar-container .jxProgressBar .jxProgressBar-text {
+    color: #336600;
+    margin: 0px;
+    padding: 2px;
+    position: absolute;
+    top: 1px;
+    left: 1px;
+    width: auto;
+    z-index: 30;
+    border: none;
+}@CHARSET "ISO-8859-1";
+
+/**
+ * Slider classes
+ */
+.jxSliderContainer {
+    width: 100%;
+    height: 10px;
+    border: 1px solid black;
+}
+
+.jxSliderKnob {
+    width: 10px;
+    height: 10px;
+    background-color: black;
+    cursor: pointer;
+}
+
+/**
  * @project         Jx
- * @revision        $Id: splitter.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: splitter.css 294 2009-04-02 12:26:26Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1739,7 +2364,7 @@
   background-color: #aaa;
 }/**
  * @project         Jx
- * @revision        $Id: tab.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: tab.css 615 2009-11-19 22:01:38Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1757,7 +2382,8 @@
      the position need to be explicitly set, as well as the width and height. */
   /* Base setup */
   position: relative;
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   overflow: hidden;
 
   width: 200px;
@@ -1793,7 +2419,7 @@
 /* BASE TAB (BUTTON) STYLES */
 /* ======================== */
 
-div.jxTabContainer {
+span.jxTabContainer {
   /* Base setup */
   display: block;
   position: relative;
@@ -1805,7 +2431,8 @@
 
 a.jxTab {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
   cursor: pointer;
   user-select: none;
@@ -1823,7 +2450,8 @@
 
 span.jxTabContent {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   font-size: 0px;
   line-height: 0px;
 
@@ -1837,6 +2465,8 @@
 
 img.jxTabIcon {
   /* Base setup */
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
 
   width: 16px;
@@ -1847,7 +2477,8 @@
 
 span.jxTabLabel {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
   cursor: pointer;
 
@@ -1868,12 +2499,11 @@
   user-select: none;
   -moz-user-select: none;
   -khtml-user-select: none;
-}
-
-a.jxTabClose img {
   width: 16px;
   height: 16px;
-  background-image: url(images/tab_close.png);  
+  background-image: url(images/tab_close.png);
+  background-position: 0px 0px;
+  background-repeat: no-repeat;
 }
 
 .jxDisabled a.jxTab,
@@ -1892,7 +2522,6 @@
 .jxBarTop div.jxTabContainer,
 .jxBarBottom div.jxTabContainer {
   /* Base setup */
-  float: left;
 }
 
 .jxBarTop a.jxTab,
@@ -1908,8 +2537,8 @@
 /* Closeable Tab */
 .jxBarTop a.jxTabClose,
 .jxBarBottom a.jxTabClose {
-  top: 3px;
-  right: 3px;
+  top: 5px;
+  right: 5px;
 }
 
 .jxBarTop .jxTabClose span.jxTabContent,
@@ -1921,7 +2550,6 @@
 .jxBarTop a.jxTab,
 .jxBarBottom a.jxTab {
   /* Base setup */
-  float: left;
 
   padding-left: 4px; /* makes room for the left of the tab bg */
   background-position: left -24px; 
@@ -1930,7 +2558,6 @@
 .jxBarTop span.jxTabContent,
 .jxBarBottom span.jxTabContent {
   /* Base setup */
-  float: left;
 
   padding: 4px 4px 4px 0px; /* makes space around the label */
   background-position: right -24px; 
@@ -2045,14 +2672,14 @@
 
 .jxBarTop img.jxTabIcon,
 .jxBarBottom img.jxTabIcon {
+  vertical-align: middle;
   /* Base setup */
-  float: left;
 }
 
 .jxBarTop span.jxTabLabel,
 .jxBarBottom span.jxTabLabel {
   /* Base setup */
-  float: left;
+  vertical-align: middle;
   height: 16px;
 
   padding: 0px 4px 0px 4px;
@@ -2063,8 +2690,8 @@
 /* VERTICAL TAB BAR - LEFT and RIGHT */
 /* ================================= */
 
-.jxBarLeft div.jxTabContainer,
-.jxBarRight div.jxTabContainer {
+.jxBarLeft span.jxTabContainer,
+.jxBarRight span.jxTabContainer {
   /* Base setup */
 }
 
@@ -2081,8 +2708,8 @@
 /* Closeable Tab */
 .jxBarLeft a.jxTabClose,
 .jxBarRight a.jxTabClose {
-  top: 3px;
-  left: 3px;
+  top: 5px;
+  left: 5px;
 }
 
 .jxBarLeft .jxTabClose span.jxTabContent,
@@ -2216,7 +2843,7 @@
 
 /**
  * @project         Jx
- * @revision        $Id: toolbar.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: toolbar.css 289 2009-04-01 18:09:24Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -2293,7 +2920,7 @@
 .jxBarBottom .jxBarScroller {
   position: absolute;
   /* much bigger than its container */
-  width: 1000%;
+  width: 10000%;
   overflow: hidden;
 }
 
@@ -2418,9 +3045,21 @@
   clear: both;
 }
 
+/*
+ * Tooltip classes
+ */
+.jxTooltip {
+	width: auto;
+	height: auto;
+	background-color: black;
+	color: white;
+	padding: 5px;
+	z-index: 65536;
+}
+
 /**
  * @project         Jx
- * @revision        $Id: tree.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: tree.css 602 2009-11-10 19:41:36Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -2435,7 +3074,7 @@
 */
 
 
-.jxTree, 
+.jxTree,
 .jxTreeRoot {
   /* relative positioning is required for IE to fix the peek-a-boo bug */
   position:relative;
@@ -2455,8 +3094,7 @@
 
 /* Node Classes */
 
-.jxTree li, 
-.jxTreeRoot li {
+li.jxTreeContainer {
   /* relative positioning is required for IE to fix the peek-a-boo bug */
   position:relative;
   display: block;
@@ -2473,12 +3111,11 @@
   -khtml-user-select: none;
 }
 
-.jxTree li {
+.jxTree li.jxTreeContainer {
   margin-left: 16px;
 }
 
-.jxTree a, 
-.jxTreeRoot a {
+a.jxTreeItem {
   position: relative;
   display: block;
   cursor: pointer;
@@ -2490,7 +3127,7 @@
   background-position: left top;
   border: none;
 
-  margin: 0px 1px 0px 15px;
+  margin: 0px 1px 0px 17px;
   padding: 0px 0px 0px 20px;
   z-index: 0;
   font-family: Arial, Helvetica, sans-serif;
@@ -2503,59 +3140,52 @@
 }
 
 
-.jxTree a:focus,
-.jxTreeRoot a:focus {
+a.jxTreeItem:focus {
   border-left: 1px dotted #75ADFF;
   border-right: 1px dotted #75ADFF;
-  margin: 0px 0px 0px 14px;
+  margin: 0px 0px 0px 16px;
   background-position: left -72px;
 }
 
-.jxTree a:hover, 
-.jxTreeRoot a:hover {
+a.jxTreeItem:hover,
+li.jxTreeContainer a.jxHover {
   /*border: 1px solid #C5E0FF;*/
-  border-left: 1px solid #C5E0FF;
-  border-right: 1px solid #C5E0FF;
-  margin: 0px 0px 0px 14px;
+  border-left: 1px solid #CDDFFD;
+  border-right: 1px solid #CDDFFD;
+  margin: 0px 0px 0px 16px;
   background-color: #CDE5FF;
   background-position: left -24px;
 }
 
-.jxTree a.jxTreeItemPressed,
-.jxTree a.jxTreeItemPressed:hover,
-.jxTreeRoot a.jxTreeItemPressed,
-.jxTreeRoot a.jxTreeItemPressed:hover {
-  border-left: 1px solid #C5E0FF;
-  border-right: 1px solid #C5E0FF;
-  margin: 0px 0px 0px 14px;
+li.jxTreeContainer a.jxSelected,
+li.jxTreeContainer a.jxSelected:hover,
+li.jxTreeContainer a.jxPressed,
+li.jxTreeContainer a.jxPressed:hover {
+  border-left: 1px solid #8AABFB;
+  border-right: 1px solid #8AABFB;
+  margin: 0px 0px 0px 16px;
   background-color: #CDE5FF;
   background-position: left -48px;
 }
 
-.jxTree .jxDisabled a,
-.jxTreeRoot.jxDisabled a {
+li.jxDisabled a.jxTreeItem {
   cursor: default;
 }
-
-.jxTree .jxDisabled a:focus,
-.jxTree .jxDisabled a.jxTreeItemPressed,
-.jxTree .jxDisabled a.jxTreeItemPressed:hover,
-.jxTree .jxDisabled a:hover,
-.jxTreeRoot .jxDisabled a:focus,
-.jxTreeRoot .jxDisabled a.jxTreeItemPressed,
-.jxTreeRoot .jxDisabled a.jxTreeItemPressed:hover,
-.jxTreeRoot .jxDisabled a:hover {
+            
+li.jxDisabled a.jxTreeItem:focus,
+li.jxDisabled a.jxTreeItem:hover {
   background-position: left top;
+  background-color: transparent;
   border: none;
-  margin: 0px 1px 0px 15px;
+  margin: 0px 1px 0px 17px;
 }
 
 .jxTreeNest {
   background-image: url(images/tree_vert_line.png);
 }
 
-.jxTreeImage,
-.jxTreeIcon {
+img.jxTreeImage,
+img.jxTreeIcon {
   position: absolute;
   display: inline;
 
@@ -2573,9 +3203,9 @@
   margin: 0px;
 }
 
-.jxTreeIcon { 
+img.jxTreeIcon { 
   height: 16px;
-  top: 1px;
+  top: 2px;
   left: 1px;
 }
 
@@ -2607,43 +3237,91 @@
   background-position: left -140px; /* plus last image */
 }
 
-.jxTreeItem .jxTreeIcon,
-.jxTreeItemLast .jxTreeIcon {
+.jxTreeLeaf .jxTreeIcon,
+.jxTreeLeafLast .jxTreeIcon {
   background-position: left 0px; /* page image */
 }
 
-.jxTreeItem .jxTreeImage {
+.jxTreeLeaf .jxTreeImage {
   background-position: left -60px; /* node image */
 }
 
-.jxTreeItemLast .jxTreeImage {
+.jxTreeLeafLast .jxTreeImage {
   background-position: left -120px; /* last node image */
 }
 
-.jxTreeItemSelected {
-  background-color: #AFD4FA;
-  font-weight:bold;
-}
 
-.jxTreeItem a,
-.jxTreeItem img,
-.jxTreeItem input,
-.jxTreeItemLast a,
-.jxTreeItemLast img,
-.jxTreeItemLast input,
-.jxTreeBranch a,
-.jxTreeBranch img,
-.jxTreeBranch input,
-.jxTreeBranchLast a,
-.jxTreeBranchLast img,
-.jxTreeBranchLast input {
+a.jxTreeItem,
+img.jxTreeImage,
+img.jxTreeIcon,
+span.jxTreeLabel,
+.jxTreeItemContainer input {
     vertical-align: middle;
 }
 
-#pbmg {
-  position: absolute;
-  right: 0px;
-  padding-top: 2px;
-}
-
-
+/* FileUploadPanel */
+.jxFileUploadPanel {
+    padding: 5px;
+}
+
+.jxFileUploadPanel .jxInputContainer {
+    width: 300px;
+}
+ 
+.jxUploadQueue {
+    width: 100%;  
+    margin-top: 10px;  
+}
+
+.jxUploadQueue div {
+    position: relative;
+    width: 95%;
+    clear: both;
+    border-top: 1px solid black;
+    padding: 2px;
+}
+
+.jxUploadQueue div span {
+    display: block;
+}
+
+.jxUploadQueue div span.jxUploadFileName {
+    float: left;
+    font-size: 16px;
+}
+
+.jxUploadQueue div span.jxUploadFileDelete,
+.jxUploadQueue div span.jxUploadFileProgress,
+.jxUploadQueue div span.jxUploadFileComplete,
+.jxUploadQueue div span.jxUploadFileError {
+    float: right;
+    width: 16px;
+    height: 16px;
+    background-repeat: no-repeat;
+    background-position: top left;
+    cursor: pointer;
+}
+
+.jxUploadQueue div span.jxUploadFileDelete {
+    background-image: url('images/delete.gif');
+}
+
+.jxUploadQueue div span.jxUploadFileProgress {
+    background-image: url('images/loading.gif');
+}
+
+.jxUploadQueue div span.jxUploadFileComplete {
+    background-image: url('images/green_tick.png');
+}
+
+.jxUploadQueue div span.jxUploadFileError {
+    background-image: url('images/error.png');
+}
+
+.jxUploadFileErrorTip {
+  padding: 4px 4px 4px 20px; 
+  border: 2px solid #ddd;
+  background: url("images/error.png") no-repeat left top;
+  color: black;
+  width: 100px;
+}
\ No newline at end of file

Modified: trunk/templates/mapserver/standard/ApplicationDefinition.xml
===================================================================
--- trunk/templates/mapserver/standard/ApplicationDefinition.xml	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/ApplicationDefinition.xml	2010-10-19 14:31:05 UTC (rev 2249)
@@ -293,10 +293,14 @@
       <Item xsi:type="SeparatorItemType">
         <Function>Separator</Function>
       </Item>
-      <Item xsi:type="WidgetItemType">
+       <Item xsi:type="WidgetItemType">
         <Function>Widget</Function>
         <Widget>Select</Widget>
       </Item>
+     <Item xsi:type="WidgetItemType">
+        <Function>Widget</Function>
+        <Widget>Select2</Widget>
+      </Item>
       <Item xsi:type="WidgetItemType">
         <Function>Widget</Function>
         <Widget>Pan</Widget>
@@ -1011,6 +1015,10 @@
       <Name>Legend</Name>
       <Type>Legend</Type>
       <StatusText/>
+      <Extension>
+        <ShowRootFolder>false</ShowRootFolder>
+        <ShowMapFolder>true</ShowMapFolder>
+      </Extension>
     </Widget>
 
 <!-- SELECTION -->
@@ -1109,6 +1117,20 @@
       <Disabled/>
     </Widget>
 
+    <Widget xsi:type="UiWidgetType">
+      <Name>Select2</Name>
+      <Type>Select</Type>
+      <StatusText>Select features by clicking and dragging.</StatusText>
+      <Extension>
+      	<QueryActiveLayer>true</QueryActiveLayer>
+      </Extension>
+      <ImageUrl>images/icons.png</ImageUrl>
+      <ImageClass>select-features</ImageClass>
+      <Tooltip>Select mode for 2</Tooltip>
+      <Label/>
+      <Disabled/>
+    </Widget>
+
 <!-- MAPMENU -->
 
     <Widget>

Modified: trunk/templates/mapserver/standard/index.html
===================================================================
--- trunk/templates/mapserver/standard/index.html	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/index.html	2010-10-19 14:31:05 UTC (rev 2249)
@@ -12,17 +12,15 @@
 <link rel="stylesheet" href="icons.css" type="text/css" media="screen" charset="utf-8">
 <style type="text/css">
     #Statusbar .spanCursorPosition,
-    #Statusbar .inputEditableScalePrefix,
     #Statusbar .inputEditableScale,
     #Statusbar .spanViewSize,
     #Statusbar .spanSelectionInfo {
         background-color: #fff;
         padding: 2px 4px;
         line-height: 18px;
-        font-size: 11px;
-        font-family: Arial, Helvetica, sans-serif;
     }
 
+
     li.jxToolItem.activityIndicator {
         float: right;
         padding: 6px 3px;

Modified: trunk/templates/mapserver/standard/themes/crispin/ie6.css
===================================================================
--- trunk/templates/mapserver/standard/themes/crispin/ie6.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/themes/crispin/ie6.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,6 +1,6 @@
 /**
  * @project         Jx
- * @revision        $Id: ie6.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: ie6.css 935 2010-05-28 17:36:13Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -18,9 +18,11 @@
  * Please note that IE8 changed how filters are written. 
  */
 .jxChromeDrag {filter: Alpha(opacity=50);}
-.jxDialogModal {filter: Alpha(opacity=20);}
 .jxDisabled {filter:Alpha(opacity=40);}
 .jxDisabled * {filter:Alpha(opacity=40);}
+.jxMask {filter:Alpha(opacity=50);}
+.jxModalMask {filter: Alpha(opacity=20);}
+.jxSpinner {filter: alpha(opacity=50);}
 iframe.jxIframeShim {filter:Alpha(opacity:0);}
 
 /* List items do not render properly under several conditions.  
@@ -52,6 +54,7 @@
   outline: expression(hideFocus='true');
 }
 
+ul.jxToolbar .jxButton:active span.jxButtonContent,
 .jxButton:active span.jxButtonContent {
   background-position: right -96px;
 }
@@ -62,6 +65,7 @@
   background-position: left -144px;
 }
 
+ul.jxToolbar .jxButtonActive:active span.jxButtonContent,
 .jxButtonActive:active span.jxButtonContent {
   background-position: right -144px;
 }
@@ -72,6 +76,7 @@
   background-position: left -120px;
 }
 
+ul.jxToolbar .jxButtonPressed:active span.jxButtonContent,
 .jxButtonPressed:active span.jxButtonContent {
   background-position: right -120px;
 }

Modified: trunk/templates/mapserver/standard/themes/crispin/ie7.css
===================================================================
--- trunk/templates/mapserver/standard/themes/crispin/ie7.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/themes/crispin/ie7.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,6 +1,6 @@
 /**
  * @project         Jx
- * @revision        $Id: ie7.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: ie7.css 978 2010-09-08 18:37:27Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -18,9 +18,11 @@
  * Please note that IE8 changed how filters are written. 
  */
 .jxChromeDrag {filter: Alpha(opacity=50);}
-.jxDialogModal {filter: Alpha(opacity=20);}
 .jxDisabled {filter:Alpha(opacity=40);}
 .jxDisabled * {filter:Alpha(opacity=40);}
+.jxMask {filter:Alpha(opacity=50);}
+.jxModalMask {filter: Alpha(opacity=20);}
+.jxSpinner {filter: alpha(opacity=50);}
 iframe.jxIframeShim {filter:Alpha(opacity:0);}
 
 /* tree item focus style */
@@ -33,6 +35,14 @@
   outline: expression(hideFocus='true');
 }
 
+.jxTree,
+.jxTreeRoot,
+.jxTreeNest,
+li.jxTreeContainer {
+ /* zoom needed to fix alignment/sizing issues in IE 7 */
+ zoom: 1;
+}
+
 /* IE versions 7 and below do not recognize the focus pseudo-class, but instead
  * use the active pseudo-class.  Other browsers use the active-pseudo-class
  * while something is being pressed so IE specific definitions are needed. */
@@ -43,6 +53,7 @@
   outline: expression(hideFocus='true');
 }
 
+ul.jxToolbar .jxButton:active span.jxButtonContent,
 .jxButton:active span.jxButtonContent {
   background-position: right -96px;
 }
@@ -53,6 +64,7 @@
   background-position: left -144px;
 }
 
+ul.jxToolbar .jxButtonActive:active span.jxButtonContent,
 .jxButtonActive:active span.jxButtonContent {
   background-position: right -144px;
 }
@@ -63,6 +75,7 @@
   background-position: left -120px;
 }
 
+ul.jxToolbar .jxButtonPressed:active span.jxButtonContent,
 .jxButtonPressed:active span.jxButtonContent {
   background-position: right -120px;
 }

Modified: trunk/templates/mapserver/standard/themes/crispin/images/button_combo.png
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/delete.gif (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/delete.gif)
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/crispin/images/dialog_chrome.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/crispin/images/emblems.png
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/error.png (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/error.png)
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/crispin/images/flyout_chrome.png
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/green_tick.png (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/green_tick.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/icons.png (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/icons.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/listitem.png (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/listitem.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/menuitem.png (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/menuitem.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/notice.png (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/notice.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/notice_error.png (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/notice_error.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/notice_success.png (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/notice_success.png)
===================================================================
(Binary files differ)

Copied: trunk/templates/mapserver/standard/themes/crispin/images/notice_warning.png (from rev 2248, sandbox/jxlib-3.0/templates/mapserver/standard/themes/crispin/images/notice_warning.png)
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/crispin/images/panel_controls.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/crispin/jxtheme.css
===================================================================
--- trunk/templates/mapserver/standard/themes/crispin/jxtheme.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/themes/crispin/jxtheme.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -21,4 +21,4 @@
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
- */body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;}q:before,q:after{content:'';}div.jxButtonContainer{display:block;position:relative;float:left;margin:0;padding:2px;border:none;}.jxButton{display:block;position:relative;float:left;margin:0;padding:0 0 0 4px;border:none;background-image:url(images/button.png);background-position:left -24px;background-repeat:no-repeat;text-decoration:none;outline:none;}a.jxButton{cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;}ul.jxToolbar .jxButton{background-position:left top;}span.jxButtonContent{display:block;position:relative;float:left;font-size:0;line-height:0;margin:0;padding:4px 4px 4px 0;border:none;backgr
 ound-image:url(images/button.png);background-position:right -24px;background-repeat:no-repeat;}ul.jxToolbar span.jxButtonContent{background-position:right top;}ul.jxToolbar .jxButtonActive,.jxButtonActive{background-position:left -72px;}ul.jxToolbar .jxButtonActive span.jxButtonContent,.jxButtonActive span.jxButtonContent{background-position:right -72px;}ul.jxToolbar .jxButton:focus,.jxButton:focus{background-position:left -96px;}ul.jxToolbar .jxButton:focus span.jxButtonContent,.jxButton:focus span.jxButtonContent{background-position:right -96px;}ul.jxToolbar .jxButtonActive:focus,.jxButtonActive:focus{background-position:left -144px;}ul.jxToolbar .jxButtonActive:focus span.jxButtonContent,.jxButtonActive:focus span.jxButtonContent{background-position:right -144px;}ul.jxToolbar .jxButton:hover,ul.jxToolbar .jxButtonActive:hover,.jxButton:hover,.jxButtonActive:hover{background-position:left -48px;}ul.jxToolbar .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxButtonActive
 :hover span.jxButtonContent,.jxButton:hover span.jxButtonContent,.jxButtonActive:hover span.jxButtonContent{background-position:right -48px;}ul.jxToolbar .jxButtonPressed,ul.jxToolbar .jxButtonPressed:focus,.jxButtonPressed,.jxButtonPressed:focus{background-position:left -120px;}ul.jxToolbar .jxButtonPressed span.jxButtonContent,ul.jxToolbar .jxButtonPressed:focus span.jxButtonContent,.jxButtonPressed span.jxButtonContent,.jxButtonPressed:focus span.jxButtonContent{background-position:right -120px;}.jxDisabled .jxButton,.jxDisabled span.jxButtonContent span{cursor:default;}ul.jxToolbar .jxDisabled .jxButton:focus,ul.jxToolbar .jxDisabled .jxButton:active,ul.jxToolbar .jxDisabled .jxButton:hover,ul.jxToolbar .jxDisabled .jxButtonPressed{background-position:left top;}.jxDisabled .jxButton:focus,.jxDisabled .jxButton:active,.jxDisabled .jxButton:hover,.jxDisabled .jxButtonPressed{background-position:left -24px;}ul.jxToolbar .jxDisabled .jxButton:focus span.jxButtonContent,ul.jx
 Toolbar .jxDisabled .jxButton:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right top;}.jxDisabled .jxButton:focus span.jxButtonContent,.jxDisabled .jxButton:active span.jxButtonContent,.jxDisabled .jxButton:hover span.jxButtonContent,.jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right -24px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus,ul.jxToolbar .jxDisabled .jxButtonActive:active,ul.jxToolbar .jxDisabled .jxButtonActive:hover,.jxDisabled .jxButtonActive:focus,.jxDisabled .jxButtonActive:active,.jxDisabled .jxButtonActive:hover{background-position:left -72px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:hover span.jxButtonContent,.jxDisabled .jxButtonActive:focus span.jxButtonContent,.jxDisa
 bled .jxButtonActive:active span.jxButtonContent,.jxDisabled .jxButtonActive:hover span.jxButtonContent{background-position:right -72px;}img.jxButtonIcon{position:relative;float:left;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxButtonContent span{display:block;position:relative;float:left;cursor:pointer;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;height:16px;white-space:nowrap;}span.jxButtonContent span.jxButtonLabel{margin:0;padding:0 4px 0 4px;color:#000;font-size:11px;}.jxButtonMenu span.jxButtonContent,.jxButtonMulti span.jxButtonContent,.jxButtonFlyout span.jxButtonContent,.jxButtonCombo span.jxButtonContent,.jxButtonEditCombo span.jxButtonContent{padding-right:0;}.jxButtonMenu span.jxButtonContent span,.jxButtonFlyout span.jxButtonContent span,.jxButtonMulti span.jxButtonContent span,.jxButtonCombo span.jxButtonContent span,.jxButtonEditCombo span.jxButtonContent span{padding-right:16px;backgrou
 nd-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose{position:absolute;display:block;padding:4px 0;font-size:0;line-height:0;right:2px;top:2px;background-image:url(images/button_multi_disclose.png);background-position:right 0;background-repeat:no-repeat;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxButtonDisclose img{width:16px;height:16px;margin:0;padding:0;border:0;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose:focus,a.jxButtonDisclose:active{background-position:right -96px;}a.jxButtonDisclose:hover{background-position:right -48px;}a.jxButtonDisclosePressed{background-position:right -120px;}.jxDisabled a.jxButtonDisclose,.jxDisabled a.jxButtonDisclose:focus,.jxDisabled a.jxButtonDisclose:active,.jxDisabled a.jxButtonDisclose:hover,.jxDisabled a.jxButtonDisclosePressed{cursor:default;background-position:righ
 t 0;}ul.jxToolbar .jxButtonHover{background-position:left -24px!important;}ul.jxToolbar .jxButtonHover span.jxButtonContent{background-position:right -24px!important;}.jxFlyout .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxFlyout{position:absolute;display:block;z-index:100;margin:0;padding:0;}.jxFlyoutContent{position:relative;display:block;overflow:auto;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}.jxButtonMulti,.jxButtonMulti span.jxButtonContent{background-image:url(images/button_multi.png);}.jxButtonEditCombo,.jxButtonEditCombo span.jxButtonContent{background-image:url(images/button_combo.png);}.jxButtonMulti span.jxButtonContent span{padding-right:21px;}.jxButtonEditCombo span.jxButtonContent span{font-size:0;}.jxButtonComboDefault span.jxButtonContent span,.jxButtonComboDefault input{font-style:italic;color:#999;}.jxButtonEditCombo input{float:left;line-height:16px;height:16px;padding:0 4px;margin:0;border:n
 one;font-size:11px;font-family:Arial,Helvetica,sans-serif;background-color:transparent;}.jxChrome{position:absolute;display:block;font-size:0;line-height:0;z-index:-1;width:100%;height:100%;top:0;left:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxChromeDrag{opacity:.5;-ms-filter:"Alpha(opacity=50)";}.jxChromeTL{position:absolute;overflow:hidden;left:0;top:0;width:50%;height:50%;}.jxChromeTR{position:absolute;overflow:hidden;left:50%;top:0;width:50%;height:50%;}.jxChromeBL{position:absolute;overflow:hidden;left:0;top:50%;width:50%;height:50%;}.jxChromeBR{position:absolute;overflow:hidden;left:50%;top:50%;width:50%;height:50%;}.jxChromeTL img{position:absolute;top:0;left:0;width:800px;height:500px;}.jxChromeTR img{position:absolute;top:0;right:0;width:800px;height:500px;}.jxChromeBL img{position:absolute;bottom:0;left:0;width:800px;height:500px;}.jxChromeBR img{position:absolute;bottom:0;right:0;width:800px;height:500px;}.jxColorBar{position:relative;ove
 rflow:hidden;}table.jxColorGrid{position:relative;border-collapse:collapse;empty-cells:show;clear:both;padding:0;margin:0;}.jxColorGrid tr{padding:0;margin:0;}.jxColorGrid td{border:1px solid #000;padding:0;margin:0;}.jxColorGrid td.emptyCell{border:0 solid #000;}.jxColorGrid td.emptyCell span{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;padding:1px;margin:0;}.jxColorGrid a.colorSwatch{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;margin:0;padding:1px;}.jxColorGrid a.borderWhite:hover{border:1px solid #fff;padding:0;}.jxColorGrid a.borderBlack:hover{border:1px solid #000;padding:0;}input.jxHexInput{width:55px;vertical-align:middle;}input.jxAlphaInput{width:30px;vertical-align:middle;}div.jxColorPreview{float:left;position:relative;width:20px;height:20px;border:1px solid #000;margin:2px;vertical-align:middle;background-image:url('images/grid.png');overflow:hidden;}.jxButtonFlyout span.jxButtonContent span.
 jxButtonSwatch{display:block;float:left;width:14px;height:14px;border:1px solid #000;background-image:url('images/grid.png');background-position:0 0;background-repeat:repeat;padding-right:0!important;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch span{display:block;width:14px;height:14px;position:absolute;padding-right:0;background:none;}div.jxColorPreview img{position:absolute;z-index:0;}div.jxColorPreview div{width:20px;height:10px;position:absolute;display:block;left:0;z-index:1;font-size:10px;line-height:0;}div.jxColorPreview div.jxColorSelected{top:0;}div.jxColorPreview div.jxColorHover{bottom:0;}label.jxColorLabel,label.jxAlphaLabel{width:auto;font-family:Arial,sans-serif;font-size:11px;line-height:24px;padding:2px;vertical-align:middle;}a.jxColorClose{position:absolute;top:0;right:0;width:16px;height:16px;}a.jxColorClose img{width:16px;height:16px;}.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;ma
 rgin:0;padding:0;}.jxDisabled{opacity:.4;-ms-filter:"Alpha(opacity=40)";}.jxDisabled *{-ms-filter:"Alpha(opacity=40)";}iframe.jxIframeShim{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;-ms-filter:"Alpha(opacity=0)";z-index:-1;}.jxDialog .jxChrome{background-image:url(images/dialog_chrome.png);}.jxDialog{display:block;z-index:1000;overflow:hidden;}.jxDialogContentContainer{z-index:1;margin:0 11px 13px 12px;border:1px solid #b7b7b7;background-color:#f0f0f0;}.jxDialogModal{position:absolute;display:block;top:0;left:0;width:100%;height:100%;background-color:#000;opacity:.2;-ms-filter:"Alpha(opacity=20)";}.jxDialogContent{display:block;position:relative;overflow:auto;padding:0;z-index:1;}.jxDialogTitle{display:block;position:relative;background-image:url(images/a_pixel.png);text-align:center;height:24px;line-height:24px;z-index:1;margin:6px 6px 0 7px;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxDialogMin .jxDialogTitle{margin-bottom:8px;}.j
 xDialogMoveable,.jxDialogMoveable .jxDialogLabel{cursor:move;}.jxDialogIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxDialogLabel{font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;cursor:default;}.jxDialogResize{position:absolute;bottom:7px;right:6px;width:16px;height:16px;z-index:2;border:0;cursor:se-resize;background-image:url(images/dialog_resize.png);}.jxDialogControls{position:absolute;top:3px;right:2px;height:16px;width:80px;}.jxDialogControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxDialogClose img{background-position:0 -32px;}.jxDialogMenu img{background-position:0 -48px;}.jxDialogHelp img{background-position:0 -64px;}.jxDialogCollapse img{background-position:0 -16px;}.jxDialogMin .jxDialogCollapse img{background-position:0 0;}.jxDialogMax .jxDialogCollapse img{back
 ground-position:0 -16px;}.jxDialogLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxDialogControls .jxButtonContainer,.jxDialogControls span.jxButtonContent,.jxDialogControls .jxButton:hover span.jxButtonContent,.jxDialogControls .jxButton:active span.jxButtonContent,.jxDialogControls .jxButtonActive span.jxButtonContent,.jxDialogControls .jxButtonActive:hover span.jxButtonContent,.jxDialogControls .jxButtonActive:active span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:hover span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:active span.jxButtonContent,.jxDialogControls .jxButton,.jxDialogControls .jxButton:hover,.jxDialogControls .jxButton:active,.jxDialogControls .jxButtonActive,.jxDialogControls .jxButtonActive:hover,.jxDialogControls .jxButtonActive:active,.jxDialogControls .jxDisabled .jxButton,.jxDialogControls .jxDisabled .jxBu
 tton:hover,.jxDialogControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent,.jxDialogControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent span,.jxDialogControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxDialogControls .jxBarContainer{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxDialogControls .jxBarScroller{left:auto;right:0;}.jxDialogControls ul.jxToolbar{float:right;}.jxDialogControls ul.jxToolbar,.jxDialogControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxForm{display:block;position:relative;overflow:hidden;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;}.jxForm label{background-color:transparent;color:#000;}.j
 xForm select,.jxForm input{color:#000;}.jxFormFloat .radioGroup{display:block;overflow:hidden;}.jxFormFloat .radioGroup input,.jxFormFloat .radioGroup label{width:auto!important;vertical-align:middle;}.jxForm .tipArea{display:block;padding:5px 0;background-color:transparent;color:#999;}.jxFormInline label{display:inline;}.jxFormFloat label{display:block;float:left;clear:left;width:75px;}.jxFormFloat select,.jxFormFloat input{float:left;}.jxFormFloat .radioGroup{float:left;}.jxFormFloat .radioGroup input,.jxFormFloat .radioGroup label{float:none;}.jxFormFloat .tipArea{clear:left;}.jxFormBlock label{display:block;}.jxGridContainer{position:absolute;top:0;left:0;border-left:0 solid #d8d8d8;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;overflow:hidden;}.jxGridTable{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;width:0;cursor:default;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal
 ;}.jxGridColHeadHide{height:0;line-height:0;font-size:0;background-color:#fff;white-space:normal;}.jxGridColHeadHide p,.jxGridRowHeadHide p{font-size:0;line-height:0;height:0;margin:0;padding:0;}.jxGridRowHeadHide{width:0;white-space:normal;}.jxGridCell{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;overflow:hidden;padding-left:3px;padding-right:3px;overflow:hidden;white-space:nowrap;cursor:cell;text-overflow:ellipsis;}.jxGridColHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_col.png');background-position:0 0;background-repeat:repeat-x;text-align:center;font-weight:bold;color:#333;cursor:default;}.jxGridRowHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url(
 'images/table_row.png');background-position:0 0;background-repeat:repeat-y;text-align:center;font-weight:bold;color:#333;cursor:default;}.jxGridRowAll{background-color:#fff;}.jxGridColumnHeaderSelected{background-color:#e1e1e1;background-position:0 -200px;}.jxGridRowHeaderSelected{background-color:#e1e1e1;background-position:-400px 0;}.jxGridColumnSelected{background-color:#f7f7f7;}.jxGridRowSelected td{background-color:#f7f7f7;}td.jxGridCellSelected{background-color:#ebebeb;}.jxGridColumnHeaderPrelight{background-color:#cee5ff;background-position:0 -300px;}.jxGridRowHeaderPrelight{background-color:#cee5ff;background-position:-600px 0;}.jxGridColumnPrelight{background-color:#e5f1ff;}.jxGridRowPrelight td{background-color:#e5f1ff;}td.jxGridCellPrelight{background-color:#cce3ff;}.jxMenuContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxButtonMenu span.jxMenuItemSpan{padding-right:16px;}.jxMenuContainer{position:absolute;display:block
 ;z-index:2000;padding:0;}ul.jxMenu,ul.jxSubMenu{display:block;position:relative;list-style-type:none;padding:1px;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}li.jxMenuItemContainer{display:block;position:relative;font-size:0;line-height:0;margin:0;padding:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxMenuItem{display:block;position:relative;overflow:hidden;text-decoration:none;cursor:pointer;outline:none;border:1px solid #fff;background-image:url(images/menuitem.png);background-repeat:no-repeat;background-position:left top;font-family:Arial,Helvetica,sans-serif;font-size:11px;text-decoration:none;margin:0;padding:0;color:#000;}a.jxMenuItemActive{background-position:left -98px;}a.jxMenuItem:focus{background-position:left -74px;}a.jxMenuItem:focus span.jxMenuItemContent{border-right:1px dotted #75ADFF;}a.jxMenuItemActive:focus{background-position:left -170px;}a.jxMenuItem:hover{background-color:#CDE5FF;background-position:left -26
 px;}a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #C5E0FF;}a.jxMenuItemActive:hover{background-position:left -122px;}a.jxMenuItemPressed,a.jxMenuItemPressed:hover{background-color:#CDE5FF;background-position:left -50px;}.jxDisabled a.jxMenuItem,.jxDisabled span.jxMenuItemContent span{cursor:default;}.jxDisabled a.jxMenuItem:focus,.jxDisabled a.jxMenuItemPressed,.jxDisabled a.jxMenuItemPressed:hover,.jxDisabled a.jxMenuItem:hover{background-color:#fff;background-position:left top;border-right:1px solid #fff;}.jxDisabled a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #fff;}span.jxMenuItemContent{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:0;line-height:0;white-space:nowrap;padding:0 20px 0 0;margin:0;border-right:1px solid #fff;}.jxButtonSubMenu span.jxMenuItemContent,.jxButtonSubMenu:hover span.jxMenuItemContent{background-image:url(images/emblems.png);background-position:right -30px;background-repeat:n
 o-repeat;}img.jxMenuItemIcon{position:absolute;top:2px;left:2px;width:16px;height:16px;background-position:left center;background-repeat:no-repeat;}span.jxMenuItemContent span{display:block;position:relative;cursor:pointer;margin:0;padding:2px 0 2px 22px;font-size:16px;line-height:16px;color:#000;}span.jxMenuItemContent span.jxMenuItemLabel{color:#000;font-size:11px;}.jxMenuItemToggle img.jxMenuItemIcon,.jxMenuItemSet img.jxMenuItemIcon{background-image:url(images/emblems.png);background-position:2px 0;background-repeat:no-repeat;}.jxMenuItemToggle a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -48px;}.jxMenuItemSet a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -64px;}ul.jxMenu span.jxMenuSeparator,ul.jxSubMenu span.jxMenuSeparator{display:block;font-size:10px;line-height:10px;background-image:url(images/toolbar_separator_v.png);background-repeat:repeat-x;background-position:left center;}.jxPanel{display:block;position:relative;}.jxPanelContent
 Container{overflow:hidden;background-color:#f0f0f0;}.jxPanelContent{position:relative;display:block;overflow:auto;background-color:#fff;margin:0;padding:0;}.jxPanelTitle{display:block;position:relative;background-image:url(images/panelbar.png);background-repeat:repeat-x;background-position:left top;height:22px;margin:0;padding:0;text-align:center;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxPanelBar{position:absolute;line-height:1px;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxPanelIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxPanelLabel{padding-left:25px;font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;}.jxPanelControls{position:absolute;top:3px;right:2px;height:16px;width:80px;overflow:hidden;}.jxPanelControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0
 ;margin:0;width:16px;height:16px;}.jxPanelClose img{background-position:0 -32px;}.jxPanelMenu img{background-position:0 -48px;}.jxPanelHelp img{background-position:0 -64px;}.jxPanelCollapse img{background-position:0 -16px;}.jxPanelMin .jxPanelCollapse img{background-position:0 0;}.jxPanelMax .jxPanelCollapse img{background-position:0 -16px;}.jxPanelMaximize img{background-position:0 0;}.jxPanelLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxPanelControls .jxButtonContainer,.jxPanelControls span.jxButtonContent,.jxPanelControls .jxButton:hover span.jxButtonContent,.jxPanelControls .jxButton:active span.jxButtonContent,.jxPanelControls .jxButtonActive span.jxButtonContent,.jxPanelControls .jxButtonActive:hover span.jxButtonContent,.jxPanelControls .jxButtonActive:active span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton:hover span.jxButtonContent,.
 jxPanelControls .jxDisabled .jxButton:active span.jxButtonContent,.jxPanelControls .jxButton,.jxPanelControls .jxButton:hover,.jxPanelControls .jxButton:active,.jxPanelControls .jxButtonActive,.jxPanelControls .jxButtonActive:hover,.jxPanelControls .jxButtonActive:active,.jxPanelControls .jxDisabled .jxButton,.jxPanelControls .jxDisabled .jxButton:hover,.jxPanelControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent,.jxPanelControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent span,.jxPanelControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxPanelControls div.jxBarTop{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxPanelControls .jxBarScroller{left:auto;right:0;}.jxPanelControls ul.jxToolbar{float:righ
 t;}.jxPanelControls ul.jxToolbar,.jxPanelControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxSplitterMask{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background-image:url(images/a_pixel.png);z-index:1;}.jxSplitBarHorizontal{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:5px;height:100%;cursor:col-resize;background-color:#f0f0f0;z-index:2;}.jxSplitBarVertical{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxSplitContainer{display:block;position:relative;margin:0;padding:0;border:none;overflow:hidden;}.jxSplitArea{display:block;position:absolute;margin:0;padding:0;border:none;z-index:0;}.jxSplitBarDrag{background-color:#eee;}.jxSnapHorizontalBefore{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxSnapHo
 rizontalAfter{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxTabSetContainer{position:relative;display:block;overflow:hidden;width:200px;height:200px;margin:0;padding:0;background-color:#fff;}.jxTabSetContainer .jxToolbarContainer{z-index:auto;}.tabContent{display:none;position:relative;width:100%;height:100%;overflow:auto;}.tabContentActive{display:block;}div.jxTabContainer{display:block;position:relative;margin:0;padding:2px;border:none;}a.jxTab{display:block;position:relative;cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;margin:0;padding:0;border:none;background-repeat:no-repeat;text-decoration:none;outline:none;}span.jxTabContent{display:block;font-size:0;line-height:0;margin:0;padding:0;border:none;background-repeat:no-repeat;}img.jxTabIcon{position:relative;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxTabLabel{display:block;position:relative;cursor:pointer;mar
 gin:0;padding:0;color:#000;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;}a.jxTabClose{display:block;position:absolute;cursor:pointer;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxTabClose img{width:16px;height:16px;background-image:url(images/tab_close.png);}.jxDisabled a.jxTab,.jxDisabled span.jxTabContent span,.jxDisabled a.jxTabClose{cursor:default;}.jxBarTop div.jxTabContainer,.jxBarBottom div.jxTabContainer{float:left;}.jxBarTop a.jxTab,.jxBarTop span.jxTabContent{background-image:url(images/tab_top.png);}.jxBarBottom a.jxTab,.jxBarBottom span.jxTabContent{background-image:url(images/tab_bottom.png);}.jxBarTop a.jxTabClose,.jxBarBottom a.jxTabClose{top:3px;right:3px;}.jxBarTop .jxTabClose span.jxTabContent,.jxBarBottom .jxTabClose span.jxTabContent{padding-right:16px;}.jxBarTop a.jxTab,.jxBarBottom a.jxTab{float:left;padding-left:4px;background-position:left -24px;}.jxBarTop span.jxTabContent,.jxBarBottom span
 .jxTabContent{float:left;padding:4px 4px 4px 0;background-position:right -24px;}.jxBarTop a.jxTabActive,.jxBarBottom a.jxTabActive{background-position:left -72px;}.jxBarTop a.jxTabActive span.jxTabContent,.jxBarBottom a.jxTabActive span.jxTabContent{background-position:right -72px;}.jxBarTop a.jxTab:focus,.jxBarBottom a.jxTab:focus{background-position:left -96px;}.jxBarTop a.jxTab:focus span.jxTabContent,.jxBarBottom a.jxTab:focus span.jxTabContent{background-position:right -96px;}.jxBarTop a.jxTabActive:focus,.jxBarBottom a.jxTabActive:focus{background-position:left -144px;}.jxBarTop a.jxTabActive:focus span.jxTabContent,.jxBarBottom a.jxTabActive:focus span.jxTabContent{background-position:right -144px;}.jxBarTop a.jxTab:hover,.jxBarTop a.jxTabActive:hover,.jxBarBottom a.jxTab:hover,.jxBarBottom a.jxTabActive:hover{background-position:left -48px;}.jxBarTop a.jxTab:hover span.jxTabContent,.jxBarTop a.jxTabActive:hover span.jxTabContent,.jxBarBottom a.jxTab:hover span.jxTabC
 ontent,.jxBarBottom a.jxTabActive:hover span.jxTabContent{background-position:right -48px;}.jxBarTop a.jxTabPressed,.jxBarTop a.jxTabPressed:focus,.jxBarBottom a.jxTabPressed,.jxBarBottom a.jxTabPressed:focus{background-position:left -120px;}.jxBarTop a.jxTabPressed span.jxTabContent,.jxBarTop a.jxTabPressed:focus span.jxTabContent,.jxBarBottom a.jxTabPressed span.jxTabContent,.jxBarBottom a.jxTabPressed:focus span.jxTabContent{background-position:right -120px;}.jxBarTop .jxDisabled a.jxTab:focus,.jxBarTop .jxDisabled a.jxTab:active,.jxBarTop .jxDisabled a.jxTab:hover,.jxBarTop .jxDisabled a.jxTabPressed,.jxBarBottom .jxDisabled a.jxTab:focus,.jxBarBottom .jxDisabled a.jxTab:active,.jxBarBottom .jxDisabled a.jxTab:hover,.jxBarBottom .jxDisabled a.jxTabPressed{background-position:left -24px;}.jxBarTop .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarTop .jxDisabled a.
 jxTabPressed span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabPressed span.jxTabContent{background-position:right -24px;}.jxBarTop .jxDisabled a.jxTabActive:focus,.jxBarTop .jxDisabled a.jxTabActive:active,.jxBarTop .jxDisabled a.jxTabActive:hover,.jxBarBottom .jxDisabled a.jxTabActive:focus,.jxBarBottom .jxDisabled a.jxTabActive:active,.jxBarBottom .jxDisabled a.jxTabActive:hover{background-position:left -72px;}.jxBarTop .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:right -72
 px;}.jxBarTop img.jxTabIcon,.jxBarBottom img.jxTabIcon{float:left;}.jxBarTop span.jxTabLabel,.jxBarBottom span.jxTabLabel{float:left;height:16px;padding:0 4px 0 4px;}.jxBarLeft a.jxTab,.jxBarLeft span.jxTabContent{background-image:url(images/tab_left.png);}.jxBarRight a.jxTab,.jxBarRight span.jxTabContent{background-image:url(images/tab_right.png);}.jxBarLeft a.jxTabClose,.jxBarRight a.jxTabClose{top:3px;left:3px;}.jxBarLeft .jxTabClose span.jxTabContent,.jxBarRight .jxTabClose span.jxTabContent{padding-top:16px;}.jxBarLeft a.jxTab,.jxBarRight a.jxTab{padding-top:4px;background-position:-24px top;}.jxBarLeft span.jxTabContent,.jxBarRight span.jxTabContent{padding:0 4px 4px 4px;background-position:-24px bottom;}.jxBarLeft a.jxTabActive,.jxBarRight a.jxTabActive{background-position:-72px top;}.jxBarLeft a.jxTabActive span.jxTabContent,.jxBarRight a.jxTabActive span.jxTabContent{background-position:-72px bottom;}.jxBarLeft a.jxTab:focus,.jxBarRight a.jxTab:focus{background-posi
 tion:-96px top;}.jxBarLeft a.jxTab:focus span.jxTabContent,.jxBarRight a.jxTab:focus span.jxTabContent{background-position:-96px bottom;}.jxBarLeft a.jxTabActive:focus,.jxBarRight a.jxTabActive:focus{background-position:-144px top;}.jxBarLeft a.jxTabActive:focus span.jxTabContent,.jxBarRight a.jxTabActive:focus span.jxTabContent{background-position:-144px bottom;}.jxBarLeft a.jxTab:hover,.jxBarLeft a.jxTabActive:hover,.jxBarRight a.jxTab:hover,.jxBarRight a.jxTabActive:hover{background-position:-48px top;}.jxBarLeft a.jxTab:hover span.jxTabContent,.jxBarLeft a.jxTabActive:hover span.jxTabContent,.jxBarRight a.jxTab:hover span.jxTabContent,.jxBarRight a.jxTabActive:hover span.jxTabContent{background-position:-48px bottom;}.jxBarLeft a.jxTabPressed,.jxBarLeft a.jxTabPressed:focus,.jxBarRight a.jxTabPressed,.jxBarRight a.jxTabPressed:focus{background-position:-120px top;}.jxBarLeft a.jxTabPressed span.jxTabContent,.jxBarLeft a.jxTabPressed:focus span.jxTabContent,.jxBarRight a.
 jxTabPressed span.jxTabContent,.jxBarRight a.jxTabPressed:focus span.jxTabContent{background-position:-120px bottom;}.jxBarLeft .jxDisabled a.jxTab:focus,.jxBarLeft .jxDisabled a.jxTab:active,.jxBarLeft .jxDisabled a.jxTab:hover,.jxBarLeft .jxDisabled a.jxTabPressed,.jxBarRight .jxDisabled a.jxTab:focus,.jxBarRight .jxDisabled a.jxTab:active,.jxBarRight .jxDisabled a.jxTab:hover,.jxBarRight .jxDisabled a.jxTabPressed{background-position:-24px top;}.jxBarLeft .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabPressed span.jxTabContent{background-position:-24px bottom;}.jxBarLeft .jxDisabled a.jxTabActive:focus,.jxBarLeft .
 jxDisabled a.jxTabActive:active,.jxBarLeft .jxDisabled a.jxTabActive:hover,.jxBarRight .jxDisabled a.jxTabActive:focus,.jxBarRight .jxDisabled a.jxTabActive:active,.jxBarRight .jxDisabled a.jxTabActive:hover{background-position:-72px top;}.jxBarLeft .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:-72px bottom;}.jxBarLeft span.jxTabLabel,.jxBarRight span.jxTabLabel{padding:4px 0 4px 0;}.jxBarContainer{display:block;position:relative;z-index:1;overflow:hidden;margin:0;padding:0;border:0;background-color:#f0f0f0;}.jxBarTop,.jxBarBottom{width:100%;height:28px;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;ove
 rflow:hidden;}.jxTabBox .jxTabBarTop{background-image:url(images/tabbar.png);background-position:0 bottom;}.jxTabBox .jxTabBarBottom{background-image:url(images/tabbar_bottom.png);background-position:0 top;}.jxBarLeft,.jxBarRight{width:auto;height:100%;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;float:left;overflow:hidden;}.jxTabBox .jxTabBarLeft{background-image:url(images/tabbar_left.png);background-repeat:repeat-y;background-position:right 0;}.jxTabBox .jxTabBarRight{background-image:url(images/tabbar_right.png);background-repeat:repeat-y;background-position:left 0;}.jxBarTop .jxBarScroller,.jxBarBottom .jxBarScroller{position:absolute;width:1000%;overflow:hidden;}.jxBarTop .jxBarScrollLeft,.jxBarBottom .jxBarScrollLeft{position:absolute;top:0;left:0;}.jxBarTop .jxBarScrollRight,.jxBarBottom .jxBarScrollRight{position:absolute;top:0;right:0;}.jxBarTop .jxBarScrollLeft img.jxButtonIcon,.jxBarBottom .jxBarScrollLeft img.jxButt
 onIcon{background-image:url(images/emblems.png);background-position:0 -80px;}.jxBarTop .jxBarScrollRight img.jxButtonIcon,.jxBarBottom .jxBarScrollRight img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -96px;}ul.jxToolbar,ul.jxTabBar{display:block;position:relative;float:left;list-style-type:none;margin:0;padding:0;border:none;}li.jxToolItem{display:block;position:relative;float:left;font-size:0;line-height:0;padding:0;margin:0;border:none;}li.jxToolItem span.jxBarSeparator{display:block;position:relative;float:left;font-size:0;line-height:0;border:0;margin:0;padding:4px;background-repeat:no-repeat;background-position:center center;}.jxBarTop li.jxToolItem span.jxBarSeparator,.jxBarBottom li.jxToolItem span.jxBarSeparator{width:8px;height:20px;background-image:url(images/toolbar_separator_h.png);}.jxBarLeft li.jxToolItem span.jxBarSeparator,.jxBarRight li.jxToolItem span.jxBarSeparator{width:20px;height:8px;background-image:url(images/toolbar_s
 eparator_v.png);}.jxBarLeft ul.jxToolbar,.jxBarLeft ul.jxTabBar,.jxBarLeft li.jxToolItem,.jxBarRight ul.jxToolbar,.jxBarRight ul.jxTabBar,.jxBarRight li.jxToolItem{clear:both;}.jxTree,.jxTreeRoot{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxTreeNest{list-style:none;margin:0;padding:0;background-repeat:repeat-y;background-position:left top;}.jxTree li,.jxTreeRoot li{position:relative;display:block;margin:0;padding:0;background-repeat:no-repeat;background-position:left top;white-space:nowrap;font-size:0;line-height:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxTree li{margin-left:16px;}.jxTree a,.jxTreeRoot a{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;background-image:url(images/tree_hover.png);background-repeat:repeat-x;background-position:left top;border:none;margin:0 1px 0 15px;padding:0 0 0 20px;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;li
 ne-height:20px;height:20px;}.jxTree a:focus,.jxTreeRoot a:focus{border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;margin:0 0 0 14px;background-position:left -72px;}.jxTree a:hover,.jxTreeRoot a:hover{border-left:1px solid #C5E0FF;border-right:1px solid #C5E0FF;margin:0 0 0 14px;background-color:#CDE5FF;background-position:left -24px;}.jxTree a.jxTreeItemPressed,.jxTree a.jxTreeItemPressed:hover,.jxTreeRoot a.jxTreeItemPressed,.jxTreeRoot a.jxTreeItemPressed:hover{border-left:1px solid #C5E0FF;border-right:1px solid #C5E0FF;margin:0 0 0 14px;background-color:#CDE5FF;background-position:left -48px;}.jxTree .jxDisabled a,.jxTreeRoot.jxDisabled a{cursor:default;}.jxTree .jxDisabled a:focus,.jxTree .jxDisabled a.jxTreeItemPressed,.jxTree .jxDisabled a.jxTreeItemPressed:hover,.jxTree .jxDisabled a:hover,.jxTreeRoot .jxDisabled a:focus,.jxTreeRoot .jxDisabled a.jxTreeItemPressed,.jxTreeRoot .jxDisabled a.jxTreeItemPressed:hover,.jxTreeRoot .jxDisabled a:hover{backgroun
 d-position:left top;border:none;margin:0 1px 0 15px;}.jxTreeNest{background-image:url(images/tree_vert_line.png);}.jxTreeImage,.jxTreeIcon{position:absolute;display:inline;left:0;top:0;width:16px;height:20px;z-index:1;background-image:url(images/tree.png);background-repeat:no-repeat;border:0;margin:0;}.jxTreeIcon{height:16px;top:1px;left:1px;}.jxTreeBranchOpen .jxTreeIcon,.jxTreeBranchLastOpen .jxTreeIcon{background-position:left -40px;}.jxTreeBranchOpen .jxTreeImage{background-position:left -100px;}.jxTreeBranchLastOpen .jxTreeImage{background-position:left -160px;}.jxTreeBranchClosed .jxTreeIcon,.jxTreeBranchLastClosed .jxTreeIcon{background-position:left -20px;}.jxTreeBranchClosed .jxTreeImage{background-position:left -80px;}.jxTreeBranchLastClosed .jxTreeImage{background-position:left -140px;}.jxTreeItem .jxTreeIcon,.jxTreeItemLast .jxTreeIcon{background-position:left 0;}.jxTreeItem .jxTreeImage{background-position:left -60px;}.jxTreeItemLast .jxTreeImage{background-posi
 tion:left -120px;}.jxTreeItemSelected{background-color:#AFD4FA;font-weight:bold;}.jxTreeItem a,.jxTreeItem img,.jxTreeItem input,.jxTreeItemLast a,.jxTreeItemLast img,.jxTreeItemLast input,.jxTreeBranch a,.jxTreeBranch img,.jxTreeBranch input,.jxTreeBranchLast a,.jxTreeBranchLast img,.jxTreeBranchLast input{vertical-align:middle;}
\ No newline at end of file
+ */body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;}q:before,q:after{content:'';}.jxButtonContainer{display:-moz-inline-box;display:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:2px;border:none;}.jxButton{display:-moz-inline-box;display:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:0 0 0 4px;border:none;background-image:url(images/button.png);background-position:left -24px;background-repeat:no-repeat;text-decoration:none;outline:none;}a.jxButton{cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;}ul.jxToolbar .jxButton{background-position:left top;}span.jxButtonContent{display:-moz-inline-box;displ
 ay:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:4px 4px 4px 0;border:none;background-image:url(images/button.png);background-position:right -24px;background-repeat:no-repeat;}ul.jxToolbar span.jxButtonContent{background-position:right top;}ul.jxToolbar .jxButtonActive,.jxButtonActive{background-position:left -72px;}ul.jxToolbar .jxButtonActive span.jxButtonContent,.jxButtonActive span.jxButtonContent{background-position:right -72px;}ul.jxToolbar .jxButton:focus,.jxButton:focus{background-position:left -96px;}ul.jxToolbar .jxButton:focus span.jxButtonContent,.jxButton:focus span.jxButtonContent{background-position:right -96px;}ul.jxToolbar .jxButtonActive:focus,.jxButtonActive:focus{background-position:left -144px;}ul.jxToolbar .jxButtonActive:focus span.jxButtonContent,.jxButtonActive:focus span.jxButtonContent{background-position:right -144px;}ul.jxToolbar .jxButton:hover,ul.jxToolbar .jxButtonActive:hover,.jxButton:hover,.jxButtonActive:hover{b
 ackground-position:left -48px;}ul.jxToolbar .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxButtonActive:hover span.jxButtonContent,.jxButton:hover span.jxButtonContent,.jxButtonActive:hover span.jxButtonContent{background-position:right -48px;}ul.jxToolbar .jxButtonPressed,ul.jxToolbar .jxButtonPressed:focus,.jxButtonPressed,.jxButtonPressed:focus{background-position:left -120px;}ul.jxToolbar .jxButtonPressed span.jxButtonContent,ul.jxToolbar .jxButtonPressed:focus span.jxButtonContent,.jxButtonPressed span.jxButtonContent,.jxButtonPressed:focus span.jxButtonContent{background-position:right -120px;}.jxDisabled .jxButton,.jxDisabled span.jxButtonContent span{cursor:default;}ul.jxToolbar .jxDisabled .jxButton:focus,ul.jxToolbar .jxDisabled .jxButton:active,ul.jxToolbar .jxDisabled .jxButton:hover,ul.jxToolbar .jxDisabled .jxButtonPressed{background-position:left top;}.jxDisabled .jxButton:focus,.jxDisabled .jxButton:active,.jxDisabled .jxButton:hover,.jxDisabled .jxButt
 onPressed{background-position:left -24px;}ul.jxToolbar .jxDisabled .jxButton:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right top;}.jxDisabled .jxButton:focus span.jxButtonContent,.jxDisabled .jxButton:active span.jxButtonContent,.jxDisabled .jxButton:hover span.jxButtonContent,.jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right -24px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus,ul.jxToolbar .jxDisabled .jxButtonActive:active,ul.jxToolbar .jxDisabled .jxButtonActive:hover,.jxDisabled .jxButtonActive:focus,.jxDisabled .jxButtonActive:active,.jxDisabled .jxButtonActive:hover{background-position:left -72px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:active span.jxButtonContent,ul.jxToolbar .jxDisab
 led .jxButtonActive:hover span.jxButtonContent,.jxDisabled .jxButtonActive:focus span.jxButtonContent,.jxDisabled .jxButtonActive:active span.jxButtonContent,.jxDisabled .jxButtonActive:hover span.jxButtonContent{background-position:right -72px;}img.jxButtonIcon{display:-moz-inline-box;display:inline-block;position:relative;vertical-align:middle;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxButtonContent span{display:-moz-inline-box;display:inline-block;position:relative;vertical-align:middle;cursor:pointer;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;height:16px;white-space:nowrap;}span.jxButtonContent span.jxButtonLabel{margin:0;padding:0 4px 0 4px;color:#000;font-size:11px;}.jxDiscloser span.jxButtonContent{padding-right:0;}.jxDiscloser span.jxButtonContent span{padding-right:16px;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose{
 position:absolute;display:-moz-inline-box;display:inline-block;padding:4px 0;font-size:0;line-height:0;right:2px;top:2px;background-image:url(images/button_multi_disclose.png);background-position:right 0;background-repeat:no-repeat;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxButtonDisclose img{width:16px;height:16px;margin:0;padding:0;border:0;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose:focus,a.jxButtonDisclose:active{background-position:right -96px;}a.jxButtonDisclose:hover{background-position:right -48px;}a.jxButtonDisclosePressed{background-position:right -120px;}.jxDisabled a.jxButtonDisclose,.jxDisabled a.jxButtonDisclose:focus,.jxDisabled a.jxButtonDisclose:active,.jxDisabled a.jxButtonDisclose:hover,.jxDisabled a.jxButtonDisclosePressed{cursor:default;background-position:right 0;}ul.jxToolbar .jxButtonHover{background-position:left -24px!important;}ul.jxTo
 olbar .jxButtonHover span.jxButtonContent{background-position:right -24px!important;}.jxFlyout .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxFlyout{position:absolute;display:block;z-index:100;margin:0;padding:0;}.jxFlyoutContent{position:relative;display:block;overflow:auto;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}.jxButtonMulti,.jxButtonMulti span.jxButtonContent{background-image:url(images/button_multi.png);}.jxButtonEditCombo,.jxButtonEditCombo span.jxButtonContent{background-image:url(images/button_combo.png);}.jxButtonMulti span.jxButtonContent span{padding-right:21px;}.jxButtonEditCombo span.jxButtonContent span{font-size:0;}.jxButtonComboDefault span.jxButtonContent span,.jxButtonComboDefault input{font-style:italic;color:#999;}.jxButtonEditCombo input{float:left;line-height:16px;height:16px;padding:0 4px;margin:0;border:none;font-size:11px;font-family:Arial,Helvetica,sans-serif;background-color:transpa
 rent;}.jxChrome{position:absolute;display:block;font-size:0;line-height:0;z-index:-1;width:100%;height:100%;top:0;left:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxChromeDrag{opacity:.5;-ms-filter:"Alpha(opacity=50)";}.jxChromeTL{position:absolute;overflow:hidden;left:0;top:0;width:50%;height:50%;}.jxChromeTR{position:absolute;overflow:hidden;left:50%;top:0;width:50%;height:50%;}.jxChromeBL{position:absolute;overflow:hidden;left:0;top:50%;width:50%;height:50%;}.jxChromeBR{position:absolute;overflow:hidden;left:50%;top:50%;width:50%;height:50%;}.jxChromeTL img{position:absolute;top:0;left:0;width:1000px;height:600px;}.jxChromeTR img{position:absolute;top:0;right:0;width:1000px;height:600px;}.jxChromeBL img{position:absolute;bottom:0;left:0;width:1000px;height:600px;}.jxChromeBR img{position:absolute;bottom:0;right:0;width:1000px;height:600px;}.jxColorBar{position:relative;overflow:hidden;}table.jxColorGrid{position:relative;border-collapse:collapse;emp
 ty-cells:show;clear:both;padding:0;margin:0;}.jxColorGrid tr{padding:0;margin:0;}.jxColorGrid td{border:1px solid #000;padding:0;margin:0;}.jxColorGrid td.emptyCell{border:0 solid #000;}.jxColorGrid td.emptyCell span{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;padding:1px;margin:0;}.jxColorGrid a.colorSwatch{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;margin:0;padding:1px;}.jxColorGrid a.borderWhite:hover{border:1px solid #fff;padding:0;}.jxColorGrid a.borderBlack:hover{border:1px solid #000;padding:0;}input.jxHexInput{width:55px;vertical-align:middle;}input.jxAlphaInput{width:30px;vertical-align:middle;}div.jxColorPreview{float:left;position:relative;width:20px;height:20px;border:1px solid #000;margin:2px;vertical-align:middle;background-image:url('images/grid.png');overflow:hidden;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch{display:block;float:left;width:14px;height:14px;border:1px soli
 d #000;background-image:url('images/grid.png');background-position:0 0;background-repeat:repeat;padding-right:0!important;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch span{display:block;width:14px;height:14px;position:absolute;padding-right:0;background:none;}div.jxColorPreview img{position:absolute;z-index:0;}div.jxColorPreview div{width:20px;height:10px;position:absolute;display:block;left:0;z-index:1;font-size:10px;line-height:0;}div.jxColorPreview div.jxColorSelected{top:0;}div.jxColorPreview div.jxColorHover{bottom:0;}label.jxColorLabel,label.jxAlphaLabel{width:auto;font-family:Arial,sans-serif;font-size:11px;line-height:24px;padding:2px;vertical-align:middle;}a.jxColorClose{position:absolute;top:0;right:0;width:16px;height:16px;}a.jxColorClose img{width:16px;height:16px;}.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;margin:0;padding:0;}.jxDisabled{opacity:.4;-ms-filter:"Alpha(opacity=40)";}.jxDi
 sabled *{-ms-filter:"Alpha(opacity=40)";}.jxMask{opacity:.5;-ms-filter:"Alpha(opacity=50)";background-color:#fff;}.jxModalMask{background-color:#000;opacity:.2;-ms-filter:"Alpha(opacity=20)";}.jxEventMask{background-image:url(images/a_pixel.png);}.jxSpinner{position:absolute;opacity:.5;-ms-filter:"Alpha(opacity=50)";z-index:999;background:#fff;}.jxSpinnerMessage{text-align:center;font-weight:bold;}.jxSpinnerSmall .jxSpinnerMessage{margin:0;padding:0;font-size:11px;line-height:24px;}.jxSpinnerLarge .jxSpinnerImage{background:url(images/spinner_24.gif) no-repeat;width:24px;height:24px;margin:0 auto;}.jxSpinnerSmall .jxSpinnerImage{background:url(images/spinner_16.gif) no-repeat;width:16px;height:16px;margin-right:4px;display:inline-block;vertical-align:middle;}.jxConfirmQuestion,.jxPrompt{text-align:center;padding:10px;margin-top:10px;}.jxDialog .jxChrome{background-image:url(images/dialog_chrome.png);}.jxDialog{display:block;z-index:1000;overflow:hidden;visibility:hidden;}.jx
 DialogContentContainer{z-index:1;margin:0 11px 13px 12px;border:1px solid #b7b7b7;background-color:#f0f0f0;}.jxDialogContent{display:block;position:relative;overflow:auto;padding:0;z-index:1;}.jxDialogTitle{display:block;position:relative;background-image:url(images/a_pixel.png);text-align:center;height:24px;line-height:24px;z-index:1;margin:6px 6px 0 7px;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxDialogMin .jxDialogTitle{margin-bottom:8px;}.jxDialogMoveable,.jxDialogMoveable .jxDialogLabel{cursor:move;}.jxDialogIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxDialogLabel{font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;cursor:default;}.jxDialogResize{position:absolute;bottom:7px;right:6px;width:16px;height:16px;z-index:2;border:0;cursor:se-resize;background-image:url(images/dialog_resize.png);}.jxDialogControls{position:absolute;top:3
 px;right:2px;height:16px;width:80px;}.jxDialogControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxDialogClose img{background-position:0 -32px;}.jxDialogMenu img{background-position:0 -48px;}.jxDialogHelp img{background-position:0 -64px;}.jxDialogCollapse img{background-position:0 -16px;}.jxDialogMin .jxDialogCollapse img{background-position:0 0;}.jxDialogMax .jxDialogCollapse img{background-position:0 -16px;}.jxDialogMaximize img{background-position:0 -80px;}.jxDialogMaximized .jxDialogMaximize img{background-position:0 -96px;}.jxDialogLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxDialogControls .jxButtonContainer,.jxDialogControls .jxButton,.jxDialogControls .jxButton:hover,.jxDialogControls .jxButton:active,.jxDialogControls .jxButtonActive,.jxDialogControls .jxButtonActive:hover,.jxDialogControls .jxButtonActive:active,.jxDial
 ogControls .jxDisabled .jxButton,.jxDialogControls .jxDisabled .jxButton:hover,.jxDialogControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxDialogControls .jxBarContainer{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxDialogControls .jxBarScroller{left:auto;right:0;}.jxDialogControls ul.jxToolbar{float:right;}.jxDialogControls ul.jxToolbar,.jxDialogControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}div.jxFileInputs{position:relative;}div.jxFileFake{position:absolute;top:0;left:0;z-index:1;}div.jxFileFake span{float:left;display:block;}div.jxFileFake .jxInputContainer{width:150px;}div.jxFileFake .jxInputText{width:135px;}div.jxFileFake .jxButtonContainer{margin-top:2px;float:left;}.jxInputFile{position:relative;text-align:right;-moz-opacity:0;filter:alpha(opacity:0);opacity:0
 ;z-index:2;margin-top:-5px;height:35px;}.jxForm{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxFieldset{display:block;position:relative;border:1px solid #ccc;margin:10px;padding:5px;}.jxFieldsetLegend,.jxFieldset legend{position:relative;margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:26px;color:#000;}.jxInputContainer{display:block;position:relative;padding:0;margin:2px;border:none;}.jxInputLabel,.jxInputTag{display:-moz-inline-box;display:inline-block;margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:26px;color:#000;}.jxInputLabel{vertical-align:top;}.jxInputTag{vertical-align:bottom;}.jxInputWrapper{display:inline-block;white-space:normal;position:relative;}.jxInputText,.jxInputPassword,.jxInputTextarea,.jxInputCombo,.jxInputColor{margin:0 4px;padding:4px;border:1px solid #bbb;width:232px;font-family:Arial,Helvetica,sans-serif;font
 -size:12px;line-height:16px;color:#000;}.jxInputCombo,.jxInputColor{padding:4px 20px 4px 20px;width:200px;}.jxInputIconHidden .jxInputCombo{padding-left:4px;width:216px;}.jxInputIcon{position:absolute;width:16px;height:16px;left:0;top:0;margin:4px 4px 4px 8px;}.jxInputContainerColor .jxInputIcon{border:1px solid #bbb;width:15px;height:15px;}.jxInputIconHidden .jxInputIcon{display:none;}.jxInputRevealer{position:absolute;width:16px;height:16px;right:0;top:0;margin:4px 8px 4px 4px;font-size:0;line-height:0;}img.jxInputRevealerIcon{background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}.jxInputRevealer .jxButtonContainer,.jxInputRevealer .jxButton{padding:0;margin:0;border:0;background-color:transparent;background-image:none;}.jxInputSelect{margin:0 4px;padding:3px 4px 3px 1px;border:1px solid #bbb;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputRadio,.jxInputCheck{margin:5px;font-family:A
 rial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputText:focus,.jxInputPassword:focus,.jxInputTextarea:focus,.jxInputSelect:focus,.jxInputCombo:focus,.jxInputColor:focus{border:1px solid #000;}.jxInputContainer .jxButtonContainer{padding:0;margin:0 4px;}.jxInputGroup{border:none;padding:0;margin:2px;}.jxInputGroup legend{font-size:0;line-height:0;padding:0;margin:0;border:none;}.jxInputGroup .jxFieldsetLegend{font-size:12px;}.jxInputGroup .jxInputLabel{width:auto;}.jxFieldError .jxInputText,.jxFieldError .jxInputPassword,.jxFieldError .jxInputTextarea,.jxFieldError .jxInputSelect,.jxFieldError .jxInputCombo,.jxFieldError .jxInputColor{background-color:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;}.jxFieldSuccess .jxInputText,.jxFieldSuccess .jxInputPassword,.jxFieldSuccess .jxInputTextarea,.jxFieldSuccess .jxInputSelect,.jxFieldSuccess .jxInputCombo,.jxFieldSuccess .jxInputColor{background-color:#E6EFC2;color:#264409;border-color:#C6D880;}.jxFieldErro
 r .jxInputText:focus,.jxFieldError .jxInputPassword:focus,.jxFieldError .jxInputTextarea:focus,.jxFieldError .jxInputSelect:focus,.jxFieldError .jxInputCombo:focus,.jxFieldError .jxInputColor:focus{border-color:#8a1f11;}.jxFieldSuccess .jxInputText:focus,.jxFieldSuccess .jxInputPassword:focus,.jxFieldSuccess .jxInputTextarea:focus,.jxFieldSuccess .jxInputSelect:focus,.jxFieldSuccess .jxInputCombo:focus,.jxFieldSuccess .jxInputColor:focus{border-color:#264409;}.jxFieldError .jxInputLabel,.jxFieldError .jxInputTag{color:#8a1f11;}.jxFieldSuccess .jxInputLabel,.jxFieldSuccess .jxInputTag{color:#264409;}.jxFormInline .jxInputContainer,form .jxFormInline .jxInputContainer,form .jxFieldset span.jxFormInline,form.jxForm span.jxFormInline{display:inline;}.jxFormInline .jxInputLabel,form .jxFormInline .jxInputLabel,form span.jxFormInline .jxInputLabel{display:inline;width:auto;}.jxFormInline .jxInputTag,form .jxFormInline .jxInputTag,form span.jxFormInline .jxInputTag{display:inline;}
 .jxFormInline .jxInputGroup,form .jxFormInline .jxInputGroup{padding-left:0;}.jxFormInline .jxInputGroup .jxFieldsetLegend,form .jxFormInline .jxInputGroup .jxFieldsetLegend{position:relative;left:auto;}.jxFormInline .jxInputGroup .jxInputLabel,form .jxFormInline .jxInputGroup .jxInputLabel{display:inline;width:auto;}.jxFormBlock .jxInputContainer,form .jxFormBlock .jxInputContainer,form .jxFieldset span.jxFormBlock,form.jxform span.jxFormBlock{display:block;}.jxFormBlock .jxInputLabel,form .jxFormBlock .jxInputLabel,form span.jxFormBlock .jxInputLabel{display:block;width:auto;margin-left:4px;}.jxFormBlock .jxInputContainerCheck .jxInputLabel,.jxFormBlock .jxInputContainerRadio .jxInputLabel,form .jxFormBlock .jxInputContainerCheck .jxInputLabel,form .jxFormBlock .jxInputContainerRadio .jxInputLabel,form span.jxFormBlock .jxInputContainerCheck .jxInputLabel,form span.jxFormBlock .jxInputContainerRadio .jxInputLabel{display:-moz-inline-box;display:inline-block;}.jxFormBlock .
 jxInputGroup,form .jxFormBlock .jxInputGroup{padding-left:0;}.jxFormBlock .jxInputGroup .jxFieldsetLegend,form .jxFormBlock .jxInputGroup .jxFieldsetLegend{position:relative;left:auto;}.jxFormBlock .jxInputGroup .jxInputLabel,form .jxFormBlock .jxInputGroup .jxInputLabel{display:-moz-inline-box;display:inline-block;width:auto;}.jxFormInlineblock .jxInputContainer,form .jxFormInlineblock .jxInputContainer,form .jxFieldset span.jxFormInlineblock,form.jxForm span.jxFormInlineblock{display:block;white-space:nowrap;}.jxFormInlineblock .jxInputLabel,form .jxFormInlineblock .jxInputLabel,form span.jxFormInlineblock .jxInputLabel{display:-moz-inline-box;display:inline-block;width:200px;}.jxFormInlineblock .jxInputGroup,form .jxFormInlineblock .jxInputGroup{padding-left:200px;}.jxFormInlineblock .jxInputGroup .jxFieldsetLegend,form .jxFormInlineblock .jxInputGroup .jxFieldsetLegend{position:absolute;left:-200px;width:200px;}.jxFormInlineblock .jxInputGroup .jxInputLabel,form .jxFormI
 nlineblock .jxInputGroup .jxInputLabel{display:-moz-inline-box;display:inline-block;width:auto;}.jxGridCellContent .jxInputContainer,.jxGridCellContent .jxInputRadio,.jxGridCellContent .jxInputCheck{display:inline;position:relative;border:none;margin:0;padding:0;font-size:0;line-height:0;color:#000;}.jxGridContainer{position:absolute;top:0;left:0;border-left:0 solid #d8d8d8;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;overflow:hidden;}.jxGridTable{width:100%;position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;cursor:default;}.jxGridTableBody{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;cursor:default;}.jxGridCell{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;overflow:hidden;}.jxGridCellContent{position:relative;display:-moz-inline-box;display:inline-block;overflow:hidden;padding:0 3px;overflow:hidd
 en;vertical-align:middle;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal;line-height:16px;white-space:nowrap;cursor:cell;text-overflow:ellipsis;}.jxGridColHead .jxGridCellContent{padding:0 3px;text-align:center;font-weight:bold;color:#333;height:100%;}.jxGridRowHead .jxGridCellContent{text-align:center;font-weight:bold;color:#333;}.jxGridColHead{height:100%;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_col.png');background-position:0 0;background-repeat:repeat-x;text-align:center;cursor:default;padding:0;white-space:nowrap;overflow:hidden;}.jxGridRowHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_row.png');background-position:0 0;background-repeat:repeat-y;text-align:center;cursor:default;o
 verflow:hidden;white-space:nowrap;}.jxGridRowAll{background-color:#fff;}.jxGridColumnHeaderSelected{background-color:#e1e1e1;background-position:0 -200px;}.jxGridRowHeaderSelected{background-color:#e1e1e1;background-position:-400px 0;}.jxGridColumnSelected{background-color:#f7f7f7;}.jxGridRowSelected td,.jxGridRowSelected th{background-color:#f7f7f7;}td.jxGridCellSelected,th.jxGridCellSelected{background-color:#ebebeb;}.jxGridColumnHeaderPrelight{background-color:#cee5ff;background-position:0 -300px;}.jxGridRowHeaderPrelight{background-color:#cee5ff;background-position:-600px 0;}.jxGridColumnPrelight{background-color:#e5f1ff;}.jxGridRowPrelight td,.jxGridRowPrelight th{background-color:#e5f1ff;}td.jxGridCellPrelight,th.jxGridCellPrelight{background-color:#cce3ff;}.jxGridHeader .jxColSortable img{vertical-align:top;width:16px;height:16px;background-image:url('images/emblems.png');background-repeat:no-repeat;background-position:right top;}.jxGridHeader .jxColSortable .jxGridCe
 llContent{margin-right:-16px;}.jxGridHeader .jxGridColumnSortedAsc img{background-position:right -162px;}.jxGridHeader .jxGridColumnSortedDesc img{background-position:right -18px;}.jxGridColumnResize,.jxGridRowResize{display:block;position:absolute;background-image:url(images/a_pixel.png);z-index:1;}.jxGridColumnResize{right:0;top:0;height:100%;width:4px;cursor:col-resize;}.jxColSortable .jxGridColumnResize{right:8px;}.jxGridRowResize{left:0;bottom:0;height:4px;width:100%;cursor:row-resize;}.jxGridEditorPopup{min-width:130px;margin:3px;font-size:10px;box-shadow:2px 2px 5px #888;-moz-box-shadow:2px 2px 5px #888;-webkit-box-shadow:2px 2px 5px #888;background-color:#ddd;border:1px solid #999;position:absolute;}.jxGridEditorPopupInnerWrapper{position:relative;height:100%;width:100%;}.jxListView{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxListView .jxListItemContainer{position:relative;display:block;outline:none;overflow:hidden;border:none;margin:0 1px;
 padding:0;}.jxListItem{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;border:none;margin:0 1px;padding:0;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;line-height:20px;height:20px;}.jxListView .jxHover{margin:0;border-left:1px solid #CDDFFD;border-right:1px solid #CDDFFD;background-image:url(images/listitem.png);background-repeat:repeat-x;background-color:#CDE5FF;background-position:left -24px;}.jxListItem:focus{margin:0;border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;background-image:url(images/listitem.png);background-repeat:repeat-x;background-position:left -72px;}.jxListView .jxPressed,.jxListView .jxSelected{margin:0;border-left:1px solid #8AABFB;border-right:1px solid #8AABFB;background-color:#CDE5FF;background-image:url(images/listitem.png);background-repeat:repeat-x;background-position:left -48px;}.jxMenuContainer .jxChrome{background-image:url(images/flyout_chrome.png);padd
 ing:5px 5px 7px 6px;}.jxButtonMenu span.jxMenuItemSpan{padding-right:16px;}.jxMenuContainer{position:absolute;top:0;left:-10000px;display:none;z-index:2000;padding:0;}ul.jxMenu{display:block;position:relative;list-style-type:none;padding:1px;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}li.jxMenuItemContainer{display:block;position:relative;font-size:0;line-height:0;margin:0;padding:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxMenuItem{display:block;position:relative;overflow:hidden;text-decoration:none;cursor:pointer;outline:none;border:1px solid #fff;background-image:url(images/menuitem.png);background-repeat:no-repeat;background-position:left top;font-family:Arial,Helvetica,sans-serif;font-size:11px;text-decoration:none;margin:0;padding:0;color:#000;}a.jxMenuItemActive{background-position:left -98px;}a.jxMenuItem:focus{background-position:left -74px;}a.jxMenuItem:focus span.jxMenuItemContent{border-right:1px dotted #75ADFF;}a
 .jxMenuItemActive:focus{background-position:left -170px;}a.jxMenuItem:hover{background-color:#CDE5FF;background-position:left -26px;}a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #C5E0FF;}a.jxMenuItemActive:hover{background-position:left -122px;}a.jxMenuItemPressed,a.jxMenuItemPressed:hover{background-color:#CDE5FF;background-position:left -50px;}.jxDisabled a.jxMenuItem,.jxDisabled span.jxMenuItemContent span{cursor:default;}.jxDisabled a.jxMenuItem:focus,.jxDisabled a.jxMenuItemPressed,.jxDisabled a.jxMenuItemPressed:hover,.jxDisabled a.jxMenuItem:hover{background-color:#fff;background-position:left top;border-right:1px solid #fff;}.jxDisabled a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #fff;}span.jxMenuItemContent{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:0;line-height:0;white-space:nowrap;padding:0 20px 0 0;margin:0;border-right:1px solid #fff;}.jxButtonSubMenu span.jxMenuItemContent,.jxButton
 SubMenu:hover span.jxMenuItemContent{background-image:url(images/emblems.png);background-position:right -30px;background-repeat:no-repeat;}img.jxMenuItemIcon{position:absolute;top:2px;left:2px;width:16px;height:16px;background-position:left center;background-repeat:no-repeat;}span.jxMenuItemContent span{display:block;position:relative;cursor:pointer;margin:0;padding:2px 0 2px 22px;font-size:16px;line-height:16px;color:#000;}span.jxMenuItemContent span.jxMenuItemLabel{color:#000;font-size:11px;}.jxMenuItemToggle img.jxMenuItemIcon,.jxMenuItemToggleSet img.jxMenuItemIcon{background-image:url(images/emblems.png);background-position:2px 0;background-repeat:no-repeat;}.jxMenuItemToggle a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -48px;}.jxMenuItemToggleSet a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -64px;}ul.jxMenu span.jxMenuSeparator{display:block;font-size:10px;line-height:10px;background-image:url(images/toolbar_separator_v.png);background
 -repeat:repeat-x;background-position:left center;}.jxMessage{text-align:center;padding:10px;margin-top:10px;}.jxNoticeListContainer{border:none;padding:0;margin:0;font-size:0;line-height:0;z-index:500;}.jxNoticeList{position:relative;}.jxNoticeItemContainer{position:relative;overflow:hidden;}.jxNoticeItemContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxHasChrome .jxNoticeItem{margin:6px 6px 8px 7px;}.jxNoticeItem{position:relative;border:2px solid #ccc;margin:0;padding:0;background-color:#f8f8f8;background-image:url(images/notice.png);background-repeat:repeat-x;background-position:left bottom;}.jxNoticeIcon{position:absolute;top:0;left:0;margin:6px;width:16px;height:16px;background-image:url(images/icons.png);background-repeat:no-repeat;background-position:0 0;}.jxNotice{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;color:#666;margin:0;border:none;padding:6px 26px 6px 10px;
 }.jxNoticeError .jxNoticeItem{background-color:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;background-image:url(images/notice_error.png);}.jxNoticeWarning .jxNoticeItem{background-color:#FFF6BF;color:#514721;border-color:#FFD324;background-image:url(images/notice_warning.png);}.jxNoticeSuccess .jxNoticeItem{background-color:#E6EFC2;color:#264409;border-color:#C6D880;background-image:url(images/notice_success.png);}.jxNoticeInformation .jxNoticeItem{background-color:#F8F8F8;color:#666;border-color:#CCC;background-image:url(images/notice.png);}.jxNoticeError .jxNotice{color:#8a1f11;padding-left:28px;}.jxNoticeWarning .jxNotice{color:#514721;padding-left:28px;}.jxNoticeSuccess .jxNotice{color:#264409;padding-left:28px;}.jxNoticeInformation .jxNotice{color:#666;padding-left:28px;}.jxNoticeError .jxNoticeIcon{background-position:0 -32px;}.jxNoticeWarning .jxNoticeIcon{background-position:0 -16px;}.jxNoticeSuccess .jxNoticeIcon{background-position:0 -48px;}.jxNoticeInformation .jxN
 oticeIcon{background-position:0 -64px;}.jxNoticeClose{position:absolute;top:0;right:0;margin:6px;width:16px;height:16px;background-image:url(images/tab_close.png);background-position:0 0;background-repeat:no-repeat;}.jxPanel{display:block;position:relative;}.jxPanelContentContainer{overflow:hidden;background-color:#f0f0f0;}.jxPanelContent{position:relative;display:block;overflow:auto;background-color:#fff;margin:0;padding:0;}.jxPanelTitle{display:block;position:relative;background-image:url(images/panelbar.png);background-repeat:repeat-x;background-position:left top;height:22px;margin:0;padding:0;text-align:center;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxPanelBar{position:absolute;line-height:1px;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxPanelIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxPanelLabel{padding-left:25px;font-family:Arial,Helvetica,sans-serif;font-si
 ze:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;}.jxPanelControls{position:absolute;top:3px;right:2px;height:16px;width:80px;overflow:hidden;}.jxPanelControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxPanelClose img{background-position:0 -32px;}.jxPanelMenu img{background-position:0 -48px;}.jxPanelHelp img{background-position:0 -64px;}.jxPanelCollapse img{background-position:0 -16px;}.jxPanelMin .jxPanelCollapse img{background-position:0 0;}.jxPanelMax .jxPanelCollapse img{background-position:0 -16px;}.jxPanelMaximize img{background-position:0 0;}.jxPanelLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxPanelControls .jxButtonContainer,.jxPanelControls .jxButton,.jxPanelControls .jxButton:hover,.jxPanelControls .jxButton:active,.jxPanelControls .jxButtonActive,.jxPanelControls .jxButtonActive:hover,.jxPanelCo
 ntrols .jxButtonActive:active,.jxPanelControls .jxDisabled .jxButton,.jxPanelControls .jxDisabled .jxButton:hover,.jxPanelControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxPanelControls div.jxBarTop{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxPanelControls .jxBarScroller{left:auto;right:0;}.jxPanelControls ul.jxToolbar{float:right;}.jxPanelControls ul.jxToolbar,.jxPanelControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxProgressBar-container{position:relative;display:block;width:100%;}.jxProgressBar-message{position:relative;display:block;color:black;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:20px;}.jxProgressBar{position:relative;display:block;width:100%;height:20px;border:none;margin:0;padding:0;}.jxProgressBar-outline{position:absolute;displa
 y:block;top:0;left:0;z-index:10;height:20px;border-left:1px solid #cbc8c8;border-right:1px solid #cbc8c8;background-image:url(images/progressbar.png);background-position:0 -140px;width:100%;}.jxProgressBar-fill{position:absolute;display:block;top:0;left:0;z-index:20;height:20px;border:none;background-image:url(images/progressbar.png);background-position:0 0;}.jxProgressStarting .jxProgressBar-fill{border:none;}.jxProgressWorking .jxProgressBar-fill{border-left:1px solid #49afe8;}.jxProgressFinished .jxProgressBar-fill{border-left:1px solid #49afe8;border-right:1px solid #49afe8;}.jxProgressBar-text{position:absolute;display:block;overflow:visible;top:0;left:1px;width:auto;height:20px;z-index:30;border:none;margin:0;padding:0 0 0 4px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:20px;white-space:nowrap;}.jxHasVerticalScrollbar,.jxHasHorizontalScrollbar{position:relative;overflow:hidden;}.jxScrollbarChildWrapper{overflow:hidden;}.jxHasVerticalScrollbar{padd
 ing-right:25px;}.jxHasVerticalScrollbar .jxScrollbarContainer{position:absolute;right:0;top:0;width:20px;height:100%;border:none;border-left:1px solid black;}.jxHasVerticalScrollbar .jxScrollLeft,.jxHasVerticalScrollbar .jxScrollRight{width:20px;height:20px;display:block;background-color:blue;}.jxHasVerticalScrollbar .jxSliderContainer{height:100%;width:20px;border:none;}.jxHasHorizontalScrollbar{padding-bottom:25px;}.jxHasHorizontalScrollbar .jxScrollbarContainer{position:absolute;bottom:0;left:0;height:20px;width:100%;border:none;border-top:1px solid black;}.jxHasHorizontalScrollbar .jxScrollLeft,.jxHasHorizontalScrollbar .jxScrollRight{width:20px;height:20px;display:block;background-color:blue;}.jxHasHorizontalScrollbar .jxScrollLeft{float:left;}.jxHasHorizontalScrollbar .jxScrollRight{float:right;}.jxHasHorizontalScrollbar .jxSlider{float:left;}.jxHasHorizontalScrollbar .jxSliderContainer{height:20px;width:100%;border:none;}.jxHasVerticalScrollbar .jxSliderKnob,.jxHasHor
 izontalScrollbar .jxSliderKnob{width:20px;height:20px;background-color:black;cursor:pointer;}.jxSliderContainer{width:100%;height:10px;border:1px solid black;}.jxSliderKnob{width:10px;height:10px;background-color:black;cursor:pointer;}.jxSplitterMask{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background-image:url(images/a_pixel.png);z-index:1;}.jxSplitBarHorizontal{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:5px;height:100%;cursor:col-resize;background-color:#f0f0f0;z-index:2;}.jxSplitBarVertical{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxSplitContainer{display:block;position:relative;margin:0;padding:0;border:none;overflow:hidden;}.jxSplitArea{display:block;position:absolute;margin:0;padding:0;border:none;z-index:0;}.jxSplitBarDrag{background-color:#eee;}.jxSnapHorizontalBef
 ore{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxSnapHorizontalAfter{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxTabSetContainer{position:relative;display:block;overflow:hidden;width:200px;height:200px;margin:0;padding:0;background-color:#fff;}.jxTabSetContainer .jxBarContainer{z-index:auto;}.tabContent{display:none;position:relative;width:100%;height:100%;overflow:auto;}.tabContentActive{display:block;}span.jxTabContainer{display:block;position:relative;margin:0;padding:2px;border:none;}a.jxTab{display:-moz-inline-box;display:inline-block;position:relative;cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;margin:0;padding:0;border:none;background-repeat:no-repeat;text-decoration:none;outline:none;}span.jxTabContent{display:-moz-inline-box;display:inline-block;font-size:0;line-height:0;margin:0;padding:0;border:none;background-repeat:no-repeat;}img.jxTabIcon{display:-moz-inline-box
 ;display:inline-block;position:relative;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxTabLabel{display:-moz-inline-box;display:inline-block;position:relative;cursor:pointer;margin:0;padding:0;color:#000;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;}a.jxTabClose{display:block;position:absolute;cursor:pointer;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;width:16px;height:16px;background-image:url(images/tab_close.png);background-position:0 0;background-repeat:no-repeat;}.jxDisabled a.jxTab,.jxDisabled span.jxTabContent span,.jxDisabled a.jxTabClose{cursor:default;}.jxTabBarTop .jxBarWrapper,.jxTabBarBottom .jxBarWrapper{padding-left:2px;}.jxBarTop span.jxTabContainer,.jxBarBottom span.jxTabContainer{margin-right:-1px;padding:2px 0;}.jxBarTop a.jxTab,.jxBarTop span.jxTabContent,.jxTabBarTop .jxBarControls a.jxButton,.jxTabBarTop .jxBarControls span.jxButtonContent{background-
 image:url(images/tab_top.png);}.jxBarBottom a.jxTab,.jxBarBottom span.jxTabContent,.jxTabBarBottom .jxBarControls a.jxButton,.jxTabBarBottom .jxBarControls span.jxButtonContent{background-image:url(images/tab_bottom.png);}.jxBarTop a.jxTabClose,.jxBarBottom a.jxTabClose{top:5px;right:5px;}.jxBarTop .jxTabClose span.jxTabContent,.jxBarBottom .jxTabClose span.jxTabContent{padding-right:16px;}.jxBarTop a.jxTab,.jxBarBottom a.jxTab{padding-left:4px;background-position:left -24px;}.jxBarTop span.jxTabContent,.jxBarBottom span.jxTabContent{padding:4px 4px 4px 0;background-position:right -24px;}.jxBarTop a.jxTabActive,.jxBarBottom a.jxTabActive{background-position:left -72px;}.jxBarTop a.jxTabActive span.jxTabContent,.jxBarBottom a.jxTabActive span.jxTabContent{background-position:right -72px;}.jxBarTop a.jxTab:focus,.jxBarBottom a.jxTab:focus{background-position:left -96px;}.jxBarTop a.jxTab:focus span.jxTabContent,.jxBarBottom a.jxTab:focus span.jxTabContent{background-position:r
 ight -96px;}.jxBarTop a.jxTabActive:focus,.jxBarBottom a.jxTabActive:focus{background-position:left -144px;}.jxBarTop a.jxTabActive:focus span.jxTabContent,.jxBarBottom a.jxTabActive:focus span.jxTabContent{background-position:right -144px;}.jxBarTop a.jxTab:hover,.jxBarTop a.jxTabActive:hover,.jxBarBottom a.jxTab:hover,.jxBarBottom a.jxTabActive:hover{background-position:left -48px;}.jxBarTop a.jxTab:hover span.jxTabContent,.jxBarTop a.jxTabActive:hover span.jxTabContent,.jxBarBottom a.jxTab:hover span.jxTabContent,.jxBarBottom a.jxTabActive:hover span.jxTabContent{background-position:right -48px;}.jxBarTop a.jxTabPressed,.jxBarTop a.jxTabPressed:focus,.jxBarBottom a.jxTabPressed,.jxBarBottom a.jxTabPressed:focus{background-position:left -120px;}.jxBarTop a.jxTabPressed span.jxTabContent,.jxBarTop a.jxTabPressed:focus span.jxTabContent,.jxBarBottom a.jxTabPressed span.jxTabContent,.jxBarBottom a.jxTabPressed:focus span.jxTabContent{background-position:right -120px;}.jxBarTo
 p .jxDisabled a.jxTab:focus,.jxBarTop .jxDisabled a.jxTab:active,.jxBarTop .jxDisabled a.jxTab:hover,.jxBarTop .jxDisabled a.jxTabPressed,.jxBarBottom .jxDisabled a.jxTab:focus,.jxBarBottom .jxDisabled a.jxTab:active,.jxBarBottom .jxDisabled a.jxTab:hover,.jxBarBottom .jxDisabled a.jxTabPressed{background-position:left -24px;}.jxBarTop .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarTop .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabPressed span.jxTabContent{background-position:right -24px;}.jxBarTop .jxDisabled a.jxTabActive:focus,.jxBarTop .jxDisabled a.jxTabActive:active,.jxBarTop .jxDisabled a.jxTabActive:hover,.jxBarBottom .jxDisabled a.jxTabActive:focus,.jxBarBo
 ttom .jxDisabled a.jxTabActive:active,.jxBarBottom .jxDisabled a.jxTabActive:hover{background-position:left -72px;}.jxBarTop .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:right -72px;}.jxBarTop img.jxTabIcon,.jxBarBottom img.jxTabIcon{vertical-align:middle;}.jxBarTop span.jxTabLabel,.jxBarBottom span.jxTabLabel{vertical-align:middle;height:16px;padding:0 4px 0 4px;}.jxTabBarLeft .jxBarWrapper,.jxTabBarRight .jxBarWrapper{padding-top:2px;}.jxBarLeft span.jxTabContainer,.jxBarRight span.jxTabContainer{margin-bottom:-1px;padding:0 2px;}.jxBarLeft a.jxTab,.jxBarLeft span.jxTabContent{background-image:url(images/tab_left.png);}.jxBarRight a.jxTab,.jxB
 arRight span.jxTabContent{background-image:url(images/tab_right.png);}.jxBarLeft a.jxTabClose,.jxBarRight a.jxTabClose{top:5px;left:5px;}.jxBarLeft .jxTabClose span.jxTabContent,.jxBarRight .jxTabClose span.jxTabContent{padding-top:16px;}.jxBarLeft a.jxTab,.jxBarRight a.jxTab{padding-top:4px;background-position:-24px top;}.jxBarLeft span.jxTabContent,.jxBarRight span.jxTabContent{padding:0 4px 4px 4px;background-position:-24px bottom;}.jxBarLeft a.jxTabActive,.jxBarRight a.jxTabActive{background-position:-72px top;}.jxBarLeft a.jxTabActive span.jxTabContent,.jxBarRight a.jxTabActive span.jxTabContent{background-position:-72px bottom;}.jxBarLeft a.jxTab:focus,.jxBarRight a.jxTab:focus{background-position:-96px top;}.jxBarLeft a.jxTab:focus span.jxTabContent,.jxBarRight a.jxTab:focus span.jxTabContent{background-position:-96px bottom;}.jxBarLeft a.jxTabActive:focus,.jxBarRight a.jxTabActive:focus{background-position:-144px top;}.jxBarLeft a.jxTabActive:focus span.jxTabContent,
 .jxBarRight a.jxTabActive:focus span.jxTabContent{background-position:-144px bottom;}.jxBarLeft a.jxTab:hover,.jxBarLeft a.jxTabActive:hover,.jxBarRight a.jxTab:hover,.jxBarRight a.jxTabActive:hover{background-position:-48px top;}.jxBarLeft a.jxTab:hover span.jxTabContent,.jxBarLeft a.jxTabActive:hover span.jxTabContent,.jxBarRight a.jxTab:hover span.jxTabContent,.jxBarRight a.jxTabActive:hover span.jxTabContent{background-position:-48px bottom;}.jxBarLeft a.jxTabPressed,.jxBarLeft a.jxTabPressed:focus,.jxBarRight a.jxTabPressed,.jxBarRight a.jxTabPressed:focus{background-position:-120px top;}.jxBarLeft a.jxTabPressed span.jxTabContent,.jxBarLeft a.jxTabPressed:focus span.jxTabContent,.jxBarRight a.jxTabPressed span.jxTabContent,.jxBarRight a.jxTabPressed:focus span.jxTabContent{background-position:-120px bottom;}.jxBarLeft .jxDisabled a.jxTab:focus,.jxBarLeft .jxDisabled a.jxTab:active,.jxBarLeft .jxDisabled a.jxTab:hover,.jxBarLeft .jxDisabled a.jxTabPressed,.jxBarRight .j
 xDisabled a.jxTab:focus,.jxBarRight .jxDisabled a.jxTab:active,.jxBarRight .jxDisabled a.jxTab:hover,.jxBarRight .jxDisabled a.jxTabPressed{background-position:-24px top;}.jxBarLeft .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabPressed span.jxTabContent{background-position:-24px bottom;}.jxBarLeft .jxDisabled a.jxTabActive:focus,.jxBarLeft .jxDisabled a.jxTabActive:active,.jxBarLeft .jxDisabled a.jxTabActive:hover,.jxBarRight .jxDisabled a.jxTabActive:focus,.jxBarRight .jxDisabled a.jxTabActive:active,.jxBarRight .jxDisabled a.jxTabActive:hover{background-position:-72px top;}.jxBarLeft .jxDisabled a.jxTabActive:focus
  span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:-72px bottom;}.jxBarLeft span.jxTabLabel,.jxBarRight span.jxTabLabel{padding:4px 0 4px 0;}.jxBarContainer{display:block;position:relative;z-index:1;overflow:hidden;margin:0;padding:0;border:0;background-color:#f0f0f0;}.jxBarTop,.jxBarBottom{width:100%;height:28px;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;overflow:hidden;}.jxTabBox .jxTabBarTop{background-image:url(images/tabbar.png);background-position:0 bottom;}.jxTabBox .jxTabBarBottom{background-image:url(images/tabbar_bottom.png);background-position:0 top;}.jxBarLeft,.jxBarRight{width:auto;height:100%;background-image:url(images/
 toolbar.png);background-repeat:repeat-x;background-position:0 0;float:left;overflow:hidden;}.jxTabBox .jxTabBarLeft{background-image:url(images/tabbar_left.png);background-repeat:repeat-y;background-position:right 0;}.jxTabBox .jxTabBarRight{background-image:url(images/tabbar_right.png);background-repeat:repeat-y;background-position:left 0;}.jxBarTop .jxBarScroller,.jxBarBottom .jxBarScroller{float:left;height:28px;overflow:hidden;z-index:0;}.jxBarTop .jxBarScroller .jxBarWrapper,.jxBarBottom .jxBarScroller .jxBarWrapper{float:left;height:28px;overflow:hidden;width:10000%;}.jxBarTop .jxBarControls .jxButtonContainer,.jxBarBottom .jxBarControls .jxButtonContainer{z-index:1;padding:2px 0;margin-left:-1px;}.jxBarTop .jxBarScrollLeft img.jxButtonIcon,.jxBarBottom .jxBarScrollLeft img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -80px;}.jxBarTop .jxBarScrollRight img.jxButtonIcon,.jxBarBottom .jxBarScrollRight img.jxButtonIcon{background-image:url(i
 mages/emblems.png);background-position:0 -96px;}.jxBarControls{float:right;position:relative;font-size:0;line-height:0;}ul.jxToolbar,ul.jxTabBar{display:block;position:relative;float:left;clear:none;list-style-type:none;margin:0;padding:0;border:none;}li.jxToolItem{display:block;position:relative;float:left;font-size:0;line-height:0;white-space:nowrap;padding:0;margin:0;border:none;}li.jxToolItem .jxInputWrapper{white-space:nowrap;}li.jxToolItem span.jxBarSeparator{display:block;position:relative;float:left;font-size:0;line-height:0;border:0;margin:0;padding:4px;background-repeat:no-repeat;background-position:center center;}.jxBarTop li.jxToolItem span.jxBarSeparator,.jxBarBottom li.jxToolItem span.jxBarSeparator{width:8px;height:20px;background-image:url(images/toolbar_separator_h.png);}.jxBarLeft li.jxToolItem span.jxBarSeparator,.jxBarRight li.jxToolItem span.jxBarSeparator{width:20px;height:8px;background-image:url(images/toolbar_separator_v.png);}.jxBarLeft ul.jxToolbar
 ,.jxBarLeft ul.jxTabBar,.jxBarLeft li.jxToolItem,.jxBarRight ul.jxToolbar,.jxBarRight ul.jxTabBar,.jxBarRight li.jxToolItem{clear:both;}.jxToolbarAlignLeft ul{float:left;}.jxToolbarAlignRight ul{float:right;}.jxToolbarAlignCenter{text-align:center;}.jxToolbarAlignCenter ul{float:none;}.jxToolbarAlignCenter ul li{float:none;display:inline;}.jxTooltip{width:auto;height:auto;background-color:black;color:white;padding:5px;z-index:65536;}.jxTree,.jxTreeRoot{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxTreeNest{list-style:none;margin:0;padding:0;background-repeat:repeat-y;background-position:left top;}li.jxTreeContainer{position:relative;display:block;margin:0;padding:0;background-repeat:no-repeat;background-position:left top;white-space:nowrap;font-size:0;line-height:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxTree li.jxTreeContainer{margin-left:16px;}a.jxTreeItem{position:relative;display:block;cursor:pointer;outline:none;overfl
 ow:hidden;background-image:url(images/tree_hover.png);background-repeat:repeat-x;background-position:left top;border:none;margin:0 1px 0 17px;padding:0 0 0 20px;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;line-height:20px;height:20px;}a.jxTreeItem:focus{border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;margin:0 0 0 16px;background-position:left -72px;}a.jxTreeItem:hover,li.jxTreeContainer a.jxHover{border-left:1px solid #CDDFFD;border-right:1px solid #CDDFFD;margin:0 0 0 16px;background-color:#CDE5FF;background-position:left -24px;}li.jxTreeContainer a.jxSelected,li.jxTreeContainer a.jxSelected:hover,li.jxTreeContainer a.jxPressed,li.jxTreeContainer a.jxPressed:hover{border-left:1px solid #8AABFB;border-right:1px solid #8AABFB;margin:0 0 0 16px;background-color:#CDE5FF;background-position:left -48px;}li.jxDisabled a.jxTreeItem{cursor:default;}li.jxDisabled a.jxTreeItem:focus,li.jxDisabled a.jxTreeItem:hover{bac
 kground-position:left top;background-color:transparent;border:none;margin:0 1px 0 17px;}.jxTreeNest{background-image:url(images/tree_vert_line.png);}img.jxTreeImage,img.jxTreeIcon{position:absolute;display:inline;left:0;top:0;width:16px;height:20px;z-index:1;background-image:url(images/tree.png);background-repeat:no-repeat;border:0;margin:0;}img.jxTreeIcon{height:16px;top:2px;left:1px;}.jxTreeBranchOpen .jxTreeIcon,.jxTreeBranchLastOpen .jxTreeIcon{background-position:left -40px;}.jxTreeBranchOpen .jxTreeImage{background-position:left -100px;}.jxTreeBranchLastOpen .jxTreeImage{background-position:left -160px;}.jxTreeBranchClosed .jxTreeIcon,.jxTreeBranchLastClosed .jxTreeIcon{background-position:left -20px;}.jxTreeBranchClosed .jxTreeImage{background-position:left -80px;}.jxTreeBranchLastClosed .jxTreeImage{background-position:left -140px;}.jxTreeLeaf .jxTreeIcon,.jxTreeLeafLast .jxTreeIcon{background-position:left 0;}.jxTreeLeaf .jxTreeImage{background-position:left -60px;}
 .jxTreeLeafLast .jxTreeImage{background-position:left -120px;}a.jxTreeItem,img.jxTreeImage,img.jxTreeIcon,span.jxTreeLabel,.jxTreeItemContainer input{vertical-align:middle;}img.jxTreeImage.jxBusy{background-image:url(images/spinner_16.gif);background-position:left top;}.jxFileUploadPanel{padding:5px;}.jxUploadQueue li{display:block;position:relative;overflow:hidden;padding:2px;}.jxUploadQueue div span{display:block;}.jxUploadQueue li span.jxUploadFileName{font-size:12px;line-height:16px;padding-left:2px;}.jxUploadQueue li span.jxUploadFileDelete,.jxUploadQueue li span.jxUploadFileProgress,.jxUploadQueue li span.jxUploadFileComplete,.jxUploadQueue li span.jxUploadFileError{position:absolute;top:2px;right:2px;width:16px;height:16px;background-repeat:no-repeat;cursor:pointer;}.jxUploadQueue li span.jxUploadFileDelete{background-image:url('images/icons.png');background-position:0 -128px;}.jxUploadQueue li span.jxUploadFileProgress{background-image:url('images/spinner_16.gif');ba
 ckground-position:top left;}.jxUploadQueue li span.jxUploadFileComplete{background-image:url('images/icons.png');background-position:0 -48px;}.jxUploadQueue li span.jxUploadFileError{background-image:url('images/icons.png');background-position:0 -32px;}.jxUploadFileErrorTip{padding:4px 4px 4px 20px;border:2px solid #ddd;background:url("images/icons.png") no-repeat 0 -32px;color:black;width:100px;}
\ No newline at end of file

Modified: trunk/templates/mapserver/standard/themes/crispin/jxtheme.uncompressed.css
===================================================================
--- trunk/templates/mapserver/standard/themes/crispin/jxtheme.uncompressed.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/themes/crispin/jxtheme.uncompressed.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -13,7 +13,7 @@
 h1,h2,h3,h4,h5,h6{font-size:100%;}
 q:before,q:after{content:'';}/**
  * @project         Jx
- * @revision        $Id: button.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: button.css 593 2009-11-09 20:29:54Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -25,11 +25,14 @@
    Buttons can use the sliding door technique with background images to horizontally
    accomodate icons with labels. */
 
-div.jxButtonContainer {
+.jxButtonContainer {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  font-size: 0px;
+  line-height: 0px;
+  /* float: left; */
 
   margin: 0px;
   padding: 2px;
@@ -39,9 +42,12 @@
 /* normal button */
 .jxButton {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  font-size: 0px;
+  line-height: 0px;
+  /* float: left; */
 
   /* Using background images, the A contains the left side of the background */
   margin: 0px; /* margins don't seem to work properly in IE */
@@ -69,9 +75,10 @@
 
 span.jxButtonContent {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  /* float: left; */
   font-size: 0px;
   line-height: 0px;
 
@@ -207,8 +214,11 @@
 
 img.jxButtonIcon {
   /* Base setup */
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  vertical-align: middle;
+  /* float: left; */
 
   width: 16px;
   height: 16px;
@@ -218,9 +228,11 @@
 
 span.jxButtonContent span {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  vertical-align: middle;
+  /* float: left; */
   cursor: pointer;
 
   font-family: Arial, Helvetica, sans-serif;
@@ -241,19 +253,11 @@
 /* JX BUTTON EXTENSION STYLES */
 /* ========================== */
 
-.jxButtonMenu span.jxButtonContent,
-.jxButtonMulti span.jxButtonContent,
-.jxButtonFlyout span.jxButtonContent,
-.jxButtonCombo span.jxButtonContent,
-.jxButtonEditCombo span.jxButtonContent {
+.jxDiscloser span.jxButtonContent {
   padding-right: 0px;
 }
 
-.jxButtonMenu span.jxButtonContent span,
-.jxButtonFlyout span.jxButtonContent span,
-.jxButtonMulti span.jxButtonContent span,
-.jxButtonCombo span.jxButtonContent span,
-.jxButtonEditCombo span.jxButtonContent span {
+.jxDiscloser span.jxButtonContent span {
   padding-right: 16px;
   background-image: url(images/emblems.png);
   background-position: right -16px;
@@ -262,7 +266,8 @@
 
 a.jxButtonDisclose {
   position: absolute;
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   padding: 4px 0px;
   font-size: 0px;
   line-height: 0px;
@@ -390,7 +395,7 @@
   background-color: transparent;
 }/**
  * @project         Jx
- * @revision        $Id: chrome.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: chrome.css 454 2009-06-03 14:50:22Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -463,37 +468,37 @@
   position: absolute; 
   top: 0px; 
   left: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeTR img { 
   position: absolute; 
   top: 0px; 
   right: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeBL img { 
   position: absolute; 
   bottom: 0px; 
   left: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeBR img { 
   position: absolute; 
   bottom: 0px; 
   right: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 /**
  * @project         Jx
- * @revision        $Id: color.css 424 2009-05-12 12:51:44Z pagameba $
+ * @revision        $Id: color.css 423 2009-05-12 12:37:56Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -664,7 +669,7 @@
     height: 16px;
 }/**
  * @project         Jx
- * @revision        $Id: common.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: common.css 736 2010-03-05 16:04:56Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -673,12 +678,98 @@
 /* COMMON STYLES */
 /* ============= */
 
-.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;margin:0;padding:0;}
-.jxDisabled{opacity:0.4;-ms-filter: "Alpha(opacity=40)";}
-.jxDisabled * {-ms-filter: "Alpha(opacity=40)";}
-iframe.jxIframeShim{position:absolute;top:0px;left:0px;width:100%;height:100%;opacity:0;-ms-filter: "Alpha(opacity=0)";z-index:-1;}/**
+.jxClearer {
+  display: block;
+  position: relative;
+  float: none;
+  clear: both;
+  font-size: 0;
+  line-height: 0;
+  width: 0;
+  height: 0;
+  margin: 0;
+  padding: 0;
+}
+
+.jxDisabled {
+  opacity: 0.4;
+  -ms-filter: "Alpha(opacity=40)";
+}
+
+.jxDisabled * {
+  -ms-filter: "Alpha(opacity=40)";
+}
+
+/* ============= */
+/*  MASK STYLES  */
+/* ============= */
+
+.jxMask {
+  opacity: 0.5;
+  -ms-filter: "Alpha(opacity=50)";
+  background-color: #fff;
+}
+
+.jxModalMask {
+  background-color: #000;
+  opacity: 0.2;
+  -ms-filter: "Alpha(opacity=20)";
+}
+
+.jxEventMask {
+  background-image: url(images/a_pixel.png);
+}
+
+
+.jxSpinner {
+  position: absolute;
+  opacity: 0.5;
+  -ms-filter: "Alpha(opacity=50)";
+  z-index: 999;
+  background: #fff;
+}
+
+/* .jxSpinnerContent { } */
+
+.jxSpinnerMessage {
+  text-align: center;
+  font-weight: bold;
+}
+
+.jxSpinnerSmall .jxSpinnerMessage {
+  margin: 0px;
+  padding: 0px;
+  font-size: 11px;
+  line-height: 24px;
+}
+
+.jxSpinnerLarge .jxSpinnerImage {
+  background: url(images/spinner_24.gif) no-repeat;
+  width: 24px;
+  height: 24px;
+  margin: 0 auto;
+}
+
+.jxSpinnerSmall .jxSpinnerImage {
+  background: url(images/spinner_16.gif) no-repeat;
+  width: 16px;
+  height: 16px;
+  margin-right: 4px;
+  display: inline-block;
+  vertical-align: middle;
+}
+
+/*iframe.jxIframeShim { }*/
+/**
+ * Confirm and Prompt dialog classes
+ */
+.jxConfirmQuestion, .jxPrompt {
+    text-align: center;
+    padding: 10px;
+    margin-top: 10px;
+}/**
  * @project         Jx
- * @revision        $Id: dialog.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: dialog.css 732 2010-03-05 14:38:36Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -696,6 +787,9 @@
   display: block;
   z-index: 1000;
   overflow: hidden;
+
+  /* initial state is hidden */
+  visibility: hidden;
 }
 
 .jxDialogContentContainer {
@@ -705,20 +799,6 @@
   background-color: #f0f0f0;
 }
 
-.jxDialogModal {
-  /* Base setup */
-  position: absolute;
-  display: block;
-  top: 0px;
-  left: 0px;
-  width: 100%;
-  height: 100%;
-
-  background-color: #000;
-  opacity: .2;
-  -ms-filter: "Alpha(opacity=20)";
-}
-
 .jxDialogContent {
   /* Base setup */
   display: block;
@@ -839,6 +919,14 @@
   background-position: 0px -16px;
 }
 
+.jxDialogMaximize img {
+  background-position: 0px -80px;
+}
+
+.jxDialogMaximized .jxDialogMaximize img {
+  background-position: 0px -96px;
+}
+
 .jxDialogLoading img {
   border: 0px;
   /* the margin needs to make up the difference between it's width/height
@@ -859,15 +947,6 @@
 /* ========================= */
 
 .jxDialogControls .jxButtonContainer,
-.jxDialogControls span.jxButtonContent,
-.jxDialogControls .jxButton:hover span.jxButtonContent,
-.jxDialogControls .jxButton:active span.jxButtonContent,
-.jxDialogControls .jxButtonActive span.jxButtonContent,
-.jxDialogControls .jxButtonActive:hover span.jxButtonContent,
-.jxDialogControls .jxButtonActive:active span.jxButtonContent,
-.jxDialogControls .jxDisabled .jxButton span.jxButtonContent,
-.jxDialogControls .jxDisabled .jxButton:hover span.jxButtonContent,
-.jxDialogControls .jxDisabled .jxButton:active span.jxButtonContent,
 .jxDialogControls .jxButton,
 .jxDialogControls .jxButton:hover,
 .jxDialogControls .jxButton:active ,
@@ -885,16 +964,6 @@
 }
 
 
-.jxDialogControls .jxButtonMenu span.jxButtonContent,
-.jxDialogControls .jxButtonFlyout span.jxButtonContent {
-  background-image: none;
-}
-
-.jxDialogControls .jxButtonMenu span.jxButtonContent span,
-.jxDialogControls .jxButtonFlyout span.jxButtonContent span {
-  padding-right: 0px;
-}
-
 /* ========================== */
 /* JX TOOLBAR STYLES OVERIDES */
 /* ========================== */
@@ -929,9 +998,50 @@
   margin: 0px;
   padding: 0px;
   border: none;
+}/**
+ * File Input classes
+ */
+div.jxFileInputs {
+    position: relative;
+}
+
+div.jxFileFake {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    z-index: 1;
+}
+
+div.jxFileFake span {
+    float: left;
+    display: block;
+}
+
+div.jxFileFake .jxInputContainer {
+    width: 150px;
+}
+
+div.jxFileFake .jxInputText {
+    width: 135px;
+} 
+
+div.jxFileFake .jxButtonContainer {
+    margin-top: 2px;
+    float: left;
+}
+
+.jxInputFile {
+    position: relative;
+    text-align: right;
+    -moz-opacity: 0;
+    filter: alpha(opacity:0);
+    opacity: 0;
+    z-index: 2;
+    margin-top: -5px;
+    height: 35px;
 }/**
  * @project         Jx
- * @revision        $Id: form.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: form.css 827 2010-04-01 14:22:49Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -939,98 +1049,441 @@
 /* =========== */
 /* FORM STYLES */
 /* =========== */
-/* JxForm classes are a set of styles that can be used forlaying out forms 
- * There are three different types of layouts: Inline, Float and Block.
- * Each can be usedto layout an entire form, or a portion of the form as is needed
+/* JxForm classes are a set of styles that can be used for laying out forms 
+ * There are three different types of layouts: Inline, Inlineblock and Block.
+ * Each can be used to layout an entire form, a fieldset or an individual input.
  */
 
- .jxForm {
+/* debuggiong styles */
+/*
+.jxForm           { background-color: yellow; }
+.jxFieldset       { background-color: khaki; }
+.jxInputGroup     { background-color: tan; }
+.jxFieldsetLegend { background-color: orange; }
+.jxInputContainer { background-color: pink; }
+.jxInputLabel     { background-color: plum; }
+.jxInputTag       { background-color: lime; }
+*/
+
+ /* Base and Typography Styles */
+ 
+.jxForm {
   display: block;
   position: relative;
-  overflow: hidden; /* ensures that floated elements are contained */
   font-family: Arial, Helvetica, sans-serif;
   font-size: 12px;
-  line-height: 18px;
+  line-height: 16px;
+  color: #000;
 }
 
-.jxForm label {
-  background-color: transparent;
-  color: #000;
+.jxFieldset {
+  display: block;
+  position: relative;
+  border: 1px solid #ccc;
+  margin: 10px;
+  padding: 5px;
 }
 
-.jxForm select, 
-.jxForm input {
+.jxFieldsetLegend,
+.jxFieldset legend {
+  position: relative;
+  margin: 0px;
+  padding: 0px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 14px;
+  line-height: 26px;
   color: #000;
 }
 
-.jxFormFloat .radioGroup {
+.jxInputContainer {
   display: block;
-  overflow: hidden;
+  position: relative;
+  padding: 0px;
+  margin: 2px;
+  border: none;
 }
 
-/* wrap the radio button in a label */
-.jxFormFloat .radioGroup input,
-.jxFormFloat .radioGroup label {
-  width: auto !important;
-  vertical-align: middle;
+.jxInputLabel,
+.jxInputTag {
+  display: -moz-inline-box;
+  display: inline-block;
+  margin: 0px;
+  padding: 0px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 26px;
+  color: #000;
 }
 
-.jxForm .tipArea {
-  display: block;
-  padding: 5px 0px;
+.jxInputLabel {
+  vertical-align: top;
+}
+
+.jxInputTag {
+  vertical-align: bottom;
+}
+
+.jxInputWrapper {
+  display: inline-block;
+  white-space: normal;
+  position: relative;
+}
+
+.jxInputText,
+.jxInputPassword,
+.jxInputTextarea,
+.jxInputCombo,
+.jxInputColor {
+  margin: 0px 4px;
+  padding: 4px;
+  border: 1px solid #bbb;
+  /* overall width is 250px, margins+padding+border is 18px */
+  width: 232px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputCombo,
+.jxInputColor {
+  padding: 4px 20px 4px 20px;
+  /* overall width is 250px, margins+padding+border is 50px */
+  width: 200px;
+}
+
+.jxInputIconHidden .jxInputCombo {
+  /* overall width is 250px, margins+padding+border is 34px */
+  padding-left: 4px;
+  width: 216px;
+}
+
+.jxInputIcon {
+  position: absolute;
+  width: 16px;
+  height: 16px;
+  left: 0px;
+  top: 0px;
+  margin: 4px 4px 4px 8px;
+}
+
+.jxInputContainerColor .jxInputIcon {
+  border: 1px solid #bbb;
+  width: 15px;
+  height: 15px;
+}
+
+.jxInputIconHidden .jxInputIcon {
+  display: none;
+}
+
+.jxInputRevealer {
+  position: absolute;
+  width: 16px;
+  height: 16px;
+  right: 0px;
+  top: 0px;
+  margin: 4px 8px 4px 4px;
+  font-size: 0px;
+  line-height: 0px;
+}
+
+img.jxInputRevealerIcon {
+  background-image: url(images/emblems.png);
+  background-position: right -16px;
+  background-repeat: no-repeat;
+}
+
+.jxInputRevealer .jxButtonContainer,
+.jxInputRevealer .jxButton {
+  padding: 0px;
+  margin: 0px;
+  border: 0px;
   background-color: transparent;
-  color: #999;
+  background-image: none;
 }
 
+.jxInputSelect {
+  margin: 0px 4px;
+  padding: 3px 4px 3px 1px;
+  border: 1px solid #bbb;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputRadio,
+.jxInputCheck {
+  margin: 5px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputText:focus,
+.jxInputPassword:focus,
+.jxInputTextarea:focus,
+.jxInputSelect:focus,
+.jxInputCombo:focus,
+.jxInputColor:focus {
+  border: 1px solid #000;
+}
+
+.jxInputContainer .jxButtonContainer {
+  padding: 0px;
+  margin: 0px 4px;
+}
+
+/* Input Group */
+
+.jxInputGroup {
+  border: none;
+  padding: 0px;
+  margin: 2px;
+}
+
+.jxInputGroup legend {
+  font-size: 0px;
+  line-height: 0px;
+  padding: 0px;
+  margin: 0px;
+  border: none;
+}
+
+.jxInputGroup .jxFieldsetLegend {
+  font-size: 12px;
+}
+
+.jxInputGroup .jxInputLabel {
+  width: auto;
+}
+
+/* Field Validation */
+
+.jxFieldError .jxInputText,
+.jxFieldError .jxInputPassword,
+.jxFieldError .jxInputTextarea,
+.jxFieldError .jxInputSelect,
+.jxFieldError .jxInputCombo,
+.jxFieldError .jxInputColor {
+  background-color: #FBE3E4; 
+  color: #8a1f11; 
+  border-color: #FBC2C4;
+}
+
+.jxFieldSuccess .jxInputText,
+.jxFieldSuccess .jxInputPassword,
+.jxFieldSuccess .jxInputTextarea,
+.jxFieldSuccess .jxInputSelect,
+.jxFieldSuccess .jxInputCombo,
+.jxFieldSuccess .jxInputColor {
+  background-color: #E6EFC2; 
+  color: #264409; 
+  border-color: #C6D880;
+}
+
+.jxFieldError .jxInputText:focus,
+.jxFieldError .jxInputPassword:focus,
+.jxFieldError .jxInputTextarea:focus,
+.jxFieldError .jxInputSelect:focus,
+.jxFieldError .jxInputCombo:focus,
+.jxFieldError .jxInputColor:focus {
+  border-color: #8a1f11;
+}
+
+.jxFieldSuccess .jxInputText:focus,
+.jxFieldSuccess .jxInputPassword:focus,
+.jxFieldSuccess .jxInputTextarea:focus,
+.jxFieldSuccess .jxInputSelect:focus,
+.jxFieldSuccess .jxInputCombo:focus,
+.jxFieldSuccess .jxInputColor:focus {
+  border-color: #264409;
+}
+
+.jxFieldError .jxInputLabel,
+.jxFieldError .jxInputTag {
+  color: #8a1f11; 
+}
+
+.jxFieldSuccess .jxInputLabel,
+.jxFieldSuccess .jxInputTag {
+  color: #264409; 
+}
+
+
+/* For Reference
+   Success, notice and error boxes from Blueprint */
+
+/* 
+.error      { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
+.notice     { background: #FFF6BF; color: #514721; border-color: #FFD324; }
+.success    { background: #E6EFC2; color: #264409; border-color: #C6D880; }
+.error a    { color: #8a1f11; }
+.notice a   { color: #514721; }
+.success a  { color: #264409; }
+*/
+
+
+
 /* INLINE FORM 
- * Wrap an Inline Form with .jxFormInline to sets up form elements to work as 
- * inline objects like they do by default */
+ * Sets up form elements to work as inline objects like they do by default
+ * These styles rely on increasing specificity to provide overrides */
 
-.jxFormInline label {
+/* Inline Input Container */
+.jxFormInline .jxInputContainer,
+form .jxFormInline .jxInputContainer,
+form .jxFieldset span.jxFormInline,
+form.jxForm span.jxFormInline {
   display: inline;
 }
 
-/* FLOAT FORM 
- * Wrap a Floating Form with .jxFormFloat to sets up form elements to work as 
- * block objects so labels can have set widths to simulate a 2 column display 
- * for label / input pairs. */
+/* Inline Label */
+.jxFormInline .jxInputLabel,
+form .jxFormInline .jxInputLabel,
+form span.jxFormInline .jxInputLabel {
+  display: inline;
+  width: auto;
+}
 
-.jxFormFloat label {
+/* Inline Tag */
+.jxFormInline .jxInputTag,
+form .jxFormInline .jxInputTag,
+form span.jxFormInline .jxInputTag {
+  display: inline;
+}
+
+/* Inline Input Group */
+.jxFormInline .jxInputGroup,
+form .jxFormInline .jxInputGroup {
+  padding-left: 0px;
+}
+
+.jxFormInline .jxInputGroup .jxFieldsetLegend,
+form .jxFormInline .jxInputGroup .jxFieldsetLegend {
+  position: relative;
+  left: auto;
+}
+
+.jxFormInline .jxInputGroup .jxInputLabel,
+form .jxFormInline .jxInputGroup .jxInputLabel {
+  display: inline;
+  width: auto;
+}
+
+
+/* BLOCK FORM 
+ *  Sets up form elements to work as block objects so they can appear stacked */
+
+/* Block Input Container */
+.jxFormBlock .jxInputContainer,
+form .jxFormBlock .jxInputContainer,
+form .jxFieldset span.jxFormBlock,
+form.jxform span.jxFormBlock {
   display: block;
-  float: left;
-  clear: left;
-  width: 75px; /* Overide the width depending on the form layout */
 }
+ 
+/* Block Label */
+.jxFormBlock .jxInputLabel,
+form .jxFormBlock .jxInputLabel,
+form span.jxFormBlock .jxInputLabel {
+  display: block;
+  width: auto;
+  margin-left: 4px;
+}
 
-.jxFormFloat select, 
-.jxFormFloat input {
-  float: left;
+/* Checks and Radios Label
+ * Most inputs are preceeded by their labels, but in the case of radio buttons
+ * and checkboxes, the inputs are followed by their labels */
+.jxFormBlock .jxInputContainerCheck .jxInputLabel,
+.jxFormBlock .jxInputContainerRadio .jxInputLabel,
+form .jxFormBlock .jxInputContainerCheck .jxInputLabel,
+form .jxFormBlock .jxInputContainerRadio .jxInputLabel,
+form span.jxFormBlock .jxInputContainerCheck .jxInputLabel,
+form span.jxFormBlock .jxInputContainerRadio .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
 }
 
-.jxFormFloat .radioGroup {
-  float: left;
+/* Block Input Group */
+.jxFormBlock .jxInputGroup,
+form .jxFormBlock .jxInputGroup {
+  padding-left: 0px;
 }
 
-/* wrap the radio button in a label */
-.jxFormFloat .radioGroup input,
-.jxFormFloat .radioGroup label {
-  float: none;
+.jxFormBlock .jxInputGroup .jxFieldsetLegend,
+form .jxFormBlock .jxInputGroup .jxFieldsetLegend {
+  position: relative;
+  left: auto;
 }
 
-.jxFormFloat .tipArea {
-  clear: left;
+.jxFormBlock .jxInputGroup .jxInputLabel,
+form .jxFormBlock .jxInputGroup .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: auto;
 }
 
-/* BLOCK FORM 
- *  Wrap a Block Form with .jxFormBlock to sets up form elements to work as 
- * block objects so they can label / input pairs */
 
-.jxFormBlock label {
+/* INLINE-BLOCK FORM 
+ * Sets up form elements to work as inline-block objects so labels can have set 
+ * widths to simulate a 2 column display for label / input pairs. */
+
+/* Inline-Block Input Container */
+.jxFormInlineblock .jxInputContainer,
+form .jxFormInlineblock .jxInputContainer,
+form .jxFieldset span.jxFormInlineblock,
+form.jxForm span.jxFormInlineblock {
   display: block;
+  white-space: nowrap;
 }
-/**
+
+/* Inline-Block Label */
+.jxFormInlineblock .jxInputLabel,
+form .jxFormInlineblock .jxInputLabel,
+form span.jxFormInlineblock .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: 200px;
+}
+
+/* Inline-Block Input Group */
+.jxFormInlineblock .jxInputGroup,
+form .jxFormInlineblock .jxInputGroup {
+  padding-left: 200px;
+}
+
+.jxFormInlineblock .jxInputGroup .jxFieldsetLegend,
+form .jxFormInlineblock .jxInputGroup .jxFieldsetLegend {
+  position: absolute;
+  left: -200px; /* for ie? */
+  width: 200px;
+}
+
+.jxFormInlineblock .jxInputGroup .jxInputLabel,
+form .jxFormInlineblock .jxInputGroup .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: auto;
+}
+
+/** Jx.Grid Overrides **/
+
+.jxGridCellContent .jxInputContainer,
+.jxGridCellContent .jxInputRadio,
+.jxGridCellContent .jxInputCheck {
+  display: inline;
+  position: relative;
+  border: none;
+  margin: 0px;
+  padding: 0px;
+  font-size: 0px;
+  line-height: 0px;
+  color: #000;
+}/**
  * @project         Jx
- * @revision        $Id: grid.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: grid.css 796 2010-03-26 19:56:43Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1051,47 +1504,46 @@
 }
 
 .jxGridTable {
+    width: 100%;
     position: relative;
     table-layout: fixed;
     border-collapse: collapse;
     border-style: none;
-    width: 0px;
+
     cursor: default;
-    font-family: Arial, Verdana, sans-serif;
-    font-size: 11px;
-    font-weight: normal;
 }
 
-.jxGridColHeadHide {
-    height: 0px;
-    line-height: 0px;
-    font-size: 0px;
-    background-color: #fff;
-    white-space: normal;
+.jxGridTableBody {
+    position: relative;
+    table-layout: fixed;
+    border-collapse: collapse;
+    border-style: none;
+   /* width: 100%;*/
+    cursor: default;
 }
 
-.jxGridColHeadHide p, .jxGridRowHeadHide p {
-    font-size: 0px;
-    line-height: 0px;
-    height: 0px;
-    margin: 0px;
-    padding: 0px;
-}
-
-.jxGridRowHeadHide {
-    width: 0px;
-    white-space: normal;
-}
-
 .jxGridCell {
     border-top: 0px solid #d8d8d8;
     border-right: 1px solid #d8d8d8;
     border-bottom: 1px solid #d8d8d8;
     border-left: 0px solid #d8d8d8;
     overflow: hidden;
-    padding-left: 3px;
-    padding-right: 3px;
+}
+
+.jxGridCellContent {
+    position: relative;
+    display: -moz-inline-box;
+    display: inline-block;
     overflow: hidden;
+    padding: 0px 3px;
+    overflow: hidden;
+    vertical-align: middle;
+    
+    font-family: Arial, Verdana, sans-serif;
+    font-size: 11px;
+    font-weight: normal;
+    line-height: 16px;
+    
     /* can change this to normal */
     white-space: nowrap;
     cursor: cell;
@@ -1099,9 +1551,24 @@
     text-overflow: ellipsis;
 }
 
-/* Nornal Styles */
+.jxGridColHead .jxGridCellContent {
+    padding: 0px 3px;
+    text-align: center;
+    font-weight: bold;
+    color: #333;
+    height: 100%;
+}
 
+.jxGridRowHead .jxGridCellContent {
+  text-align: center;
+  font-weight: bold;
+  color: #333;
+}
+/* Normal Styles */
+
 .jxGridColHead {
+    height: 100%;
+  
     border-top: 0px solid  #d8d8d8;
     border-right: 1px solid #d8d8d8;
     border-bottom: 1px solid #d8d8d8;
@@ -1112,9 +1579,11 @@
     background-repeat: repeat-x;
 
     text-align: center;
-    font-weight: bold;
-    color: #333;
     cursor: default;
+    padding: 0px;
+    white-space: nowrap;
+    
+    overflow: hidden;
 }
 
 .jxGridRowHead {
@@ -1128,9 +1597,9 @@
     background-repeat: repeat-y;
 
     text-align: center;
-    font-weight: bold;
-    color: #333;
     cursor: default;
+    overflow: hidden;
+    white-space: nowrap;
 }
 
 /* Alternating Row Styles */
@@ -1160,11 +1629,13 @@
     background-color: #f7f7f7;
 }
 
-.jxGridRowSelected td {
+.jxGridRowSelected td,
+.jxGridRowSelected th {
     background-color: #f7f7f7;
 }
 
-td.jxGridCellSelected {
+td.jxGridCellSelected,
+th.jxGridCellSelected {
     background-color: #ebebeb;
 }
 
@@ -1184,17 +1655,159 @@
     background-color: #e5f1ff;
 }
 
-.jxGridRowPrelight td {
+.jxGridRowPrelight td,
+.jxGridRowPrelight th {
     background-color: #e5f1ff;
 }
 
-td.jxGridCellPrelight {
+td.jxGridCellPrelight,
+th.jxGridCellPrelight {
   background-color: #cce3ff;
 }
 
+.jxGridHeader .jxColSortable img {
+    vertical-align: top;
+    width: 16px;
+    height: 16px;
+    background-image: url('images/emblems.png');
+    background-repeat: no-repeat;
+    background-position: right top;
+}
+
+.jxGridHeader .jxColSortable .jxGridCellContent {
+  margin-right: -16px; /* recenter the column heading text */
+}
+ 
+.jxGridHeader .jxGridColumnSortedAsc img {
+    background-position: right -162px;
+}
+ 
+.jxGridHeader .jxGridColumnSortedDesc img {
+    background-position: right -18px;
+}
+ 
+.jxGridColumnResize,
+.jxGridRowResize {
+    display: block;
+    position: absolute;
+    background-image: url(images/a_pixel.png);
+    z-index: 1;
+    
+}
+.jxGridColumnResize {
+    right: 0px;
+    top: 0px;
+    height: 100%;
+    width: 4px;
+    cursor: col-resize;
+}
+
+.jxColSortable .jxGridColumnResize {
+  right: 8px;
+}
+
+.jxGridRowResize {
+    left: 0px;
+    bottom: 0px;
+    height: 4px;
+    width: 100%;
+    cursor: row-resize;
+}
+
+/* Editor Styles */
+.jxGridEditorPopup {
+  min-width: 130px;
+  margin: 3px;
+  font-size: 10px;
+  box-shadow: 2px 2px 5px #888; /* i really like the boxshadow */
+  -moz-box-shadow : 2px 2px 5px #888;
+  -webkit-box-shadow : 2px 2px 5px #888;
+  /*
+  border-radius: 5px;
+  -moz-border-radius: 5px;
+  -webkit-border-radius: 5px;
+  */
+  background-color: #dddddd;
+  border: 1px solid #999999;
+  position: absolute;
+}
+
+.jxGridEditorPopupInnerWrapper {
+  position: relative;
+  height: 100%;
+  width: 100%;
+}.jxListView {
+  position:relative;
+  display: block;
+  list-style: none;
+  margin: 0px;
+  padding: 0px;
+}
+
+.jxListView .jxListItemContainer {
+    position: relative;
+    display: block;
+    outline: none;
+    overflow: hidden;
+    
+    border: none;
+    margin: 0px 1px;
+    padding: 0px;
+}
+
+.jxListItem {
+    position: relative;
+    display: block;
+    cursor: pointer;
+    outline: none;
+    overflow: hidden;
+
+    border: none;
+    margin: 0px 1px;
+    padding: 0px;
+    z-index: 0;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 11px;
+    color: #000;
+    text-decoration: none;
+    /* Line Height needs to be an even number so branches line up properly */
+    line-height: 20px;
+    height: 20px;
+}
+
+.jxListView .jxHover {
+    margin: 0px;
+    border-left: 1px solid #CDDFFD;
+    border-right: 1px solid #CDDFFD;
+    background-image: url(images/listitem.png);
+    background-repeat: repeat-x;
+    background-color: #CDE5FF;
+    background-position: left -24px;
+    
+}
+
+.jxListItem:focus {
+    margin: 0px;
+    border-left: 1px dotted #75ADFF;
+    border-right: 1px dotted #75ADFF;
+    background-image: url(images/listitem.png);
+    background-repeat: repeat-x;
+    background-position: left -72px;
+}
+
+.jxListView .jxPressed,
+.jxListView .jxSelected {
+  margin: 0px;
+  border-left: 1px solid #8AABFB;
+  border-right: 1px solid #8AABFB;
+  background-color: #CDE5FF;
+  background-image: url(images/listitem.png);
+  background-repeat: repeat-x;
+  background-position: left -48px;
+}
 /**
  * @project         Jx
- * @revision        $Id: menu.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: menu.css 601 2009-11-10 18:44:35Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1223,13 +1836,14 @@
 
 .jxMenuContainer {
   position: absolute;
-  display: block;
+  top: 0;
+  left: -10000px;
+  display: none;
   z-index: 2000;
   padding: 0px;
 }
 
-ul.jxMenu,
-ul.jxSubMenu {
+ul.jxMenu {
   /* Base setup */
   display: block;
   position: relative;
@@ -1387,7 +2001,7 @@
 }
 
 .jxMenuItemToggle img.jxMenuItemIcon,
-.jxMenuItemSet img.jxMenuItemIcon {
+.jxMenuItemToggleSet img.jxMenuItemIcon {
   background-image: url(images/emblems.png);
   background-position: 2px 0px;
   background-repeat: no-repeat;
@@ -1397,12 +2011,11 @@
   background-position: 2px -48px;
 }
 
-.jxMenuItemSet a.jxMenuItemActive img.jxMenuItemIcon {
+.jxMenuItemToggleSet a.jxMenuItemActive img.jxMenuItemIcon {
   background-position: 2px -64px;
 }
 
-ul.jxMenu span.jxMenuSeparator,
-ul.jxSubMenu span.jxMenuSeparator {
+ul.jxMenu span.jxMenuSeparator {
   /* Base setup */
   display: block;
 
@@ -1411,9 +2024,117 @@
   background-image: url(images/toolbar_separator_v.png);
   background-repeat: repeat-x;
   background-position: left center;
+}
+/**
+ * Confirm dialog classes
+ */
+.jxMessage {
+    text-align: center;
+    padding: 10px;
+    margin-top: 10px;
+}/* For Reference
+   Success, notice and error boxes from Blueprint */
+
+/* 
+.error      { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
+.notice     { background: #FFF6BF; color: #514721; border-color: #FFD324; }
+.success    { background: #E6EFC2; color: #264409; border-color: #C6D880; }
+.error a    { color: #8a1f11; }
+.notice a   { color: #514721; }
+.success a  { color: #264409; }
+*/
+
+
+.jxNoticeListContainer {
+  border: none;
+  padding: 0px;
+  margin: 0px;
+  font-size: 0px;
+  line-height: 0px;
+  z-index: 500;
+}
+
+.jxNoticeList {
+    position: relative;
+}
+
+.jxNoticeItemContainer {
+    position: relative;
+    overflow: hidden;
+}
+
+.jxNoticeItemContainer .jxChrome {
+  background-image: url(images/flyout_chrome.png);
+  padding: 5px 5px 7px 6px;
+}
+
+.jxHasChrome .jxNoticeItem  {
+  margin: 6px 6px 8px 7px;
+}
+
+.jxNoticeItem {
+    position: relative;
+    border: 2px solid #ccc;
+    margin: 0px;
+    padding: 0px;
+    background-color: #f8f8f8;
+    background-image: url(images/notice.png);
+    background-repeat: repeat-x;
+    background-position: left bottom;
+}
+
+.jxNoticeIcon {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  margin: 6px;
+  width: 16px;
+  height: 16px;
+  background-image: url(images/icons.png);
+  background-repeat: no-repeat;
+  background-position: 0px 0px;
+}
+
+.jxNotice {
+    display: block;
+    position: relative;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    line-height: 18px;
+    color: #666;
+    margin: 0px;
+    border: none;
+    padding: 6px 26px 6px 10px; 
+}                          
+
+.jxNoticeError .jxNoticeItem { background-color: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; background-image: url(images/notice_error.png);}
+.jxNoticeWarning .jxNoticeItem { background-color: #FFF6BF; color: #514721; border-color: #FFD324; background-image: url(images/notice_warning.png);}
+.jxNoticeSuccess .jxNoticeItem { background-color: #E6EFC2; color: #264409; border-color: #C6D880; background-image: url(images/notice_success.png);}
+.jxNoticeInformation .jxNoticeItem { background-color: #F8F8F8; color: #666666; border-color: #CCCCCC; background-image: url(images/notice.png);}
+
+.jxNoticeError .jxNotice { color: #8a1f11; padding-left: 28px; }
+.jxNoticeWarning .jxNotice { color: #514721; padding-left: 28px; }
+.jxNoticeSuccess .jxNotice { color: #264409; padding-left: 28px; }
+.jxNoticeInformation .jxNotice { color: #666666; padding-left: 28px; }
+
+.jxNoticeError .jxNoticeIcon { background-position: 0px -32px; }
+.jxNoticeWarning .jxNoticeIcon { background-position: 0px -16px; }
+.jxNoticeSuccess .jxNoticeIcon { background-position: 0px -48px; }            
+.jxNoticeInformation .jxNoticeIcon { background-position: 0px -64px; }
+
+.jxNoticeClose {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    margin: 6px;
+    width: 16px;
+    height: 16px;
+    background-image: url(images/tab_close.png);
+    background-position: 0px 0px;
+    background-repeat: no-repeat;
 }/**
  * @project         Jx
- * @revision        $Id: panel.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: panel.css 716 2010-03-02 16:07:19Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1575,15 +2296,6 @@
 /* ========================= */
 
 .jxPanelControls .jxButtonContainer,
-.jxPanelControls span.jxButtonContent,
-.jxPanelControls .jxButton:hover span.jxButtonContent,
-.jxPanelControls .jxButton:active span.jxButtonContent,
-.jxPanelControls .jxButtonActive span.jxButtonContent,
-.jxPanelControls .jxButtonActive:hover span.jxButtonContent,
-.jxPanelControls .jxButtonActive:active span.jxButtonContent,
-.jxPanelControls .jxDisabled .jxButton span.jxButtonContent,
-.jxPanelControls .jxDisabled .jxButton:hover span.jxButtonContent,
-.jxPanelControls .jxDisabled .jxButton:active span.jxButtonContent,
 .jxPanelControls .jxButton,
 .jxPanelControls .jxButton:hover,
 .jxPanelControls .jxButton:active ,
@@ -1600,16 +2312,7 @@
   background-image: none;
 }
 
-.jxPanelControls .jxButtonMenu span.jxButtonContent,
-.jxPanelControls .jxButtonFlyout span.jxButtonContent {
-  background-image: none;
-}
 
-.jxPanelControls .jxButtonMenu span.jxButtonContent span,
-.jxPanelControls .jxButtonFlyout span.jxButtonContent span {
-  padding-right: 0px;
-}
-
 /* ========================== */
 /* JX TOOLBAR STYLES OVERIDES */
 /* ========================== */
@@ -1644,9 +2347,193 @@
   margin: 0px;
   padding: 0px;
   border: none;
-}/**
+}/**
+ * progress bar classes
+ */
+.jxProgressBar-container {
+    position: relative;
+    display: block;
+    width: 100%;
+}
+
+.jxProgressBar-message {
+    position: relative;
+    display: block;
+    color: black;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    line-height: 20px;
+}
+
+.jxProgressBar {
+    position: relative;
+    display: block;
+    width: 100%;
+    height: 20px;
+    border: none;
+    margin: 0px;
+    padding: 0px;
+}
+
+.jxProgressBar-outline {
+    position: absolute;
+    display: block;
+    top: 0px;
+    left: 0px;
+    z-index: 10;
+    height: 20px;
+    border-left: 1px solid #cbc8c8;
+    border-right: 1px solid #cbc8c8;
+    background-image: url(images/progressbar.png);
+    background-position: 0px -140px;
+    width: 100%;
+}
+
+.jxProgressBar-fill {
+    position: absolute;
+    display: block;
+    top: 0px;
+    left: 0px;
+    z-index: 20;
+    height: 20px;
+    border: none;
+    background-image: url(images/progressbar.png);
+    background-position: 0px 0px;
+}
+
+.jxProgressStarting .jxProgressBar-fill {
+    border: none;
+}
+
+.jxProgressWorking .jxProgressBar-fill {
+    border-left: 1px solid #49afe8;
+}
+
+.jxProgressFinished .jxProgressBar-fill {
+    border-left: 1px solid #49afe8;
+    border-right: 1px solid #49afe8;
+}
+
+.jxProgressBar-text {
+    position: absolute;
+    display: block;
+    overflow: visible;
+    top: 0px;
+    left: 1px;
+    width: auto;
+    height: 20px;
+    z-index: 30;
+    border: none;
+    margin: 0px;
+    padding: 0px 0px 0px 4px;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    line-height: 20px;
+    white-space: nowrap;
+}
+.jxHasVerticalScrollbar,
+.jxHasHorizontalScrollbar {
+    position: relative;
+    overflow: hidden;
+}
+
+.jxScrollbarChildWrapper {
+    overflow: hidden;
+}
+
+.jxHasVerticalScrollbar {
+    padding-right: 25px;
+}
+
+.jxHasVerticalScrollbar .jxScrollbarContainer {
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 20px;
+    height: 100%;
+    border: none;
+    border-left: 1px solid black;
+}
+
+.jxHasVerticalScrollbar .jxScrollLeft,
+.jxHasVerticalScrollbar .jxScrollRight {
+    width: 20px;
+    height: 20px;
+    display: block;
+    background-color: blue;
+}
+
+.jxHasVerticalScrollbar .jxSliderContainer {
+    height: 100%;
+    width: 20px;
+    border: none;
+}
+
+.jxHasHorizontalScrollbar {
+    padding-bottom: 25px;
+}
+
+.jxHasHorizontalScrollbar .jxScrollbarContainer {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 20px;
+    width: 100%;
+    border: none;
+    border-top: 1px solid black;
+}
+
+.jxHasHorizontalScrollbar .jxScrollLeft,
+.jxHasHorizontalScrollbar .jxScrollRight {
+    width: 20px;
+    height: 20px;
+    display: block;
+    background-color: blue;
+}
+ 
+.jxHasHorizontalScrollbar .jxScrollLeft {
+    float: left;
+}
+
+.jxHasHorizontalScrollbar .jxScrollRight {
+    float: right;
+}
+
+.jxHasHorizontalScrollbar .jxSlider {
+    float: left;
+}
+
+.jxHasHorizontalScrollbar .jxSliderContainer {
+    height: 20px;
+    width: 100%;
+    border: none;
+}
+
+.jxHasVerticalScrollbar .jxSliderKnob,
+.jxHasHorizontalScrollbar .jxSliderKnob {
+    width: 20px;
+    height: 20px;
+    background-color: black;
+    cursor: pointer;
+}/**
+ * Slider classes
+ */
+.jxSliderContainer {
+    width: 100%;
+    height: 10px;
+    border: 1px solid black;
+}
+
+.jxSliderKnob {
+    width: 10px;
+    height: 10px;
+    background-color: black;
+    cursor: pointer;
+}
+
+/**
  * @project         Jx
- * @revision        $Id: splitter.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: splitter.css 294 2009-04-02 12:26:26Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1738,7 +2625,7 @@
   background-color: #aaa;
 }/**
  * @project         Jx
- * @revision        $Id: tab.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: tab.css 875 2010-04-24 06:10:53Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1766,7 +2653,7 @@
   background-color: #fff;
 }
 
-.jxTabSetContainer  .jxToolbarContainer {
+.jxTabSetContainer  .jxBarContainer {
   /* Base setup */
   z-index: auto;
 }
@@ -1792,7 +2679,7 @@
 /* BASE TAB (BUTTON) STYLES */
 /* ======================== */
 
-div.jxTabContainer {
+span.jxTabContainer {
   /* Base setup */
   display: block;
   position: relative;
@@ -1804,7 +2691,8 @@
 
 a.jxTab {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
   cursor: pointer;
   user-select: none;
@@ -1822,7 +2710,8 @@
 
 span.jxTabContent {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   font-size: 0px;
   line-height: 0px;
 
@@ -1836,6 +2725,8 @@
 
 img.jxTabIcon {
   /* Base setup */
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
 
   width: 16px;
@@ -1846,7 +2737,8 @@
 
 span.jxTabLabel {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
   cursor: pointer;
 
@@ -1867,12 +2759,11 @@
   user-select: none;
   -moz-user-select: none;
   -khtml-user-select: none;
-}
-
-a.jxTabClose img {
   width: 16px;
   height: 16px;
-  background-image: url(images/tab_close.png);  
+  background-image: url(images/tab_close.png);
+  background-position: 0px 0px;
+  background-repeat: no-repeat;
 }
 
 .jxDisabled a.jxTab,
@@ -1888,27 +2779,37 @@
 /* HORIZONTALTAB BAR - TOP and BOTTOM TABS */
 /* ======================================= */
 
-.jxBarTop div.jxTabContainer,
-.jxBarBottom div.jxTabContainer {
+.jxTabBarTop .jxBarWrapper, 
+.jxTabBarBottom .jxBarWrapper {
+  padding-left: 2px;
+}
+
+.jxBarTop span.jxTabContainer,
+.jxBarBottom span.jxTabContainer {
   /* Base setup */
-  float: left;
+  margin-right: -1px;
+  padding: 2px 0px;
 }
 
 .jxBarTop a.jxTab,
-.jxBarTop span.jxTabContent {
+.jxBarTop span.jxTabContent,
+.jxTabBarTop .jxBarControls a.jxButton,
+.jxTabBarTop .jxBarControls span.jxButtonContent{
   background-image: url(images/tab_top.png);
 }
 
 .jxBarBottom a.jxTab,
-.jxBarBottom span.jxTabContent {
+.jxBarBottom span.jxTabContent,
+.jxTabBarBottom .jxBarControls a.jxButton,
+.jxTabBarBottom .jxBarControls span.jxButtonContent {
   background-image: url(images/tab_bottom.png);
 }
 
 /* Closeable Tab */
 .jxBarTop a.jxTabClose,
 .jxBarBottom a.jxTabClose {
-  top: 3px;
-  right: 3px;
+  top: 5px;
+  right: 5px;
 }
 
 .jxBarTop .jxTabClose span.jxTabContent,
@@ -1920,7 +2821,6 @@
 .jxBarTop a.jxTab,
 .jxBarBottom a.jxTab {
   /* Base setup */
-  float: left;
 
   padding-left: 4px; /* makes room for the left of the tab bg */
   background-position: left -24px; 
@@ -1929,7 +2829,6 @@
 .jxBarTop span.jxTabContent,
 .jxBarBottom span.jxTabContent {
   /* Base setup */
-  float: left;
 
   padding: 4px 4px 4px 0px; /* makes space around the label */
   background-position: right -24px; 
@@ -2044,14 +2943,14 @@
 
 .jxBarTop img.jxTabIcon,
 .jxBarBottom img.jxTabIcon {
+  vertical-align: middle;
   /* Base setup */
-  float: left;
 }
 
 .jxBarTop span.jxTabLabel,
 .jxBarBottom span.jxTabLabel {
   /* Base setup */
-  float: left;
+  vertical-align: middle;
   height: 16px;
 
   padding: 0px 4px 0px 4px;
@@ -2062,11 +2961,19 @@
 /* VERTICAL TAB BAR - LEFT and RIGHT */
 /* ================================= */
 
-.jxBarLeft div.jxTabContainer,
-.jxBarRight div.jxTabContainer {
+.jxTabBarLeft .jxBarWrapper, 
+.jxTabBarRight .jxBarWrapper {
+  padding-top: 2px;
+}
+
+.jxBarLeft span.jxTabContainer,
+.jxBarRight span.jxTabContainer {
   /* Base setup */
+  margin-bottom: -1px;
+  padding: 0px 2px;
 }
 
+
 .jxBarLeft a.jxTab,
 .jxBarLeft span.jxTabContent {
   background-image: url(images/tab_left.png);
@@ -2080,8 +2987,8 @@
 /* Closeable Tab */
 .jxBarLeft a.jxTabClose,
 .jxBarRight a.jxTabClose {
-  top: 3px;
-  left: 3px;
+  top: 5px;
+  left: 5px;
 }
 
 .jxBarLeft .jxTabClose span.jxTabContent,
@@ -2215,7 +3122,7 @@
 
 /**
  * @project         Jx
- * @revision        $Id: toolbar.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: toolbar.css 912 2010-05-21 21:33:08Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -2288,26 +3195,29 @@
   background-position: left 0px;
 }
 
+
 .jxBarTop .jxBarScroller,
 .jxBarBottom .jxBarScroller {
-  position: absolute;
-  /* much bigger than its container */
-  width: 1000%;
+  float: left;
+  height: 28px;
   overflow: hidden;
+  z-index: 0;
 }
 
-.jxBarTop .jxBarScrollLeft,
-.jxBarBottom .jxBarScrollLeft {
-  position: absolute;
-  top: 0px;
-  left: 0px;
+.jxBarTop .jxBarScroller .jxBarWrapper,
+.jxBarBottom .jxBarScroller .jxBarWrapper {
+  float: left;
+  height: 28px;
+  overflow: hidden;
+  width: 10000%;
 }
 
-.jxBarTop .jxBarScrollRight,
-.jxBarBottom .jxBarScrollRight {
-  position: absolute;
-  top: 0px;
-  right: 0px;
+.jxBarTop .jxBarControls .jxButtonContainer,
+.jxBarBottom .jxBarControls .jxButtonContainer {
+  /* float: right; */
+  z-index: 1;
+  padding: 2px 0px;
+  margin-left: -1px;
 }
 
 .jxBarTop .jxBarScrollLeft img.jxButtonIcon,
@@ -2322,6 +3232,13 @@
   background-position: 0px -96px;
 }
 
+.jxBarControls {
+    float: right;
+    position: relative;
+    font-size: 0px;
+    line-height: 0px;
+}
+
 /* The jx toolbar and tabbar are both built out of a UL
    The margins/padding are flattened out, and the list markers are hidden
    UL's are floated left so multiple toolbars can be in the samae row.
@@ -2342,6 +3259,7 @@
   display: block;
   position: relative;
   float: left;
+  clear: none;
   list-style-type: none;
 
   margin: 0px;  /* margins don't seem to work properly in IE */
@@ -2364,12 +3282,17 @@
   float: left;
   font-size: 0px;
   line-height: 0px;
-
+  white-space: nowrap;
   padding: 0px;
   margin: 0px;  /* margins don't seem to work properly in IE */
   border: none;
 }
 
+/* inputs in toolbars wrap in IE */
+li.jxToolItem .jxInputWrapper {
+  white-space: nowrap;
+}
+
 /* Seperator height should match that of button images
    and the margins+padding+border should add up to the same total too. */
 
@@ -2417,9 +3340,42 @@
   clear: both;
 }
 
+
+/* Aligning options */
+.jxToolbarAlignLeft ul {
+    float: left;
+}
+
+.jxToolbarAlignRight ul {
+    float: right;
+}
+
+.jxToolbarAlignCenter {
+    text-align: center;
+}
+
+.jxToolbarAlignCenter ul {
+    float: none;
+}
+
+.jxToolbarAlignCenter ul li {
+    float: none;
+    display: inline;
+}/*
+ * Tooltip classes
+ */
+.jxTooltip {
+	width: auto;
+	height: auto;
+	background-color: black;
+	color: white;
+	padding: 5px;
+	z-index: 65536;
+}
+
 /**
  * @project         Jx
- * @revision        $Id: tree.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: tree.css 755 2010-03-15 03:09:37Z jonlb at comcast.net $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -2434,7 +3390,7 @@
 */
 
 
-.jxTree, 
+.jxTree,
 .jxTreeRoot {
   /* relative positioning is required for IE to fix the peek-a-boo bug */
   position:relative;
@@ -2454,8 +3410,7 @@
 
 /* Node Classes */
 
-.jxTree li, 
-.jxTreeRoot li {
+li.jxTreeContainer {
   /* relative positioning is required for IE to fix the peek-a-boo bug */
   position:relative;
   display: block;
@@ -2472,12 +3427,11 @@
   -khtml-user-select: none;
 }
 
-.jxTree li {
+.jxTree li.jxTreeContainer {
   margin-left: 16px;
 }
 
-.jxTree a, 
-.jxTreeRoot a {
+a.jxTreeItem {
   position: relative;
   display: block;
   cursor: pointer;
@@ -2489,7 +3443,7 @@
   background-position: left top;
   border: none;
 
-  margin: 0px 1px 0px 15px;
+  margin: 0px 1px 0px 17px;
   padding: 0px 0px 0px 20px;
   z-index: 0;
   font-family: Arial, Helvetica, sans-serif;
@@ -2502,59 +3456,52 @@
 }
 
 
-.jxTree a:focus,
-.jxTreeRoot a:focus {
+a.jxTreeItem:focus {
   border-left: 1px dotted #75ADFF;
   border-right: 1px dotted #75ADFF;
-  margin: 0px 0px 0px 14px;
+  margin: 0px 0px 0px 16px;
   background-position: left -72px;
 }
 
-.jxTree a:hover, 
-.jxTreeRoot a:hover {
+a.jxTreeItem:hover,
+li.jxTreeContainer a.jxHover {
   /*border: 1px solid #C5E0FF;*/
-  border-left: 1px solid #C5E0FF;
-  border-right: 1px solid #C5E0FF;
-  margin: 0px 0px 0px 14px;
+  border-left: 1px solid #CDDFFD;
+  border-right: 1px solid #CDDFFD;
+  margin: 0px 0px 0px 16px;
   background-color: #CDE5FF;
   background-position: left -24px;
 }
 
-.jxTree a.jxTreeItemPressed,
-.jxTree a.jxTreeItemPressed:hover,
-.jxTreeRoot a.jxTreeItemPressed,
-.jxTreeRoot a.jxTreeItemPressed:hover {
-  border-left: 1px solid #C5E0FF;
-  border-right: 1px solid #C5E0FF;
-  margin: 0px 0px 0px 14px;
+li.jxTreeContainer a.jxSelected,
+li.jxTreeContainer a.jxSelected:hover,
+li.jxTreeContainer a.jxPressed,
+li.jxTreeContainer a.jxPressed:hover {
+  border-left: 1px solid #8AABFB;
+  border-right: 1px solid #8AABFB;
+  margin: 0px 0px 0px 16px;
   background-color: #CDE5FF;
   background-position: left -48px;
 }
 
-.jxTree .jxDisabled a,
-.jxTreeRoot.jxDisabled a {
+li.jxDisabled a.jxTreeItem {
   cursor: default;
 }
-
-.jxTree .jxDisabled a:focus,
-.jxTree .jxDisabled a.jxTreeItemPressed,
-.jxTree .jxDisabled a.jxTreeItemPressed:hover,
-.jxTree .jxDisabled a:hover,
-.jxTreeRoot .jxDisabled a:focus,
-.jxTreeRoot .jxDisabled a.jxTreeItemPressed,
-.jxTreeRoot .jxDisabled a.jxTreeItemPressed:hover,
-.jxTreeRoot .jxDisabled a:hover {
+            
+li.jxDisabled a.jxTreeItem:focus,
+li.jxDisabled a.jxTreeItem:hover {
   background-position: left top;
+  background-color: transparent;
   border: none;
-  margin: 0px 1px 0px 15px;
+  margin: 0px 1px 0px 17px;
 }
 
 .jxTreeNest {
   background-image: url(images/tree_vert_line.png);
 }
 
-.jxTreeImage,
-.jxTreeIcon {
+img.jxTreeImage,
+img.jxTreeIcon {
   position: absolute;
   display: inline;
 
@@ -2572,9 +3519,9 @@
   margin: 0px;
 }
 
-.jxTreeIcon { 
+img.jxTreeIcon { 
   height: 16px;
-  top: 1px;
+  top: 2px;
   left: 1px;
 }
 
@@ -2606,36 +3553,106 @@
   background-position: left -140px; /* plus last image */
 }
 
-.jxTreeItem .jxTreeIcon,
-.jxTreeItemLast .jxTreeIcon {
+.jxTreeLeaf .jxTreeIcon,
+.jxTreeLeafLast .jxTreeIcon {
   background-position: left 0px; /* page image */
 }
 
-.jxTreeItem .jxTreeImage {
+.jxTreeLeaf .jxTreeImage {
   background-position: left -60px; /* node image */
 }
 
-.jxTreeItemLast .jxTreeImage {
+.jxTreeLeafLast .jxTreeImage {
   background-position: left -120px; /* last node image */
 }
 
-.jxTreeItemSelected {
-  background-color: #AFD4FA;
-  font-weight:bold;
-}
 
-.jxTreeItem a,
-.jxTreeItem img,
-.jxTreeItem input,
-.jxTreeItemLast a,
-.jxTreeItemLast img,
-.jxTreeItemLast input,
-.jxTreeBranch a,
-.jxTreeBranch img,
-.jxTreeBranch input,
-.jxTreeBranchLast a,
-.jxTreeBranchLast img,
-.jxTreeBranchLast input {
+a.jxTreeItem,
+img.jxTreeImage,
+img.jxTreeIcon,
+span.jxTreeLabel,
+.jxTreeItemContainer input {
     vertical-align: middle;
 }
 
+img.jxTreeImage.jxBusy {
+	background-image: url(images/spinner_16.gif);
+    background-position: left top;
+}
+
+/* FileUploadPanel */
+.jxFileUploadPanel {
+    padding: 5px;
+}
+
+.jxFileUploadPanel .jxInputContainer {
+    /*width: 300px;*/
+}
+ 
+.jxUploadQueue {
+    /*width: 100%;*/  
+    /*margin-top: 10px;*/  
+}
+
+.jxUploadQueue li {
+    display: block;
+    position: relative;
+    overflow: hidden;
+    /*width: 95%;*/
+    /*clear: both;*/
+    /*border-top: 1px solid black;*/
+    padding: 2px;
+}
+
+.jxUploadQueue div span {
+    display: block;
+}
+
+.jxUploadQueue li span.jxUploadFileName {
+    /*float: left;*/
+    font-size: 12px;
+    line-height: 16px;
+    padding-left: 2px;
+}
+
+.jxUploadQueue li span.jxUploadFileDelete,
+.jxUploadQueue li span.jxUploadFileProgress,
+.jxUploadQueue li span.jxUploadFileComplete,
+.jxUploadQueue li span.jxUploadFileError {
+    /*float: right;*/
+    position: absolute;
+    top: 2px;
+    right: 2px;
+    width: 16px;
+    height: 16px;
+    background-repeat: no-repeat;
+    cursor: pointer;
+}
+
+.jxUploadQueue li span.jxUploadFileDelete {
+    background-image: url('images/icons.png');
+    background-position: 0px -128px;
+}
+
+.jxUploadQueue li span.jxUploadFileProgress {
+    background-image: url('images/spinner_16.gif');
+    background-position: top left;
+}
+
+.jxUploadQueue li span.jxUploadFileComplete {
+    background-image: url('images/icons.png');
+    background-position: 0px -48px;
+}
+
+.jxUploadQueue li span.jxUploadFileError {
+    background-image: url('images/icons.png');
+    background-position: 0px -32px;
+}
+
+.jxUploadFileErrorTip {
+  padding: 4px 4px 4px 20px; 
+  border: 2px solid #ddd;
+  background: url("images/icons.png") no-repeat 0px -32px;
+  color: black;
+  width: 100px;
+}

Modified: trunk/templates/mapserver/standard/themes/delicious/ie6.css
===================================================================
--- trunk/templates/mapserver/standard/themes/delicious/ie6.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/themes/delicious/ie6.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,6 +1,6 @@
 /**
  * @project         Jx
- * @revision        $Id: ie6.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: ie6.css 935 2010-05-28 17:36:13Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -18,9 +18,12 @@
  * Please note that IE8 changed how filters are written. 
  */
 .jxChromeDrag {filter: Alpha(opacity=50);}
-.jxDialogModal {filter: Alpha(opacity=20);}
 .jxDisabled {filter:Alpha(opacity=40);}
 .jxDisabled * {filter:Alpha(opacity=40);}
+.jxMask {filter:Alpha(opacity=50);}
+.jxModalMask {filter: Alpha(opacity=20);}
+.jxSpinner {filter: alpha(opacity=50);}
+
 iframe.jxIframeShim {filter:Alpha(opacity:0);}
 
 /* List items do not render properly under several conditions.  
@@ -52,6 +55,7 @@
   outline: expression(hideFocus='true');
 }
 
+ul.jxToolbar .jxButton:active span.jxButtonContent,
 .jxButton:active span.jxButtonContent {
   background-position: right -96px;
 }
@@ -62,6 +66,7 @@
   background-position: left -144px;
 }
 
+ul.jxToolbar .jxButtonActive:active span.jxButtonContent,
 .jxButtonActive:active span.jxButtonContent {
   background-position: right -144px;
 }
@@ -72,6 +77,7 @@
   background-position: left -120px;
 }
 
+ul.jxToolbar .jxButtonPressed:active span.jxButtonContent,
 .jxButtonPressed:active span.jxButtonContent {
   background-position: right -120px;
 }

Modified: trunk/templates/mapserver/standard/themes/delicious/ie7.css
===================================================================
--- trunk/templates/mapserver/standard/themes/delicious/ie7.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/themes/delicious/ie7.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,6 +1,6 @@
 /**
  * @project         Jx
- * @revision        $Id: ie7.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: ie7.css 979 2010-09-08 19:34:36Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -18,9 +18,11 @@
  * Please note that IE8 changed how filters are written. 
  */
 .jxChromeDrag {filter: Alpha(opacity=50);}
-.jxDialogModal {filter: Alpha(opacity=20);}
 .jxDisabled {filter:Alpha(opacity=40);}
 .jxDisabled * {filter:Alpha(opacity=40);}
+.jxMask {filter:Alpha(opacity=50);}
+.jxModalMask {filter: Alpha(opacity=20);}
+.jxSpinner {filter: alpha(opacity=50);}
 iframe.jxIframeShim {filter:Alpha(opacity:0);}
 
 /* tree item focus style */
@@ -33,6 +35,14 @@
   outline: expression(hideFocus='true');
 }
 
+.jxTree,
+.jxTreeRoot,
+.jxTreeNest,
+li.jxTreeContainer {
+ /* zoom needed to fix alignment/sizing issues in IE 7 */
+ zoom: 1;
+}
+
 /* IE versions 7 and below do not recognize the focus pseudo-class, but instead
  * use the active pseudo-class.  Other browsers use the active-pseudo-class
  * while something is being pressed so IE specific definitions are needed. */
@@ -43,6 +53,7 @@
   outline: expression(hideFocus='true');
 }
 
+ul.jxToolbar .jxButton:active span.jxButtonContent,
 .jxButton:active span.jxButtonContent {
   background-position: right -96px;
 }
@@ -53,6 +64,7 @@
   background-position: left -144px;
 }
 
+ul.jxToolbar .jxButtonActive:active span.jxButtonContent,
 .jxButtonActive:active span.jxButtonContent {
   background-position: right -144px;
 }
@@ -63,6 +75,7 @@
   background-position: left -120px;
 }
 
+ul.jxToolbar .jxButtonPressed:active span.jxButtonContent,
 .jxButtonPressed:active span.jxButtonContent {
   background-position: right -120px;
 }

Modified: trunk/templates/mapserver/standard/themes/delicious/images/button_combo.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/images/dialog_chrome.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/images/emblems.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/images/flyout_chrome.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/images/panel_controls.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/images/tab_bottom.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/images/tab_close.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/images/tab_left.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/images/tab_right.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/images/tab_top.png
===================================================================
(Binary files differ)

Modified: trunk/templates/mapserver/standard/themes/delicious/jxtheme.css
===================================================================
--- trunk/templates/mapserver/standard/themes/delicious/jxtheme.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/themes/delicious/jxtheme.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -21,4 +21,4 @@
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
- */body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;}q:before,q:after{content:'';}div.jxButtonContainer{display:block;position:relative;float:left;margin:0;padding:2px;border:none;}.jxButton{display:block;position:relative;float:left;margin:0;padding:0 0 0 4px;border:none;background-image:url(images/button.png);background-position:left -24px;background-repeat:no-repeat;text-decoration:none;outline:none;}a.jxButton{cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;}ul.jxToolbar .jxButton{background-position:left top;}span.jxButtonContent{display:block;position:relative;float:left;font-size:0;line-height:0;margin:0;padding:4px 4px 4px 0;border:none;backgr
 ound-image:url(images/button.png);background-position:right -24px;background-repeat:no-repeat;}ul.jxToolbar span.jxButtonContent{background-position:right top;}ul.jxToolbar .jxButtonActive,.jxButtonActive{background-position:left -72px;}ul.jxToolbar .jxButtonActive span.jxButtonContent,.jxButtonActive span.jxButtonContent{background-position:right -72px;}ul.jxToolbar .jxButton:focus,.jxButton:focus{background-position:left -96px;}ul.jxToolbar .jxButton:focus span.jxButtonContent,.jxButton:focus span.jxButtonContent{background-position:right -96px;}ul.jxToolbar .jxButtonActive:focus,.jxButtonActive:focus{background-position:left -144px;}ul.jxToolbar .jxButtonActive:focus span.jxButtonContent,.jxButtonActive:focus span.jxButtonContent{background-position:right -144px;}ul.jxToolbar .jxButton:hover,ul.jxToolbar .jxButtonActive:hover,.jxButton:hover,.jxButtonActive:hover{background-position:left -48px;}ul.jxToolbar .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxButtonActive
 :hover span.jxButtonContent,.jxButton:hover span.jxButtonContent,.jxButtonActive:hover span.jxButtonContent{background-position:right -48px;}ul.jxToolbar .jxButtonPressed,ul.jxToolbar .jxButtonPressed:focus,.jxButtonPressed,.jxButtonPressed:focus{background-position:left -120px;}ul.jxToolbar .jxButtonPressed span.jxButtonContent,ul.jxToolbar .jxButtonPressed:focus span.jxButtonContent,.jxButtonPressed span.jxButtonContent,.jxButtonPressed:focus span.jxButtonContent{background-position:right -120px;}.jxDisabled .jxButton,.jxDisabled span.jxButtonContent span{cursor:default;}ul.jxToolbar .jxDisabled .jxButton:focus,ul.jxToolbar .jxDisabled .jxButton:active,ul.jxToolbar .jxDisabled .jxButton:hover,ul.jxToolbar .jxDisabled .jxButtonPressed{background-position:left top;}.jxDisabled .jxButton:focus,.jxDisabled .jxButton:active,.jxDisabled .jxButton:hover,.jxDisabled .jxButtonPressed{background-position:left -24px;}ul.jxToolbar .jxDisabled .jxButton:focus span.jxButtonContent,ul.jx
 Toolbar .jxDisabled .jxButton:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right top;}.jxDisabled .jxButton:focus span.jxButtonContent,.jxDisabled .jxButton:active span.jxButtonContent,.jxDisabled .jxButton:hover span.jxButtonContent,.jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right -24px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus,ul.jxToolbar .jxDisabled .jxButtonActive:active,ul.jxToolbar .jxDisabled .jxButtonActive:hover,.jxDisabled .jxButtonActive:focus,.jxDisabled .jxButtonActive:active,.jxDisabled .jxButtonActive:hover{background-position:left -72px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:hover span.jxButtonContent,.jxDisabled .jxButtonActive:focus span.jxButtonContent,.jxDisa
 bled .jxButtonActive:active span.jxButtonContent,.jxDisabled .jxButtonActive:hover span.jxButtonContent{background-position:right -72px;}img.jxButtonIcon{position:relative;float:left;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxButtonContent span{display:block;position:relative;float:left;cursor:pointer;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;height:16px;white-space:nowrap;}span.jxButtonContent span.jxButtonLabel{margin:0;padding:0 4px 0 4px;color:#000;font-size:11px;}.jxButtonMenu span.jxButtonContent,.jxButtonMulti span.jxButtonContent,.jxButtonFlyout span.jxButtonContent,.jxButtonCombo span.jxButtonContent,.jxButtonEditCombo span.jxButtonContent{padding-right:0;}.jxButtonMenu span.jxButtonContent span,.jxButtonFlyout span.jxButtonContent span,.jxButtonMulti span.jxButtonContent span,.jxButtonCombo span.jxButtonContent span,.jxButtonEditCombo span.jxButtonContent span{padding-right:16px;backgrou
 nd-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose{position:absolute;display:block;padding:4px 0;font-size:0;line-height:0;right:2px;top:2px;background-image:url(images/button_multi_disclose.png);background-position:right 0;background-repeat:no-repeat;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxButtonDisclose img{width:16px;height:16px;margin:0;padding:0;border:0;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose:focus,a.jxButtonDisclose:active{background-position:right -96px;}a.jxButtonDisclose:hover{background-position:right -48px;}a.jxButtonDisclosePressed{background-position:right -120px;}.jxDisabled a.jxButtonDisclose,.jxDisabled a.jxButtonDisclose:focus,.jxDisabled a.jxButtonDisclose:active,.jxDisabled a.jxButtonDisclose:hover,.jxDisabled a.jxButtonDisclosePressed{cursor:default;background-position:righ
 t 0;}ul.jxToolbar .jxButtonHover{background-position:left -24px!important;}ul.jxToolbar .jxButtonHover span.jxButtonContent{background-position:right -24px!important;}.jxFlyout .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxFlyout{position:absolute;display:block;z-index:100;margin:0;padding:0;}.jxFlyoutContent{position:relative;display:block;overflow:auto;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}.jxButtonMulti,.jxButtonMulti span.jxButtonContent{background-image:url(images/button_multi.png);}.jxButtonEditCombo,.jxButtonEditCombo span.jxButtonContent{background-image:url(images/button_combo.png);}.jxButtonMulti span.jxButtonContent span{padding-right:21px;}.jxButtonEditCombo span.jxButtonContent span{font-size:0;}.jxButtonComboDefault span.jxButtonContent span,.jxButtonComboDefault input{font-style:italic;color:#999;}.jxButtonEditCombo input{float:left;line-height:16px;height:16px;padding:0 4px;margin:0;border:n
 one;font-size:11px;font-family:Arial,Helvetica,sans-serif;background-color:transparent;}.jxChrome{position:absolute;display:block;font-size:0;line-height:0;z-index:-1;width:100%;height:100%;top:0;left:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxChromeDrag{opacity:.5;-ms-filter:"Alpha(opacity=50)";}.jxChromeTL{position:absolute;overflow:hidden;left:0;top:0;width:50%;height:50%;}.jxChromeTR{position:absolute;overflow:hidden;left:50%;top:0;width:50%;height:50%;}.jxChromeBL{position:absolute;overflow:hidden;left:0;top:50%;width:50%;height:50%;}.jxChromeBR{position:absolute;overflow:hidden;left:50%;top:50%;width:50%;height:50%;}.jxChromeTL img{position:absolute;top:0;left:0;width:800px;height:500px;}.jxChromeTR img{position:absolute;top:0;right:0;width:800px;height:500px;}.jxChromeBL img{position:absolute;bottom:0;left:0;width:800px;height:500px;}.jxChromeBR img{position:absolute;bottom:0;right:0;width:800px;height:500px;}.jxColorBar{position:relative;ove
 rflow:hidden;}table.jxColorGrid{position:relative;border-collapse:collapse;empty-cells:show;clear:both;padding:0;margin:0;}.jxColorGrid tr{padding:0;margin:0;}.jxColorGrid td{border:1px solid #000;padding:0;margin:0;}.jxColorGrid td.emptyCell{border:0 solid #000;}.jxColorGrid td.emptyCell span{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;padding:1px;margin:0;}.jxColorGrid a.colorSwatch{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;margin:0;padding:1px;}.jxColorGrid a.borderWhite:hover{border:1px solid #fff;padding:0;}.jxColorGrid a.borderBlack:hover{border:1px solid #000;padding:0;}input.jxHexInput{width:55px;vertical-align:middle;}input.jxAlphaInput{width:30px;vertical-align:middle;}div.jxColorPreview{float:left;position:relative;width:20px;height:20px;border:1px solid #000;margin:2px;vertical-align:middle;background-image:url('images/grid.png');overflow:hidden;}.jxButtonFlyout span.jxButtonContent span.
 jxButtonSwatch{display:block;float:left;width:14px;height:14px;border:1px solid #000;background-image:url('images/grid.png');background-position:0 0;background-repeat:repeat;padding-right:0!important;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch span{display:block;width:14px;height:14px;position:absolute;padding-right:0;background:none;}div.jxColorPreview img{position:absolute;z-index:0;}div.jxColorPreview div{width:20px;height:10px;position:absolute;display:block;left:0;z-index:1;font-size:10px;line-height:0;}div.jxColorPreview div.jxColorSelected{top:0;}div.jxColorPreview div.jxColorHover{bottom:0;}label.jxColorLabel,label.jxAlphaLabel{width:auto;font-family:Arial,sans-serif;font-size:11px;line-height:24px;padding:2px;vertical-align:middle;}a.jxColorClose{position:absolute;top:0;right:0;width:16px;height:16px;}a.jxColorClose img{width:16px;height:16px;}.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;ma
 rgin:0;padding:0;}.jxDisabled{opacity:.4;-ms-filter:"Alpha(opacity=40)";}.jxDisabled *{-ms-filter:"Alpha(opacity=40)";}iframe.jxIframeShim{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;-ms-filter:"Alpha(opacity=0)";z-index:-1;}.jxDialog .jxChrome{background-image:url(images/dialog_chrome.png);}.jxDialog{display:block;z-index:1000;overflow:hidden;}.jxDialogContentContainer{z-index:1;margin:0 11px 13px 12px;border:1px solid #b7b7b7;background-color:#f0f0f0;}.jxDialogModal{position:absolute;display:block;top:0;left:0;width:100%;height:100%;background-color:#000;opacity:.2;-ms-filter:"Alpha(opacity=20)";}.jxDialogContent{display:block;position:relative;overflow:auto;padding:0;z-index:1;}.jxDialogTitle{display:block;position:relative;background-image:url(images/a_pixel.png);text-align:center;height:24px;line-height:24px;z-index:1;margin:6px 6px 0 7px;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxDialogMin .jxDialogTitle{margin-bottom:8px;}.j
 xDialogMoveable,.jxDialogMoveable .jxDialogLabel{cursor:move;}.jxDialogIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxDialogLabel{font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;cursor:default;}.jxDialogResize{position:absolute;bottom:7px;right:6px;width:16px;height:16px;z-index:2;border:0;cursor:se-resize;background-image:url(images/dialog_resize.png);}.jxDialogControls{position:absolute;top:3px;right:2px;height:16px;width:80px;}.jxDialogControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxDialogClose img{background-position:0 -32px;}.jxDialogMenu img{background-position:0 -48px;}.jxDialogHelp img{background-position:0 -64px;}.jxDialogCollapse img{background-position:0 -16px;}.jxDialogMin .jxDialogCollapse img{background-position:0 0;}.jxDialogMax .jxDialogCollapse img{back
 ground-position:0 -16px;}.jxDialogLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxDialogControls .jxButtonContainer,.jxDialogControls span.jxButtonContent,.jxDialogControls .jxButton:hover span.jxButtonContent,.jxDialogControls .jxButton:active span.jxButtonContent,.jxDialogControls .jxButtonActive span.jxButtonContent,.jxDialogControls .jxButtonActive:hover span.jxButtonContent,.jxDialogControls .jxButtonActive:active span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:hover span.jxButtonContent,.jxDialogControls .jxDisabled .jxButton:active span.jxButtonContent,.jxDialogControls .jxButton,.jxDialogControls .jxButton:hover,.jxDialogControls .jxButton:active,.jxDialogControls .jxButtonActive,.jxDialogControls .jxButtonActive:hover,.jxDialogControls .jxButtonActive:active,.jxDialogControls .jxDisabled .jxButton,.jxDialogControls .jxDisabled .jxBu
 tton:hover,.jxDialogControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent,.jxDialogControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxDialogControls .jxButtonMenu span.jxButtonContent span,.jxDialogControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxDialogControls .jxBarContainer{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxDialogControls .jxBarScroller{left:auto;right:0;}.jxDialogControls ul.jxToolbar{float:right;}.jxDialogControls ul.jxToolbar,.jxDialogControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxForm{display:block;position:relative;overflow:hidden;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;}.jxForm label{background-color:transparent;color:#000;}.j
 xForm select,.jxForm input{color:#000;}.jxFormFloat .radioGroup{display:block;overflow:hidden;}.jxFormFloat .radioGroup input,.jxFormFloat .radioGroup label{width:auto!important;vertical-align:middle;}.jxForm .tipArea{display:block;padding:5px 0;background-color:transparent;color:#999;}.jxFormInline label{display:inline;}.jxFormFloat label{display:block;float:left;clear:left;width:75px;}.jxFormFloat select,.jxFormFloat input{float:left;}.jxFormFloat .radioGroup{float:left;}.jxFormFloat .radioGroup input,.jxFormFloat .radioGroup label{float:none;}.jxFormFloat .tipArea{clear:left;}.jxFormBlock label{display:block;}.jxGridContainer{position:absolute;top:0;left:0;border-left:0 solid #d8d8d8;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;overflow:hidden;}.jxGridTable{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;width:0;cursor:default;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal
 ;}.jxGridColHeadHide{height:0;line-height:0;font-size:0;background-color:#fff;white-space:normal;}.jxGridColHeadHide p,.jxGridRowHeadHide p{font-size:0;line-height:0;height:0;margin:0;padding:0;}.jxGridRowHeadHide{width:0;white-space:normal;}.jxGridCell{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;overflow:hidden;padding-left:3px;padding-right:3px;overflow:hidden;white-space:nowrap;cursor:cell;text-overflow:ellipsis;}.jxGridColHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_col.png');background-position:0 0;background-repeat:repeat-x;text-align:center;font-weight:bold;color:#333;cursor:default;}.jxGridRowHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url(
 'images/table_row.png');background-position:0 0;background-repeat:repeat-y;text-align:center;font-weight:bold;color:#333;cursor:default;}.jxGridRowAll{background-color:#fff;}.jxGridColumnHeaderSelected{background-color:#e1e1e1;background-position:0 -200px;}.jxGridRowHeaderSelected{background-color:#e1e1e1;background-position:-400px 0;}.jxGridColumnSelected{background-color:#f7f7f7;}.jxGridRowSelected td{background-color:#f7f7f7;}td.jxGridCellSelected{background-color:#ebebeb;}.jxGridColumnHeaderPrelight{background-color:#cee5ff;background-position:0 -300px;}.jxGridRowHeaderPrelight{background-color:#cee5ff;background-position:-600px 0;}.jxGridColumnPrelight{background-color:#e5f1ff;}.jxGridRowPrelight td{background-color:#e5f1ff;}td.jxGridCellPrelight{background-color:#cce3ff;}.jxMenuContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxButtonMenu span.jxMenuItemSpan{padding-right:16px;}.jxMenuContainer{position:absolute;display:block
 ;z-index:2000;padding:0;}ul.jxMenu,ul.jxSubMenu{display:block;position:relative;list-style-type:none;padding:1px;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}li.jxMenuItemContainer{display:block;position:relative;font-size:0;line-height:0;margin:0;padding:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxMenuItem{display:block;position:relative;overflow:hidden;text-decoration:none;cursor:pointer;outline:none;border:1px solid #fff;background-image:url(images/menuitem.png);background-repeat:no-repeat;background-position:left top;font-family:Arial,Helvetica,sans-serif;font-size:11px;text-decoration:none;margin:0;padding:0;color:#000;}a.jxMenuItemActive{background-position:left -98px;}a.jxMenuItem:focus{background-position:left -74px;}a.jxMenuItem:focus span.jxMenuItemContent{border-right:1px dotted #75ADFF;}a.jxMenuItemActive:focus{background-position:left -170px;}a.jxMenuItem:hover{background-color:#CDE5FF;background-position:left -26
 px;}a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #C5E0FF;}a.jxMenuItemActive:hover{background-position:left -122px;}a.jxMenuItemPressed,a.jxMenuItemPressed:hover{background-color:#CDE5FF;background-position:left -50px;}.jxDisabled a.jxMenuItem,.jxDisabled span.jxMenuItemContent span{cursor:default;}.jxDisabled a.jxMenuItem:focus,.jxDisabled a.jxMenuItemPressed,.jxDisabled a.jxMenuItemPressed:hover,.jxDisabled a.jxMenuItem:hover{background-color:#fff;background-position:left top;border-right:1px solid #fff;}.jxDisabled a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #fff;}span.jxMenuItemContent{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:0;line-height:0;white-space:nowrap;padding:0 20px 0 0;margin:0;border-right:1px solid #fff;}.jxButtonSubMenu span.jxMenuItemContent,.jxButtonSubMenu:hover span.jxMenuItemContent{background-image:url(images/emblems.png);background-position:right -30px;background-repeat:n
 o-repeat;}img.jxMenuItemIcon{position:absolute;top:2px;left:2px;width:16px;height:16px;background-position:left center;background-repeat:no-repeat;}span.jxMenuItemContent span{display:block;position:relative;cursor:pointer;margin:0;padding:2px 0 2px 22px;font-size:16px;line-height:16px;color:#000;}span.jxMenuItemContent span.jxMenuItemLabel{color:#000;font-size:11px;}.jxMenuItemToggle img.jxMenuItemIcon,.jxMenuItemSet img.jxMenuItemIcon{background-image:url(images/emblems.png);background-position:2px 0;background-repeat:no-repeat;}.jxMenuItemToggle a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -48px;}.jxMenuItemSet a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -64px;}ul.jxMenu span.jxMenuSeparator,ul.jxSubMenu span.jxMenuSeparator{display:block;font-size:10px;line-height:10px;background-image:url(images/toolbar_separator_v.png);background-repeat:repeat-x;background-position:left center;}.jxPanel{display:block;position:relative;}.jxPanelContent
 Container{overflow:hidden;background-color:#f0f0f0;}.jxPanelContent{position:relative;display:block;overflow:auto;background-color:#fff;margin:0;padding:0;}.jxPanelTitle{display:block;position:relative;background-image:url(images/panelbar.png);background-repeat:repeat-x;background-position:left top;height:22px;margin:0;padding:0;text-align:center;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxPanelBar{position:absolute;line-height:1px;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxPanelIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxPanelLabel{padding-left:25px;font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;}.jxPanelControls{position:absolute;top:3px;right:2px;height:16px;width:80px;overflow:hidden;}.jxPanelControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0
 ;margin:0;width:16px;height:16px;}.jxPanelClose img{background-position:0 -32px;}.jxPanelMenu img{background-position:0 -48px;}.jxPanelHelp img{background-position:0 -64px;}.jxPanelCollapse img{background-position:0 -16px;}.jxPanelMin .jxPanelCollapse img{background-position:0 0;}.jxPanelMax .jxPanelCollapse img{background-position:0 -16px;}.jxPanelMaximize img{background-position:0 0;}.jxPanelLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxPanelControls .jxButtonContainer,.jxPanelControls span.jxButtonContent,.jxPanelControls .jxButton:hover span.jxButtonContent,.jxPanelControls .jxButton:active span.jxButtonContent,.jxPanelControls .jxButtonActive span.jxButtonContent,.jxPanelControls .jxButtonActive:hover span.jxButtonContent,.jxPanelControls .jxButtonActive:active span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton span.jxButtonContent,.jxPanelControls .jxDisabled .jxButton:hover span.jxButtonContent,.
 jxPanelControls .jxDisabled .jxButton:active span.jxButtonContent,.jxPanelControls .jxButton,.jxPanelControls .jxButton:hover,.jxPanelControls .jxButton:active,.jxPanelControls .jxButtonActive,.jxPanelControls .jxButtonActive:hover,.jxPanelControls .jxButtonActive:active,.jxPanelControls .jxDisabled .jxButton,.jxPanelControls .jxDisabled .jxButton:hover,.jxPanelControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent,.jxPanelControls .jxButtonFlyout span.jxButtonContent{background-image:none;}.jxPanelControls .jxButtonMenu span.jxButtonContent span,.jxPanelControls .jxButtonFlyout span.jxButtonContent span{padding-right:0;}.jxPanelControls div.jxBarTop{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxPanelControls .jxBarScroller{left:auto;right:0;}.jxPanelControls ul.jxToolbar{float:righ
 t;}.jxPanelControls ul.jxToolbar,.jxPanelControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxSplitterMask{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background-image:url(images/a_pixel.png);z-index:1;}.jxSplitBarHorizontal{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:5px;height:100%;cursor:col-resize;background-color:#f0f0f0;z-index:2;}.jxSplitBarVertical{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxSplitContainer{display:block;position:relative;margin:0;padding:0;border:none;overflow:hidden;}.jxSplitArea{display:block;position:absolute;margin:0;padding:0;border:none;z-index:0;}.jxSplitBarDrag{background-color:#eee;}.jxSnapHorizontalBefore{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxSnapHo
 rizontalAfter{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxTabSetContainer{position:relative;display:block;overflow:hidden;width:200px;height:200px;margin:0;padding:0;background-color:#fff;}.jxTabSetContainer .jxToolbarContainer{z-index:auto;}.tabContent{display:none;position:relative;width:100%;height:100%;overflow:auto;}.tabContentActive{display:block;}div.jxTabContainer{display:block;position:relative;margin:0;padding:2px;border:none;}a.jxTab{display:block;position:relative;cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;margin:0;padding:0;border:none;background-repeat:no-repeat;text-decoration:none;outline:none;}span.jxTabContent{display:block;font-size:0;line-height:0;margin:0;padding:0;border:none;background-repeat:no-repeat;}img.jxTabIcon{position:relative;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxTabLabel{display:block;position:relative;cursor:pointer;mar
 gin:0;padding:0;color:#000;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;}a.jxTabClose{display:block;position:absolute;cursor:pointer;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxTabClose img{width:16px;height:16px;background-image:url(images/tab_close.png);}.jxDisabled a.jxTab,.jxDisabled span.jxTabContent span,.jxDisabled a.jxTabClose{cursor:default;}.jxBarTop div.jxTabContainer,.jxBarBottom div.jxTabContainer{float:left;}.jxBarTop a.jxTab,.jxBarTop span.jxTabContent{background-image:url(images/tab_top.png);}.jxBarBottom a.jxTab,.jxBarBottom span.jxTabContent{background-image:url(images/tab_bottom.png);}.jxBarTop a.jxTabClose,.jxBarBottom a.jxTabClose{top:3px;right:3px;}.jxBarTop .jxTabClose span.jxTabContent,.jxBarBottom .jxTabClose span.jxTabContent{padding-right:16px;}.jxBarTop a.jxTab,.jxBarBottom a.jxTab{float:left;padding-left:4px;background-position:left -24px;}.jxBarTop span.jxTabContent,.jxBarBottom span
 .jxTabContent{float:left;padding:4px 4px 4px 0;background-position:right -24px;}.jxBarTop a.jxTabActive,.jxBarBottom a.jxTabActive{background-position:left -72px;}.jxBarTop a.jxTabActive span.jxTabContent,.jxBarBottom a.jxTabActive span.jxTabContent{background-position:right -72px;}.jxBarTop a.jxTab:focus,.jxBarBottom a.jxTab:focus{background-position:left -96px;}.jxBarTop a.jxTab:focus span.jxTabContent,.jxBarBottom a.jxTab:focus span.jxTabContent{background-position:right -96px;}.jxBarTop a.jxTabActive:focus,.jxBarBottom a.jxTabActive:focus{background-position:left -144px;}.jxBarTop a.jxTabActive:focus span.jxTabContent,.jxBarBottom a.jxTabActive:focus span.jxTabContent{background-position:right -144px;}.jxBarTop a.jxTab:hover,.jxBarTop a.jxTabActive:hover,.jxBarBottom a.jxTab:hover,.jxBarBottom a.jxTabActive:hover{background-position:left -48px;}.jxBarTop a.jxTab:hover span.jxTabContent,.jxBarTop a.jxTabActive:hover span.jxTabContent,.jxBarBottom a.jxTab:hover span.jxTabC
 ontent,.jxBarBottom a.jxTabActive:hover span.jxTabContent{background-position:right -48px;}.jxBarTop a.jxTabPressed,.jxBarTop a.jxTabPressed:focus,.jxBarBottom a.jxTabPressed,.jxBarBottom a.jxTabPressed:focus{background-position:left -120px;}.jxBarTop a.jxTabPressed span.jxTabContent,.jxBarTop a.jxTabPressed:focus span.jxTabContent,.jxBarBottom a.jxTabPressed span.jxTabContent,.jxBarBottom a.jxTabPressed:focus span.jxTabContent{background-position:right -120px;}.jxBarTop .jxDisabled a.jxTab:focus,.jxBarTop .jxDisabled a.jxTab:active,.jxBarTop .jxDisabled a.jxTab:hover,.jxBarTop .jxDisabled a.jxTabPressed,.jxBarBottom .jxDisabled a.jxTab:focus,.jxBarBottom .jxDisabled a.jxTab:active,.jxBarBottom .jxDisabled a.jxTab:hover,.jxBarBottom .jxDisabled a.jxTabPressed{background-position:left -24px;}.jxBarTop .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarTop .jxDisabled a.
 jxTabPressed span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabPressed span.jxTabContent{background-position:right -24px;}.jxBarTop .jxDisabled a.jxTabActive:focus,.jxBarTop .jxDisabled a.jxTabActive:active,.jxBarTop .jxDisabled a.jxTabActive:hover,.jxBarBottom .jxDisabled a.jxTabActive:focus,.jxBarBottom .jxDisabled a.jxTabActive:active,.jxBarBottom .jxDisabled a.jxTabActive:hover{background-position:left -72px;}.jxBarTop .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:right -72
 px;}.jxBarTop img.jxTabIcon,.jxBarBottom img.jxTabIcon{float:left;}.jxBarTop span.jxTabLabel,.jxBarBottom span.jxTabLabel{float:left;height:16px;padding:0 4px 0 4px;}.jxBarLeft a.jxTab,.jxBarLeft span.jxTabContent{background-image:url(images/tab_left.png);}.jxBarRight a.jxTab,.jxBarRight span.jxTabContent{background-image:url(images/tab_right.png);}.jxBarLeft a.jxTabClose,.jxBarRight a.jxTabClose{top:3px;left:3px;}.jxBarLeft .jxTabClose span.jxTabContent,.jxBarRight .jxTabClose span.jxTabContent{padding-top:16px;}.jxBarLeft a.jxTab,.jxBarRight a.jxTab{padding-top:4px;background-position:-24px top;}.jxBarLeft span.jxTabContent,.jxBarRight span.jxTabContent{padding:0 4px 4px 4px;background-position:-24px bottom;}.jxBarLeft a.jxTabActive,.jxBarRight a.jxTabActive{background-position:-72px top;}.jxBarLeft a.jxTabActive span.jxTabContent,.jxBarRight a.jxTabActive span.jxTabContent{background-position:-72px bottom;}.jxBarLeft a.jxTab:focus,.jxBarRight a.jxTab:focus{background-posi
 tion:-96px top;}.jxBarLeft a.jxTab:focus span.jxTabContent,.jxBarRight a.jxTab:focus span.jxTabContent{background-position:-96px bottom;}.jxBarLeft a.jxTabActive:focus,.jxBarRight a.jxTabActive:focus{background-position:-144px top;}.jxBarLeft a.jxTabActive:focus span.jxTabContent,.jxBarRight a.jxTabActive:focus span.jxTabContent{background-position:-144px bottom;}.jxBarLeft a.jxTab:hover,.jxBarLeft a.jxTabActive:hover,.jxBarRight a.jxTab:hover,.jxBarRight a.jxTabActive:hover{background-position:-48px top;}.jxBarLeft a.jxTab:hover span.jxTabContent,.jxBarLeft a.jxTabActive:hover span.jxTabContent,.jxBarRight a.jxTab:hover span.jxTabContent,.jxBarRight a.jxTabActive:hover span.jxTabContent{background-position:-48px bottom;}.jxBarLeft a.jxTabPressed,.jxBarLeft a.jxTabPressed:focus,.jxBarRight a.jxTabPressed,.jxBarRight a.jxTabPressed:focus{background-position:-120px top;}.jxBarLeft a.jxTabPressed span.jxTabContent,.jxBarLeft a.jxTabPressed:focus span.jxTabContent,.jxBarRight a.
 jxTabPressed span.jxTabContent,.jxBarRight a.jxTabPressed:focus span.jxTabContent{background-position:-120px bottom;}.jxBarLeft .jxDisabled a.jxTab:focus,.jxBarLeft .jxDisabled a.jxTab:active,.jxBarLeft .jxDisabled a.jxTab:hover,.jxBarLeft .jxDisabled a.jxTabPressed,.jxBarRight .jxDisabled a.jxTab:focus,.jxBarRight .jxDisabled a.jxTab:active,.jxBarRight .jxDisabled a.jxTab:hover,.jxBarRight .jxDisabled a.jxTabPressed{background-position:-24px top;}.jxBarLeft .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabPressed span.jxTabContent{background-position:-24px bottom;}.jxBarLeft .jxDisabled a.jxTabActive:focus,.jxBarLeft .
 jxDisabled a.jxTabActive:active,.jxBarLeft .jxDisabled a.jxTabActive:hover,.jxBarRight .jxDisabled a.jxTabActive:focus,.jxBarRight .jxDisabled a.jxTabActive:active,.jxBarRight .jxDisabled a.jxTabActive:hover{background-position:-72px top;}.jxBarLeft .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:-72px bottom;}.jxBarLeft span.jxTabLabel,.jxBarRight span.jxTabLabel{padding:4px 0 4px 0;}.jxBarContainer{display:block;position:relative;z-index:1;overflow:hidden;margin:0;padding:0;border:0;background-color:#f0f0f0;}.jxBarTop,.jxBarBottom{width:100%;height:28px;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;ove
 rflow:hidden;}.jxTabBox .jxTabBarTop{background-image:url(images/tabbar.png);background-position:0 bottom;}.jxTabBox .jxTabBarBottom{background-image:url(images/tabbar_bottom.png);background-position:0 top;}.jxBarLeft,.jxBarRight{width:auto;height:100%;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;float:left;overflow:hidden;}.jxTabBox .jxTabBarLeft{background-image:url(images/tabbar_left.png);background-repeat:repeat-y;background-position:right 0;}.jxTabBox .jxTabBarRight{background-image:url(images/tabbar_right.png);background-repeat:repeat-y;background-position:left 0;}.jxBarTop .jxBarScroller,.jxBarBottom .jxBarScroller{position:absolute;width:1000%;overflow:hidden;}.jxBarTop .jxBarScrollLeft,.jxBarBottom .jxBarScrollLeft{position:absolute;top:0;left:0;}.jxBarTop .jxBarScrollRight,.jxBarBottom .jxBarScrollRight{position:absolute;top:0;right:0;}.jxBarTop .jxBarScrollLeft img.jxButtonIcon,.jxBarBottom .jxBarScrollLeft img.jxButt
 onIcon{background-image:url(images/emblems.png);background-position:0 -80px;}.jxBarTop .jxBarScrollRight img.jxButtonIcon,.jxBarBottom .jxBarScrollRight img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -96px;}ul.jxToolbar,ul.jxTabBar{display:block;position:relative;float:left;list-style-type:none;margin:0;padding:0;border:none;}li.jxToolItem{display:block;position:relative;float:left;font-size:0;line-height:0;padding:0;margin:0;border:none;}li.jxToolItem span.jxBarSeparator{display:block;position:relative;float:left;font-size:0;line-height:0;border:0;margin:0;padding:4px;background-repeat:no-repeat;background-position:center center;}.jxBarTop li.jxToolItem span.jxBarSeparator,.jxBarBottom li.jxToolItem span.jxBarSeparator{width:8px;height:20px;background-image:url(images/toolbar_separator_h.png);}.jxBarLeft li.jxToolItem span.jxBarSeparator,.jxBarRight li.jxToolItem span.jxBarSeparator{width:20px;height:8px;background-image:url(images/toolbar_s
 eparator_v.png);}.jxBarLeft ul.jxToolbar,.jxBarLeft ul.jxTabBar,.jxBarLeft li.jxToolItem,.jxBarRight ul.jxToolbar,.jxBarRight ul.jxTabBar,.jxBarRight li.jxToolItem{clear:both;}.jxTree,.jxTreeRoot{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxTreeNest{list-style:none;margin:0;padding:0;background-repeat:repeat-y;background-position:left top;}.jxTree li,.jxTreeRoot li{position:relative;display:block;margin:0;padding:0;background-repeat:no-repeat;background-position:left top;white-space:nowrap;font-size:0;line-height:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxTree li{margin-left:16px;}.jxTree a,.jxTreeRoot a{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;background-image:url(images/tree_hover.png);background-repeat:repeat-x;background-position:left top;border:none;margin:0 1px 0 15px;padding:0 0 0 20px;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;li
 ne-height:20px;height:20px;}.jxTree a:focus,.jxTreeRoot a:focus{border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;margin:0 0 0 14px;background-position:left -72px;}.jxTree a:hover,.jxTreeRoot a:hover{border-left:1px solid #C5E0FF;border-right:1px solid #C5E0FF;margin:0 0 0 14px;background-color:#CDE5FF;background-position:left -24px;}.jxTree a.jxTreeItemPressed,.jxTree a.jxTreeItemPressed:hover,.jxTreeRoot a.jxTreeItemPressed,.jxTreeRoot a.jxTreeItemPressed:hover{border-left:1px solid #C5E0FF;border-right:1px solid #C5E0FF;margin:0 0 0 14px;background-color:#CDE5FF;background-position:left -48px;}.jxTree .jxDisabled a,.jxTreeRoot.jxDisabled a{cursor:default;}.jxTree .jxDisabled a:focus,.jxTree .jxDisabled a.jxTreeItemPressed,.jxTree .jxDisabled a.jxTreeItemPressed:hover,.jxTree .jxDisabled a:hover,.jxTreeRoot .jxDisabled a:focus,.jxTreeRoot .jxDisabled a.jxTreeItemPressed,.jxTreeRoot .jxDisabled a.jxTreeItemPressed:hover,.jxTreeRoot .jxDisabled a:hover{backgroun
 d-position:left top;border:none;margin:0 1px 0 15px;}.jxTreeNest{background-image:url(images/tree_vert_line.png);}.jxTreeImage,.jxTreeIcon{position:absolute;display:inline;left:0;top:0;width:16px;height:20px;z-index:1;background-image:url(images/tree.png);background-repeat:no-repeat;border:0;margin:0;}.jxTreeIcon{height:16px;top:1px;left:1px;}.jxTreeBranchOpen .jxTreeIcon,.jxTreeBranchLastOpen .jxTreeIcon{background-position:left -40px;}.jxTreeBranchOpen .jxTreeImage{background-position:left -100px;}.jxTreeBranchLastOpen .jxTreeImage{background-position:left -160px;}.jxTreeBranchClosed .jxTreeIcon,.jxTreeBranchLastClosed .jxTreeIcon{background-position:left -20px;}.jxTreeBranchClosed .jxTreeImage{background-position:left -80px;}.jxTreeBranchLastClosed .jxTreeImage{background-position:left -140px;}.jxTreeItem .jxTreeIcon,.jxTreeItemLast .jxTreeIcon{background-position:left 0;}.jxTreeItem .jxTreeImage{background-position:left -60px;}.jxTreeItemLast .jxTreeImage{background-posi
 tion:left -120px;}.jxTreeItemSelected{background-color:#AFD4FA;font-weight:bold;}.jxTreeItem a,.jxTreeItem img,.jxTreeItem input,.jxTreeItemLast a,.jxTreeItemLast img,.jxTreeItemLast input,.jxTreeBranch a,.jxTreeBranch img,.jxTreeBranch input,.jxTreeBranchLast a,.jxTreeBranchLast img,.jxTreeBranchLast input{vertical-align:middle;}
\ No newline at end of file
+ */body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;}q:before,q:after{content:'';}.jxButtonContainer{display:-moz-inline-box;display:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:2px;border:none;}.jxButton{display:-moz-inline-box;display:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:0 0 0 4px;border:none;background-image:url(images/button.png);background-position:left -24px;background-repeat:no-repeat;text-decoration:none;outline:none;}a.jxButton{cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;}ul.jxToolbar .jxButton{background-position:left top;}span.jxButtonContent{display:-moz-inline-box;displ
 ay:inline-block;position:relative;font-size:0;line-height:0;margin:0;padding:4px 4px 4px 0;border:none;background-image:url(images/button.png);background-position:right -24px;background-repeat:no-repeat;}ul.jxToolbar span.jxButtonContent{background-position:right top;}ul.jxToolbar .jxButtonActive,.jxButtonActive{background-position:left -72px;}ul.jxToolbar .jxButtonActive span.jxButtonContent,.jxButtonActive span.jxButtonContent{background-position:right -72px;}ul.jxToolbar .jxButton:focus,.jxButton:focus{background-position:left -96px;}ul.jxToolbar .jxButton:focus span.jxButtonContent,.jxButton:focus span.jxButtonContent{background-position:right -96px;}ul.jxToolbar .jxButtonActive:focus,.jxButtonActive:focus{background-position:left -144px;}ul.jxToolbar .jxButtonActive:focus span.jxButtonContent,.jxButtonActive:focus span.jxButtonContent{background-position:right -144px;}ul.jxToolbar .jxButton:hover,ul.jxToolbar .jxButtonActive:hover,.jxButton:hover,.jxButtonActive:hover{b
 ackground-position:left -48px;}ul.jxToolbar .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxButtonActive:hover span.jxButtonContent,.jxButton:hover span.jxButtonContent,.jxButtonActive:hover span.jxButtonContent{background-position:right -48px;}ul.jxToolbar .jxButtonPressed,ul.jxToolbar .jxButtonPressed:focus,.jxButtonPressed,.jxButtonPressed:focus{background-position:left -120px;}ul.jxToolbar .jxButtonPressed span.jxButtonContent,ul.jxToolbar .jxButtonPressed:focus span.jxButtonContent,.jxButtonPressed span.jxButtonContent,.jxButtonPressed:focus span.jxButtonContent{background-position:right -120px;}.jxDisabled .jxButton,.jxDisabled span.jxButtonContent span{cursor:default;}ul.jxToolbar .jxDisabled .jxButton:focus,ul.jxToolbar .jxDisabled .jxButton:active,ul.jxToolbar .jxDisabled .jxButton:hover,ul.jxToolbar .jxDisabled .jxButtonPressed{background-position:left top;}.jxDisabled .jxButton:focus,.jxDisabled .jxButton:active,.jxDisabled .jxButton:hover,.jxDisabled .jxButt
 onPressed{background-position:left -24px;}ul.jxToolbar .jxDisabled .jxButton:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:active span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButton:hover span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right top;}.jxDisabled .jxButton:focus span.jxButtonContent,.jxDisabled .jxButton:active span.jxButtonContent,.jxDisabled .jxButton:hover span.jxButtonContent,.jxDisabled .jxButtonPressed span.jxButtonContent{background-position:right -24px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus,ul.jxToolbar .jxDisabled .jxButtonActive:active,ul.jxToolbar .jxDisabled .jxButtonActive:hover,.jxDisabled .jxButtonActive:focus,.jxDisabled .jxButtonActive:active,.jxDisabled .jxButtonActive:hover{background-position:left -72px;}ul.jxToolbar .jxDisabled .jxButtonActive:focus span.jxButtonContent,ul.jxToolbar .jxDisabled .jxButtonActive:active span.jxButtonContent,ul.jxToolbar .jxDisab
 led .jxButtonActive:hover span.jxButtonContent,.jxDisabled .jxButtonActive:focus span.jxButtonContent,.jxDisabled .jxButtonActive:active span.jxButtonContent,.jxDisabled .jxButtonActive:hover span.jxButtonContent{background-position:right -72px;}img.jxButtonIcon{display:-moz-inline-box;display:inline-block;position:relative;vertical-align:middle;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxButtonContent span{display:-moz-inline-box;display:inline-block;position:relative;vertical-align:middle;cursor:pointer;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;height:16px;white-space:nowrap;}span.jxButtonContent span.jxButtonLabel{margin:0;padding:0 4px 0 4px;color:#000;font-size:11px;}.jxDiscloser span.jxButtonContent{padding-right:0;}.jxDiscloser span.jxButtonContent span{padding-right:16px;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose{
 position:absolute;display:-moz-inline-box;display:inline-block;padding:4px 0;font-size:0;line-height:0;right:2px;top:2px;background-image:url(images/button_multi_disclose.png);background-position:right 0;background-repeat:no-repeat;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxButtonDisclose img{width:16px;height:16px;margin:0;padding:0;border:0;background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}a.jxButtonDisclose:focus,a.jxButtonDisclose:active{background-position:right -96px;}a.jxButtonDisclose:hover{background-position:right -48px;}a.jxButtonDisclosePressed{background-position:right -120px;}.jxDisabled a.jxButtonDisclose,.jxDisabled a.jxButtonDisclose:focus,.jxDisabled a.jxButtonDisclose:active,.jxDisabled a.jxButtonDisclose:hover,.jxDisabled a.jxButtonDisclosePressed{cursor:default;background-position:right 0;}ul.jxToolbar .jxButtonHover{background-position:left -24px!important;}ul.jxTo
 olbar .jxButtonHover span.jxButtonContent{background-position:right -24px!important;}.jxFlyout .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxFlyout{position:absolute;display:block;z-index:100;margin:0;padding:0;}.jxFlyoutContent{position:relative;display:block;overflow:auto;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}.jxButtonMulti,.jxButtonMulti span.jxButtonContent{background-image:url(images/button_multi.png);}.jxButtonEditCombo,.jxButtonEditCombo span.jxButtonContent{background-image:url(images/button_combo.png);}.jxButtonMulti span.jxButtonContent span{padding-right:21px;}.jxButtonEditCombo span.jxButtonContent span{font-size:0;}.jxButtonComboDefault span.jxButtonContent span,.jxButtonComboDefault input{font-style:italic;color:#999;}.jxButtonEditCombo input{float:left;line-height:16px;height:16px;padding:0 4px;margin:0;border:none;font-size:11px;font-family:Arial,Helvetica,sans-serif;background-color:transpa
 rent;}.jxChrome{position:absolute;display:block;font-size:0;line-height:0;z-index:-1;width:100%;height:100%;top:0;left:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxChromeDrag{opacity:.5;-ms-filter:"Alpha(opacity=50)";}.jxChromeTL{position:absolute;overflow:hidden;left:0;top:0;width:50%;height:50%;}.jxChromeTR{position:absolute;overflow:hidden;left:50%;top:0;width:50%;height:50%;}.jxChromeBL{position:absolute;overflow:hidden;left:0;top:50%;width:50%;height:50%;}.jxChromeBR{position:absolute;overflow:hidden;left:50%;top:50%;width:50%;height:50%;}.jxChromeTL img{position:absolute;top:0;left:0;width:1000px;height:600px;}.jxChromeTR img{position:absolute;top:0;right:0;width:1000px;height:600px;}.jxChromeBL img{position:absolute;bottom:0;left:0;width:1000px;height:600px;}.jxChromeBR img{position:absolute;bottom:0;right:0;width:1000px;height:600px;}.jxColorBar{position:relative;overflow:hidden;}table.jxColorGrid{position:relative;border-collapse:collapse;emp
 ty-cells:show;clear:both;padding:0;margin:0;}.jxColorGrid tr{padding:0;margin:0;}.jxColorGrid td{border:1px solid #000;padding:0;margin:0;}.jxColorGrid td.emptyCell{border:0 solid #000;}.jxColorGrid td.emptyCell span{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;padding:1px;margin:0;}.jxColorGrid a.colorSwatch{display:block;width:7px;height:7px;line-height:0;font-size:0;border:0 solid #000;margin:0;padding:1px;}.jxColorGrid a.borderWhite:hover{border:1px solid #fff;padding:0;}.jxColorGrid a.borderBlack:hover{border:1px solid #000;padding:0;}input.jxHexInput{width:55px;vertical-align:middle;}input.jxAlphaInput{width:30px;vertical-align:middle;}div.jxColorPreview{float:left;position:relative;width:20px;height:20px;border:1px solid #000;margin:2px;vertical-align:middle;background-image:url('images/grid.png');overflow:hidden;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch{display:block;float:left;width:14px;height:14px;border:1px soli
 d #000;background-image:url('images/grid.png');background-position:0 0;background-repeat:repeat;padding-right:0!important;}.jxButtonFlyout span.jxButtonContent span.jxButtonSwatch span{display:block;width:14px;height:14px;position:absolute;padding-right:0;background:none;}div.jxColorPreview img{position:absolute;z-index:0;}div.jxColorPreview div{width:20px;height:10px;position:absolute;display:block;left:0;z-index:1;font-size:10px;line-height:0;}div.jxColorPreview div.jxColorSelected{top:0;}div.jxColorPreview div.jxColorHover{bottom:0;}label.jxColorLabel,label.jxAlphaLabel{width:auto;font-family:Arial,sans-serif;font-size:11px;line-height:24px;padding:2px;vertical-align:middle;}a.jxColorClose{position:absolute;top:0;right:0;width:16px;height:16px;}a.jxColorClose img{width:16px;height:16px;}.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;margin:0;padding:0;}.jxDisabled{opacity:.4;-ms-filter:"Alpha(opacity=40)";}.jxDi
 sabled *{-ms-filter:"Alpha(opacity=40)";}.jxMask{opacity:.5;-ms-filter:"Alpha(opacity=50)";background-color:#fff;}.jxModalMask{background-color:#000;opacity:.2;-ms-filter:"Alpha(opacity=20)";}.jxEventMask{background-image:url(images/a_pixel.png);}.jxSpinner{position:absolute;opacity:.5;-ms-filter:"Alpha(opacity=50)";z-index:999;background:#fff;}.jxSpinnerMessage{text-align:center;font-weight:bold;}.jxSpinnerSmall .jxSpinnerMessage{margin:0;padding:0;font-size:11px;line-height:24px;}.jxSpinnerLarge .jxSpinnerImage{background:url(images/spinner_24.gif) no-repeat;width:24px;height:24px;margin:0 auto;}.jxSpinnerSmall .jxSpinnerImage{background:url(images/spinner_16.gif) no-repeat;width:16px;height:16px;margin-right:4px;display:inline-block;vertical-align:middle;}.jxConfirmQuestion,.jxPrompt{text-align:center;padding:10px;margin-top:10px;}.jxDialog .jxChrome{background-image:url(images/dialog_chrome.png);}.jxDialog{display:block;z-index:1000;overflow:hidden;visibility:hidden;}.jx
 DialogContentContainer{z-index:1;margin:0 11px 13px 12px;border:1px solid #b7b7b7;background-color:#f0f0f0;}.jxDialogContent{display:block;position:relative;overflow:auto;padding:0;z-index:1;}.jxDialogTitle{display:block;position:relative;background-image:url(images/a_pixel.png);text-align:center;height:24px;line-height:24px;z-index:1;margin:6px 6px 0 7px;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxDialogMin .jxDialogTitle{margin-bottom:8px;}.jxDialogMoveable,.jxDialogMoveable .jxDialogLabel{cursor:move;}.jxDialogIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxDialogLabel{font-family:Arial,Helvetica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;cursor:default;}.jxDialogResize{position:absolute;bottom:7px;right:6px;width:16px;height:16px;z-index:2;border:0;cursor:se-resize;background-image:url(images/dialog_resize.png);}.jxDialogControls{position:absolute;top:3
 px;right:2px;height:16px;width:80px;}.jxDialogControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxDialogClose img{background-position:0 -32px;}.jxDialogMenu img{background-position:0 -48px;}.jxDialogHelp img{background-position:0 -64px;}.jxDialogCollapse img{background-position:0 -16px;}.jxDialogMin .jxDialogCollapse img{background-position:0 0;}.jxDialogMax .jxDialogCollapse img{background-position:0 -16px;}.jxDialogMaximize img{background-position:0 -80px;}.jxDialogMaximized .jxDialogMaximize img{background-position:0 -96px;}.jxDialogLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxDialogControls .jxButtonContainer,.jxDialogControls .jxButton,.jxDialogControls .jxButton:hover,.jxDialogControls .jxButton:active,.jxDialogControls .jxButtonActive,.jxDialogControls .jxButtonActive:hover,.jxDialogControls .jxButtonActive:active,.jxDial
 ogControls .jxDisabled .jxButton,.jxDialogControls .jxDisabled .jxButton:hover,.jxDialogControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxDialogControls .jxBarContainer{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxDialogControls .jxBarScroller{left:auto;right:0;}.jxDialogControls ul.jxToolbar{float:right;}.jxDialogControls ul.jxToolbar,.jxDialogControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}div.jxFileInputs{position:relative;}div.jxFileFake{position:absolute;top:0;left:0;z-index:1;}div.jxFileFake span{float:left;display:block;}div.jxFileFake .jxInputContainer{width:150px;}div.jxFileFake .jxInputText{width:135px;}div.jxFileFake .jxButtonContainer{margin-top:2px;float:left;}.jxInputFile{position:relative;text-align:right;-moz-opacity:0;filter:alpha(opacity:0);opacity:0
 ;z-index:2;margin-top:-5px;height:35px;}.jxForm{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxFieldset{display:block;position:relative;border:1px solid #ccc;margin:10px;padding:5px;}.jxFieldsetLegend,.jxFieldset legend{position:relative;margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;font-size:14px;line-height:26px;color:#000;}.jxInputContainer{display:block;position:relative;padding:0;margin:2px;border:none;}.jxInputLabel,.jxInputTag{display:-moz-inline-box;display:inline-block;margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:26px;color:#000;}.jxInputLabel{vertical-align:top;}.jxInputTag{vertical-align:bottom;}.jxInputWrapper{display:inline-block;white-space:normal;position:relative;}.jxInputText,.jxInputPassword,.jxInputTextarea,.jxInputCombo,.jxInputColor{margin:0 4px;padding:4px;border:1px solid #bbb;width:232px;font-family:Arial,Helvetica,sans-serif;font
 -size:12px;line-height:16px;color:#000;}.jxInputCombo,.jxInputColor{padding:4px 20px 4px 20px;width:200px;}.jxInputIconHidden .jxInputCombo{padding-left:4px;width:216px;}.jxInputIcon{position:absolute;width:16px;height:16px;left:0;top:0;margin:4px 4px 4px 8px;}.jxInputContainerColor .jxInputIcon{border:1px solid #bbb;width:15px;height:15px;}.jxInputIconHidden .jxInputIcon{display:none;}.jxInputRevealer{position:absolute;width:16px;height:16px;right:0;top:0;margin:4px 8px 4px 4px;font-size:0;line-height:0;}img.jxInputRevealerIcon{background-image:url(images/emblems.png);background-position:right -16px;background-repeat:no-repeat;}.jxInputRevealer .jxButtonContainer,.jxInputRevealer .jxButton{padding:0;margin:0;border:0;background-color:transparent;background-image:none;}.jxInputSelect{margin:0 4px;padding:3px 4px 3px 1px;border:1px solid #bbb;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputRadio,.jxInputCheck{margin:5px;font-family:A
 rial,Helvetica,sans-serif;font-size:12px;line-height:16px;color:#000;}.jxInputText:focus,.jxInputPassword:focus,.jxInputTextarea:focus,.jxInputSelect:focus,.jxInputCombo:focus,.jxInputColor:focus{border:1px solid #000;}.jxInputContainer .jxButtonContainer{padding:0;margin:0 4px;}.jxInputGroup{border:none;padding:0;margin:2px;}.jxInputGroup legend{font-size:0;line-height:0;padding:0;margin:0;border:none;}.jxInputGroup .jxFieldsetLegend{font-size:12px;}.jxInputGroup .jxInputLabel{width:auto;}.jxFieldError .jxInputText,.jxFieldError .jxInputPassword,.jxFieldError .jxInputTextarea,.jxFieldError .jxInputSelect,.jxFieldError .jxInputCombo,.jxFieldError .jxInputColor{background-color:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;}.jxFieldSuccess .jxInputText,.jxFieldSuccess .jxInputPassword,.jxFieldSuccess .jxInputTextarea,.jxFieldSuccess .jxInputSelect,.jxFieldSuccess .jxInputCombo,.jxFieldSuccess .jxInputColor{background-color:#E6EFC2;color:#264409;border-color:#C6D880;}.jxFieldErro
 r .jxInputText:focus,.jxFieldError .jxInputPassword:focus,.jxFieldError .jxInputTextarea:focus,.jxFieldError .jxInputSelect:focus,.jxFieldError .jxInputCombo:focus,.jxFieldError .jxInputColor:focus{border-color:#8a1f11;}.jxFieldSuccess .jxInputText:focus,.jxFieldSuccess .jxInputPassword:focus,.jxFieldSuccess .jxInputTextarea:focus,.jxFieldSuccess .jxInputSelect:focus,.jxFieldSuccess .jxInputCombo:focus,.jxFieldSuccess .jxInputColor:focus{border-color:#264409;}.jxFieldError .jxInputLabel,.jxFieldError .jxInputTag{color:#8a1f11;}.jxFieldSuccess .jxInputLabel,.jxFieldSuccess .jxInputTag{color:#264409;}.jxFormInline .jxInputContainer,form .jxFormInline .jxInputContainer,form .jxFieldset span.jxFormInline,form.jxForm span.jxFormInline{display:inline;}.jxFormInline .jxInputLabel,form .jxFormInline .jxInputLabel,form span.jxFormInline .jxInputLabel{display:inline;width:auto;}.jxFormInline .jxInputTag,form .jxFormInline .jxInputTag,form span.jxFormInline .jxInputTag{display:inline;}
 .jxFormInline .jxInputGroup,form .jxFormInline .jxInputGroup{padding-left:0;}.jxFormInline .jxInputGroup .jxFieldsetLegend,form .jxFormInline .jxInputGroup .jxFieldsetLegend{position:relative;left:auto;}.jxFormInline .jxInputGroup .jxInputLabel,form .jxFormInline .jxInputGroup .jxInputLabel{display:inline;width:auto;}.jxFormBlock .jxInputContainer,form .jxFormBlock .jxInputContainer,form .jxFieldset span.jxFormBlock,form.jxform span.jxFormBlock{display:block;}.jxFormBlock .jxInputLabel,form .jxFormBlock .jxInputLabel,form span.jxFormBlock .jxInputLabel{display:block;width:auto;margin-left:4px;}.jxFormBlock .jxInputContainerCheck .jxInputLabel,.jxFormBlock .jxInputContainerRadio .jxInputLabel,form .jxFormBlock .jxInputContainerCheck .jxInputLabel,form .jxFormBlock .jxInputContainerRadio .jxInputLabel,form span.jxFormBlock .jxInputContainerCheck .jxInputLabel,form span.jxFormBlock .jxInputContainerRadio .jxInputLabel{display:-moz-inline-box;display:inline-block;}.jxFormBlock .
 jxInputGroup,form .jxFormBlock .jxInputGroup{padding-left:0;}.jxFormBlock .jxInputGroup .jxFieldsetLegend,form .jxFormBlock .jxInputGroup .jxFieldsetLegend{position:relative;left:auto;}.jxFormBlock .jxInputGroup .jxInputLabel,form .jxFormBlock .jxInputGroup .jxInputLabel{display:-moz-inline-box;display:inline-block;width:auto;}.jxFormInlineblock .jxInputContainer,form .jxFormInlineblock .jxInputContainer,form .jxFieldset span.jxFormInlineblock,form.jxForm span.jxFormInlineblock{display:block;white-space:nowrap;}.jxFormInlineblock .jxInputLabel,form .jxFormInlineblock .jxInputLabel,form span.jxFormInlineblock .jxInputLabel{display:-moz-inline-box;display:inline-block;width:200px;}.jxFormInlineblock .jxInputGroup,form .jxFormInlineblock .jxInputGroup{padding-left:200px;}.jxFormInlineblock .jxInputGroup .jxFieldsetLegend,form .jxFormInlineblock .jxInputGroup .jxFieldsetLegend{position:absolute;left:-200px;width:200px;}.jxFormInlineblock .jxInputGroup .jxInputLabel,form .jxFormI
 nlineblock .jxInputGroup .jxInputLabel{display:-moz-inline-box;display:inline-block;width:auto;}.jxGridCellContent .jxInputContainer,.jxGridCellContent .jxInputRadio,.jxGridCellContent .jxInputCheck{display:inline;position:relative;border:none;margin:0;padding:0;font-size:0;line-height:0;color:#000;}.jxGridContainer{position:absolute;top:0;left:0;border-left:0 solid #d8d8d8;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;overflow:hidden;}.jxGridTable{width:100%;position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;cursor:default;}.jxGridTableBody{position:relative;table-layout:fixed;border-collapse:collapse;border-style:none;cursor:default;}.jxGridCell{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;overflow:hidden;}.jxGridCellContent{position:relative;display:-moz-inline-box;display:inline-block;overflow:hidden;padding:0 3px;overflow:hidd
 en;vertical-align:middle;font-family:Arial,Verdana,sans-serif;font-size:11px;font-weight:normal;line-height:16px;white-space:nowrap;cursor:cell;text-overflow:ellipsis;}.jxGridColHead .jxGridCellContent{padding:0 3px;text-align:center;font-weight:bold;color:#333;height:100%;}.jxGridRowHead .jxGridCellContent{text-align:center;font-weight:bold;color:#333;}.jxGridColHead{height:100%;border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_col.png');background-position:0 0;background-repeat:repeat-x;text-align:center;cursor:default;padding:0;white-space:nowrap;overflow:hidden;}.jxGridRowHead{border-top:0 solid #d8d8d8;border-right:1px solid #d8d8d8;border-bottom:1px solid #d8d8d8;border-left:0 solid #d8d8d8;background-color:#f2f2f2;background-image:url('images/table_row.png');background-position:0 0;background-repeat:repeat-y;text-align:center;cursor:default;o
 verflow:hidden;white-space:nowrap;}.jxGridRowAll{background-color:#fff;}.jxGridColumnHeaderSelected{background-color:#e1e1e1;background-position:0 -200px;}.jxGridRowHeaderSelected{background-color:#e1e1e1;background-position:-400px 0;}.jxGridColumnSelected{background-color:#f7f7f7;}.jxGridRowSelected td,.jxGridRowSelected th{background-color:#f7f7f7;}td.jxGridCellSelected,th.jxGridCellSelected{background-color:#ebebeb;}.jxGridColumnHeaderPrelight{background-color:#cee5ff;background-position:0 -300px;}.jxGridRowHeaderPrelight{background-color:#cee5ff;background-position:-600px 0;}.jxGridColumnPrelight{background-color:#e5f1ff;}.jxGridRowPrelight td,.jxGridRowPrelight th{background-color:#e5f1ff;}td.jxGridCellPrelight,th.jxGridCellPrelight{background-color:#cce3ff;}.jxGridHeader .jxColSortable img{vertical-align:top;width:16px;height:16px;background-image:url('images/emblems.png');background-repeat:no-repeat;background-position:right top;}.jxGridHeader .jxColSortable .jxGridCe
 llContent{margin-right:-16px;}.jxGridHeader .jxGridColumnSortedAsc img{background-position:right -162px;}.jxGridHeader .jxGridColumnSortedDesc img{background-position:right -18px;}.jxGridColumnResize,.jxGridRowResize{display:block;position:absolute;background-image:url(images/a_pixel.png);z-index:1;}.jxGridColumnResize{right:0;top:0;height:100%;width:4px;cursor:col-resize;}.jxColSortable .jxGridColumnResize{right:8px;}.jxGridRowResize{left:0;bottom:0;height:4px;width:100%;cursor:row-resize;}.jxGridEditorPopup{min-width:130px;margin:3px;font-size:10px;box-shadow:2px 2px 5px #888;-moz-box-shadow:2px 2px 5px #888;-webkit-box-shadow:2px 2px 5px #888;background-color:#ddd;border:1px solid #999;position:absolute;}.jxGridEditorPopupInnerWrapper{position:relative;height:100%;width:100%;}.jxListView{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxListView .jxListItemContainer{position:relative;display:block;outline:none;overflow:hidden;border:none;margin:0 1px;
 padding:0;}.jxListItem{position:relative;display:block;cursor:pointer;outline:none;overflow:hidden;border:none;margin:0 1px;padding:0;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;line-height:20px;height:20px;}.jxListView .jxHover{margin:0;border-left:1px solid #CDDFFD;border-right:1px solid #CDDFFD;background-image:url(images/listitem.png);background-repeat:repeat-x;background-color:#CDE5FF;background-position:left -24px;}.jxListItem:focus{margin:0;border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;background-image:url(images/listitem.png);background-repeat:repeat-x;background-position:left -72px;}.jxListView .jxPressed,.jxListView .jxSelected{margin:0;border-left:1px solid #8AABFB;border-right:1px solid #8AABFB;background-color:#CDE5FF;background-image:url(images/listitem.png);background-repeat:repeat-x;background-position:left -48px;}.jxMenuContainer .jxChrome{background-image:url(images/flyout_chrome.png);padd
 ing:5px 5px 7px 6px;}.jxButtonMenu span.jxMenuItemSpan{padding-right:16px;}.jxMenuContainer{position:absolute;top:0;left:-10000px;display:none;z-index:2000;padding:0;}ul.jxMenu{display:block;position:relative;list-style-type:none;padding:1px;margin:6px 6px 8px 7px;background-color:#fff;border:1px solid #999;}li.jxMenuItemContainer{display:block;position:relative;font-size:0;line-height:0;margin:0;padding:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}a.jxMenuItem{display:block;position:relative;overflow:hidden;text-decoration:none;cursor:pointer;outline:none;border:1px solid #fff;background-image:url(images/menuitem.png);background-repeat:no-repeat;background-position:left top;font-family:Arial,Helvetica,sans-serif;font-size:11px;text-decoration:none;margin:0;padding:0;color:#000;}a.jxMenuItemActive{background-position:left -98px;}a.jxMenuItem:focus{background-position:left -74px;}a.jxMenuItem:focus span.jxMenuItemContent{border-right:1px dotted #75ADFF;}a
 .jxMenuItemActive:focus{background-position:left -170px;}a.jxMenuItem:hover{background-color:#CDE5FF;background-position:left -26px;}a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #C5E0FF;}a.jxMenuItemActive:hover{background-position:left -122px;}a.jxMenuItemPressed,a.jxMenuItemPressed:hover{background-color:#CDE5FF;background-position:left -50px;}.jxDisabled a.jxMenuItem,.jxDisabled span.jxMenuItemContent span{cursor:default;}.jxDisabled a.jxMenuItem:focus,.jxDisabled a.jxMenuItemPressed,.jxDisabled a.jxMenuItemPressed:hover,.jxDisabled a.jxMenuItem:hover{background-color:#fff;background-position:left top;border-right:1px solid #fff;}.jxDisabled a.jxMenuItem:hover span.jxMenuItemContent{border-right:1px solid #fff;}span.jxMenuItemContent{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:0;line-height:0;white-space:nowrap;padding:0 20px 0 0;margin:0;border-right:1px solid #fff;}.jxButtonSubMenu span.jxMenuItemContent,.jxButton
 SubMenu:hover span.jxMenuItemContent{background-image:url(images/emblems.png);background-position:right -30px;background-repeat:no-repeat;}img.jxMenuItemIcon{position:absolute;top:2px;left:2px;width:16px;height:16px;background-position:left center;background-repeat:no-repeat;}span.jxMenuItemContent span{display:block;position:relative;cursor:pointer;margin:0;padding:2px 0 2px 22px;font-size:16px;line-height:16px;color:#000;}span.jxMenuItemContent span.jxMenuItemLabel{color:#000;font-size:11px;}.jxMenuItemToggle img.jxMenuItemIcon,.jxMenuItemToggleSet img.jxMenuItemIcon{background-image:url(images/emblems.png);background-position:2px 0;background-repeat:no-repeat;}.jxMenuItemToggle a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -48px;}.jxMenuItemToggleSet a.jxMenuItemActive img.jxMenuItemIcon{background-position:2px -64px;}ul.jxMenu span.jxMenuSeparator{display:block;font-size:10px;line-height:10px;background-image:url(images/toolbar_separator_v.png);background
 -repeat:repeat-x;background-position:left center;}@CHARSET "ISO-8859-1";.jxMessage{text-align:center;padding:10px;margin-top:10px;}.jxNoticeListContainer{border:none;padding:0;margin:0;font-size:0;line-height:0;z-index:500;}.jxNoticeList{position:relative;}.jxNoticeItemContainer{position:relative;overflow:hidden;}.jxNoticeItemContainer .jxChrome{background-image:url(images/flyout_chrome.png);padding:5px 5px 7px 6px;}.jxHasChrome .jxNoticeItem{margin:6px 6px 8px 7px;}.jxNoticeItem{position:relative;border:2px solid #ccc;margin:0;padding:0;background-color:#f8f8f8;background-image:url(images/notice.png);background-repeat:repeat-x;background-position:left bottom;}.jxNoticeIcon{position:absolute;top:0;left:0;margin:6px;width:16px;height:16px;background-image:url(images/icons.png);background-repeat:no-repeat;background-position:0 0;}.jxNotice{display:block;position:relative;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:18px;color:#666;margin:0;border:none;padd
 ing:6px 26px 6px 10px;}.jxNoticeError .jxNoticeItem{background-color:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;background-image:url(images/notice_error.png);}.jxNoticeWarning .jxNoticeItem{background-color:#FFF6BF;color:#514721;border-color:#FFD324;background-image:url(images/notice_warning.png);}.jxNoticeSuccess .jxNoticeItem{background-color:#E6EFC2;color:#264409;border-color:#C6D880;background-image:url(images/notice_success.png);}.jxNoticeInformation .jxNoticeItem{background-color:#F8F8F8;color:#666;border-color:#CCC;background-image:url(images/notice.png);}.jxNoticeError .jxNotice{color:#8a1f11;padding-left:28px;}.jxNoticeWarning .jxNotice{color:#514721;padding-left:28px;}.jxNoticeSuccess .jxNotice{color:#264409;padding-left:28px;}.jxNoticeInformation .jxNotice{color:#666;padding-left:28px;}.jxNoticeError .jxNoticeIcon{background-position:0 -32px;}.jxNoticeWarning .jxNoticeIcon{background-position:0 -16px;}.jxNoticeSuccess .jxNoticeIcon{background-position:0 -48px;}.jx
 NoticeInformation .jxNoticeIcon{background-position:0 -64px;}.jxNoticeClose{position:absolute;top:0;right:0;margin:6px;width:16px;height:16px;background-image:url(images/tab_close.png);background-position:0 0;background-repeat:no-repeat;}.jxPanel{display:block;position:relative;}.jxPanelContentContainer{overflow:hidden;background-color:#f0f0f0;}.jxPanelContent{position:relative;display:block;overflow:auto;background-color:#fff;margin:0;padding:0;}.jxPanelTitle{display:block;position:relative;background-image:url(images/panelbar.png);background-repeat:repeat-x;background-position:left top;height:22px;margin:0;padding:0;text-align:center;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxPanelBar{position:absolute;line-height:1px;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxPanelIcon{position:absolute;left:2px;top:3px;width:16px;height:16px;border:none;padding:0;margin:0;}.jxPanelLabel{padding-left:25px;font-family:Arial,Helvet
 ica,sans-serif;font-size:11px;font-weight:bold;line-height:21px;color:#000;white-space:nowrap;}.jxPanelControls{position:absolute;top:3px;right:2px;height:16px;width:80px;overflow:hidden;}.jxPanelControls img{background-image:url('images/panel_controls.png');background-repeat:no-repeat;border:0;margin:0;width:16px;height:16px;}.jxPanelClose img{background-position:0 -32px;}.jxPanelMenu img{background-position:0 -48px;}.jxPanelHelp img{background-position:0 -64px;}.jxPanelCollapse img{background-position:0 -16px;}.jxPanelMin .jxPanelCollapse img{background-position:0 0;}.jxPanelMax .jxPanelCollapse img{background-position:0 -16px;}.jxPanelMaximize img{background-position:0 0;}.jxPanelLoading img{border:0;margin:0;width:16px;height:16px;visibility:hidden;position:absolute;top:1px;left:2px;}.jxPanelControls .jxButtonContainer,.jxPanelControls .jxButton,.jxPanelControls .jxButton:hover,.jxPanelControls .jxButton:active,.jxPanelControls .jxButtonActive,.jxPanelControls .jxButtonA
 ctive:hover,.jxPanelControls .jxButtonActive:active,.jxPanelControls .jxDisabled .jxButton,.jxPanelControls .jxDisabled .jxButton:hover,.jxPanelControls .jxDisabled .jxButton:active{padding:0;margin:0;border:none;background-color:transparent;background-image:none;}.jxPanelControls div.jxBarTop{position:absolute;right:0;background-image:none;background-color:transparent;margin:0;padding:0;border:none;height:16px;}.jxPanelControls .jxBarScroller{left:auto;right:0;}.jxPanelControls ul.jxToolbar{float:right;}.jxPanelControls ul.jxToolbar,.jxPanelControls li.jxToolItem{background-image:none;background-color:transparent;margin:0;padding:0;border:none;}.jxProgressBar-container{position:relative;display:block;width:100%;}.jxProgressBar-message{position:relative;display:block;color:black;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:20px;}.jxProgressBar{position:relative;display:block;width:100%;height:20px;border:none;margin:0;padding:0;}.jxProgressBar-outline{po
 sition:absolute;display:block;top:0;left:0;z-index:10;height:20px;border-left:1px solid #cbc8c8;border-right:1px solid #cbc8c8;background-image:url(images/progressbar.png);background-position:0 -140px;width:100%;}.jxProgressBar-fill{position:absolute;display:block;top:0;left:0;z-index:20;height:20px;border:none;background-image:url(images/progressbar.png);background-position:0 0;}.jxProgressStarting .jxProgressBar-fill{border:none;}.jxProgressWorking .jxProgressBar-fill{border-left:1px solid #49afe8;}.jxProgressFinished .jxProgressBar-fill{border-left:1px solid #49afe8;border-right:1px solid #49afe8;}.jxProgressBar-text{position:absolute;display:block;overflow:visible;top:0;left:1px;width:auto;height:20px;z-index:30;border:none;margin:0;padding:0 0 0 4px;font-family:Arial,Helvetica,sans-serif;font-size:12px;line-height:20px;white-space:nowrap;}.jxHasVerticalScrollbar,.jxHasHorizontalScrollbar{position:relative;overflow:hidden;}.jxScrollbarChildWrapper{overflow:hidden;}.jxHas
 VerticalScrollbar{padding-right:25px;}.jxHasVerticalScrollbar .jxScrollbarContainer{position:absolute;right:0;top:0;width:20px;height:100%;border:none;border-left:1px solid black;}.jxHasVerticalScrollbar .jxScrollLeft,.jxHasVerticalScrollbar .jxScrollRight{width:20px;height:20px;display:block;background-color:blue;}.jxHasVerticalScrollbar .jxSliderContainer{height:100%;width:20px;border:none;}.jxHasHorizontalScrollbar{padding-bottom:25px;}.jxHasHorizontalScrollbar .jxScrollbarContainer{position:absolute;bottom:0;left:0;height:20px;width:100%;border:none;border-top:1px solid black;}.jxHasHorizontalScrollbar .jxScrollLeft,.jxHasHorizontalScrollbar .jxScrollRight{width:20px;height:20px;display:block;background-color:blue;}.jxHasHorizontalScrollbar .jxScrollLeft{float:left;}.jxHasHorizontalScrollbar .jxScrollRight{float:right;}.jxHasHorizontalScrollbar .jxSlider{float:left;}.jxHasHorizontalScrollbar .jxSliderContainer{height:20px;width:100%;border:none;}.jxHasVerticalScrollbar .
 jxSliderKnob,.jxHasHorizontalScrollbar .jxSliderKnob{width:20px;height:20px;background-color:black;cursor:pointer;}@CHARSET "ISO-8859-1";.jxSliderContainer{width:100%;height:10px;border:1px solid black;}.jxSliderKnob{width:10px;height:10px;background-color:black;cursor:pointer;}.jxSplitterMask{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background-image:url(images/a_pixel.png);z-index:1;}.jxSplitBarHorizontal{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:5px;height:100%;cursor:col-resize;background-color:#f0f0f0;z-index:2;}.jxSplitBarVertical{display:block;position:absolute;font-size:0;line-height:0;margin:0;padding:0;border:none;width:100%;height:5px;cursor:row-resize;background-color:#f0f0f0;z-index:2;}.jxSplitContainer{display:block;position:relative;margin:0;padding:0;border:none;overflow:hidden;}.jxSplitArea{display:block;position:absolute;margin:0;padding:0;border:none;z-index:0;}.jxSplitBarDrag
 {background-color:#eee;}.jxSnapHorizontalBefore{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxSnapHorizontalAfter{width:5px;height:5px;position:absolute;top:0;left:0;background-color:#aaa;}.jxTabSetContainer{position:relative;display:block;overflow:hidden;width:200px;height:200px;margin:0;padding:0;background-color:#fff;}.jxTabSetContainer .jxBarContainer{z-index:auto;}.tabContent{display:none;position:relative;width:100%;height:100%;overflow:auto;}.tabContentActive{display:block;}span.jxTabContainer{display:block;position:relative;margin:0;padding:2px;border:none;}a.jxTab{display:-moz-inline-box;display:inline-block;position:relative;cursor:pointer;user-select:none;-moz-user-select:none;-khtml-user-select:none;margin:0;padding:0;border:none;background-repeat:no-repeat;text-decoration:none;outline:none;}span.jxTabContent{display:-moz-inline-box;display:inline-block;font-size:0;line-height:0;margin:0;padding:0;border:none;background-repeat:no-r
 epeat;}img.jxTabIcon{display:-moz-inline-box;display:inline-block;position:relative;width:16px;height:16px;background-position:center center;background-repeat:no-repeat;}span.jxTabLabel{display:-moz-inline-box;display:inline-block;position:relative;cursor:pointer;margin:0;padding:0;color:#000;font-family:Arial,Helvetica,sans-serif;font-size:11px;line-height:16px;}a.jxTabClose{display:block;position:absolute;cursor:pointer;outline:none;user-select:none;-moz-user-select:none;-khtml-user-select:none;width:16px;height:16px;background-image:url(images/tab_close.png);background-position:0 0;background-repeat:no-repeat;}.jxDisabled a.jxTab,.jxDisabled span.jxTabContent span,.jxDisabled a.jxTabClose{cursor:default;}.jxTabBarTop .jxBarWrapper,.jxTabBarBottom .jxBarWrapper{padding-left:2px;}.jxBarTop span.jxTabContainer,.jxBarBottom span.jxTabContainer{margin-right:-1px;padding:2px 0;}.jxBarTop a.jxTab,.jxBarTop span.jxTabContent,.jxTabBarTop .jxBarControls a.jxButton,.jxTabBarTop .jx
 BarControls span.jxButtonContent{background-image:url(images/tab_top.png);}.jxBarBottom a.jxTab,.jxBarBottom span.jxTabContent,.jxTabBarBottom .jxBarControls a.jxButton,.jxTabBarBottom .jxBarControls span.jxButtonContent{background-image:url(images/tab_bottom.png);}.jxBarTop a.jxTabClose,.jxBarBottom a.jxTabClose{top:5px;right:5px;}.jxBarTop .jxTabClose span.jxTabContent,.jxBarBottom .jxTabClose span.jxTabContent{padding-right:16px;}.jxBarTop a.jxTab,.jxBarBottom a.jxTab{padding-left:4px;background-position:left -24px;}.jxBarTop span.jxTabContent,.jxBarBottom span.jxTabContent{padding:4px 4px 4px 0;background-position:right -24px;}.jxBarTop a.jxTabActive,.jxBarBottom a.jxTabActive{background-position:left -72px;}.jxBarTop a.jxTabActive span.jxTabContent,.jxBarBottom a.jxTabActive span.jxTabContent{background-position:right -72px;}.jxBarTop a.jxTab:focus,.jxBarBottom a.jxTab:focus{background-position:left -96px;}.jxBarTop a.jxTab:focus span.jxTabContent,.jxBarBottom a.jxTab:f
 ocus span.jxTabContent{background-position:right -96px;}.jxBarTop a.jxTabActive:focus,.jxBarBottom a.jxTabActive:focus{background-position:left -144px;}.jxBarTop a.jxTabActive:focus span.jxTabContent,.jxBarBottom a.jxTabActive:focus span.jxTabContent{background-position:right -144px;}.jxBarTop a.jxTab:hover,.jxBarTop a.jxTabActive:hover,.jxBarBottom a.jxTab:hover,.jxBarBottom a.jxTabActive:hover{background-position:left -48px;}.jxBarTop a.jxTab:hover span.jxTabContent,.jxBarTop a.jxTabActive:hover span.jxTabContent,.jxBarBottom a.jxTab:hover span.jxTabContent,.jxBarBottom a.jxTabActive:hover span.jxTabContent{background-position:right -48px;}.jxBarTop a.jxTabPressed,.jxBarTop a.jxTabPressed:focus,.jxBarBottom a.jxTabPressed,.jxBarBottom a.jxTabPressed:focus{background-position:left -120px;}.jxBarTop a.jxTabPressed span.jxTabContent,.jxBarTop a.jxTabPressed:focus span.jxTabContent,.jxBarBottom a.jxTabPressed span.jxTabContent,.jxBarBottom a.jxTabPressed:focus span.jxTabConten
 t{background-position:right -120px;}.jxBarTop .jxDisabled a.jxTab:focus,.jxBarTop .jxDisabled a.jxTab:active,.jxBarTop .jxDisabled a.jxTab:hover,.jxBarTop .jxDisabled a.jxTabPressed,.jxBarBottom .jxDisabled a.jxTab:focus,.jxBarBottom .jxDisabled a.jxTab:active,.jxBarBottom .jxDisabled a.jxTab:hover,.jxBarBottom .jxDisabled a.jxTabPressed{background-position:left -24px;}.jxBarTop .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarTop .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabPressed span.jxTabContent{background-position:right -24px;}.jxBarTop .jxDisabled a.jxTabActive:focus,.jxBarTop .jxDisabled a.jxTabActive:active,.jxBarTop .jxDisabled a.jxTabActive:hover,.jxBarBot
 tom .jxDisabled a.jxTabActive:focus,.jxBarBottom .jxDisabled a.jxTabActive:active,.jxBarBottom .jxDisabled a.jxTabActive:hover{background-position:left -72px;}.jxBarTop .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarTop .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarBottom .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:right -72px;}.jxBarTop img.jxTabIcon,.jxBarBottom img.jxTabIcon{vertical-align:middle;}.jxBarTop span.jxTabLabel,.jxBarBottom span.jxTabLabel{vertical-align:middle;height:16px;padding:0 4px 0 4px;}.jxTabBarLeft .jxBarWrapper,.jxTabBarRight .jxBarWrapper{padding-top:2px;}.jxBarLeft span.jxTabContainer,.jxBarRight span.jxTabContainer{margin-bottom:-1px;padding:0 2px;}.jxBarLeft a.jxTab,.jxBarLeft span.jxTabContent{background-image:url(im
 ages/tab_left.png);}.jxBarRight a.jxTab,.jxBarRight span.jxTabContent{background-image:url(images/tab_right.png);}.jxBarLeft a.jxTabClose,.jxBarRight a.jxTabClose{top:5px;left:5px;}.jxBarLeft .jxTabClose span.jxTabContent,.jxBarRight .jxTabClose span.jxTabContent{padding-top:16px;}.jxBarLeft a.jxTab,.jxBarRight a.jxTab{padding-top:4px;background-position:-24px top;}.jxBarLeft span.jxTabContent,.jxBarRight span.jxTabContent{padding:0 4px 4px 4px;background-position:-24px bottom;}.jxBarLeft a.jxTabActive,.jxBarRight a.jxTabActive{background-position:-72px top;}.jxBarLeft a.jxTabActive span.jxTabContent,.jxBarRight a.jxTabActive span.jxTabContent{background-position:-72px bottom;}.jxBarLeft a.jxTab:focus,.jxBarRight a.jxTab:focus{background-position:-96px top;}.jxBarLeft a.jxTab:focus span.jxTabContent,.jxBarRight a.jxTab:focus span.jxTabContent{background-position:-96px bottom;}.jxBarLeft a.jxTabActive:focus,.jxBarRight a.jxTabActive:focus{background-position:-144px top;}.jxBa
 rLeft a.jxTabActive:focus span.jxTabContent,.jxBarRight a.jxTabActive:focus span.jxTabContent{background-position:-144px bottom;}.jxBarLeft a.jxTab:hover,.jxBarLeft a.jxTabActive:hover,.jxBarRight a.jxTab:hover,.jxBarRight a.jxTabActive:hover{background-position:-48px top;}.jxBarLeft a.jxTab:hover span.jxTabContent,.jxBarLeft a.jxTabActive:hover span.jxTabContent,.jxBarRight a.jxTab:hover span.jxTabContent,.jxBarRight a.jxTabActive:hover span.jxTabContent{background-position:-48px bottom;}.jxBarLeft a.jxTabPressed,.jxBarLeft a.jxTabPressed:focus,.jxBarRight a.jxTabPressed,.jxBarRight a.jxTabPressed:focus{background-position:-120px top;}.jxBarLeft a.jxTabPressed span.jxTabContent,.jxBarLeft a.jxTabPressed:focus span.jxTabContent,.jxBarRight a.jxTabPressed span.jxTabContent,.jxBarRight a.jxTabPressed:focus span.jxTabContent{background-position:-120px bottom;}.jxBarLeft .jxDisabled a.jxTab:focus,.jxBarLeft .jxDisabled a.jxTab:active,.jxBarLeft .jxDisabled a.jxTab:hover,.jxBarLe
 ft .jxDisabled a.jxTabPressed,.jxBarRight .jxDisabled a.jxTab:focus,.jxBarRight .jxDisabled a.jxTab:active,.jxBarRight .jxDisabled a.jxTab:hover,.jxBarRight .jxDisabled a.jxTabPressed{background-position:-24px top;}.jxBarLeft .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabPressed span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTab:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabPressed span.jxTabContent{background-position:-24px bottom;}.jxBarLeft .jxDisabled a.jxTabActive:focus,.jxBarLeft .jxDisabled a.jxTabActive:active,.jxBarLeft .jxDisabled a.jxTabActive:hover,.jxBarRight .jxDisabled a.jxTabActive:focus,.jxBarRight .jxDisabled a.jxTabActive:active,.jxBarRight .jxDisabled a.jxTabActive:hover{background-position:-72px top
 ;}.jxBarLeft .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarLeft .jxDisabled a.jxTabActive:hover span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:focus span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:active span.jxTabContent,.jxBarRight .jxDisabled a.jxTabActive:hover span.jxTabContent{background-position:-72px bottom;}.jxBarLeft span.jxTabLabel,.jxBarRight span.jxTabLabel{padding:4px 0 4px 0;}.jxBarContainer{display:block;position:relative;z-index:1;overflow:hidden;margin:0;padding:0;border:0;background-color:#f0f0f0;}.jxBarTop,.jxBarBottom{width:100%;height:28px;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;overflow:hidden;}.jxTabBox .jxTabBarTop{background-image:url(images/tabbar.png);background-position:0 bottom;}.jxTabBox .jxTabBarBottom{background-image:url(images/tabbar_bottom.png);background-position:0 top;}.jxBarLeft,.jxBarRight{width:a
 uto;height:100%;background-image:url(images/toolbar.png);background-repeat:repeat-x;background-position:0 0;float:left;overflow:hidden;}.jxTabBox .jxTabBarLeft{background-image:url(images/tabbar_left.png);background-repeat:repeat-y;background-position:right 0;}.jxTabBox .jxTabBarRight{background-image:url(images/tabbar_right.png);background-repeat:repeat-y;background-position:left 0;}.jxBarTop .jxBarScroller,.jxBarBottom .jxBarScroller{float:left;height:28px;overflow:hidden;z-index:0;}.jxBarTop .jxBarScroller .jxBarWrapper,.jxBarBottom .jxBarScroller .jxBarWrapper{float:left;height:28px;overflow:hidden;width:10000%;}.jxBarTop .jxBarControls .jxButtonContainer,.jxBarBottom .jxBarControls .jxButtonContainer{z-index:1;padding:2px 0;margin-left:-1px;}.jxBarTop .jxBarScrollLeft img.jxButtonIcon,.jxBarBottom .jxBarScrollLeft img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -80px;}.jxBarTop .jxBarScrollRight img.jxButtonIcon,.jxBarBottom .jxBarScrollR
 ight img.jxButtonIcon{background-image:url(images/emblems.png);background-position:0 -96px;}.jxBarControls{float:right;position:relative;font-size:0;line-height:0;}ul.jxToolbar,ul.jxTabBar{display:block;position:relative;float:left;clear:none;list-style-type:none;margin:0;padding:0;border:none;}li.jxToolItem{display:block;position:relative;float:left;font-size:0;line-height:0;white-space:nowrap;padding:0;margin:0;border:none;}li.jxToolItem .jxInputWrapper{white-space:nowrap;}li.jxToolItem span.jxBarSeparator{display:block;position:relative;float:left;font-size:0;line-height:0;border:0;margin:0;padding:4px;background-repeat:no-repeat;background-position:center center;}.jxBarTop li.jxToolItem span.jxBarSeparator,.jxBarBottom li.jxToolItem span.jxBarSeparator{width:8px;height:20px;background-image:url(images/toolbar_separator_h.png);}.jxBarLeft li.jxToolItem span.jxBarSeparator,.jxBarRight li.jxToolItem span.jxBarSeparator{width:20px;height:8px;background-image:url(images/toolb
 ar_separator_v.png);}.jxBarLeft ul.jxToolbar,.jxBarLeft ul.jxTabBar,.jxBarLeft li.jxToolItem,.jxBarRight ul.jxToolbar,.jxBarRight ul.jxTabBar,.jxBarRight li.jxToolItem{clear:both;}.jxToolbarAlignLeft ul{float:left;}.jxToolbarAlignRight ul{float:right;}.jxToolbarAlignCenter{text-align:center;}.jxToolbarAlignCenter ul{float:none;}.jxToolbarAlignCenter ul li{float:none;display:inline;}.jxTooltip{width:auto;height:auto;background-color:black;color:white;padding:5px;z-index:65536;}.jxTree,.jxTreeRoot{position:relative;display:block;list-style:none;margin:0;padding:0;}.jxTreeNest{list-style:none;margin:0;padding:0;background-repeat:repeat-y;background-position:left top;}li.jxTreeContainer{position:relative;display:block;margin:0;padding:0;background-repeat:no-repeat;background-position:left top;white-space:nowrap;font-size:0;line-height:0;user-select:none;-moz-user-select:none;-khtml-user-select:none;}.jxTree li.jxTreeContainer{margin-left:16px;}a.jxTreeItem{position:relative;disp
 lay:block;cursor:pointer;outline:none;overflow:hidden;background-image:url(images/tree_hover.png);background-repeat:repeat-x;background-position:left top;border:none;margin:0 1px 0 17px;padding:0 0 0 20px;z-index:0;font-family:Arial,Helvetica,sans-serif;font-size:11px;color:#000;text-decoration:none;line-height:20px;height:20px;}a.jxTreeItem:focus{border-left:1px dotted #75ADFF;border-right:1px dotted #75ADFF;margin:0 0 0 16px;background-position:left -72px;}a.jxTreeItem:hover,li.jxTreeContainer a.jxHover{border-left:1px solid #CDDFFD;border-right:1px solid #CDDFFD;margin:0 0 0 16px;background-color:#CDE5FF;background-position:left -24px;}li.jxTreeContainer a.jxSelected,li.jxTreeContainer a.jxSelected:hover,li.jxTreeContainer a.jxPressed,li.jxTreeContainer a.jxPressed:hover{border-left:1px solid #8AABFB;border-right:1px solid #8AABFB;margin:0 0 0 16px;background-color:#CDE5FF;background-position:left -48px;}li.jxDisabled a.jxTreeItem{cursor:default;}li.jxDisabled a.jxTreeIte
 m:focus,li.jxDisabled a.jxTreeItem:hover{background-position:left top;background-color:transparent;border:none;margin:0 1px 0 17px;}.jxTreeNest{background-image:url(images/tree_vert_line.png);}img.jxTreeImage,img.jxTreeIcon{position:absolute;display:inline;left:0;top:0;width:16px;height:20px;z-index:1;background-image:url(images/tree.png);background-repeat:no-repeat;border:0;margin:0;}img.jxTreeIcon{height:16px;top:2px;left:1px;}.jxTreeBranchOpen .jxTreeIcon,.jxTreeBranchLastOpen .jxTreeIcon{background-position:left -40px;}.jxTreeBranchOpen .jxTreeImage{background-position:left -100px;}.jxTreeBranchLastOpen .jxTreeImage{background-position:left -160px;}.jxTreeBranchClosed .jxTreeIcon,.jxTreeBranchLastClosed .jxTreeIcon{background-position:left -20px;}.jxTreeBranchClosed .jxTreeImage{background-position:left -80px;}.jxTreeBranchLastClosed .jxTreeImage{background-position:left -140px;}.jxTreeLeaf .jxTreeIcon,.jxTreeLeafLast .jxTreeIcon{background-position:left 0;}.jxTreeLeaf .
 jxTreeImage{background-position:left -60px;}.jxTreeLeafLast .jxTreeImage{background-position:left -120px;}a.jxTreeItem,img.jxTreeImage,img.jxTreeIcon,span.jxTreeLabel,.jxTreeItemContainer input{vertical-align:middle;}img.jxTreeImage.jxBusy{background-image:url(images/spinner_16.gif);background-position:left top;}.jxFileUploadPanel{padding:5px;}.jxUploadQueue li{display:block;position:relative;overflow:hidden;padding:2px;}.jxUploadQueue div span{display:block;}.jxUploadQueue li span.jxUploadFileName{font-size:12px;line-height:16px;padding-left:2px;}.jxUploadQueue li span.jxUploadFileDelete,.jxUploadQueue li span.jxUploadFileProgress,.jxUploadQueue li span.jxUploadFileComplete,.jxUploadQueue li span.jxUploadFileError{position:absolute;top:2px;right:2px;width:16px;height:16px;background-repeat:no-repeat;cursor:pointer;}.jxUploadQueue li span.jxUploadFileDelete{background-image:url('images/icons.png');background-position:0 -128px;}.jxUploadQueue li span.jxUploadFileProgress{back
 ground-image:url('images/spinner_16.gif');background-position:top left;}.jxUploadQueue li span.jxUploadFileComplete{background-image:url('images/icons.png');background-position:0 -48px;}.jxUploadQueue li span.jxUploadFileError{background-image:url('images/icons.png');background-position:0 -32px;}.jxUploadFileErrorTip{padding:4px 4px 4px 20px;border:2px solid #ddd;background:url("images/icons.png") no-repeat 0 -32px;color:black;width:100px;}
\ No newline at end of file

Modified: trunk/templates/mapserver/standard/themes/delicious/jxtheme.uncompressed.css
===================================================================
--- trunk/templates/mapserver/standard/themes/delicious/jxtheme.uncompressed.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/templates/mapserver/standard/themes/delicious/jxtheme.uncompressed.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -13,7 +13,7 @@
 h1,h2,h3,h4,h5,h6{font-size:100%;}
 q:before,q:after{content:'';}/**
  * @project         Jx
- * @revision        $Id: button.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: button.css 593 2009-11-09 20:29:54Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -25,11 +25,14 @@
    Buttons can use the sliding door technique with background images to horizontally
    accomodate icons with labels. */
 
-div.jxButtonContainer {
+.jxButtonContainer {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  font-size: 0px;
+  line-height: 0px;
+  /* float: left; */
 
   margin: 0px;
   padding: 2px;
@@ -39,9 +42,12 @@
 /* normal button */
 .jxButton {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  font-size: 0px;
+  line-height: 0px;
+  /* float: left; */
 
   /* Using background images, the A contains the left side of the background */
   margin: 0px; /* margins don't seem to work properly in IE */
@@ -69,9 +75,10 @@
 
 span.jxButtonContent {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  /* float: left; */
   font-size: 0px;
   line-height: 0px;
 
@@ -207,8 +214,11 @@
 
 img.jxButtonIcon {
   /* Base setup */
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  vertical-align: middle;
+  /* float: left; */
 
   width: 16px;
   height: 16px;
@@ -218,9 +228,11 @@
 
 span.jxButtonContent span {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
-  float: left;
+  vertical-align: middle;
+  /* float: left; */
   cursor: pointer;
 
   font-family: Arial, Helvetica, sans-serif;
@@ -241,19 +253,11 @@
 /* JX BUTTON EXTENSION STYLES */
 /* ========================== */
 
-.jxButtonMenu span.jxButtonContent,
-.jxButtonMulti span.jxButtonContent,
-.jxButtonFlyout span.jxButtonContent,
-.jxButtonCombo span.jxButtonContent,
-.jxButtonEditCombo span.jxButtonContent {
+.jxDiscloser span.jxButtonContent {
   padding-right: 0px;
 }
 
-.jxButtonMenu span.jxButtonContent span,
-.jxButtonFlyout span.jxButtonContent span,
-.jxButtonMulti span.jxButtonContent span,
-.jxButtonCombo span.jxButtonContent span,
-.jxButtonEditCombo span.jxButtonContent span {
+.jxDiscloser span.jxButtonContent span {
   padding-right: 16px;
   background-image: url(images/emblems.png);
   background-position: right -16px;
@@ -262,7 +266,8 @@
 
 a.jxButtonDisclose {
   position: absolute;
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   padding: 4px 0px;
   font-size: 0px;
   line-height: 0px;
@@ -390,7 +395,7 @@
   background-color: transparent;
 }/**
  * @project         Jx
- * @revision        $Id: chrome.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: chrome.css 454 2009-06-03 14:50:22Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -463,37 +468,37 @@
   position: absolute; 
   top: 0px; 
   left: 0px;
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeTR img { 
   position: absolute; 
   top: 0px; 
   right: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeBL img { 
   position: absolute; 
   bottom: 0px; 
   left: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 .jxChromeBR img { 
   position: absolute; 
   bottom: 0px; 
   right: 0px; 
-  width: 800px;
-  height:500px;
+  width: 1000px;
+  height: 600px;
 }
 
 /**
  * @project         Jx
- * @revision        $Id: color.css 424 2009-05-12 12:51:44Z pagameba $
+ * @revision        $Id: color.css 423 2009-05-12 12:37:56Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -664,7 +669,7 @@
     height: 16px;
 }/**
  * @project         Jx
- * @revision        $Id: common.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: common.css 736 2010-03-05 16:04:56Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -673,12 +678,99 @@
 /* COMMON STYLES */
 /* ============= */
 
-.jxClearer{display:block;position:relative;float:none;clear:both;font-size:0;line-height:0;width:0;height:0;margin:0;padding:0;}
-.jxDisabled{opacity:0.4;-ms-filter: "Alpha(opacity=40)";}
-.jxDisabled * {-ms-filter: "Alpha(opacity=40)";}
-iframe.jxIframeShim{position:absolute;top:0px;left:0px;width:100%;height:100%;opacity:0;-ms-filter: "Alpha(opacity=0)";z-index:-1;}/**
+.jxClearer {
+  display: block;
+  position: relative;
+  float: none;
+  clear: both;
+  font-size: 0;
+  line-height: 0;
+  width: 0;
+  height: 0;
+  margin: 0;
+  padding: 0;
+}
+
+.jxDisabled {
+  opacity: 0.4;
+  -ms-filter: "Alpha(opacity=40)";
+}
+
+.jxDisabled * {
+  -ms-filter: "Alpha(opacity=40)";
+}
+
+/* ============= */
+/*  MASK STYLES  */
+/* ============= */
+
+.jxMask {
+  opacity: 0.5;
+  -ms-filter: "Alpha(opacity=50)";
+  background-color: #fff;
+}
+
+.jxModalMask {
+  background-color: #000;
+  opacity: 0.2;
+  -ms-filter: "Alpha(opacity=20)";
+}
+
+.jxEventMask {
+  background-image: url(images/a_pixel.png);
+}
+
+
+.jxSpinner {
+  position: absolute;
+  opacity: 0.5;
+  -ms-filter: "Alpha(opacity=50)";
+  z-index: 999;
+  background: #fff;
+}
+
+/* .jxSpinnerContent { } */
+
+.jxSpinnerMessage {
+  text-align: center;
+  font-weight: bold;
+}
+
+.jxSpinnerSmall .jxSpinnerMessage {
+  margin: 0px;
+  padding: 0px;
+  font-size: 11px;
+  line-height: 24px;
+}
+
+.jxSpinnerLarge .jxSpinnerImage {
+  background: url(images/spinner_24.gif) no-repeat;
+  width: 24px;
+  height: 24px;
+  margin: 0 auto;
+}
+
+.jxSpinnerSmall .jxSpinnerImage {
+  background: url(images/spinner_16.gif) no-repeat;
+  width: 16px;
+  height: 16px;
+  margin-right: 4px;
+  display: inline-block;
+  vertical-align: middle;
+}
+
+/*iframe.jxIframeShim { }*/
+
+/**
+ * Confirm and Prompt dialog classes
+ */
+.jxConfirmQuestion, .jxPrompt {
+    text-align: center;
+    padding: 10px;
+    margin-top: 10px;
+}/**
  * @project         Jx
- * @revision        $Id: dialog.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: dialog.css 732 2010-03-05 14:38:36Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -696,6 +788,9 @@
   display: block;
   z-index: 1000;
   overflow: hidden;
+  
+  /* initial state is hidden */
+  visibility: hidden;
 }
 
 .jxDialogContentContainer {
@@ -705,20 +800,6 @@
   background-color: #f0f0f0;
 }
 
-.jxDialogModal {
-  /* Base setup */
-  position: absolute;
-  display: block;
-  top: 0px;
-  left: 0px;
-  width: 100%;
-  height: 100%;
-
-  background-color: #000;
-  opacity: .2;
-  -ms-filter: "Alpha(opacity=20)";
-}
-
 .jxDialogContent {
   /* Base setup */
   display: block;
@@ -839,6 +920,14 @@
   background-position: 0px -16px;
 }
 
+.jxDialogMaximize img {
+  background-position: 0px -80px;
+}
+
+.jxDialogMaximized .jxDialogMaximize img {
+  background-position: 0px -96px;
+}
+
 .jxDialogLoading img {
   border: 0px;
   /* the margin needs to make up the difference between it's width/height
@@ -859,15 +948,6 @@
 /* ========================= */
 
 .jxDialogControls .jxButtonContainer,
-.jxDialogControls span.jxButtonContent,
-.jxDialogControls .jxButton:hover span.jxButtonContent,
-.jxDialogControls .jxButton:active span.jxButtonContent,
-.jxDialogControls .jxButtonActive span.jxButtonContent,
-.jxDialogControls .jxButtonActive:hover span.jxButtonContent,
-.jxDialogControls .jxButtonActive:active span.jxButtonContent,
-.jxDialogControls .jxDisabled .jxButton span.jxButtonContent,
-.jxDialogControls .jxDisabled .jxButton:hover span.jxButtonContent,
-.jxDialogControls .jxDisabled .jxButton:active span.jxButtonContent,
 .jxDialogControls .jxButton,
 .jxDialogControls .jxButton:hover,
 .jxDialogControls .jxButton:active ,
@@ -885,16 +965,6 @@
 }
 
 
-.jxDialogControls .jxButtonMenu span.jxButtonContent,
-.jxDialogControls .jxButtonFlyout span.jxButtonContent {
-  background-image: none;
-}
-
-.jxDialogControls .jxButtonMenu span.jxButtonContent span,
-.jxDialogControls .jxButtonFlyout span.jxButtonContent span {
-  padding-right: 0px;
-}
-
 /* ========================== */
 /* JX TOOLBAR STYLES OVERIDES */
 /* ========================== */
@@ -929,9 +999,50 @@
   margin: 0px;
   padding: 0px;
   border: none;
+}/**
+ * File Input classes
+ */
+div.jxFileInputs {
+    position: relative;
+}
+
+div.jxFileFake {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    z-index: 1;
+}
+
+div.jxFileFake span {
+    float: left;
+    display: block;
+}
+
+div.jxFileFake .jxInputContainer {
+    width: 150px;
+}
+
+div.jxFileFake .jxInputText {
+    width: 135px;
+} 
+
+div.jxFileFake .jxButtonContainer {
+    margin-top: 2px;
+    float: left;
+}
+
+.jxInputFile {
+    position: relative;
+    text-align: right;
+    -moz-opacity: 0;
+    filter: alpha(opacity:0);
+    opacity: 0;
+    z-index: 2;
+    margin-top: -5px;
+    height: 35px;
 }/**
  * @project         Jx
- * @revision        $Id: form.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: form.css 827 2010-04-01 14:22:49Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -939,98 +1050,441 @@
 /* =========== */
 /* FORM STYLES */
 /* =========== */
-/* JxForm classes are a set of styles that can be used forlaying out forms 
- * There are three different types of layouts: Inline, Float and Block.
- * Each can be usedto layout an entire form, or a portion of the form as is needed
+/* JxForm classes are a set of styles that can be used for laying out forms 
+ * There are three different types of layouts: Inline, Inlineblock and Block.
+ * Each can be used to layout an entire form, a fieldset or an individual input.
  */
 
- .jxForm {
+/* debuggiong styles */
+/*
+.jxForm           { background-color: yellow; }
+.jxFieldset       { background-color: khaki; }
+.jxInputGroup     { background-color: tan; }
+.jxFieldsetLegend { background-color: orange; }
+.jxInputContainer { background-color: pink; }
+.jxInputLabel     { background-color: plum; }
+.jxInputTag       { background-color: lime; }
+*/
+
+ /* Base and Typography Styles */
+ 
+.jxForm {
   display: block;
   position: relative;
-  overflow: hidden; /* ensures that floated elements are contained */
   font-family: Arial, Helvetica, sans-serif;
   font-size: 12px;
-  line-height: 18px;
+  line-height: 16px;
+  color: #000;
 }
 
-.jxForm label {
-  background-color: transparent;
-  color: #000;
+.jxFieldset {
+  display: block;
+  position: relative;
+  border: 1px solid #ccc;
+  margin: 10px;
+  padding: 5px;
 }
 
-.jxForm select, 
-.jxForm input {
+.jxFieldsetLegend,
+.jxFieldset legend {
+  position: relative;
+  margin: 0px;
+  padding: 0px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 14px;
+  line-height: 26px;
   color: #000;
 }
 
-.jxFormFloat .radioGroup {
+.jxInputContainer {
   display: block;
-  overflow: hidden;
+  position: relative;
+  padding: 0px;
+  margin: 2px;
+  border: none;
 }
 
-/* wrap the radio button in a label */
-.jxFormFloat .radioGroup input,
-.jxFormFloat .radioGroup label {
-  width: auto !important;
-  vertical-align: middle;
+.jxInputLabel,
+.jxInputTag {
+  display: -moz-inline-box;
+  display: inline-block;
+  margin: 0px;
+  padding: 0px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 26px;
+  color: #000;
 }
 
-.jxForm .tipArea {
-  display: block;
-  padding: 5px 0px;
+.jxInputLabel {
+  vertical-align: top;
+}
+
+.jxInputTag {
+  vertical-align: bottom;
+}
+
+.jxInputWrapper {
+  display: inline-block;
+  white-space: normal;
+  position: relative;
+}
+
+.jxInputText,
+.jxInputPassword,
+.jxInputTextarea,
+.jxInputCombo,
+.jxInputColor {
+  margin: 0px 4px;
+  padding: 4px;
+  border: 1px solid #bbb;
+  /* overall width is 250px, margins+padding+border is 18px */
+  width: 232px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputCombo,
+.jxInputColor {
+  padding: 4px 20px 4px 20px;
+  /* overall width is 250px, margins+padding+border is 50px */
+  width: 200px;
+}
+
+.jxInputIconHidden .jxInputCombo {
+  /* overall width is 250px, margins+padding+border is 34px */
+  padding-left: 4px;
+  width: 216px;
+}
+
+.jxInputIcon {
+  position: absolute;
+  width: 16px;
+  height: 16px;
+  left: 0px;
+  top: 0px;
+  margin: 4px 4px 4px 8px;
+}
+
+.jxInputContainerColor .jxInputIcon {
+  border: 1px solid #bbb;
+  width: 15px;
+  height: 15px;
+}
+
+.jxInputIconHidden .jxInputIcon {
+  display: none;
+}
+
+.jxInputRevealer {
+  position: absolute;
+  width: 16px;
+  height: 16px;
+  right: 0px;
+  top: 0px;
+  margin: 4px 8px 4px 4px;
+  font-size: 0px;
+  line-height: 0px;
+}
+
+img.jxInputRevealerIcon {
+  background-image: url(images/emblems.png);
+  background-position: right -16px;
+  background-repeat: no-repeat;
+}
+
+.jxInputRevealer .jxButtonContainer,
+.jxInputRevealer .jxButton {
+  padding: 0px;
+  margin: 0px;
+  border: 0px;
   background-color: transparent;
-  color: #999;
+  background-image: none;
 }
 
+.jxInputSelect {
+  margin: 0px 4px;
+  padding: 3px 4px 3px 1px;
+  border: 1px solid #bbb;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputRadio,
+.jxInputCheck {
+  margin: 5px;
+  font-family: Arial, Helvetica, sans-serif;
+  font-size: 12px;
+  line-height: 16px;
+  color: #000;
+}
+
+.jxInputText:focus,
+.jxInputPassword:focus,
+.jxInputTextarea:focus,
+.jxInputSelect:focus,
+.jxInputCombo:focus,
+.jxInputColor:focus {
+  border: 1px solid #000;
+}
+
+.jxInputContainer .jxButtonContainer {
+  padding: 0px;
+  margin: 0px 4px;
+}
+
+/* Input Group */
+
+.jxInputGroup {
+  border: none;
+  padding: 0px;
+  margin: 2px;
+}
+
+.jxInputGroup legend {
+  font-size: 0px;
+  line-height: 0px;
+  padding: 0px;
+  margin: 0px;
+  border: none;
+}
+
+.jxInputGroup .jxFieldsetLegend {
+  font-size: 12px;
+}
+
+.jxInputGroup .jxInputLabel {
+  width: auto;
+}
+
+/* Field Validation */
+
+.jxFieldError .jxInputText,
+.jxFieldError .jxInputPassword,
+.jxFieldError .jxInputTextarea,
+.jxFieldError .jxInputSelect,
+.jxFieldError .jxInputCombo,
+.jxFieldError .jxInputColor {
+  background-color: #FBE3E4; 
+  color: #8a1f11; 
+  border-color: #FBC2C4;
+}
+
+.jxFieldSuccess .jxInputText,
+.jxFieldSuccess .jxInputPassword,
+.jxFieldSuccess .jxInputTextarea,
+.jxFieldSuccess .jxInputSelect,
+.jxFieldSuccess .jxInputCombo,
+.jxFieldSuccess .jxInputColor {
+  background-color: #E6EFC2; 
+  color: #264409; 
+  border-color: #C6D880;
+}
+
+.jxFieldError .jxInputText:focus,
+.jxFieldError .jxInputPassword:focus,
+.jxFieldError .jxInputTextarea:focus,
+.jxFieldError .jxInputSelect:focus,
+.jxFieldError .jxInputCombo:focus,
+.jxFieldError .jxInputColor:focus {
+  border-color: #8a1f11;
+}
+
+.jxFieldSuccess .jxInputText:focus,
+.jxFieldSuccess .jxInputPassword:focus,
+.jxFieldSuccess .jxInputTextarea:focus,
+.jxFieldSuccess .jxInputSelect:focus,
+.jxFieldSuccess .jxInputCombo:focus,
+.jxFieldSuccess .jxInputColor:focus {
+  border-color: #264409;
+}
+
+.jxFieldError .jxInputLabel,
+.jxFieldError .jxInputTag {
+  color: #8a1f11; 
+}
+
+.jxFieldSuccess .jxInputLabel,
+.jxFieldSuccess .jxInputTag {
+  color: #264409; 
+}
+
+
+/* For Reference
+   Success, notice and error boxes from Blueprint */
+
+/* 
+.error      { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
+.notice     { background: #FFF6BF; color: #514721; border-color: #FFD324; }
+.success    { background: #E6EFC2; color: #264409; border-color: #C6D880; }
+.error a    { color: #8a1f11; }
+.notice a   { color: #514721; }
+.success a  { color: #264409; }
+*/
+
+
+
 /* INLINE FORM 
- * Wrap an Inline Form with .jxFormInline to sets up form elements to work as 
- * inline objects like they do by default */
+ * Sets up form elements to work as inline objects like they do by default
+ * These styles rely on increasing specificity to provide overrides */
 
-.jxFormInline label {
+/* Inline Input Container */
+.jxFormInline .jxInputContainer,
+form .jxFormInline .jxInputContainer,
+form .jxFieldset span.jxFormInline,
+form.jxForm span.jxFormInline {
   display: inline;
 }
 
-/* FLOAT FORM 
- * Wrap a Floating Form with .jxFormFloat to sets up form elements to work as 
- * block objects so labels can have set widths to simulate a 2 column display 
- * for label / input pairs. */
+/* Inline Label */
+.jxFormInline .jxInputLabel,
+form .jxFormInline .jxInputLabel,
+form span.jxFormInline .jxInputLabel {
+  display: inline;
+  width: auto;
+}
 
-.jxFormFloat label {
+/* Inline Tag */
+.jxFormInline .jxInputTag,
+form .jxFormInline .jxInputTag,
+form span.jxFormInline .jxInputTag {
+  display: inline;
+}
+
+/* Inline Input Group */
+.jxFormInline .jxInputGroup,
+form .jxFormInline .jxInputGroup {
+  padding-left: 0px;
+}
+
+.jxFormInline .jxInputGroup .jxFieldsetLegend,
+form .jxFormInline .jxInputGroup .jxFieldsetLegend {
+  position: relative;
+  left: auto;
+}
+
+.jxFormInline .jxInputGroup .jxInputLabel,
+form .jxFormInline .jxInputGroup .jxInputLabel {
+  display: inline;
+  width: auto;
+}
+
+
+/* BLOCK FORM 
+ *  Sets up form elements to work as block objects so they can appear stacked */
+
+/* Block Input Container */
+.jxFormBlock .jxInputContainer,
+form .jxFormBlock .jxInputContainer,
+form .jxFieldset span.jxFormBlock,
+form.jxform span.jxFormBlock {
   display: block;
-  float: left;
-  clear: left;
-  width: 75px; /* Overide the width depending on the form layout */
 }
+ 
+/* Block Label */
+.jxFormBlock .jxInputLabel,
+form .jxFormBlock .jxInputLabel,
+form span.jxFormBlock .jxInputLabel {
+  display: block;
+  width: auto;
+  margin-left: 4px;
+}
 
-.jxFormFloat select, 
-.jxFormFloat input {
-  float: left;
+/* Checks and Radios Label
+ * Most inputs are preceeded by their labels, but in the case of radio buttons
+ * and checkboxes, the inputs are followed by their labels */
+.jxFormBlock .jxInputContainerCheck .jxInputLabel,
+.jxFormBlock .jxInputContainerRadio .jxInputLabel,
+form .jxFormBlock .jxInputContainerCheck .jxInputLabel,
+form .jxFormBlock .jxInputContainerRadio .jxInputLabel,
+form span.jxFormBlock .jxInputContainerCheck .jxInputLabel,
+form span.jxFormBlock .jxInputContainerRadio .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
 }
 
-.jxFormFloat .radioGroup {
-  float: left;
+/* Block Input Group */
+.jxFormBlock .jxInputGroup,
+form .jxFormBlock .jxInputGroup {
+  padding-left: 0px;
 }
 
-/* wrap the radio button in a label */
-.jxFormFloat .radioGroup input,
-.jxFormFloat .radioGroup label {
-  float: none;
+.jxFormBlock .jxInputGroup .jxFieldsetLegend,
+form .jxFormBlock .jxInputGroup .jxFieldsetLegend {
+  position: relative;
+  left: auto;
 }
 
-.jxFormFloat .tipArea {
-  clear: left;
+.jxFormBlock .jxInputGroup .jxInputLabel,
+form .jxFormBlock .jxInputGroup .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: auto;
 }
 
-/* BLOCK FORM 
- *  Wrap a Block Form with .jxFormBlock to sets up form elements to work as 
- * block objects so they can label / input pairs */
 
-.jxFormBlock label {
+/* INLINE-BLOCK FORM 
+ * Sets up form elements to work as inline-block objects so labels can have set 
+ * widths to simulate a 2 column display for label / input pairs. */
+
+/* Inline-Block Input Container */
+.jxFormInlineblock .jxInputContainer,
+form .jxFormInlineblock .jxInputContainer,
+form .jxFieldset span.jxFormInlineblock,
+form.jxForm span.jxFormInlineblock {
   display: block;
+  white-space: nowrap;
 }
-/**
+
+/* Inline-Block Label */
+.jxFormInlineblock .jxInputLabel,
+form .jxFormInlineblock .jxInputLabel,
+form span.jxFormInlineblock .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: 200px;
+}
+
+/* Inline-Block Input Group */
+.jxFormInlineblock .jxInputGroup,
+form .jxFormInlineblock .jxInputGroup {
+  padding-left: 200px;
+}
+
+.jxFormInlineblock .jxInputGroup .jxFieldsetLegend,
+form .jxFormInlineblock .jxInputGroup .jxFieldsetLegend {
+  position: absolute;
+  left: -200px; /* for ie? */
+  width: 200px;
+}
+
+.jxFormInlineblock .jxInputGroup .jxInputLabel,
+form .jxFormInlineblock .jxInputGroup .jxInputLabel {
+  display: -moz-inline-box;
+  display: inline-block;
+  width: auto;
+}
+
+/** Jx.Grid Overrides **/
+
+.jxGridCellContent .jxInputContainer,
+.jxGridCellContent .jxInputRadio,
+.jxGridCellContent .jxInputCheck {
+  display: inline;
+  position: relative;
+  border: none;
+  margin: 0px;
+  padding: 0px;
+  font-size: 0px;
+  line-height: 0px;
+  color: #000;
+}/**
  * @project         Jx
- * @revision        $Id: grid.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: grid.css 796 2010-03-26 19:56:43Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1051,47 +1505,46 @@
 }
 
 .jxGridTable {
+    width: 100%;
     position: relative;
     table-layout: fixed;
     border-collapse: collapse;
     border-style: none;
-    width: 0px;
+
     cursor: default;
-    font-family: Arial, Verdana, sans-serif;
-    font-size: 11px;
-    font-weight: normal;
 }
 
-.jxGridColHeadHide {
-    height: 0px;
-    line-height: 0px;
-    font-size: 0px;
-    background-color: #fff;
-    white-space: normal;
+.jxGridTableBody {
+    position: relative;
+    table-layout: fixed;
+    border-collapse: collapse;
+    border-style: none;
+   /* width: 100%;*/
+    cursor: default;
 }
 
-.jxGridColHeadHide p, .jxGridRowHeadHide p {
-    font-size: 0px;
-    line-height: 0px;
-    height: 0px;
-    margin: 0px;
-    padding: 0px;
-}
-
-.jxGridRowHeadHide {
-    width: 0px;
-    white-space: normal;
-}
-
 .jxGridCell {
     border-top: 0px solid #d8d8d8;
     border-right: 1px solid #d8d8d8;
     border-bottom: 1px solid #d8d8d8;
     border-left: 0px solid #d8d8d8;
     overflow: hidden;
-    padding-left: 3px;
-    padding-right: 3px;
+}
+
+.jxGridCellContent {
+    position: relative;
+    display: -moz-inline-box;
+    display: inline-block;
     overflow: hidden;
+    padding: 0px 3px;
+    overflow: hidden;
+    vertical-align: middle;
+    
+    font-family: Arial, Verdana, sans-serif;
+    font-size: 11px;
+    font-weight: normal;
+    line-height: 16px;
+    
     /* can change this to normal */
     white-space: nowrap;
     cursor: cell;
@@ -1099,9 +1552,24 @@
     text-overflow: ellipsis;
 }
 
-/* Nornal Styles */
+.jxGridColHead .jxGridCellContent {
+    padding: 0px 3px;
+    text-align: center;
+    font-weight: bold;
+    color: #333;
+    height: 100%;
+}
 
+.jxGridRowHead .jxGridCellContent {
+  text-align: center;
+  font-weight: bold;
+  color: #333;
+}
+/* Normal Styles */
+
 .jxGridColHead {
+    height: 100%;
+  
     border-top: 0px solid  #d8d8d8;
     border-right: 1px solid #d8d8d8;
     border-bottom: 1px solid #d8d8d8;
@@ -1112,9 +1580,11 @@
     background-repeat: repeat-x;
 
     text-align: center;
-    font-weight: bold;
-    color: #333;
     cursor: default;
+    padding: 0px;
+    white-space: nowrap;
+    
+    overflow: hidden;
 }
 
 .jxGridRowHead {
@@ -1128,9 +1598,9 @@
     background-repeat: repeat-y;
 
     text-align: center;
-    font-weight: bold;
-    color: #333;
     cursor: default;
+    overflow: hidden;
+    white-space: nowrap;
 }
 
 /* Alternating Row Styles */
@@ -1160,11 +1630,13 @@
     background-color: #f7f7f7;
 }
 
-.jxGridRowSelected td {
+.jxGridRowSelected td,
+.jxGridRowSelected th {
     background-color: #f7f7f7;
 }
 
-td.jxGridCellSelected {
+td.jxGridCellSelected,
+th.jxGridCellSelected {
     background-color: #ebebeb;
 }
 
@@ -1184,17 +1656,159 @@
     background-color: #e5f1ff;
 }
 
-.jxGridRowPrelight td {
+.jxGridRowPrelight td,
+.jxGridRowPrelight th {
     background-color: #e5f1ff;
 }
 
-td.jxGridCellPrelight {
+td.jxGridCellPrelight,
+th.jxGridCellPrelight {
   background-color: #cce3ff;
 }
 
+.jxGridHeader .jxColSortable img {
+    vertical-align: top;
+    width: 16px;
+    height: 16px;
+    background-image: url('images/emblems.png');
+    background-repeat: no-repeat;
+    background-position: right top;
+}
+
+.jxGridHeader .jxColSortable .jxGridCellContent {
+  margin-right: -16px; /* recenter the column heading text */
+}
+ 
+.jxGridHeader .jxGridColumnSortedAsc img {
+    background-position: right -162px;
+}
+ 
+.jxGridHeader .jxGridColumnSortedDesc img {
+    background-position: right -18px;
+}
+ 
+.jxGridColumnResize,
+.jxGridRowResize {
+    display: block;
+    position: absolute;
+    background-image: url(images/a_pixel.png);
+    z-index: 1;
+    
+}
+.jxGridColumnResize {
+    right: 0px;
+    top: 0px;
+    height: 100%;
+    width: 4px;
+    cursor: col-resize;
+}
+
+.jxColSortable .jxGridColumnResize {
+  right: 8px;
+}
+
+.jxGridRowResize {
+    left: 0px;
+    bottom: 0px;
+    height: 4px;
+    width: 100%;
+    cursor: row-resize;
+}
+
+/* Editor Styles */
+.jxGridEditorPopup {
+  min-width: 130px;
+  margin: 3px;
+  font-size: 10px;
+  box-shadow: 2px 2px 5px #888; /* i really like the boxshadow */
+  -moz-box-shadow : 2px 2px 5px #888;
+  -webkit-box-shadow : 2px 2px 5px #888;
+  /*
+  border-radius: 5px;
+  -moz-border-radius: 5px;
+  -webkit-border-radius: 5px;
+  */
+  background-color: #dddddd;
+  border: 1px solid #999999;
+  position: absolute;
+}
+
+.jxGridEditorPopupInnerWrapper {
+  position: relative;
+  height: 100%;
+  width: 100%;
+}.jxListView {
+  position:relative;
+  display: block;
+  list-style: none;
+  margin: 0px;
+  padding: 0px;
+}
+
+.jxListView .jxListItemContainer {
+    position: relative;
+    display: block;
+    outline: none;
+    overflow: hidden;
+    
+    border: none;
+    margin: 0px 1px;
+    padding: 0px;
+}
+
+.jxListItem {
+    position: relative;
+    display: block;
+    cursor: pointer;
+    outline: none;
+    overflow: hidden;
+
+    border: none;
+    margin: 0px 1px;
+    padding: 0px;
+    z-index: 0;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 11px;
+    color: #000;
+    text-decoration: none;
+    /* Line Height needs to be an even number so branches line up properly */
+    line-height: 20px;
+    height: 20px;
+}
+
+.jxListView .jxHover {
+    margin: 0px;
+    border-left: 1px solid #CDDFFD;
+    border-right: 1px solid #CDDFFD;
+    background-image: url(images/listitem.png);
+    background-repeat: repeat-x;
+    background-color: #CDE5FF;
+    background-position: left -24px;
+    
+}
+
+.jxListItem:focus {
+    margin: 0px;
+    border-left: 1px dotted #75ADFF;
+    border-right: 1px dotted #75ADFF;
+    background-image: url(images/listitem.png);
+    background-repeat: repeat-x;
+    background-position: left -72px;
+}
+
+.jxListView .jxPressed,
+.jxListView .jxSelected {
+  margin: 0px;
+  border-left: 1px solid #8AABFB;
+  border-right: 1px solid #8AABFB;
+  background-color: #CDE5FF;
+  background-image: url(images/listitem.png);
+  background-repeat: repeat-x;
+  background-position: left -48px;
+}
 /**
  * @project         Jx
- * @revision        $Id: menu.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: menu.css 601 2009-11-10 18:44:35Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1222,14 +1836,15 @@
 */
 
 .jxMenuContainer {
-  position: absolute;
-  display: block;
-  z-index: 2000;
-  padding: 0px;
+    position: absolute;
+    top: 0;
+    left: -10000px;
+    display: none;
+    z-index: 2000;
+    padding: 0px;
 }
 
-ul.jxMenu,
-ul.jxSubMenu {
+ul.jxMenu {
   /* Base setup */
   display: block;
   position: relative;
@@ -1387,7 +2002,7 @@
 }
 
 .jxMenuItemToggle img.jxMenuItemIcon,
-.jxMenuItemSet img.jxMenuItemIcon {
+.jxMenuItemToggleSet img.jxMenuItemIcon {
   background-image: url(images/emblems.png);
   background-position: 2px 0px;
   background-repeat: no-repeat;
@@ -1397,12 +2012,11 @@
   background-position: 2px -48px;
 }
 
-.jxMenuItemSet a.jxMenuItemActive img.jxMenuItemIcon {
+.jxMenuItemToggleSet a.jxMenuItemActive img.jxMenuItemIcon {
   background-position: 2px -64px;
 }
 
-ul.jxMenu span.jxMenuSeparator,
-ul.jxSubMenu span.jxMenuSeparator {
+ul.jxMenu span.jxMenuSeparator {
   /* Base setup */
   display: block;
 
@@ -1412,9 +2026,119 @@
   background-repeat: repeat-x;
   background-position: left center;
 }
-/**
+ at CHARSET "ISO-8859-1";
+
+
+/**
+ * Confirm dialog classes
+ */
+.jxMessage {
+    text-align: center;
+    padding: 10px;
+    margin-top: 10px;
+}/* For Reference
+   Success, notice and error boxes from Blueprint */
+
+/* 
+.error      { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; }
+.notice     { background: #FFF6BF; color: #514721; border-color: #FFD324; }
+.success    { background: #E6EFC2; color: #264409; border-color: #C6D880; }
+.error a    { color: #8a1f11; }
+.notice a   { color: #514721; }
+.success a  { color: #264409; }
+*/
+
+
+.jxNoticeListContainer {
+  border: none;
+  padding: 0px;
+  margin: 0px;
+  font-size: 0px;
+  line-height: 0px;
+  z-index: 500;
+}
+
+.jxNoticeList {
+    position: relative;
+}
+
+.jxNoticeItemContainer {
+    position: relative;
+    overflow: hidden;
+}
+
+.jxNoticeItemContainer .jxChrome {
+  background-image: url(images/flyout_chrome.png);
+  padding: 5px 5px 7px 6px;
+}
+
+.jxHasChrome .jxNoticeItem  {
+  margin: 6px 6px 8px 7px;
+}
+
+.jxNoticeItem {
+    position: relative;
+    border: 2px solid #ccc;
+    margin: 0px;
+    padding: 0px;
+    background-color: #f8f8f8;
+    background-image: url(images/notice.png);
+    background-repeat: repeat-x;
+    background-position: left bottom;
+}
+
+.jxNoticeIcon {
+  position: absolute;
+  top: 0px;
+  left: 0px;
+  margin: 6px;
+  width: 16px;
+  height: 16px;
+  background-image: url(images/icons.png);
+  background-repeat: no-repeat;
+  background-position: 0px 0px;
+}
+
+.jxNotice {
+    display: block;
+    position: relative;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    line-height: 18px;
+    color: #666;
+    margin: 0px;
+    border: none;
+    padding: 6px 26px 6px 10px; 
+}                          
+
+.jxNoticeError .jxNoticeItem { background-color: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; background-image: url(images/notice_error.png);}
+.jxNoticeWarning .jxNoticeItem { background-color: #FFF6BF; color: #514721; border-color: #FFD324; background-image: url(images/notice_warning.png);}
+.jxNoticeSuccess .jxNoticeItem { background-color: #E6EFC2; color: #264409; border-color: #C6D880; background-image: url(images/notice_success.png);}
+.jxNoticeInformation .jxNoticeItem { background-color: #F8F8F8; color: #666666; border-color: #CCCCCC; background-image: url(images/notice.png);}
+
+.jxNoticeError .jxNotice { color: #8a1f11; padding-left: 28px; }
+.jxNoticeWarning .jxNotice { color: #514721; padding-left: 28px; }
+.jxNoticeSuccess .jxNotice { color: #264409; padding-left: 28px; }
+.jxNoticeInformation .jxNotice { color: #666666; padding-left: 28px; }
+
+.jxNoticeError .jxNoticeIcon { background-position: 0px -32px; }
+.jxNoticeWarning .jxNoticeIcon { background-position: 0px -16px; }
+.jxNoticeSuccess .jxNoticeIcon { background-position: 0px -48px; }            
+.jxNoticeInformation .jxNoticeIcon { background-position: 0px -64px; }
+
+.jxNoticeClose {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    margin: 6px;
+    width: 16px;
+    height: 16px;
+    background-image: url(images/tab_close.png);
+    background-position: 0px 0px;
+    background-repeat: no-repeat;
+}/**
  * @project         Jx
- * @revision        $Id: panel.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: panel.css 716 2010-03-02 16:07:19Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1576,15 +2300,6 @@
 /* ========================= */
 
 .jxPanelControls .jxButtonContainer,
-.jxPanelControls span.jxButtonContent,
-.jxPanelControls .jxButton:hover span.jxButtonContent,
-.jxPanelControls .jxButton:active span.jxButtonContent,
-.jxPanelControls .jxButtonActive span.jxButtonContent,
-.jxPanelControls .jxButtonActive:hover span.jxButtonContent,
-.jxPanelControls .jxButtonActive:active span.jxButtonContent,
-.jxPanelControls .jxDisabled .jxButton span.jxButtonContent,
-.jxPanelControls .jxDisabled .jxButton:hover span.jxButtonContent,
-.jxPanelControls .jxDisabled .jxButton:active span.jxButtonContent,
 .jxPanelControls .jxButton,
 .jxPanelControls .jxButton:hover,
 .jxPanelControls .jxButton:active ,
@@ -1601,16 +2316,7 @@
   background-image: none;
 }
 
-.jxPanelControls .jxButtonMenu span.jxButtonContent,
-.jxPanelControls .jxButtonFlyout span.jxButtonContent {
-  background-image: none;
-}
 
-.jxPanelControls .jxButtonMenu span.jxButtonContent span,
-.jxPanelControls .jxButtonFlyout span.jxButtonContent span {
-  padding-right: 0px;
-}
-
 /* ========================== */
 /* JX TOOLBAR STYLES OVERIDES */
 /* ========================== */
@@ -1645,9 +2351,195 @@
   margin: 0px;
   padding: 0px;
   border: none;
-}/**
+}/**
+ * progress bar classes
+ */
+.jxProgressBar-container {
+    position: relative;
+    display: block;
+    width: 100%;
+}
+
+.jxProgressBar-message {
+    position: relative;
+    display: block;
+    color: black;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    line-height: 20px;
+}
+
+.jxProgressBar {
+    position: relative;
+    display: block;
+    width: 100%;
+    height: 20px;
+    border: none;
+    margin: 0px;
+    padding: 0px;
+}
+
+.jxProgressBar-outline {
+    position: absolute;
+    display: block;
+    top: 0px;
+    left: 0px;
+    z-index: 10;
+    height: 20px;
+    border-left: 1px solid #cbc8c8;
+    border-right: 1px solid #cbc8c8;
+    background-image: url(images/progressbar.png);
+    background-position: 0px -140px;
+    width: 100%;
+}
+
+.jxProgressBar-fill {
+    position: absolute;
+    display: block;
+    top: 0px;
+    left: 0px;
+    z-index: 20;
+    height: 20px;
+    border: none;
+    background-image: url(images/progressbar.png);
+    background-position: 0px 0px;
+}
+
+.jxProgressStarting .jxProgressBar-fill {
+    border: none;
+}
+
+.jxProgressWorking .jxProgressBar-fill {
+    border-left: 1px solid #49afe8;
+}
+
+.jxProgressFinished .jxProgressBar-fill {
+    border-left: 1px solid #49afe8;
+    border-right: 1px solid #49afe8;
+}
+
+.jxProgressBar-text {
+    position: absolute;
+    display: block;
+    overflow: visible;
+    top: 0px;
+    left: 1px;
+    width: auto;
+    height: 20px;
+    z-index: 30;
+    border: none;
+    margin: 0px;
+    padding: 0px 0px 0px 4px;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 12px;
+    line-height: 20px;
+    white-space: nowrap;
+}
+.jxHasVerticalScrollbar,
+.jxHasHorizontalScrollbar {
+    position: relative;
+    overflow: hidden;
+}
+
+.jxScrollbarChildWrapper {
+    overflow: hidden;
+}
+
+.jxHasVerticalScrollbar {
+    padding-right: 25px;
+}
+
+.jxHasVerticalScrollbar .jxScrollbarContainer {
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 20px;
+    height: 100%;
+    border: none;
+    border-left: 1px solid black;
+}
+
+.jxHasVerticalScrollbar .jxScrollLeft,
+.jxHasVerticalScrollbar .jxScrollRight {
+    width: 20px;
+    height: 20px;
+    display: block;
+    background-color: blue;
+}
+
+.jxHasVerticalScrollbar .jxSliderContainer {
+    height: 100%;
+    width: 20px;
+    border: none;
+}
+
+.jxHasHorizontalScrollbar {
+    padding-bottom: 25px;
+}
+
+.jxHasHorizontalScrollbar .jxScrollbarContainer {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 20px;
+    width: 100%;
+    border: none;
+    border-top: 1px solid black;
+}
+
+.jxHasHorizontalScrollbar .jxScrollLeft,
+.jxHasHorizontalScrollbar .jxScrollRight {
+    width: 20px;
+    height: 20px;
+    display: block;
+    background-color: blue;
+}
+ 
+.jxHasHorizontalScrollbar .jxScrollLeft {
+    float: left;
+}
+
+.jxHasHorizontalScrollbar .jxScrollRight {
+    float: right;
+}
+
+.jxHasHorizontalScrollbar .jxSlider {
+    float: left;
+}
+
+.jxHasHorizontalScrollbar .jxSliderContainer {
+    height: 20px;
+    width: 100%;
+    border: none;
+}
+
+.jxHasVerticalScrollbar .jxSliderKnob,
+.jxHasHorizontalScrollbar .jxSliderKnob {
+    width: 20px;
+    height: 20px;
+    background-color: black;
+    cursor: pointer;
+}@CHARSET "ISO-8859-1";
+
+/**
+ * Slider classes
+ */
+.jxSliderContainer {
+    width: 100%;
+    height: 10px;
+    border: 1px solid black;
+}
+
+.jxSliderKnob {
+    width: 10px;
+    height: 10px;
+    background-color: black;
+    cursor: pointer;
+}
+
+/**
  * @project         Jx
- * @revision        $Id: splitter.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: splitter.css 294 2009-04-02 12:26:26Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1739,7 +2631,7 @@
   background-color: #aaa;
 }/**
  * @project         Jx
- * @revision        $Id: tab.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: tab.css 875 2010-04-24 06:10:53Z fred.warnock $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -1767,7 +2659,7 @@
   background-color: #fff;
 }
 
-.jxTabSetContainer  .jxToolbarContainer {
+.jxTabSetContainer  .jxBarContainer {
   /* Base setup */
   z-index: auto;
 }
@@ -1793,7 +2685,7 @@
 /* BASE TAB (BUTTON) STYLES */
 /* ======================== */
 
-div.jxTabContainer {
+span.jxTabContainer {
   /* Base setup */
   display: block;
   position: relative;
@@ -1805,7 +2697,8 @@
 
 a.jxTab {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
   cursor: pointer;
   user-select: none;
@@ -1823,7 +2716,8 @@
 
 span.jxTabContent {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   font-size: 0px;
   line-height: 0px;
 
@@ -1837,6 +2731,8 @@
 
 img.jxTabIcon {
   /* Base setup */
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
 
   width: 16px;
@@ -1847,7 +2743,8 @@
 
 span.jxTabLabel {
   /* Base setup */
-  display: block;
+  display: -moz-inline-box;
+  display: inline-block;
   position: relative;
   cursor: pointer;
 
@@ -1868,12 +2765,11 @@
   user-select: none;
   -moz-user-select: none;
   -khtml-user-select: none;
-}
-
-a.jxTabClose img {
   width: 16px;
   height: 16px;
-  background-image: url(images/tab_close.png);  
+  background-image: url(images/tab_close.png);
+  background-position: 0px 0px;
+  background-repeat: no-repeat;
 }
 
 .jxDisabled a.jxTab,
@@ -1889,27 +2785,37 @@
 /* HORIZONTALTAB BAR - TOP and BOTTOM TABS */
 /* ======================================= */
 
-.jxBarTop div.jxTabContainer,
-.jxBarBottom div.jxTabContainer {
+.jxTabBarTop .jxBarWrapper, 
+.jxTabBarBottom .jxBarWrapper {
+  padding-left: 2px;
+}
+
+.jxBarTop span.jxTabContainer,
+.jxBarBottom span.jxTabContainer {
   /* Base setup */
-  float: left;
+  margin-right: -1px;
+  padding: 2px 0px;
 }
 
 .jxBarTop a.jxTab,
-.jxBarTop span.jxTabContent {
+.jxBarTop span.jxTabContent,
+.jxTabBarTop .jxBarControls a.jxButton,
+.jxTabBarTop .jxBarControls span.jxButtonContent{
   background-image: url(images/tab_top.png);
 }
 
 .jxBarBottom a.jxTab,
-.jxBarBottom span.jxTabContent {
+.jxBarBottom span.jxTabContent,
+.jxTabBarBottom .jxBarControls a.jxButton,
+.jxTabBarBottom .jxBarControls span.jxButtonContent {
   background-image: url(images/tab_bottom.png);
 }
 
 /* Closeable Tab */
 .jxBarTop a.jxTabClose,
 .jxBarBottom a.jxTabClose {
-  top: 3px;
-  right: 3px;
+  top: 5px;
+  right: 5px;
 }
 
 .jxBarTop .jxTabClose span.jxTabContent,
@@ -1921,7 +2827,6 @@
 .jxBarTop a.jxTab,
 .jxBarBottom a.jxTab {
   /* Base setup */
-  float: left;
 
   padding-left: 4px; /* makes room for the left of the tab bg */
   background-position: left -24px; 
@@ -1930,7 +2835,6 @@
 .jxBarTop span.jxTabContent,
 .jxBarBottom span.jxTabContent {
   /* Base setup */
-  float: left;
 
   padding: 4px 4px 4px 0px; /* makes space around the label */
   background-position: right -24px; 
@@ -2045,14 +2949,14 @@
 
 .jxBarTop img.jxTabIcon,
 .jxBarBottom img.jxTabIcon {
+  vertical-align: middle;
   /* Base setup */
-  float: left;
 }
 
 .jxBarTop span.jxTabLabel,
 .jxBarBottom span.jxTabLabel {
   /* Base setup */
-  float: left;
+  vertical-align: middle;
   height: 16px;
 
   padding: 0px 4px 0px 4px;
@@ -2063,11 +2967,19 @@
 /* VERTICAL TAB BAR - LEFT and RIGHT */
 /* ================================= */
 
-.jxBarLeft div.jxTabContainer,
-.jxBarRight div.jxTabContainer {
+.jxTabBarLeft .jxBarWrapper, 
+.jxTabBarRight .jxBarWrapper {
+  padding-top: 2px;
+}
+
+.jxBarLeft span.jxTabContainer,
+.jxBarRight span.jxTabContainer {
   /* Base setup */
+  margin-bottom: -1px;
+  padding: 0px 2px;
 }
 
+
 .jxBarLeft a.jxTab,
 .jxBarLeft span.jxTabContent {
   background-image: url(images/tab_left.png);
@@ -2081,8 +2993,8 @@
 /* Closeable Tab */
 .jxBarLeft a.jxTabClose,
 .jxBarRight a.jxTabClose {
-  top: 3px;
-  left: 3px;
+  top: 5px;
+  left: 5px;
 }
 
 .jxBarLeft .jxTabClose span.jxTabContent,
@@ -2216,7 +3128,7 @@
 
 /**
  * @project         Jx
- * @revision        $Id: toolbar.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: toolbar.css 912 2010-05-21 21:33:08Z pagameba $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -2289,26 +3201,29 @@
   background-position: left 0px;
 }
 
+
 .jxBarTop .jxBarScroller,
 .jxBarBottom .jxBarScroller {
-  position: absolute;
-  /* much bigger than its container */
-  width: 1000%;
+  float: left;
+  height: 28px;
   overflow: hidden;
+  z-index: 0;
 }
 
-.jxBarTop .jxBarScrollLeft,
-.jxBarBottom .jxBarScrollLeft {
-  position: absolute;
-  top: 0px;
-  left: 0px;
+.jxBarTop .jxBarScroller .jxBarWrapper,
+.jxBarBottom .jxBarScroller .jxBarWrapper {
+  float: left;
+  height: 28px;
+  overflow: hidden;
+  width: 10000%;
 }
 
-.jxBarTop .jxBarScrollRight,
-.jxBarBottom .jxBarScrollRight {
-  position: absolute;
-  top: 0px;
-  right: 0px;
+.jxBarTop .jxBarControls .jxButtonContainer,
+.jxBarBottom .jxBarControls .jxButtonContainer {
+  /* float: right; */
+  z-index: 1;
+  padding: 2px 0px;
+  margin-left: -1px;
 }
 
 .jxBarTop .jxBarScrollLeft img.jxButtonIcon,
@@ -2323,6 +3238,13 @@
   background-position: 0px -96px;
 }
 
+.jxBarControls {
+    float: right;
+    position: relative;
+    font-size: 0px;
+    line-height: 0px;
+}
+
 /* The jx toolbar and tabbar are both built out of a UL
    The margins/padding are flattened out, and the list markers are hidden
    UL's are floated left so multiple toolbars can be in the samae row.
@@ -2343,6 +3265,7 @@
   display: block;
   position: relative;
   float: left;
+  clear: none;
   list-style-type: none;
 
   margin: 0px;  /* margins don't seem to work properly in IE */
@@ -2365,12 +3288,18 @@
   float: left;
   font-size: 0px;
   line-height: 0px;
-
+  white-space: nowrap;
   padding: 0px;
   margin: 0px;  /* margins don't seem to work properly in IE */
   border: none;
 }
 
+/* inputs in toolbars wrap in IE */
+li.jxToolItem .jxInputWrapper {
+  white-space: nowrap;
+}
+
+
 /* Seperator height should match that of button images
    and the margins+padding+border should add up to the same total too. */
 
@@ -2417,10 +3346,42 @@
   /* Base setup */
   clear: both;
 }
+/* Aligning options */
+.jxToolbarAlignLeft ul {
+    float: left;
+}
 
+.jxToolbarAlignRight ul {
+    float: right;
+}
+
+.jxToolbarAlignCenter {
+    text-align: center;
+}
+
+.jxToolbarAlignCenter ul {
+    float: none;
+}
+
+
+.jxToolbarAlignCenter ul li {
+    float: none;
+    display: inline;
+}/*
+ * Tooltip classes
+ */
+.jxTooltip {
+	width: auto;
+	height: auto;
+	background-color: black;
+	color: white;
+	padding: 5px;
+	z-index: 65536;
+}
+
 /**
  * @project         Jx
- * @revision        $Id: tree.css 324 2009-04-08 19:11:06Z pagameba $
+ * @revision        $Id: tree.css 755 2010-03-15 03:09:37Z jonlb at comcast.net $
  * @author          Fred Warnock (fwarnock at dmsolutions.ca)
  * @copyright       (c) 2006 DM Solutions Group Inc.
  */
@@ -2435,7 +3396,7 @@
 */
 
 
-.jxTree, 
+.jxTree,
 .jxTreeRoot {
   /* relative positioning is required for IE to fix the peek-a-boo bug */
   position:relative;
@@ -2455,8 +3416,7 @@
 
 /* Node Classes */
 
-.jxTree li, 
-.jxTreeRoot li {
+li.jxTreeContainer {
   /* relative positioning is required for IE to fix the peek-a-boo bug */
   position:relative;
   display: block;
@@ -2473,12 +3433,11 @@
   -khtml-user-select: none;
 }
 
-.jxTree li {
+.jxTree li.jxTreeContainer {
   margin-left: 16px;
 }
 
-.jxTree a, 
-.jxTreeRoot a {
+a.jxTreeItem {
   position: relative;
   display: block;
   cursor: pointer;
@@ -2490,7 +3449,7 @@
   background-position: left top;
   border: none;
 
-  margin: 0px 1px 0px 15px;
+  margin: 0px 1px 0px 17px;
   padding: 0px 0px 0px 20px;
   z-index: 0;
   font-family: Arial, Helvetica, sans-serif;
@@ -2503,59 +3462,52 @@
 }
 
 
-.jxTree a:focus,
-.jxTreeRoot a:focus {
+a.jxTreeItem:focus {
   border-left: 1px dotted #75ADFF;
   border-right: 1px dotted #75ADFF;
-  margin: 0px 0px 0px 14px;
+  margin: 0px 0px 0px 16px;
   background-position: left -72px;
 }
 
-.jxTree a:hover, 
-.jxTreeRoot a:hover {
+a.jxTreeItem:hover,
+li.jxTreeContainer a.jxHover {
   /*border: 1px solid #C5E0FF;*/
-  border-left: 1px solid #C5E0FF;
-  border-right: 1px solid #C5E0FF;
-  margin: 0px 0px 0px 14px;
+  border-left: 1px solid #CDDFFD;
+  border-right: 1px solid #CDDFFD;
+  margin: 0px 0px 0px 16px;
   background-color: #CDE5FF;
   background-position: left -24px;
 }
 
-.jxTree a.jxTreeItemPressed,
-.jxTree a.jxTreeItemPressed:hover,
-.jxTreeRoot a.jxTreeItemPressed,
-.jxTreeRoot a.jxTreeItemPressed:hover {
-  border-left: 1px solid #C5E0FF;
-  border-right: 1px solid #C5E0FF;
-  margin: 0px 0px 0px 14px;
+li.jxTreeContainer a.jxSelected,
+li.jxTreeContainer a.jxSelected:hover,
+li.jxTreeContainer a.jxPressed,
+li.jxTreeContainer a.jxPressed:hover {
+  border-left: 1px solid #8AABFB;
+  border-right: 1px solid #8AABFB;
+  margin: 0px 0px 0px 16px;
   background-color: #CDE5FF;
   background-position: left -48px;
 }
 
-.jxTree .jxDisabled a,
-.jxTreeRoot.jxDisabled a {
+li.jxDisabled a.jxTreeItem {
   cursor: default;
 }
-
-.jxTree .jxDisabled a:focus,
-.jxTree .jxDisabled a.jxTreeItemPressed,
-.jxTree .jxDisabled a.jxTreeItemPressed:hover,
-.jxTree .jxDisabled a:hover,
-.jxTreeRoot .jxDisabled a:focus,
-.jxTreeRoot .jxDisabled a.jxTreeItemPressed,
-.jxTreeRoot .jxDisabled a.jxTreeItemPressed:hover,
-.jxTreeRoot .jxDisabled a:hover {
+            
+li.jxDisabled a.jxTreeItem:focus,
+li.jxDisabled a.jxTreeItem:hover {
   background-position: left top;
+  background-color: transparent;
   border: none;
-  margin: 0px 1px 0px 15px;
+  margin: 0px 1px 0px 17px;
 }
 
 .jxTreeNest {
   background-image: url(images/tree_vert_line.png);
 }
 
-.jxTreeImage,
-.jxTreeIcon {
+img.jxTreeImage,
+img.jxTreeIcon {
   position: absolute;
   display: inline;
 
@@ -2573,9 +3525,9 @@
   margin: 0px;
 }
 
-.jxTreeIcon { 
+img.jxTreeIcon { 
   height: 16px;
-  top: 1px;
+  top: 2px;
   left: 1px;
 }
 
@@ -2607,36 +3559,104 @@
   background-position: left -140px; /* plus last image */
 }
 
-.jxTreeItem .jxTreeIcon,
-.jxTreeItemLast .jxTreeIcon {
+.jxTreeLeaf .jxTreeIcon,
+.jxTreeLeafLast .jxTreeIcon {
   background-position: left 0px; /* page image */
 }
 
-.jxTreeItem .jxTreeImage {
+.jxTreeLeaf .jxTreeImage {
   background-position: left -60px; /* node image */
 }
 
-.jxTreeItemLast .jxTreeImage {
+.jxTreeLeafLast .jxTreeImage {
   background-position: left -120px; /* last node image */
 }
 
-.jxTreeItemSelected {
-  background-color: #AFD4FA;
-  font-weight:bold;
-}
 
-.jxTreeItem a,
-.jxTreeItem img,
-.jxTreeItem input,
-.jxTreeItemLast a,
-.jxTreeItemLast img,
-.jxTreeItemLast input,
-.jxTreeBranch a,
-.jxTreeBranch img,
-.jxTreeBranch input,
-.jxTreeBranchLast a,
-.jxTreeBranchLast img,
-.jxTreeBranchLast input {
+a.jxTreeItem,
+img.jxTreeImage,
+img.jxTreeIcon,
+span.jxTreeLabel,
+.jxTreeItemContainer input {
     vertical-align: middle;
 }
 
+img.jxTreeImage.jxBusy {
+    background-image: url(images/spinner_16.gif);
+    background-position: left top;
+}/* FileUploadPanel */
+.jxFileUploadPanel {
+    padding: 5px;
+}
+
+.jxFileUploadPanel .jxInputContainer {
+    /*width: 300px;*/
+}
+
+.jxUploadQueue {
+    /*width: 100%;*/
+    /*margin-top: 10px;*/
+}
+
+.jxUploadQueue li {
+    display: block;
+    position: relative;
+    overflow: hidden;
+    /*width: 95%;*/
+    /*clear: both;*/
+    /*border-top: 1px solid black;*/
+    padding: 2px;
+}
+
+.jxUploadQueue div span {
+    display: block;
+}
+
+.jxUploadQueue li span.jxUploadFileName {
+    /*float: left;*/
+    font-size: 12px;
+    line-height: 16px;
+    padding-left: 2px;
+}
+
+.jxUploadQueue li span.jxUploadFileDelete,
+.jxUploadQueue li span.jxUploadFileProgress,
+.jxUploadQueue li span.jxUploadFileComplete,
+.jxUploadQueue li span.jxUploadFileError {
+    /*float: right;*/
+    position: absolute;
+    top: 2px;
+    right: 2px;
+    width: 16px;
+    height: 16px;
+    background-repeat: no-repeat;
+    cursor: pointer;
+}
+
+.jxUploadQueue li span.jxUploadFileDelete {
+    background-image: url('images/icons.png');
+    background-position: 0px -128px;
+}
+
+.jxUploadQueue li span.jxUploadFileProgress {
+    background-image: url('images/spinner_16.gif');
+    background-position: top left;
+}
+
+.jxUploadQueue li span.jxUploadFileComplete {
+    background-image: url('images/icons.png');
+    background-position: 0px -48px;
+}
+
+.jxUploadQueue li span.jxUploadFileError {
+    background-image: url('images/icons.png');
+    background-position: 0px -32px;
+}
+
+.jxUploadFileErrorTip {
+  padding: 4px 4px 4px 20px;
+  border: 2px solid #ddd;
+  background: url("images/icons.png") no-repeat 0px -32px;
+  color: black;
+  width: 100px;
+}

Modified: trunk/widgets/ActivityIndicator.js
===================================================================
--- trunk/widgets/ActivityIndicator.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/ActivityIndicator.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -50,6 +50,7 @@
         this.getMap().registerForEvent(Fusion.Event.MAP_BUSY_CHANGED, 
                               OpenLayers.Function.bind(this.busyChanged, this));
     },
+    
     busyChanged: function() {
         this.element.style.visibility = this.getMap().isBusy() ? 'visible' : 'hidden';
     }

Deleted: trunk/widgets/AddWMSLayer/AddWMSLayer.css
===================================================================
--- sandbox/jxlib-3.0/widgets/AddWMSLayer/AddWMSLayer.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/AddWMSLayer/AddWMSLayer.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,30 +0,0 @@
-/* SHEET STYLES */
-
-.addWMSDialog {
-  padding: 5px;
-  font-size: 12px;
-  line-height: 18px;
-  font-family: Arial, Helvetica, sans-serif;
-}
-
-.addWMSDialog label {
-  display: block;
-  float: left;
-  clear: left;
-  background-color:transparent;
-  color:#000000;
-}
-
-.addWMSDialog select, 
-.addWMSDialog textarea,
-.addWMSDialog input {
-  float: left;
-}
-
-#AddWMSLayersContent {
-  overflow: auto;
-  border: 1px solid #999;
-  background-color: #fff;
-  height: 200px;
-}
-

Copied: trunk/widgets/AddWMSLayer/AddWMSLayer.css (from rev 2248, sandbox/jxlib-3.0/widgets/AddWMSLayer/AddWMSLayer.css)
===================================================================
--- trunk/widgets/AddWMSLayer/AddWMSLayer.css	                        (rev 0)
+++ trunk/widgets/AddWMSLayer/AddWMSLayer.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,30 @@
+/* SHEET STYLES */
+
+.addWMSDialog {
+  padding: 5px;
+  font-size: 12px;
+  line-height: 18px;
+  font-family: Arial, Helvetica, sans-serif;
+}
+
+.addWMSDialog label {
+  display: block;
+  float: left;
+  clear: left;
+  background-color:transparent;
+  color:#000000;
+}
+
+.addWMSDialog select, 
+.addWMSDialog textarea,
+.addWMSDialog input {
+  float: left;
+}
+
+#AddWMSLayersContent {
+  overflow: auto;
+  border: 1px solid #999;
+  background-color: #fff;
+  height: 200px;
+}
+

Deleted: trunk/widgets/AddWMSLayer/AddWMSLayer.html
===================================================================
--- sandbox/jxlib-3.0/widgets/AddWMSLayer/AddWMSLayer.html	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/AddWMSLayer/AddWMSLayer.html	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,14 +0,0 @@
-<div id="AddWMSLayerDialog" class="addWMSDialog">
-  <div>
-      WMS Server URL
-      <input type="text" id="wmsServerName" style="width:300px;"/>
-      <div id="listLayersButton"></div>
-  </div>
-  <span class="jxClearer">&nbsp;</span>
-  <div id="AddWMSLayerList">
-    Select a layer from the list
-    <div id="AddWMSLayersContent" class=""></div>
-  </div>
-  <span class="jxClearer">&nbsp;</span>
-</div>
-

Copied: trunk/widgets/AddWMSLayer/AddWMSLayer.html (from rev 2248, sandbox/jxlib-3.0/widgets/AddWMSLayer/AddWMSLayer.html)
===================================================================
--- trunk/widgets/AddWMSLayer/AddWMSLayer.html	                        (rev 0)
+++ trunk/widgets/AddWMSLayer/AddWMSLayer.html	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,14 @@
+<div id="AddWMSLayerDialog" class="addWMSDialog">
+  <div>
+      WMS Server URL
+      <input type="text" id="wmsServerName" style="width:300px;"/>
+      <div id="listLayersButton"></div>
+  </div>
+  <span class="jxClearer">&nbsp;</span>
+  <div id="AddWMSLayerList">
+    Select a layer from the list
+    <div id="AddWMSLayersContent" class=""></div>
+  </div>
+  <span class="jxClearer">&nbsp;</span>
+</div>
+

Deleted: trunk/widgets/AddWMSLayer/AddWMSLayer.php
===================================================================
--- sandbox/jxlib-3.0/widgets/AddWMSLayer/AddWMSLayer.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/AddWMSLayer/AddWMSLayer.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,184 +0,0 @@
-<?php
-
-include "../../layers/MapServer/php/Common.php";
-include "../../common/php/Utilities.php";
-
-isset($_GET["wmsservicetitle"])?$gwmsServiceTitle = $_GET["wmsservicetitle"] : $wmsservicetitle = NULL;
-
-
-if (!isset($mapName)) {
-    die('mapname not set');
-}
-
-if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
-    
-     switch($_REQUEST["action"]){
-        case "listLayersWMS":{
-            $szObject = getLayerListFromWMS($_REQUEST["server"]);
-            break;
-        }
-        case "addLayer":{
-            $szObject = addLayer($_REQUEST["value"]);
-            break;
-        }    
-    }
-
-$szJsonData =  var2json($szObject);
-
-header('Content-type: text/plain');
-header('X-JSON: true');
-echo $szJsonData;
-
-}
-
-function getLayerListFromWMS($szServerName){
-    include ('wms-parser.php');
-    global $gszWMSServerName;
-    global $gwmsServiceTitle;
-    
-    // add serverName as a global
-    $gszWMSServerName = $szServerName;
-    
-    //$wmsServer = "http://atlas.gc.ca/cgi-bin/atlaswms_en?VERSION=1.1.1&REQUEST=GetCapabilities&SERVICE=WMS";
-    $join = strpos($szServerName,'?')?'&':'?';
-    $getCapRequest = $join."VERSION=1.1.1&REQUEST=GetCapabilities&SERVICE=WMS";
-    //$xmlData = readData($gszWMSServerName.$getCapRequest);
-    //echo "issuing request -:".$gszWMSServerName.$getCapRequest;
-    $xmlData = file_get_contents($gszWMSServerName.$getCapRequest);
-    //echo $xmlData;
-    
-    if ($xmlData) {
-      //check that it is a valid WMS capabiltities response
-      $check = stripos($xmlData, 'WMT_MS_Capabilities');
-
-      if ($check !== false) {
-        $xmlParse = simplexml_load_string($xmlData);
-        if ($xmlParse) {
-          $gwmsServiceTitle = $xmlParse->Service->Title;
-          $szGetCapabilities = & new CapabilitiesParser();
-          $szGetCapabilities->parse($xmlData);
-          $szGetCapabilities->free_parser();
-          $oReturn = prepareWMS($szGetCapabilities);
-        } else {
-          $oReturn->error = 'Error with GetCapabilities request: '.$gszWMSServerName.$getCapRequest;
-          $oReturn->message = 'Response is not valid XML: \n'.$xmlData;
-        }
-      } else {
-        $oReturn->error = 'Error with GetCapabilities request: '.$gszWMSServerName.$getCapRequest;
-        $oReturn->message = 'Response is not a WMS capabilities document: \n'.$xmlData;
-      }
-    } else {
-      $oReturn->error = 'Error with GetCapabilities request: '.$gszWMSServerName.$getCapRequest;
-      $oReturn->message = 'No response from server';
-    }
-    
-    return $oReturn;
-}
-
-function prepareWMS($szObj){
-    global $gszWMSServerName;
-    global $gwmsServiceTitle;
-    $nCount=0;
-
-      //print_r($szObj);
-    foreach($szObj->layers as $key=>$value){
-      if ( isset($value["Name"]) && $value["layer_id"]>0 ) {
-        $oTmp = NULL;
-        $oTmp->layertype     = 'wms';
-
-        $oTmp->wmsservicetitle = strval($gwmsServiceTitle[0]);
-        $oTmp->name         = $value["Name"];
-        $oTmp->owstitle     = $value["Title"];
-        $oTmp->srs            = $value["SRS"];
-        $oTmp->group         = "null";
-        // setting to image/png for now should be looked into upon later.
-        $oTmp->imageformat  = "image/png";
-        $oTmp->servername   = $gszWMSServerName;
-        $oTmp->queryable    = $value["queryable"]?$value["queryable"]:"0";
-        $bbox         = $value["LatLonBoundingBox"];
-        $oTmp->minx   = $bbox["minx"];
-        $oTmp->miny   = $bbox["miny"];
-        $oTmp->maxx   = $bbox["maxx"];
-        $oTmp->maxy   = $bbox["maxy"];
-        $oTmp->metadataurl = $value["MetadataURL"]?$value["MetadataURL"]:"";
-        
-        $oReturn[$nCount++] = $oTmp;
-      }
-    }
-
-return $oReturn;
-}
-
-function addLayer($szValue){
-    global $mapName;
-    global $gwmsServiceTitle;
-    
-    $szReturn->addedLayer = false;
-    
-    // adding a layer from the atlas
-    if($_REQUEST["layertype"]== 'atlas'){
-        $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
-        $oCatalogMapFile = ms_newMapObj(APP_LAYER_CATALOG);
-        
-         for($i = 0; $i < $oCatalogMapFile->numlayers; $i++){
-            $oLayer = $oCatalogMapFile->getLayer( $i );
-            
-            if($oLayer->name == $_REQUEST["layername"] && $oLayer->group == $_REQUEST["group"]){
-                //found layer clone it.
-                ms_newLayerObj( $oMap, $oLayer );
-                $szReturn->addedLayer = true;
-                }
-            }
-        // save map file.
-        $oMap->save($_SESSION['maps'][$mapName]);    
-        }
-        
-    // adding a layer from a wms server
-    if($_REQUEST["layertype"]== 'wms'){
-        $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
-        $oLayer = ms_newLayerObj($oMap);
-        
-        if ($_REQUEST["metadataurl"]) {
-          $metadataurl = $_REQUEST["metadataurl"];
-        } else {
-          $join = strpos($_REQUEST["servername"],"?")?"&":"?";
-          $metadataurl = $_REQUEST["servername"].$join."request=GetCapabilities&service=WMS&version=1.1.1";
-        }
-
-        $oLayer->set("name",$_REQUEST["layername"]);
-        $oLayer->set("status", MS_ON );
-        //$oLayer->set("connectiontype",MS_WMS);
-        // in mapserver 5.4 we need to set the connection type with the method "setConnectionType"
-        $oLayer->setConnectionType(MS_WMS);
-        $oLayer->set("connection", $_REQUEST["servername"]);
-        $oLayer->set("type",MS_LAYER_RASTER);
-        $oLayer->setMetaData("legendLabel",$_REQUEST["owstitle"]);
-        
-        $aSRS = split(" ",$_REQUEST["srs"]);
-        
-        $oLayer->setMetaData("ows_name",$_REQUEST["layername"]);
-        $oLayer->setMetaData("ows_format",$_REQUEST["imageFormat"]);
-        $oLayer->setMetaData("ows_server_version","1.1.1");
-        $oLayer->setMetaData("ows_srs",$aSRS[0]);
-        $oLayer->setMetaData("wms_metadataurl_href", $metadataurl);
-        $oLayer->setMetaData("selectable",$_REQUEST["queryable"]=='1'?'true':'false');
-        
-        $oLayer->setprojection("+init=".strtolower($aSRS[0]));
-        
-        $oMap->save($_SESSION['maps'][$mapName]);    
-        
-        $szReturn->wms_connection = $_REQUEST["servername"];
-        $szReturn->wms_format = $_REQUEST["imageFormat"];
-        $szReturn->wms_name = $_REQUEST["layername"];
-        $szReturn->wms_title = $_REQUEST["owstitle"];
-        $szReturn->wms_version = "1.1.1";
-        $szReturn->wms_srs =$aSRS[0];
-        $szReturn->wmsservicetitle = $gwmsServiceTitle;
-
-        $szReturn->addedLayer = true;
-        }
-        
-    return $szReturn;
-    }
-    
-?>
\ No newline at end of file

Copied: trunk/widgets/AddWMSLayer/AddWMSLayer.php (from rev 2248, sandbox/jxlib-3.0/widgets/AddWMSLayer/AddWMSLayer.php)
===================================================================
--- trunk/widgets/AddWMSLayer/AddWMSLayer.php	                        (rev 0)
+++ trunk/widgets/AddWMSLayer/AddWMSLayer.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,184 @@
+<?php
+
+include "../../layers/MapServer/php/Common.php";
+include "../../common/php/Utilities.php";
+
+isset($_GET["wmsservicetitle"])?$gwmsServiceTitle = $_GET["wmsservicetitle"] : $wmsservicetitle = NULL;
+
+
+if (!isset($mapName)) {
+    die('mapname not set');
+}
+
+if (isset($_SESSION['maps']) && isset($_SESSION['maps'][$mapName])) {
+    
+     switch($_REQUEST["action"]){
+        case "listLayersWMS":{
+            $szObject = getLayerListFromWMS($_REQUEST["server"]);
+            break;
+        }
+        case "addLayer":{
+            $szObject = addLayer($_REQUEST["value"]);
+            break;
+        }    
+    }
+
+$szJsonData =  var2json($szObject);
+
+header('Content-type: text/plain');
+header('X-JSON: true');
+echo $szJsonData;
+
+}
+
+function getLayerListFromWMS($szServerName){
+    include ('wms-parser.php');
+    global $gszWMSServerName;
+    global $gwmsServiceTitle;
+    
+    // add serverName as a global
+    $gszWMSServerName = $szServerName;
+    
+    //$wmsServer = "http://atlas.gc.ca/cgi-bin/atlaswms_en?VERSION=1.1.1&REQUEST=GetCapabilities&SERVICE=WMS";
+    $join = strpos($szServerName,'?')?'&':'?';
+    $getCapRequest = $join."VERSION=1.1.1&REQUEST=GetCapabilities&SERVICE=WMS";
+    //$xmlData = readData($gszWMSServerName.$getCapRequest);
+    //echo "issuing request -:".$gszWMSServerName.$getCapRequest;
+    $xmlData = file_get_contents($gszWMSServerName.$getCapRequest);
+    //echo $xmlData;
+    
+    if ($xmlData) {
+      //check that it is a valid WMS capabiltities response
+      $check = stripos($xmlData, 'WMT_MS_Capabilities');
+
+      if ($check !== false) {
+        $xmlParse = simplexml_load_string($xmlData);
+        if ($xmlParse) {
+          $gwmsServiceTitle = $xmlParse->Service->Title;
+          $szGetCapabilities = & new CapabilitiesParser();
+          $szGetCapabilities->parse($xmlData);
+          $szGetCapabilities->free_parser();
+          $oReturn = prepareWMS($szGetCapabilities);
+        } else {
+          $oReturn->error = 'Error with GetCapabilities request: '.$gszWMSServerName.$getCapRequest;
+          $oReturn->message = 'Response is not valid XML: \n'.$xmlData;
+        }
+      } else {
+        $oReturn->error = 'Error with GetCapabilities request: '.$gszWMSServerName.$getCapRequest;
+        $oReturn->message = 'Response is not a WMS capabilities document: \n'.$xmlData;
+      }
+    } else {
+      $oReturn->error = 'Error with GetCapabilities request: '.$gszWMSServerName.$getCapRequest;
+      $oReturn->message = 'No response from server';
+    }
+    
+    return $oReturn;
+}
+
+function prepareWMS($szObj){
+    global $gszWMSServerName;
+    global $gwmsServiceTitle;
+    $nCount=0;
+
+      //print_r($szObj);
+    foreach($szObj->layers as $key=>$value){
+      if ( isset($value["Name"]) && $value["layer_id"]>0 ) {
+        $oTmp = NULL;
+        $oTmp->layertype     = 'wms';
+
+        $oTmp->wmsservicetitle = strval($gwmsServiceTitle[0]);
+        $oTmp->name         = $value["Name"];
+        $oTmp->owstitle     = $value["Title"];
+        $oTmp->srs            = $value["SRS"];
+        $oTmp->group         = "null";
+        // setting to image/png for now should be looked into upon later.
+        $oTmp->imageformat  = "image/png";
+        $oTmp->servername   = $gszWMSServerName;
+        $oTmp->queryable    = $value["queryable"]?$value["queryable"]:"0";
+        $bbox         = $value["LatLonBoundingBox"];
+        $oTmp->minx   = $bbox["minx"];
+        $oTmp->miny   = $bbox["miny"];
+        $oTmp->maxx   = $bbox["maxx"];
+        $oTmp->maxy   = $bbox["maxy"];
+        $oTmp->metadataurl = $value["MetadataURL"]?$value["MetadataURL"]:"";
+        
+        $oReturn[$nCount++] = $oTmp;
+      }
+    }
+
+return $oReturn;
+}
+
+function addLayer($szValue){
+    global $mapName;
+    global $gwmsServiceTitle;
+    
+    $szReturn->addedLayer = false;
+    
+    // adding a layer from the atlas
+    if($_REQUEST["layertype"]== 'atlas'){
+        $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
+        $oCatalogMapFile = ms_newMapObj(APP_LAYER_CATALOG);
+        
+         for($i = 0; $i < $oCatalogMapFile->numlayers; $i++){
+            $oLayer = $oCatalogMapFile->getLayer( $i );
+            
+            if($oLayer->name == $_REQUEST["layername"] && $oLayer->group == $_REQUEST["group"]){
+                //found layer clone it.
+                ms_newLayerObj( $oMap, $oLayer );
+                $szReturn->addedLayer = true;
+                }
+            }
+        // save map file.
+        $oMap->save($_SESSION['maps'][$mapName]);    
+        }
+        
+    // adding a layer from a wms server
+    if($_REQUEST["layertype"]== 'wms'){
+        $oMap = ms_newMapObj($_SESSION['maps'][$mapName]);
+        $oLayer = ms_newLayerObj($oMap);
+        
+        if ($_REQUEST["metadataurl"]) {
+          $metadataurl = $_REQUEST["metadataurl"];
+        } else {
+          $join = strpos($_REQUEST["servername"],"?")?"&":"?";
+          $metadataurl = $_REQUEST["servername"].$join."request=GetCapabilities&service=WMS&version=1.1.1";
+        }
+
+        $oLayer->set("name",$_REQUEST["layername"]);
+        $oLayer->set("status", MS_ON );
+        //$oLayer->set("connectiontype",MS_WMS);
+        // in mapserver 5.4 we need to set the connection type with the method "setConnectionType"
+        $oLayer->setConnectionType(MS_WMS);
+        $oLayer->set("connection", $_REQUEST["servername"]);
+        $oLayer->set("type",MS_LAYER_RASTER);
+        $oLayer->setMetaData("legendLabel",$_REQUEST["owstitle"]);
+        
+        $aSRS = split(" ",$_REQUEST["srs"]);
+        
+        $oLayer->setMetaData("ows_name",$_REQUEST["layername"]);
+        $oLayer->setMetaData("ows_format",$_REQUEST["imageFormat"]);
+        $oLayer->setMetaData("ows_server_version","1.1.1");
+        $oLayer->setMetaData("ows_srs",$aSRS[0]);
+        $oLayer->setMetaData("wms_metadataurl_href", $metadataurl);
+        $oLayer->setMetaData("selectable",$_REQUEST["queryable"]=='1'?'true':'false');
+        
+        $oLayer->setprojection("+init=".strtolower($aSRS[0]));
+        
+        $oMap->save($_SESSION['maps'][$mapName]);    
+        
+        $szReturn->wms_connection = $_REQUEST["servername"];
+        $szReturn->wms_format = $_REQUEST["imageFormat"];
+        $szReturn->wms_name = $_REQUEST["layername"];
+        $szReturn->wms_title = $_REQUEST["owstitle"];
+        $szReturn->wms_version = "1.1.1";
+        $szReturn->wms_srs =$aSRS[0];
+        $szReturn->wmsservicetitle = $gwmsServiceTitle;
+
+        $szReturn->addedLayer = true;
+        }
+        
+    return $szReturn;
+    }
+    
+?>
\ No newline at end of file

Deleted: trunk/widgets/AddWMSLayer/wms-parser.php
===================================================================
--- sandbox/jxlib-3.0/widgets/AddWMSLayer/wms-parser.php	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/AddWMSLayer/wms-parser.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,252 +0,0 @@
-<?php
-// $Id: wms-parser.php,v 1.1 2007/10/28 05:41:11 pdeschamps Exp $
-
-// CubeWerx requires at least 10M for wms capabilities
-ini_set( 'memory_limit', '20M' );
-
-/**
- *
- * inherited: +style, +crs, =latlonboundingbox, =boundingbox, +authorityurl
- *   =dimension, =attribution, =min/maxscaledenominator, =queryable, =cascaded
- *   =opaque
- * attribution: title, onlineresource->xlink:href, logourl(format,onlineresource)
- * style: name, title, legendurl(format,onlineresource->xlink:href)
- */
-class CapabilitiesParser
-{
-    var $parser;
-
-    var $version = NULL; // wms version
-    var $root = NULL; // root element name (depends on wms version)
-    var $path = ''; // current path (excluding root)
-
-    var $layers = array();
-    var $layer_level = 0;
-    var $layer_props;
-    var $inAttribution = false;
-    var $inStyle = false;
-    var $inMetadata = false;
-
-    var $element = ''; // current element
-    var $cbLayer = NULL;
-    var $cbLayerParams = NULL;
-    var $numLayers = 0;
-    
-    function CapabilitiesParser( )
-    {
-        $this->parser = xml_parser_create( "UTF-8" );
-        xml_parser_set_option( $this->parser, XML_OPTION_CASE_FOLDING, 0 );
-        xml_set_element_handler( $this->parser, array(&$this,"_startElement"), array(&$this,"_endElement") );
-        xml_set_character_data_handler( $this->parser, array(&$this,"_characterData") );
-        $this->layer_level = 0;
-        $this->layer_props = array();
-        $this->layer_props[ $this->layer_level ] = array( );
-        $this->layers = array( );
-    }
-    
-    function free_parser( )
-    {
-        xml_parser_free( $this->parser );
-    }
-    
-    function inSomething()
-    {
-        return $this->inAttribution || $this->inStyle !== false;
-    }
-    
-    function parse( $capabilities, $cbLayer = NULL, $cbLayerParams = NULL )
-    {
-        $this->cbLayer = $cbLayer;
-        $this->cbLayerParams = $cbLayerParams;
-        return xml_parse( $this->parser, $capabilities, TRUE );
-    }
-    
-    function _startElement( $parser, $name, $attrs )
-    {
-//print '_startElement ' . $name . "\n";
-        if( ! $this->root )
-        {
-            $this->root = $name;
-            $this->version = $attrs['version'];
-            $this->layer_props[ $this->layer_level ]['wms_version'] = $this->version;
-        }
-        else
-        {
-            $this->path .= '/' . $name;
-        }
-        $this->element = $name;
-        
-        switch( $name )
-        {
-            case 'Attribution':
-                $this->inAttribution = true;
-                break;
-            case 'Style':
-                $this->inStyle = true;
-//                $this->layer_props[ $this->layer_level ]['Style'] = array( );
-                break;
-            case 'MetadataURL':
-                $this->inMetadata = true;
-                break;
-            case 'OnlineResource':
-                if( $this->inStyle !== false )
-                {
-                    $this->layer_props[ $this->layer_level ]['Style'][ $this->inStyle ] = $attrs['xlink:href'];
-                    break;
-                }
-                if( $this->inMetadata !== false )
-                {
-                    $this->layer_props[ $this->layer_level ]['MetadataURL'] = $attrs['xlink:href'];
-                }
-                break;
-            case 'Layer':
-                // the previous layer is a parent layer of the new one
-                if( ! isset($this->layer_props[ $this->layer_level ]['layer_id']) )
-                {
-                    $this->layer_props[ $this->layer_level ]['layer_id'] = $this->numLayers++;
-                    if( $this->cbLayer )
-                        eval( $this->cbLayer . "(\$this->layer_props[\$this->layer_level], \$this->cbLayerParams );" );
-                    else
-                    {
-//                    print $this->layer_level . ' : ' . var_export($this->layer_props[ $this->layer_level ],true) . '<br>';
-                        $this->layers[] = $this->layer_props[ $this->layer_level ];
-                    }
-                }
-                else
-                {
-                    // adjust layer of $this->layer_props[ $this->layer_level ]
-                    // count child layers?
-                }
-                $this->layer_level += 1;
-                // copy layer_props for new layer level and merge new attrs
-                // @todo: merge only inherited props!
-                $inherited_values = array();
-                $inherited_properties = array( 'Style', 'SRS', 'LatLonBoundingBox', 'BoundingBox', 'queryable', 'opaque', 'cascaded' );
-                foreach( $inherited_properties as $prop )
-                {
-                    if( isset($this->layer_props[ $this->layer_level - 1 ][$prop]) )
-                        $inherited_values[$prop] = $this->layer_props[ $this->layer_level - 1 ][$prop];
-                }
-                $this->layer_props[ $this->layer_level ] = array_merge( $inherited_values, $attrs );
-                
-//                print var_export( array($name,$attrs,$this->layer_level), true ) . '<br>';
-                break;
-            case 'LatLonBoundingBox';
-                if( $this->layer_level > 0 )
-                    $this->layer_props[ $this->layer_level][ $name ] = $attrs;
-                break;
-            case 'BoundingBox':
-                if( $this->layer_level > 0 )
-                {
-                    if( ! isset($this->layer_props[ $this->layer_level][ $name ]) )
-                        $this->layer_props[ $this->layer_level][ $name ] = array();
-                    $SRS = $attrs['SRS'];
-                    unset( $attrs['SRS'] );
-                    $this->layer_props[ $this->layer_level][ $name ][$SRS] = $attrs;
-                }
-                break;
-            default:
-//                print $this->path . ':' . var_export($attrs,true) . '<br>';
-                break;
-        }
-//        print $this->path . ': attrs = ' . var_export($attrs,true) . '<br>';
-    }
-    
-    function _characterData( $parser, $data )
-    {
-//print '_characterData ' . $this->element . ':' . $data . "\n";
-        if( $this->element != '' )
-        {
-            $text = trim($data);
-            if( $this->layer_level == 0 )
-            {
-                switch( $this->element )
-                {
-                    case 'Title':
-                    case 'Name':
-                    case 'Abstract':
-    //print 'abstract=' . $test . '<br/>';
-                        $this->layer_props[ $this->layer_level][ $this->element ] .= $text;
-                        break;
-                }
-            }
-            if( $this->layer_level > 0 && ! $this->inSomething() )
-            {
-                switch( $this->element )
-                {
-                    case 'Title':
-                    case 'Name':
-                        // if not a style name!!
-                        $this->layer_props[ $this->layer_level][ $this->element ] .= $text;
-                        break;
-                    case 'Abstract':
-    //print 'abstract=' . $test . '<br/>';
-                        // if not a style name!!
-                        $this->layer_props[ $this->layer_level][ $this->element ] .= $text;
-                        break;
-                    case 'SRS':
-                        if( isset($this->layer_props[ $this->layer_level][ $this->element ]) 
-                        && $this->layer_props[ $this->layer_level][ $this->element ] != '' )
-                        {
-                            $this->layer_props[ $this->layer_level][ $this->element ] .= ' ';
-                        }
-                        $this->layer_props[ $this->layer_level][ $this->element ] .= $text;
-                        break;
-                }
-            }
-            else if( $this->inStyle !== false )
-            {
-                switch( $this->element )
-                {
-                    case 'Name':
-                        $this->inStyle .= $text;
-                        break;
-                }
-            }
-//            print $this->path . ':<b>' . $this->element . '=' . $text . '|</b><br>';
-//            print '---> ' . $this->layer_level . '/' . var_export($this->layer_props[ $this->layer_level],true) . '<br>';
-            
-            //$this->element = ''; // previously here instead of at start of _endElement
-        }
-    }
-    
-    function _endElement( $parser, $name )
-    {
-//print '_endElement ' . $name . "\n";
-        $this->element = ''; // instead of at end of _characterData function
-        
-        $this->path = substr( $this->path, 0, -(1+strlen($name)) );
-        switch( $name )
-        {
-            case 'Layer':
-                if( ! $this->layer_props[ $this->layer_level ]['layer_id'] )
-                {
-                    $this->layer_props[ $this->layer_level ]['layer_id'] = $this->numLayers++;
-                    if( $this->cbLayer )
-                        eval( $this->cbLayer . "(\$this->layer_props[\$this->layer_level], \$this->cbLayerParams );" );
-                    else
-                    {
-//                    print $this->layer_level . ' : ' . var_export($this->layer_props[ $this->layer_level ],true) . '<br>';
-                        $this->layers[] = $this->layer_props[ $this->layer_level ];
-                    }
-                }
-                $this->layer_level -= 1;
-                array_pop( $this->layer_props );
-                break;
-            case 'Attribution':
-                $this->inAttribution = false;
-                break;
-            case 'Style':
-                $this->inStyle = false;
-                break;
-            case 'Metadata':
-                $this->inMetadata = false;
-                break;
-            default:
-                break;
-        }
-    }
-    
-} // CapabilitiesParser
-
-?>

Copied: trunk/widgets/AddWMSLayer/wms-parser.php (from rev 2248, sandbox/jxlib-3.0/widgets/AddWMSLayer/wms-parser.php)
===================================================================
--- trunk/widgets/AddWMSLayer/wms-parser.php	                        (rev 0)
+++ trunk/widgets/AddWMSLayer/wms-parser.php	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,252 @@
+<?php
+// $Id: wms-parser.php,v 1.1 2007/10/28 05:41:11 pdeschamps Exp $
+
+// CubeWerx requires at least 10M for wms capabilities
+ini_set( 'memory_limit', '20M' );
+
+/**
+ *
+ * inherited: +style, +crs, =latlonboundingbox, =boundingbox, +authorityurl
+ *   =dimension, =attribution, =min/maxscaledenominator, =queryable, =cascaded
+ *   =opaque
+ * attribution: title, onlineresource->xlink:href, logourl(format,onlineresource)
+ * style: name, title, legendurl(format,onlineresource->xlink:href)
+ */
+class CapabilitiesParser
+{
+    var $parser;
+
+    var $version = NULL; // wms version
+    var $root = NULL; // root element name (depends on wms version)
+    var $path = ''; // current path (excluding root)
+
+    var $layers = array();
+    var $layer_level = 0;
+    var $layer_props;
+    var $inAttribution = false;
+    var $inStyle = false;
+    var $inMetadata = false;
+
+    var $element = ''; // current element
+    var $cbLayer = NULL;
+    var $cbLayerParams = NULL;
+    var $numLayers = 0;
+    
+    function CapabilitiesParser( )
+    {
+        $this->parser = xml_parser_create( "UTF-8" );
+        xml_parser_set_option( $this->parser, XML_OPTION_CASE_FOLDING, 0 );
+        xml_set_element_handler( $this->parser, array(&$this,"_startElement"), array(&$this,"_endElement") );
+        xml_set_character_data_handler( $this->parser, array(&$this,"_characterData") );
+        $this->layer_level = 0;
+        $this->layer_props = array();
+        $this->layer_props[ $this->layer_level ] = array( );
+        $this->layers = array( );
+    }
+    
+    function free_parser( )
+    {
+        xml_parser_free( $this->parser );
+    }
+    
+    function inSomething()
+    {
+        return $this->inAttribution || $this->inStyle !== false;
+    }
+    
+    function parse( $capabilities, $cbLayer = NULL, $cbLayerParams = NULL )
+    {
+        $this->cbLayer = $cbLayer;
+        $this->cbLayerParams = $cbLayerParams;
+        return xml_parse( $this->parser, $capabilities, TRUE );
+    }
+    
+    function _startElement( $parser, $name, $attrs )
+    {
+//print '_startElement ' . $name . "\n";
+        if( ! $this->root )
+        {
+            $this->root = $name;
+            $this->version = $attrs['version'];
+            $this->layer_props[ $this->layer_level ]['wms_version'] = $this->version;
+        }
+        else
+        {
+            $this->path .= '/' . $name;
+        }
+        $this->element = $name;
+        
+        switch( $name )
+        {
+            case 'Attribution':
+                $this->inAttribution = true;
+                break;
+            case 'Style':
+                $this->inStyle = true;
+//                $this->layer_props[ $this->layer_level ]['Style'] = array( );
+                break;
+            case 'MetadataURL':
+                $this->inMetadata = true;
+                break;
+            case 'OnlineResource':
+                if( $this->inStyle !== false )
+                {
+                    $this->layer_props[ $this->layer_level ]['Style'][ $this->inStyle ] = $attrs['xlink:href'];
+                    break;
+                }
+                if( $this->inMetadata !== false )
+                {
+                    $this->layer_props[ $this->layer_level ]['MetadataURL'] = $attrs['xlink:href'];
+                }
+                break;
+            case 'Layer':
+                // the previous layer is a parent layer of the new one
+                if( ! isset($this->layer_props[ $this->layer_level ]['layer_id']) )
+                {
+                    $this->layer_props[ $this->layer_level ]['layer_id'] = $this->numLayers++;
+                    if( $this->cbLayer )
+                        eval( $this->cbLayer . "(\$this->layer_props[\$this->layer_level], \$this->cbLayerParams );" );
+                    else
+                    {
+//                    print $this->layer_level . ' : ' . var_export($this->layer_props[ $this->layer_level ],true) . '<br>';
+                        $this->layers[] = $this->layer_props[ $this->layer_level ];
+                    }
+                }
+                else
+                {
+                    // adjust layer of $this->layer_props[ $this->layer_level ]
+                    // count child layers?
+                }
+                $this->layer_level += 1;
+                // copy layer_props for new layer level and merge new attrs
+                // @todo: merge only inherited props!
+                $inherited_values = array();
+                $inherited_properties = array( 'Style', 'SRS', 'LatLonBoundingBox', 'BoundingBox', 'queryable', 'opaque', 'cascaded' );
+                foreach( $inherited_properties as $prop )
+                {
+                    if( isset($this->layer_props[ $this->layer_level - 1 ][$prop]) )
+                        $inherited_values[$prop] = $this->layer_props[ $this->layer_level - 1 ][$prop];
+                }
+                $this->layer_props[ $this->layer_level ] = array_merge( $inherited_values, $attrs );
+                
+//                print var_export( array($name,$attrs,$this->layer_level), true ) . '<br>';
+                break;
+            case 'LatLonBoundingBox';
+                if( $this->layer_level > 0 )
+                    $this->layer_props[ $this->layer_level][ $name ] = $attrs;
+                break;
+            case 'BoundingBox':
+                if( $this->layer_level > 0 )
+                {
+                    if( ! isset($this->layer_props[ $this->layer_level][ $name ]) )
+                        $this->layer_props[ $this->layer_level][ $name ] = array();
+                    $SRS = $attrs['SRS'];
+                    unset( $attrs['SRS'] );
+                    $this->layer_props[ $this->layer_level][ $name ][$SRS] = $attrs;
+                }
+                break;
+            default:
+//                print $this->path . ':' . var_export($attrs,true) . '<br>';
+                break;
+        }
+//        print $this->path . ': attrs = ' . var_export($attrs,true) . '<br>';
+    }
+    
+    function _characterData( $parser, $data )
+    {
+//print '_characterData ' . $this->element . ':' . $data . "\n";
+        if( $this->element != '' )
+        {
+            $text = trim($data);
+            if( $this->layer_level == 0 )
+            {
+                switch( $this->element )
+                {
+                    case 'Title':
+                    case 'Name':
+                    case 'Abstract':
+    //print 'abstract=' . $test . '<br/>';
+                        $this->layer_props[ $this->layer_level][ $this->element ] .= $text;
+                        break;
+                }
+            }
+            if( $this->layer_level > 0 && ! $this->inSomething() )
+            {
+                switch( $this->element )
+                {
+                    case 'Title':
+                    case 'Name':
+                        // if not a style name!!
+                        $this->layer_props[ $this->layer_level][ $this->element ] .= $text;
+                        break;
+                    case 'Abstract':
+    //print 'abstract=' . $test . '<br/>';
+                        // if not a style name!!
+                        $this->layer_props[ $this->layer_level][ $this->element ] .= $text;
+                        break;
+                    case 'SRS':
+                        if( isset($this->layer_props[ $this->layer_level][ $this->element ]) 
+                        && $this->layer_props[ $this->layer_level][ $this->element ] != '' )
+                        {
+                            $this->layer_props[ $this->layer_level][ $this->element ] .= ' ';
+                        }
+                        $this->layer_props[ $this->layer_level][ $this->element ] .= $text;
+                        break;
+                }
+            }
+            else if( $this->inStyle !== false )
+            {
+                switch( $this->element )
+                {
+                    case 'Name':
+                        $this->inStyle .= $text;
+                        break;
+                }
+            }
+//            print $this->path . ':<b>' . $this->element . '=' . $text . '|</b><br>';
+//            print '---> ' . $this->layer_level . '/' . var_export($this->layer_props[ $this->layer_level],true) . '<br>';
+            
+            //$this->element = ''; // previously here instead of at start of _endElement
+        }
+    }
+    
+    function _endElement( $parser, $name )
+    {
+//print '_endElement ' . $name . "\n";
+        $this->element = ''; // instead of at end of _characterData function
+        
+        $this->path = substr( $this->path, 0, -(1+strlen($name)) );
+        switch( $name )
+        {
+            case 'Layer':
+                if( ! $this->layer_props[ $this->layer_level ]['layer_id'] )
+                {
+                    $this->layer_props[ $this->layer_level ]['layer_id'] = $this->numLayers++;
+                    if( $this->cbLayer )
+                        eval( $this->cbLayer . "(\$this->layer_props[\$this->layer_level], \$this->cbLayerParams );" );
+                    else
+                    {
+//                    print $this->layer_level . ' : ' . var_export($this->layer_props[ $this->layer_level ],true) . '<br>';
+                        $this->layers[] = $this->layer_props[ $this->layer_level ];
+                    }
+                }
+                $this->layer_level -= 1;
+                array_pop( $this->layer_props );
+                break;
+            case 'Attribution':
+                $this->inAttribution = false;
+                break;
+            case 'Style':
+                $this->inStyle = false;
+                break;
+            case 'Metadata':
+                $this->inMetadata = false;
+                break;
+            default:
+                break;
+        }
+    }
+    
+} // CapabilitiesParser
+
+?>

Copied: trunk/widgets/AddWMSLayer.js (from rev 2248, sandbox/jxlib-3.0/widgets/AddWMSLayer.js)
===================================================================
--- trunk/widgets/AddWMSLayer.js	                        (rev 0)
+++ trunk/widgets/AddWMSLayer.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,212 @@
+/**
+ * Fusion.Widget.AddWMSLayer
+ *
+ * $Id: Print.js 1906 2009-09-23 22:07:49Z chrisclaydon $
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+ /********************************************************************
+ * Class: Fusion.Widget.AddWMSLayer
+ *
+ * Opens a dialog box with a list of layers from a WMS server that can be added
+ * to the map.
+ *
+ * **********************************************************************/
+
+Fusion.Widget.AddWMSLayer = OpenLayers.Class(Fusion.Widget, {
+    
+    initializeWidget: function(widgetTag) {
+        var json = widgetTag.extension;
+        
+        this.serviceURL = json.ServiceURL ? json.ServiceURL[0] : '';
+        
+        this.dialogContentURL = Fusion.getFusionURL() + widgetTag.location + 'AddWMSLayer/AddWMSLayer.html';
+        this.addWMSLayerURL = widgetTag.location + 'AddWMSLayer/AddWMSLayer.php';
+        Fusion.addWidgetStyleSheet(widgetTag.location + 'AddWMSLayer/AddWMSLayer.css');
+        var onload = OpenLayers.Function.bind(this.contentLoaded, this);
+        this.domObj.set('load', {onComplete: onload});
+        this.domObj.load(this.dialogContentURL);
+    },
+    
+    contentLoaded: function() {
+        this.outputDiv = this.domObj.getElementById('AddWMSLayersContent');
+        this.urlInput = this.domObj.getElementById('wmsServerName');
+        if (this.serviceURL != '') {
+          this.urlInput.value = this.serviceURL;
+        }
+        var listButton = new Jx.Button({
+            label: 'List Layers',
+            onClick: OpenLayers.Function.bind(this.initializeWMS, this)
+        }).addTo(this.domObj.getElementById('listLayersButton'));
+    },
+    
+    initializeWMS: function() {
+        this.listLayersWait();
+        //prep the server URL to remove WMS params
+        var serverURL = this.urlInput.value;
+        if (serverURL.length >0) {
+          var newParams = [];
+          var urlParams = serverURL.split('?')
+          if (urlParams.length > 1) {
+            var params = urlParams[1].split('&');
+            for (var j=0; j<params.length; ++j) {
+              if (params[j].toLowerCase().indexOf('request')!=-1) continue;
+              if (params[j].toLowerCase().indexOf('version')!=-1) continue;
+              newParams.push(params[j]);
+            }
+            urlParams[1] = newParams.join('&');
+          }
+          serverURL = urlParams.join('?');
+          
+          var maps = this.oMap.getAllMaps(); 
+          var map = maps[0];
+          
+           var opts = {
+              parameters: {
+                  session: map.getSessionID(),
+                  mapname: map._sMapname,
+                  action: 'listLayersWMS',
+                  server: serverURL
+              }, 
+              onComplete: OpenLayers.Function.bind(this.wmsListLayers, this)
+          };
+          Fusion.ajaxRequest(this.addWMSLayerURL, opts);
+        }
+    },
+    
+/* 
+function catalogListLayers - CB from catalogManagerInitialize() with object create the html
+                             required to add the layers to the map. clicking on image spawns
+                             addCatalogLayer which inturn add's the clicked layer to the map.
+*/    
+    wmsListLayers: function(r) {
+      if (r.responseText) {
+        var gCatalogLayersObj;
+        try {
+          eval('gCatalogLayersObj='+r.responseText);
+        } catch (e) {
+          gCatalogLayersObj = {'error': e.stack};
+        }
+        if (gCatalogLayersObj) {
+        
+          this.outputDiv.innerHTML = '';
+            
+          if (gCatalogLayersObj.error) {
+            this.outputDiv.innerHTML = gCatalogLayersObj.error + '<br>' + gCatalogLayersObj.message;
+            return;
+          }
+            
+          var ul = document.createElement('ul');
+          ul.id = 'catalogListLayerUL';
+          this.outputDiv.appendChild(ul);
+      
+          for(var i=0;i<gCatalogLayersObj.length;i++){
+              var szOwsTitle = gCatalogLayersObj[i].owstitle;
+              if (szOwsTitle.length < 1) {
+                szOwsTitle = gCatalogLayersObj[i].name;
+              }
+              var li = document.createElement('li');
+              ul.appendChild(li);
+              
+              var a = document.createElement('a');
+              a.href = "javascript:void(0)";
+              a.layertype = gCatalogLayersObj[i].layertype;
+              a.layername =  gCatalogLayersObj[i].name;
+              a.owstitle =  gCatalogLayersObj[i].owstitle;
+              a.group =  gCatalogLayersObj[i].group;
+              a.srs = gCatalogLayersObj[i].srs;
+              a.imageFormat = gCatalogLayersObj[i].imageformat;
+              a.servername = gCatalogLayersObj[i].servername;
+              a.wmsservicetitle = gCatalogLayersObj[i].wmsservicetitle;
+              a.queryable = gCatalogLayersObj[i].queryable;
+              a.metadataurl = gCatalogLayersObj[i].metadataurl;
+              a.minx = gCatalogLayersObj[i].minx;
+              a.miny = gCatalogLayersObj[i].miny;
+              a.maxx = gCatalogLayersObj[i].maxx;
+              a.maxy = gCatalogLayersObj[i].maxy;
+              
+              a.onclick = OpenLayers.Function.bind(this.addWMSLayer, this, a);
+              
+              li.appendChild(a);
+              
+              a.innerHTML = szOwsTitle;
+              li.appendChild(a);
+          }
+        }
+      }
+    },
+    
+    listLayersWait: function() {
+        this.outputDiv.innerHTML = 'Request in progress...';
+    },
+    
+    /* 
+    function addWMSLayer - adds the clicked layer from the interface created by catalogListLayers
+                               then loads browseCatalog.php to add the clicked layer to the current 
+                               session map file. calls addCatalogLayerCB for a return responce.
+    
+    */    
+    addWMSLayer: function(cb){
+        var maps = this.oMap.getAllMaps(); 
+        var map = maps[0];
+        
+        //prep the servername to remove existing WMS params
+        var params = {
+            session: map.getSessionID(),
+            mapname: map._sMapname,
+            action: 'addLayer',
+            layertype: cb.layertype,
+            layername: cb.layername,
+            group: cb.group,
+            owstitle: cb.owstitle,
+            srs: map.oLayerOL.projection.projCode,
+            imageFormat: cb.imageFormat,
+            servername: cb.servername,
+            wmsservicetitle: cb.wmsservicetitle,
+            queryable: cb.queryable,
+            metadataurl: cb.metadataurl ? cb.metadataurl : ''
+        }
+        
+        // switch image to a different src.
+        //cb.src = 'images/icons/legend-layer.png';
+        var opts = {parameters: params, onComplete: OpenLayers.Function.bind(this.addWMSLayerCB, this)};
+        Fusion.ajaxRequest(this.addWMSLayerURL, opts);
+    },
+    
+    /* 
+    function addWMSLayerCB - CB func from addWMSLayer. The Layer is now added to mapfile, 
+                                 if o.addedLayer = true else something when wrong.
+    */    
+    addWMSLayerCB: function(r) {
+        var o = '';
+        eval('o='+r.responseText);    
+
+        if(o.addedLayer == true){
+          var map = this.oMap; 
+          var maps = map.getAllMaps();
+          map.triggerEvent(Fusion.Event.WMS_LAYER_ADDED, new Array(o));
+          maps[0].reloadMap();
+        } else {
+           // d.log('addCatalogLayerCB:could not add layer');
+        }
+    }
+    
+});

Deleted: trunk/widgets/CursorPosition/CursorPosition.css
===================================================================
--- sandbox/jxlib-3.0/widgets/CursorPosition/CursorPosition.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/CursorPosition/CursorPosition.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,17 +0,0 @@
-.spanCursorPosition {
-    display: -moz-inline-box;
-    display: inline-block;
-    padding: 2px;
-    margin: 0;
-    border: none;
-    width: 200px;
-    font-family: Arial, Helvetica, sans-serif;
-    font-size: 11px;
-    line-height: 16px;
-    background-color: transparent;
-    color: #000;
-}
-
-.jxToolItem .spanCursorPosition {
-    padding: 6px 2px;
-}
\ No newline at end of file

Copied: trunk/widgets/CursorPosition/CursorPosition.css (from rev 2248, sandbox/jxlib-3.0/widgets/CursorPosition/CursorPosition.css)
===================================================================
--- trunk/widgets/CursorPosition/CursorPosition.css	                        (rev 0)
+++ trunk/widgets/CursorPosition/CursorPosition.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,17 @@
+.spanCursorPosition {
+    display: -moz-inline-box;
+    display: inline-block;
+    padding: 2px;
+    margin: 0;
+    border: none;
+    width: 200px;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 11px;
+    line-height: 16px;
+    background-color: transparent;
+    color: #000;
+}
+
+.jxToolItem .spanCursorPosition {
+    padding: 6px 2px;
+}
\ No newline at end of file

Modified: trunk/widgets/CursorPosition.js
===================================================================
--- trunk/widgets/CursorPosition.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/CursorPosition.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -83,6 +83,9 @@
         
         this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.setUnits, this));
         this.registerParameter('Units');
+        
+        Fusion.addWidgetStyleSheet(widgetTag.location + '/CursorPosition/CursorPosition.css');
+        
     },
     
     setUiObject: function(uiObj) {

Modified: trunk/widgets/EditableScale/EditableScale.css
===================================================================
--- trunk/widgets/EditableScale/EditableScale.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/EditableScale/EditableScale.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,5 +1,28 @@
 .inputEditableScale {
-	border: 0;
+    margin: 2px;
+    padding: 4px;
+	border: 1px solid #bbb;
+    background-color: #fff;
+	color: #000;
 	font-family: Arial, Helvetica, sans-serif;
 	font-size: 11px;
+	line-height: 16px;
+}
+
+.inputEditableScalePrefix {
+    display: -moz-inline-box;
+    display: inline-block;
+    padding: 2px;
+    margin: 0;
+    border: none;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 11px;
+    line-height: 16px;
+    background-color: transparent;
+    color: #000;
+    
+}
+
+.jxToolItem .inputEditableScalePrefix {
+    padding: 6px 2px;
 }
\ No newline at end of file

Copied: trunk/widgets/GetFeatureInfo.js (from rev 2248, sandbox/jxlib-3.0/widgets/GetFeatureInfo.js)
===================================================================
--- trunk/widgets/GetFeatureInfo.js	                        (rev 0)
+++ trunk/widgets/GetFeatureInfo.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,164 @@
+/**
+ * Fusion.Widget.GetFeatureInfo
+ *
+ * $Id: GetFeatureInfo.js 1377 2008-04-16 19:27:32Z madair $
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+ /********************************************************************
+ * Class: Fusion.Widget.GetFeatureInfo
+ *
+ * A widget that displays a pre-configured search form to the user and then
+ * runs the search.  Searches are done on the attributes of specifiedd layers.
+ *
+ * uses JavaScript Scale Bar for MapServer 
+ * (http://mapserver.commenspace.org/tools/scalebar/
+ * **********************************************************************/
+
+Fusion.Event.GET_FEATURE_INFO_COMPLETE = Fusion.Event.lastEventId++;
+
+Fusion.Widget.GetFeatureInfo = OpenLayers.Class(Fusion.Widget, 
+{
+    wmsLayers: [],
+    isExclusive: true,
+    uiClass: Jx.Button,
+    
+    initializeWidget: function(widgetTag) {
+        //console.log('GetFeatureInfo.initialize');
+        var json = widgetTag.extension;
+        
+        var map = this.getMap().oMapOL;
+        // create a new event handler for single click query
+        this.handler = new OpenLayers.Handler.Click({ 'map': map }, {
+            'click': OpenLayers.Function.bind(this.issueRequest, this) }); 
+        
+        this.registerEventID(Fusion.Event.GET_FEATURE_INFO_COMPLETE);
+        this.getMap().registerEventID(Fusion.Event.WMS_LAYER_ADDED);
+        this.getMap().registerForEvent(Fusion.Event.WMS_LAYER_ADDED, 
+                          OpenLayers.Function.bind(this.layerAdded,this));
+        this.cursorNormal = ["url('images/grab.cur'),move", 'grab', '-moz-grab', 'move'];
+    },
+    
+    /**
+     * (public) activate()
+     *
+     * activate the measure tool
+     */
+    activateTool: function() {
+        this.getMap().activateWidget(this);
+    },
+
+    activate: function() {
+        this.handler.activate();
+    },
+    
+    deactivate: function() {
+        this.handler.deactivate();
+    },
+    
+    issueRequest: function(e) {
+      if (this.wmsLayers.length < 1) {
+        alert("there are no WMS layers to query");
+        return;
+      }
+      
+      var map = this.getMap();
+      var queryLayer = map.getActiveLayer();
+      var layer = null;
+      if (queryLayer) {
+        for (var i=0; i<this.wmsLayers.length; ++i) {
+          if (queryLayer.layerName == this.wmsLayers[i].wms_name) {
+            layer = this.wmsLayers[i];
+            break;
+          }
+        }
+      }
+      if (!layer) {
+        alert('Please select a WMS layer by clicking on the layer name in the legend.');
+        return;
+      }
+      
+      var infoFormat = 'text/html';
+      if (e.shiftKey) {
+        infoFormat = 'text/plain';
+      } else if (e.ctrlKey) {
+        infoFormat = 'text/xml';
+      }
+      var size = map.oMapOL.getSize();
+      var mouseLoc = map.oMapOL.getLonLatFromPixel(e.xy);
+      //for (var i=0; i<this.wmsLayers.length; ++i) {
+        //var layer = this.wmsLayers[i];
+        var params = {
+                      REQUEST: "GetFeatureInfo",
+                      VERSION: layer.wms_version,
+                      SRS: layer.wms_srs,
+                      FORMAT: layer.wms_format,
+                      STYLES: "",
+                      EXCEPTIONS: "application/vnd.ogc.se_xml",
+                      BBOX: map.getCurrentExtents().toBBOX(),
+                      X: e.xy.x,
+                      Y: e.xy.y,
+                      INFO_FORMAT: infoFormat,
+                      LAYERS: layer.wms_name,
+                      QUERY_LAYERS: layer.wms_name,
+                      WIDTH: size.w,
+                      HEIGHT: size.h};
+        var join = (layer.wms_connection.indexOf('?')<0) ? '?' : '&';
+        var url = layer.wms_connection + join + OpenLayers.Util.getParameterString(params);
+        /*
+        var proxy = '/fusion-1.1/ext/coin/php/RemoteQuery.php?rawoutput=true&remoteUrl=';
+        url = proxy + url;
+        var infoDialog = new Jx.Dialog({
+            contentURL: url,
+            modal: false,
+            title: 'Feature information for '+layer.wms_name,
+            resizable: true
+        });
+        infoDialog.open();
+        */
+        //console.log(url);
+        window.open(url);
+        /*
+        OpenLayers.Request.GET({
+            url: layer.wms_connection, 
+            params: params,
+            success: OpenLayers.Function.bind(this.displayResults, this), 
+            failure: OpenLayers.Function.bind(this.getFeatureInfoFailed, this), 
+            scope: this
+        });
+        */
+      //}
+      OpenLayers.Event.stop(e);
+    },
+    
+    displayResults: function(arg1, arg2) {
+    },
+    
+    getFeatureInfoFailed: function(arg1, arg2) {
+    },
+    
+    layerAdded: function(eventId, layers) {
+      for (var i=0; i<layers.length; ++i) {
+        this.wmsLayers.push(layers[i]);
+      }
+    }
+});
+

Modified: trunk/widgets/InvokeURL.js
===================================================================
--- trunk/widgets/InvokeURL.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/InvokeURL.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -68,6 +68,9 @@
         var map = this.getMap();
         if (this.bSelectionOnly || !map) {
             if (map && map.hasSelection()) {
+                if (map.aMaps[0]._sQueryfile) {
+                  this.additionalParameters.push('queryfile='+map.aMaps[0]._sQueryfile);
+                }
                 if (this.action) {
                     this.action.setEnabled(true);
                 } else {
@@ -95,9 +98,9 @@
         
         var map = this.getMap();
         var params = [];
-        params.push('LOCALE='+Fusion.locale);
-        params.push('SESSION='+map.getSessionID());
-        params.push('MAPNAME='+map.getMapName());
+        params.push('locale='+Fusion.locale);
+        params.push('session='+map.getSessionID());
+        params.push('mapname='+map.getMapName());
         params = params.concat(this.additionalParameters);
         if (url.indexOf('?') < 0) {
             url += '?';

Modified: trunk/widgets/LayerManager.js
===================================================================
--- trunk/widgets/LayerManager.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/LayerManager.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -58,6 +58,7 @@
         //console.log("initializeWidget");
         var json = widgetTag.extension;
         this.delIconSrc = json.DeleteIcon ? json.DeleteIcon[0] : 'images/icons/select-delete.png';
+        this.infoIconSrc = json.LayerInfoIcon ? json.LayerInfoIcon[0] : 'images/icons/tree_layer_info.png';
     
         Fusion.addWidgetStyleSheet(widgetTag.location + 'LayerManager/LayerManager.css');
         this.cursorNormal = ["url('images/grab.cur'),move", 'grab', '-moz-grab', 'move'];
@@ -65,19 +66,26 @@
         this.map = this.getMap();
         this.map.registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.mapLoaded, this));
         this.map.registerForEvent(Fusion.Event.MAP_RELOADED, OpenLayers.Function.bind(this.mapReLoaded, this));
+        this.map.registerForEvent(Fusion.Event.MAP_SCALE_RANGE_LOADED, OpenLayers.Function.bind(this.scaleRangeLoaded, this));
         // update changes to the legend in this widget
         this.map.aMaps[0].registerForEvent(Fusion.Event.LAYER_PROPERTY_CHANGED, OpenLayers.Function.bind(this.layerChanged,this));
     },
     
-    mapLoaded: function() {
+    scaleRangeLoaded: function() {
         this.draw();
     },
-    layerChanged: function() {
+    mapLoaded: function() {
+        //this.draw();
+    },
+    
+    layerChanged: function(eventId, layer) {
+        this.updateLayer(this.map.aMaps[0]);
         this.updateSessionMapFile();
     },
     mapReLoaded: function(){
         this.draw();
    },
+   
    /**
      * remove the dom objects representing the legend layers and groups
      */
@@ -85,7 +93,7 @@
         //console.log("clear");
         while (node.childNodes.length > 0) {
           this.clear(node.childNodes[0]);
-            node.destroy(node.childNodes[0]);
+            node.removeChild(node.childNodes[0]);
         }
     },
   
@@ -98,7 +106,7 @@
         //console.log("draw");
       if (this.mapList) {
         this.clear(this.mapList);
-        this.mapList.destroy();
+        //this.mapList.destroy();
         this.mapList = null;
       }
        
@@ -136,10 +144,10 @@
 
     processMapBlock: function(blockDom, map) {
       //console.log("processMapBlock");
-      var mapBlockList = document.createElement('ul');
-      mapBlockList.className = 'jxLmanSet';
-      mapBlockList.id = 'fusionLayerManager_'+map.getMapName();
-      blockDom.appendChild(mapBlockList);
+      this.mapBlockList = document.createElement('ul');
+      this.mapBlockList.className = 'jxLmanSet';
+      this.mapBlockList.id = 'fusionLayerManager_'+map.getMapName();
+      blockDom.appendChild(this.mapBlockList);
       map.layerPrefix = 'layer_';   //TODO make this unique for each block
       //this process all layers within an OL layer
       var processArray = map.aLayers;
@@ -148,7 +156,7 @@
         var blockItem = document.createElement('li');
         blockItem.className = 'jxLmanLayer';
         blockItem.id = map.layerPrefix+i;
-        mapBlockList.appendChild(blockItem);
+        this.mapBlockList.appendChild(blockItem);
         this.createItemHtml(blockItem, processArray[i]);
         blockItem.layer = processArray[i];
       }
@@ -157,12 +165,19 @@
                 constrain: true,
                 clone: false,
                 revert: true,
-                onComplete: OpenLayers.Function.bind(this.updateLayer, $(mapBlockList.id), map)
+                onComplete: OpenLayers.Function.bind(this.updateLayer, this, map)
             };
-    var mySortables = new Sortables(mapBlockList.id, sortableOptions);
+      var mySortables = new Sortables(this.mapBlockList.id, sortableOptions);
     },
    
   createItemHtml: function(parent, layer) {
+    var infoIcon = document.createElement('img');
+    infoIcon.src = this.infoIconSrc;
+    OpenLayers.Event.observe(infoIcon, 'click', OpenLayers.Function.bind(this.showLayerInfo, this, layer));
+    infoIcon.style.visibility = 'hidden';
+    //re-do this as Jx template
+    //parent.appendChild(infoIcon);
+    
     var delIcon = document.createElement('img');
     delIcon.src = this.delIconSrc;
     OpenLayers.Event.observe(delIcon, 'click', OpenLayers.Function.bind(this.deleteLayer, this, layer));
@@ -179,6 +194,23 @@
       visSelect.checked = false;
     }
     
+    var img = document.createElement('img');
+    var scale = layer.oMap.getScale();
+    var range = layer.getScaleRange(scale);
+    if (range && range.styles.length>0) {
+        var style = range.styles[0];//TODO: handle multiple styles?
+        var iconX = 0;
+        var iconY = 0;
+        if (style && style.iconX >= 0 && style.iconY >= 0) {
+            iconX = -1 * (style.iconX);
+            iconY = -1 * (style.iconY);
+        }
+        img.src = Jx.aPixel.src;
+        img.style.backgroundImage = 'url('+style.iconOpt.url+')';
+        img.style.backgroundPosition = iconX + 'px ' + iconY + 'px';
+        parent.appendChild(img);
+    }
+    
     var label = document.createElement('a');
     label.innerHTML = layer.legendLabel;
     OpenLayers.Event.observe(label, 'mouseover', OpenLayers.Function.bind(this.setGrabCursor, this));
@@ -186,31 +218,36 @@
     OpenLayers.Event.observe(label, 'mouseout', OpenLayers.Function.bind(this.setNormalCursor, this));
     parent.appendChild(label);
     
-    OpenLayers.Event.observe(parent, 'mouseover', OpenLayers.Function.bind(this.setHandleVis, this, delIcon));
-    OpenLayers.Event.observe(parent, 'mouseout', OpenLayers.Function.bind(this.setHandleHide, this, delIcon));
+    OpenLayers.Event.observe(parent, 'mouseover', OpenLayers.Function.bind(this.setHandleVis, this, delIcon, infoIcon));
+    OpenLayers.Event.observe(parent, 'mouseout', OpenLayers.Function.bind(this.setHandleHide, this, delIcon, infoIcon));
   },
   
-  setHandleVis: function(delIcon) {
+  setHandleVis: function(delIcon, infoIcon) {
     delIcon.style.visibility = 'visible';
+    infoIcon.style.visibility = 'visible';
   },
   
-  setHandleHide: function(delIcon) {
+  setHandleHide: function(delIcon, infoIcon) {
     delIcon.style.visibility = 'hidden';
+    infoIcon.style.visibility = 'hidden';
   },
   
   setGrabCursor: function(ev) {
-    this.setCursor(this.cursorDrag, ev.currentTarget.parentNode);
+    var targetLI = (new Event(ev)).target.parentNode;
+    this.setCursor(this.cursorDrag, targetLI);
   },
   
   setDragCursor: function(ev) {
-   this.setCursor(this.cursorDrag, ev.currentTarget.parentNode);
+    var targetLI = (new Event(ev)).target.parentNode;
+   this.setCursor(this.cursorDrag, targetLI);
   },
   
   setNormalCursor: function(ev) {
-    this.setCursor('auto', ev.currentTarget.parentNode);
+    var targetLI = (new Event(ev)).target.parentNode;
+    this.setCursor('auto', targetLI);
   },
   
-  setCursor : function(cursor, domObj) {
+  setCursor: function(cursor, domObj) {
       this.cursor = cursor;
       if (cursor && cursor.length && typeof cursor == 'object') {
           for (var i = 0; i < cursor.length; i++) {
@@ -226,42 +263,66 @@
       }
   },
   
-  updateLayer: function(map, ul) {
+  updateLayer: function(map) {
    //console.log("updateLayer");
+   //console.log("updateLayer");
     //reorder the layers in the client as well as the session
     var aLayerIndex = [];
     var aIds = [];
-    var nLayers = this.childNodes.length;
+    var nLayers = this.mapBlockList.childNodes.length;
     for (var i=0; i<nLayers; ++i) {
-      aIds[i] = this.childNodes[i].id.split('_');
+      aIds[i] = this.mapBlockList.childNodes[i].id.split('_');
       var index = parseInt(aIds[i].pop());
       aLayerIndex.push(index);
-      this.childNodes[i].id = '';
+      this.mapBlockList.childNodes[i].id = '';
     }
     
     //reset the ID's on the LI elements to be in order
-    for (var i=0; i<this.childNodes.length; ++i) {
-      var node = this.childNodes[i];
+    for (var i=0; i<this.mapBlockList.childNodes.length; ++i) {
+      var node = this.mapBlockList.childNodes[i];
       aIds[i].push(i);
       node.id = aIds[i].join('_');
-      node.childNodes[1].checked = node.layer.isVisible()
+      node.childNodes[2].checked = node.layer.isVisible()
     }
+    
+    //check tos ee if the layer indexes have been modified
+    var indexModified = false;
+    if (aLayerIndex.length == map.aLayers.length) {
+      for (var i=0; i<aLayerIndex.length; ++i) {
+        if (aLayerIndex[i] != i) {
+          indexModified = true;
+          break;
+        }
+      }
+    } else {
+      indexModified = true;
+    }
 
-    map.reorderLayers(aLayerIndex);
+    if (indexModified) {
+        map.reorderLayers(aLayerIndex);
+    }
   },
    
-  updateMapBlock: function(map, ul) {
+  updateMapBlock: function(map) {
     //reorder the OL layers
   },
   
   deleteLayer: function(layer, ev) {
    // console.log("deleteLayer");
     var targetLI = (new Event(ev)).target.parentNode;
-    var ul = targetLI.parentNode;
     $(targetLI).dispose();
-    this.updateLayer(layer.oMap, ul);
+    
+    this.oMap.layerRoot.deleteLayer(layer.uniqueId);
+    this.updateLayer(layer.oMap);
   },
   
+  showLayerInfo: function(layer, ev) {
+    var layerInfoUrl = layer.oMap.getLayerInfoUrl(layer.layerName);
+    if (layerInfoUrl) {
+      window.open(layerInfoUrl);
+    }
+  },
+  
   visChanged: function(layer2, ev) {
     var target = (new Event(ev)).target;
     var layer = target.parentNode.layer;

Modified: trunk/widgets/Legend/Legend.css
===================================================================
--- trunk/widgets/Legend/Legend.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/Legend/Legend.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,48 +1,73 @@
-.fusionLegendItemCheckbox span,
-.fusionLegendFolder span,
-.fusionLegendItem span,
-.fusionLegendItemCheckbox input,
-.fusionLegendFolder input,
-.fusionLegendItemCheckbox .jxTreeIcon,
-.fusionLegendFolder .jxTreeIcon,
-.fusionLegendItem .jxTreeIcon {
-  position: relative;
-  vertical-align: middle;
+/* The tree item a tag */
+.jxTreeContainer a.fusionCheckboxItem  {
+    padding-left: 36px;
 }
 
-.jxTree a.fusionLegendItem,
-.jxTreeRoot a.fusionLegendItem,
-.jxTree a.fusionLegendFolder,
-.jxTreeRoot a.fusionLegendFolder,
-.jxTree a.fusionLegendItemCheckbox,
-.jxTreeRoot a.fusionLegendItemCheckbox,
-.jxTree a.fusionLegendFolder:hover, 
-.jxTreeRoot a.fusionLegendFolder:hover,
-.jxTree a.fusionLegendItemCheckbox:hover, 
-.jxTreeRoot a.fusionLegendItemCheckbox:hover {
-    padding: 0px;
+a.fusionCheckboxItem img.jxTreeIcon {
+    left: 16px;
 }
 
-.jxTreeBranchOpen .fusionLegendItemCheckbox .jxTreeIcon, 
-.jxTreeBranchLastOpen .fusionLegendItemCheckbox .jxTreeIcon {
+li.jxUnselectable a.jxTreeItem {
+  cursor: default;
+}
+            
+.fusionLegendTreeRoot li.jxUnselectable a.jxPressed,
+.fusionLegendTreeRoot li.jxUnselectable a.jxTreeItem:focus,
+.fusionLegendTreeRoot li.jxUnselectable a.jxTreeItem:hover {
   background-position: left top;
+  background-color: transparent;
+  border: none;
+  margin: 0px 1px 0px 17px;
 }
 
-.jxTreeBranchClosed .fusionLegendItemCheckbox .jxTreeIcon, 
-.jxTreeBranchLastClosed .fusionLegendItemCheckbox .jxTreeIcon {
-  background-position: left top;
+
+/* The checkbox */
+.fusionLegendCheckboxContainer {
+    position: absolute;
+    left: 16px;
+    top: 0px;
+    height: 20px;
+    width: 16px;
+    z-index: 2;
+    line-height: 20px;
+    text-align: center;
+    font-size: 0px;
 }
 
-span.fusionLegendLabel {
-  padding-left: 4px;
+.fusionLegendCheckboxInput {
+    vertical-align: middle;
 }
 
-.jxTree li,
-.jxTreeRoot li {
-    zoom: 1;
+/* Group Info Icon */
+.fusionShowGroupInfo .fusionGroupInfo {
+    display: block;
 }
 
-.jxTreeIcon {
-    top: auto !important;
-    left: auto !important;
-}
\ No newline at end of file
+.fusionGroupInfo {
+    display: none;
+}
+
+.fusionGroupInfoIcon {
+    
+}
+
+/* Layer Information Icon */
+.fusionLayerInfo {
+    display: none;
+    position: absolute;
+    right: 1px;
+    top: 0px;
+    height: 20px;
+    width: 16px;
+    z-index: 2;
+    line-height: 20px;
+    font-size: 0px;
+}
+
+.fusionShowLayerInfo .fusionLayerInfo {
+    display: block;
+}
+
+.fusionLayerInfoIcon {
+    vertical-align: middle;
+}

Modified: trunk/widgets/Legend.js
===================================================================
--- trunk/widgets/Legend.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/Legend.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,954 +1,933 @@
-/**
- * Fusion.Widget.Legend
- *
- * $Id$
- *
- * Copyright (c) 2007, DM Solutions Group Inc.
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
- /********************************************************************
- * Class: Fusion.Widget.Legend
- *
- * A widget to display a legend of all layers.
- *
- * **********************************************************************/
-
-Fusion.Widget.Legend = OpenLayers.Class(Fusion.Widget,  {
-
-    /**
-     * Constant: defaultLayerDWFIcon
-     * {String} The default image for DWF layer
-     */
-    defaultLayerDWFIcon: 'images/icons/legend-DWF.png',
-
-    /**
-     * Constant: defaultLayerRasterIcon
-     * {String} The default image for Raster layer
-     */
-    defaultLayerRasterIcon: 'images/icons/legend-raster.png',
-
-    /**
-     * Constant: defaultLayerThemeIcon
-     * {String} The default image for layers that are currently themed.
-     */
-    defaultLayerThemeIcon: 'images/icons/legend-theme.png',
-
-    /**
-     * Constant: defaultDisabledLayerIcon
-     * {String} The default image for layers that are out of scale.
-     */
-    defaultDisabledLayerIcon: 'images/icons/legend-layer.png',
-
-    /**
-     * Constant: defaultRootFolderIcon
-     * {String} The default image for the root folder
-     */
-    defaultRootFolderIcon: 'images/icons/legend-map.png',
-
-    /**
-     * Constant: defaultLayerInfoIcon
-     * {String} The default image for layer info
-     */
-    defaultLayerInfoIcon: 'images/icons/tree_layer_info.png',
-
-    /**
-     * Constant: defaultGroupInfoIcon
-     * {String} The default image for groupd info
-     */
-    defaultGroupInfoIcon: 'images/icons/tree_group_info.png',
-
-    initializeWidget: function(widgetTag) {
-        // TODO: maybe it's a good idea to do a function like Fusion.Widget.BindRenderer.. for limit the code
-        // duplication if we plan to apply this pattern to others widgets
-        Fusion.addWidgetStyleSheet(widgetTag.location + 'Legend/Legend.css');
-
-        // TODO: maybe it's a good idea to do a function like Fusion.Widget.BindRenderer.. for limit the code
-        //       duplication if we plan to apply this pattern to others widgets
-        var json = widgetTag.extension;
-        if (json.LegendRenderer)
-        {
-            var renderer = eval(json.LegendRenderer[0]);
-            if (renderer && renderer.prototype.CLASS_NAME
-                && renderer.prototype.CLASS_NAME == "Fusion.Widget.Legend.LegendRenderer") {
-                this.renderer = new renderer(this, widgetTag);
-            } else if (typeof renderer == "function") {
-                var renderFunction = renderer;
-                this.renderer = new Fusion.Widget.Legend.LegendRenderer(this);
-                this.renderer.mapLoaded = renderFunction;
-                this.renderer.mapReloaded = renderFunction;
-                this.renderer.mapLoading = false;
-            } else {
-                this.renderer = new Fusion.Widget.Legend.LegendRendererDefault(this, widgetTag);
-            }
-        } else {
-            this.renderer = new Fusion.Widget.Legend.LegendRendererDefault(this, widgetTag);
-        }
-
-        if (this.renderer.mapReloaded)
-            this.getMap().registerForEvent(Fusion.Event.MAP_RELOADED,
-                                           OpenLayers.Function.bind(this.renderer.mapReloaded, this.renderer));
-        if (this.renderer.mapLoading)
-            this.getMap().registerForEvent(Fusion.Event.MAP_LOADING,
-                                           OpenLayers.Function.bind(this.renderer.mapLoading,this.renderer));
-        if (this.renderer.mapLoaded)
-            this.getMap().registerForEvent(Fusion.Event.MAP_LOADED,
-                                           OpenLayers.Function.bind(this.renderer.mapLoaded, this.renderer));
-    }
-});
-
-/* Class: Fusion.Widget.Legend.LegendRenderer
- * This is a class designed to help users to create their own renderer
- * for customize the legend.
- */
-Fusion.Widget.Legend.LegendRenderer = OpenLayers.Class(
-{
-     /**
-     * Property: oLegend
-     * {<Fusion.Widget.Legend>} The parent widget that uses
-     *                                  the renderer.
-     */
-    oLegend: null,
-
-    /**
-     * Property: layerRoot
-     * {Groups} The groups of all layers.
-     *
-     */
-    layerRoot: null,
-
-    initialize: function(legend) {
-        this.oLegend = legend;
-        this.layerRoot = this.getMap().layerRoot;
-    },
-
-    /**
-     * Method: renderLegend
-     * Abstract method that have the main purpose to draw the legend. This method
-     * should be implemented by all concrete class.
-     *
-     */
-    renderLegend: function() {},
-
-    /**defaultDisabledLayerIcon
-     * Method: mapLoading
-     * Abstract method that handle the event: Fusion.Event.MAP_LOADING. This method
-     * is optional.
-     *
-     */
-    mapLoading: function() {},
-
-    /**
-     * Method: mapLoaded
-     * Abstract method that handle the event: Fusion.Event.MAP_LOADED. This method
-     * occur only at the first load of the map and should be implemented by all concrete class.
-     *
-     */
-    mapLoaded: function() {},
-
-     /**
-     * Method: mapReloaded
-     * Abstract method that handle the event: Fusion.Event.MAP_RELOADED. This method
-     * should be implemented by all concrete class.
-     *
-     */
-    mapReloaded: function() {},
-
-    /**
-     * Method: getMap
-     * Helper method to obtains the map.
-     *
-     * Returns:
-     * {<Fusion.Maps>} The map that uses the SelectionPanel Widget.
-     */
-    getMap: function() {
-        return this.oLegend.getMap();
-    },
-
-    CLASS_NAME: "Fusion.Widget.Legend.LegendRenderer"
-});
-
-
-/* Class: Fusion.Widget.Legend.LegendRendererDefault
- * This class provide a default legend as a collapsable tree.
- *
- */
-
-Fusion.Widget.Legend.LegendRendererDefault = OpenLayers.Class(Fusion.Widget.Legend.LegendRenderer,
-{
-    /**
-     * Property: showRootFolder
-     * {Boolean} This controls whether the tree will have a single root node that
-     * contains the name of the map as its label.  By default, the root node does
-     * not appear.  Set to "true" or "1" to make the root node appear.
-     */
-    showRootFolder: false,
-
-    /**
-     * Property: currentNode
-     * {Jx.TreeNode} The current selected node.
-     */
-    currentNode: null,
-
-    /**
-     * Property: bIsDrawn
-     * {Boolean} Determine if the map is drawn.
-     */
-    bIsDrawn: false,
-
-    /**
-     * Property: targetFolder
-     * {Jx.TreeFolder} The current TreeFolder that the mouse will interact with.
-     */
-    targetFolder: null,
-
-    /**
-     * Property: bIncludeVisToggle
-     * {Boolean} Determine if non-visible layer must be draw in the legend.
-     */
-    bIncludeVisToggle: true,
-
-    initialize: function(legend, widgetTag) {
-        Fusion.Widget.Legend.LegendRenderer.prototype.initialize.apply(this, [legend]);
-
-        var json = widgetTag.extension;
-        this.imgLayerDWFIcon = json.LayerDWFIcon ? json.LayerDWFIcon[0] : this.oLegend.defaultLayerDWFIcon;
-        this.imgLayerRasterIcon = json.LayerRasterIcon ? json.LayerRasterIcon[0] : this.oLegend.defaultLayerRasterIcon;
-        this.imgLayerThemeIcon = json.LayerThemeIcon ? json.LayerThemeIcon[0] : this.oLegend.defaultLayerThemeIcon;
-        this.imgDisabledLayerIcon = json.DisabledLayerIcon ? json.DisabledLayerIcon[0] : this.oLegend.defaultDisabledLayerIcon;
-        this.imgLayerInfoIcon = json.LayerInfoIcon ? json.LayerInfoIcon[0] : this.oLegend.defaultLayerInfoIcon;
-        this.imgGroupInfoIcon = json.GroupInfoIcon ? json.GroupInfoIcon[0] : this.oLegend.defaultGroupInfoIcon;
-
-        //not used?
-        //this.layerInfoURL = json.LayerInfoURL ? json.LayerInfoURL[0] : '';
-        this.selectedLayer = null;
-
-        this.oTree = new Jx.Tree({parent:this.oLegend.domObj});
-
-        this.hideInvisibleLayers = (json.HideInvisibleLayers && json.HideInvisibleLayers[0]) == 'true' ? true : false;
-        //don't show the root folder by default
-        this.showRootFolder = (json.ShowRootFolder && json.ShowRootFolder[0] == 'true') ? true:false;
-        //do show the map folder by default
-        this.showMapFolder = (json.ShowMapFolder && json.ShowMapFolder[0] == 'false') ? false:true;
-
-        if (!this.showRootFolder) {
-            //console.log('supressing root folder');
-            this.oRoot = this.oTree;
-        } else {
-           // console.log('showing root folder');
-            var opt = {
-                label: OpenLayers.i18n('defaultMapTitle'),
-                open: true,
-                draw: this.renderFolderCheckbox,
-                contextMenu: this.getContextMenu(),
-                'class':'fusionLegendFolder'
-            };
-            this.oRoot = new Jx.TreeFolder(opt);
-            this.oTree.append(this.oRoot);
-            this.oRoot.options.contextMenu.add(
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('collapse'),
-                    onClick: OpenLayers.Function.bind(this.collapseBranch, this, this.oRoot)
-                }),
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('expand'),
-                    onClick: OpenLayers.Function.bind(this.expandBranch, this, this.oRoot)
-                })
-            );
-        }
-
-        this.extentsChangedWatcher = this.update.bind(this);
-    },
-
-    getContextMenu: function() {
-        return new Jx.Menu.Context(this.name).add(
-            new Jx.Menu.Item({
-                label: OpenLayers.i18n('refresh'),
-                onClick: OpenLayers.Function.bind(this.update, this)
-            }),
-            new Jx.Menu.Item({
-                label: OpenLayers.i18n('collapseAll'),
-                onClick: OpenLayers.Function.bind(this.collapseAll, this)
-            }),
-            new Jx.Menu.Item({
-                label: OpenLayers.i18n('expandAll'),
-                onClick: OpenLayers.Function.bind(this.expandAll, this)
-            })
-        );
-    },
-
-    expandAll: function(folder) {
-        for (var i=0; i<this.oTree.nodes.length; i++) {
-            var item = this.oTree.nodes[i];
-            if (item instanceof Jx.TreeFolder) {
-              item.expand();
-              this.recurseTree('expand', item);
-            }
-        }
-        if (this.showRootFolder) {
-          this.oRoot.expand();
-        }
-    },
-
-    collapseAll: function(folder) {
-        for (var i=0; i<this.oTree.nodes.length; i++) {
-            var item = this.oTree.nodes[i];
-            if (item instanceof Jx.TreeFolder) {
-              item.collapse();
-              this.recurseTree('collapse', item);
-            }
-        }
-        if (this.showRootFolder) {
-          this.oRoot.collapse();
-        }
-    },
-
-    collapseBranch: function(folder) {
-        folder.collapse();
-    },
-
-    expandBranch: function(folder) {
-        folder.expand();
-    },
-
-  /**
-     * recursively descend the tree applying the request operation which is either 'collapse' or 'expand'
-     *
-     * @param op the operation to execute
-     * @param the folder to operate on
-     */
-    recurseTree: function(op, folder) {
-        for (var i=0; i<folder.nodes.length; i++) {
-            var item = folder.nodes[i];
-            if (item instanceof Jx.TreeFolder) {
-                this.recurseTree(op, item);
-                item[op]();
-            }
-        }
-    },
-
-    scaleRangesLoaded: function() {
-        this.layerRoot = this.getMap().layerRoot;
-        this.renderLegend();
-    },
-    mapLoading: function() {
-        this.getMap().deregisterForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.extentsChangedWatcher);
-        this.clear();
-    },
-
-    mapLoaded: function() {
-        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.extentsChangedWatcher);
-        this.getMap().loadScaleRanges(OpenLayers.Function.bind(this.scaleRangesLoaded, this));
-    },
-
-    mapReloaded: function() {
-        this.getMap().loadScaleRanges(OpenLayers.Function.bind(this.scaleRangesLoaded, this));
-    },
-    /**
-     * the map state has become invalid in some way (layer added, removed,
-     * ect).  For now, we just re-request the map state from the server
-     * which calls draw which recreates the entire legend from scratch
-     *
-     * TODO: more fine grained updating of the legend would be nice
-     */
-    invalidate: function() {
-        this.draw();
-    },
-
-    /**
-     * Callback for legend XML response. Creates a list of layers and sets up event
-     * handling. Create groups if applicable.
-     * TODO: error handling
-     *
-     * @param r Object the reponse xhr object
-     */
-    /*renderLegend: function(r) {
-        this.bIsDrawn = false;
-        this.clear();
-
-        if (this.showRootFolder) {
-            this.oRoot.itemLabelobj.innerHTML = this.getMap().getMapTitle();
-        }
-        var startGroup = this.layerRoot;
-        if (!this.showMapFolder) {
-          startGroup = this.layerRoot.groups[0];
-        }
-        if (!startGroup.legend) {
-            startGroup.legend = {};
-            startGroup.legend.treeItem = this.oRoot;
-        }
-        for (var i=0; i<startGroup.groups.length; i++) {
-            //startGroup.groups[i].visible = true;
-            this.processMapGroup(startGroup.groups[i], this.oRoot);
-        }
-        for (var i=0; i<startGroup.layers.length; i++) {
-            this.processMapLayer(startGroup.layers[i], this.oRoot);
-        }
-        this.bIsDrawn = true;
-        this.update();
-    },*/
-    renderLegend: function(r) {
-        this.bIsDrawn = false;
-        this.clear();
-
-        if (this.showRootFolder) {
-            this.oRoot.itemLabelobj.innerHTML = this.getMap().getMapTitle();
-        }
-
-        if (this.showMapFolder) {
-        this.renderGroup(this.layerRoot);
-        } else {
-        if (this.layerRoot.groups.length > 0) {
-            for (var i = 0; i < this.layerRoot.groups.length; i++)
-                this.renderGroup(this.layerRoot.groups[i]);
-        } else {
-            for (var i = 0; i < group.layers.length; i++)
-                this.processMapLayer(group.layers[i], this.oRoot);
-        }
-        }
-
-        this.bIsDrawn = true;
-        this.update();
-    },
-
-    renderGroup: function(group) {
-        if (!group.legend) {
-            group.legend = {};
-            group.legend.treeItem = this.oRoot;
-        }
-        for (var i = 0; i < group.groups.length; i++) {
-            this.processMapGroup(group.groups[i], this.oRoot);
-        }
-        for (var i = 0; i < group.layers.length; i++) {
-            this.processMapLayer(group.layers[i], this.oRoot);
-        }
-    },
-
-    processMapGroup: function(group, folder) {
-        if (group.displayInLegend) {
-            /* make a 'namespace' on the group object to store legend-related info */
-            group.legend = {};
-            var opt = {
-                label: group.legendLabel,
-                open: group.expandInLegend,
-                draw: this.renderFolderCheckbox,
-                contextMenu: this.getContextMenu(),
-                'class':'fusionLegendFolder'
-            };
-            group.legend.treeItem = new Jx.TreeFolder(opt);
-            group.legend.treeItem.domObj.store('data', group);
-            group.legend.treeItem.options.contextMenu.add(
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('collapse'),
-                    onClick: OpenLayers.Function.bind(this.collapseBranch, this, group.legend.treeItem)
-                }),
-                new Jx.Menu.Item({
-                    label: OpenLayers.i18n('expand'),
-                    onClick: OpenLayers.Function.bind(this.expandBranch, this, group.legend.treeItem)
-                })
-            );
-
-            folder.append(group.legend.treeItem);
-            if(group.legend.treeItem.checkBox)
-            {
-                group.legend.treeItem.checkBox.checked = group.visible?true:false;
-                OpenLayers.Event.observe(group.legend.treeItem.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, group));
-            }
-
-            var groupInfo = group.oMap.getGroupInfoUrl(group.groupName);
-            if (groupInfo) {
-                var a = document.createElement('a');
-                a.href = groupInfo;
-                if (groupInfo.indexOf('javascript:') < 0) {
-                  a.target = '_blank';
-                }
-                var img = document.createElement('img');
-                Jx.addToImgQueue({element:img, src: this.imgGroupInfoIcon});
-                img.border = 0;
-                a.appendChild(img);
-                group.legend.treeItem.domObj.insertBefore(a, group.legend.treeItem.domObj.childNodes[4]);
-            }
-            if (this.oSelectionListener) {
-                group.legend.treeItem.addEvent('click', OpenLayers.Function.bind(this.selectionChanged, this));
-            }
-            for (var i=0; i<group.groups.length; i++) {
-                this.processMapGroup(group.groups[i], group.legend.treeItem);
-            }
-            for (var i=0; i<group.layers.length; i++) {
-                this.processMapLayer(group.layers[i], group.legend.treeItem);
-            }
-        }
-    },
-
-    processMapLayer: function(layer, folder) {
-        /* make a 'namespace' on the layer object to store legend-related info */
-        layer.legend = {};
-        layer.legend.parentItem = folder;
-        layer.legend.currentRange = null;
-        layer.oMap.registerForEvent(Fusion.Event.LAYER_PROPERTY_CHANGED, OpenLayers.Function.bind(this.layerPropertyChanged, this));
-    },
-
-    layerPropertyChanged: function(eventID, layer) {
-        if(layer.legend.treeItem.checkBox)
-        {
-            layer.legend.treeItem.checkBox.checked = layer.isVisible();
-        }
-    },
-
-    update: function() {
-        if (this.bIsDrawn) {
-            window.setTimeout(OpenLayers.Function.bind(this._update, this), 1);
-        }
-    },
-
-    /**
-     * update the tree when the map scale changes
-     */
-    _update: function() {
-        var map = this.getMap();
-        var currentScale = map.getScale();
-        for (var i=0; i<map.layerRoot.groups.length; i++) {
-            this.updateGroupLayers(map.layerRoot.groups[i], currentScale);
-        }
-        for (var i=0; i<map.layerRoot.layers.length; i++) {
-            this.updateLayer(map.layerRoot.layers[i], currentScale);
-        }
-    },
-
-    /**
-     * remove the dom objects representing the legend layers and groups
-     */
-    clear: function() {
-        while (this.oRoot.nodes.length > 0) {
-            this.oRoot.remove(this.oRoot.nodes[0]);
-        }
-    },
-
-    selectionChanged: function(o) {
-        if (this.currentNode) {
-          //console.log(this.currentNode);
-            $(this.currentNode.domObj.childNodes[1]).removeClass('jxTreeItemSelected');
-        }
-        this.currentNode = o;
-        $(this.currentNode.domObj.childNodes[1]).addClass('jxTreeItemSelected');
-
-        var data = o.domObj.retrieve('data');
-        if (data instanceof Fusion.Layers.Group) {
-            this.getMap().setActiveLayer(null);
-        } else {
-            this.getMap().setActiveLayer(data);
-        }
-    },
-    updateGroupLayers: function(group, fScale) {
-        for (var i=0; i<group.groups.length; i++) {
-            this.updateGroupLayers(group.groups[i], fScale);
-        }
-        for (var i=0; i<group.layers.length; i++) {
-            this.updateLayer(group.layers[i], fScale);
-        }
-    },
-    updateLayer: function(layer, fScale) {
-
-        var checkbox = layer.isBaseMapLayer ? false : this.bIncludeVisToggle;
-        if (!layer.displayInLegend || !layer.legend) {
-            return;
-        }
-        var range = layer.getScaleRange(fScale);
-        if (range == layer.legend.currentRange && layer.legend.treeItem) {
-            return;
-        }
-
-        layer.legend.currentRange = range;
-        if (range != null) {
-            if (range.styles.length > 1) {
-                //tree item needs to be a folder
-                if (!layer.legend.treeItem) {
-                    layer.legend.treeItem = this.createFolderItem(layer, checkbox);
-                    if(layer.legend.treeItem.checkBox)
-                    {
-                        OpenLayers.Event.observe(layer.legend.treeItem.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, layer));
-                    }
-                    layer.parentGroup.legend.treeItem.append(layer.legend.treeItem);
-                } else if (layer.legend.treeItem instanceof Jx.TreeItem) {
-                    var insertAt = this.clearTreeItem(layer);
-                    layer.legend.treeItem = this.createFolderItem(layer, checkbox);
-                    if(layer.legend.treeItem.checkBox)
-                    {
-                        OpenLayers.Event.observe(layer.legend.treeItem.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, layer));
-                    }
-                    layer.parentGroup.legend.treeItem.insert(layer.legend.treeItem, insertAt);
-                } else {
-                    while(layer.legend.treeItem.nodes.length > 0) {
-                        layer.legend.treeItem.remove(layer.legend.treeItem.nodes[0]);
-                    }
-                }
-                for (var i=0; i<range.styles.length; i++) {
-                    var item = this.createTreeItem(layer, range.styles[i], fScale, false);
-                    layer.legend.treeItem.append(item);
-                }
-            } else {
-
-                var style = range.styles[0];
-                if (style && !style.legendLabel) {
-                  style.legendLabel = layer.legendLabel;
-                }
-                if (!layer.legend.treeItem) {
-                    layer.legend.treeItem = this.createTreeItem(layer, style, fScale, checkbox);
-                    if (checkbox) {
-                      OpenLayers.Event.observe(layer.legend.treeItem.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, layer));
-                    }
-
-                    layer.parentGroup.legend.treeItem.append(layer.legend.treeItem);
-                } else if (layer.legend.treeItem instanceof Jx.TreeFolder) {
-                    var insertAt = this.clearTreeItem(layer);
-                    layer.legend.treeItem = this.createTreeItem(layer, style, fScale, checkbox);
-                    if (checkbox) {
-                      OpenLayers.Event.observe(layer.legend.treeItem.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, layer));
-                    }
-
-                    layer.parentGroup.legend.treeItem.insert(layer.legend.treeItem, insertAt);
-                } else {
-                    if (range.styles.length > 0) {
-                        layer.legend.treeItem.domImg.style.backgroundImage = 'url('+layer.oMap.getLegendImageURL(fScale, layer, range.styles[0])+')';
-                        layer.legend.treeItem.domImg.style.backgroundPosition = '0px 0px';
-                        $(layer.legend.treeItem.domObj).removeClass('jxDisabled');
-                    } else {
-                        $(layer.legend.treeItem.domObj).addClass('jxDisabled');
-                    }
-                }
-            }
-            if (checkbox) {
-              layer.legend.treeItem.checkBox.checked = layer.visible?true:false;
-              if (layer.layerTypes[0] == 4 || layer.layerTypes[0] == 5 || range.styles.length > 0) {
-                layer.legend.treeItem.checkBox.disabled = false;
-              } else {
-                layer.legend.treeItem.checkBox.disabled = true;
-              }
-            }
-        } else {
-            if (this.hideInvisibleLayers) {
-                if (layer.legend.treeItem) {
-                    layer.parentGroup.legend.treeItem.remove(layer.legend.treeItem);
-                    layer.legend.treeItem = null;
-                }
-            } else {
-              var newTreeItem = this.createTreeItem(layer, {legendLabel: layer.legendLabel}, null, checkbox);
-                if (checkbox) {
-                  OpenLayers.Event.observe(newTreeItem.checkBox, 'click', OpenLayers.Function.bind(this.stateChanged, this, layer));
-                }
-                if (layer.legend.treeItem) {
-                    if (checkbox) layer.legend.treeItem.checkBox.disabled = true;
-                    layer.parentGroup.legend.treeItem.replace(newTreeItem, layer.legend.treeItem);
-                    layer.legend.treeItem.finalize();
-                } else {
-                    layer.parentGroup.legend.treeItem.append(newTreeItem);
-                }
-                layer.legend.treeItem = newTreeItem;
-            }
-        }
-        if (layer.legend.treeItem) {
-            layer.legend.treeItem.domObj.store('data', layer);
-        }
-    },
-
-    createFolderItem: function(layer, hasCheckbox) {
-        var opt = {
-            label: layer.legendLabel == '' ? '&nbsp;' : layer.legendLabel,
-            open: layer.expandInLegend,
-            draw: hasCheckbox ? this.renderFolderCheckbox : this.renderFolder,
-            'class':'fusionLegendItemCheckbox',
-            contextMenu: this.getContextMenu(),
-            // image overrides
-            image: this.imgLayerThemeIcon
-        };
-        var folder = new Jx.TreeFolder(opt);
-        folder.options.contextMenu.add(
-            new Jx.Menu.Item({
-                label: OpenLayers.i18n('collapse'),
-                onClick: OpenLayers.Function.bind(this.collapseBranch, this, folder)
-            }),
-            new Jx.Menu.Item({
-                label: OpenLayers.i18n('expand'),
-                onClick: OpenLayers.Function.bind(this.expandBranch, this, folder)
-            })
-        );
-
-
-        var layerInfo = layer.oMap.getLayerInfoUrl(layer.layerName);
-        if (layerInfo) {
-            var a = document.createElement('a');
-            a.href = layerInfo;
-            if (layerInfo.indexOf('javascript:') < 0) {
-              a.target = '_blank';
-            }
-            var img = document.createElement('img');
-            Jx.addToImgQueue({element:img, src:this.imgLayerInfoIcon});
-            img.border = 0;
-            a.appendChild(img);
-            folder.domObj.insertBefore(a, folder.domObj.childNodes[4]);
-        }
-        folder.addEvent('click', OpenLayers.Function.bind(this.selectionChanged, this));
-
-        return folder;
-    },
-    createTreeItem: function(layer, style, scale, bCheckBox) {
-        var opt = {};
-        opt.statusIsDefault = layer.statusDefault;
-
-        //set the label
-        if (style && style.legendLabel) {
-            opt.label = style.legendLabel == '' ? '&nbsp;' : style.legendLabel;
-        } else {
-            opt.label = layer.legendLabel == '' ? '&nbsp;' : layer.legendLabel;
-        }
-
-        //set the checkbox rendererer
-        if (bCheckBox ) {
-            opt.draw = this.renderItemCheckBox;
-        } else {
-            opt.draw = this.renderItem;
-        }
-
-        if (!style) {
-            opt.image = this.imgDisabledLayerIcon;
-            opt.enabled = false;
-        } else {
-            if(style.iconOpt && style.iconOpt.url){
-                opt.image = style.iconOpt.url;
-            }else{
-                opt.image = layer.oMap.getLegendImageURL(scale, layer, style);
-            }
-        }
-        // MapGuide DWF and Raster layer
-         // MapGuide Raster and DWF layer
-        if(layer.layerTypes[0] == 4){
-            opt.image = this.imgLayerRasterIcon;
-            opt.enabled = true;
-        } else if(layer.layerTypes[0] == 5){
-            opt.image = this.imgLayerDWFIcon;
-            opt.enabled = true;
-        }
-        opt.contextMenu = this.getContextMenu();
-
-        var item = new Jx.TreeItem(opt);
-        if (style && style.iconOpt && style.iconX >= 0 && style.iconY >= 0) {
-            item.domImg;
-            item.domImg.style.backgroundImage = 'url('+opt.image+')';
-            item.domImg.src = Jx.aPixel.src;
-            item.domImg.style.backgroundPosition = (-1*style.iconX) + 'px ' + (-1*style.iconY) + 'px';
-            if (style.iconOpt.width) {
-                item.domImg.style.width = style.iconOpt.width + 'px';
-            }
-            if (style.iconOpt.height) {
-                item.domImg.style.height = style.iconOpt.height + 'px';
-            }
-        }
-
-        if (bCheckBox) {
-            //item.domObj.insertBefore(layer.legend.checkBox, item.domObj.childNodes[1]);
-            /* only need to add layer info if it has a check box too */
-            var layerInfo = layer.oMap.getLayerInfoUrl(layer.layerName);
-            if (layerInfo) {
-                var a = document.createElement('a');
-                a.href = layerInfo;
-                if (layerInfo.indexOf('javascript:') < 0) {
-                  a.target = '_blank';
-                }
-                var img = document.createElement('img');
-                Jx.addToImgQueue({element:img, src: this.imgLayerInfoIcon});
-                img.border = 0;
-                a.appendChild(img);
-                item.domObj.insertBefore(a, item.domObj.childNodes[4]);
-            }
-        }
-
-        item.addEvent('click', OpenLayers.Function.bind(this.selectionChanged, this));
-
-        return item;
-    },
-    clearTreeItem: function(layer) {
-        var prevSibling = null;
-        if (layer.legend.treeItem && layer.legend.treeItem.owner) {
-            prevSibling = layer.legend.treeItem.domObj.previousSibling;
-            layer.legend.treeItem.domObj.store('data', null);
-            layer.legend.treeItem.owner.remove(layer.legend.treeItem);
-            layer.legend.treeItem.finalize();
-            layer.legend.treeItem = null;
-        }
-        return prevSibling;
-    },
-    stateChanged: function(obj, event) {
-        if (obj.legend && obj.legend.treeItem.checkBox) {
-            if (obj.legend.treeItem.checkBox.checked) {
-                obj.show();
-            } else {
-                obj.hide();
-            }
-        }
-        OpenLayers.Event.stop(event, true);
-    },
-
-    renderFolder: function() {
-        var domA = new Element('a',{
-            'class':this.options['class'],
-            href:'javascript:void(0)',
-            events: {
-                click: this.selected.bindWithEvent(this),
-                dblclick: this.selected.bindWithEvent(this),
-                contextmenu: this.options.contextMenu.show.bindWithEvent(this.options.contextMenu)
-            }
-        });
-
-
-        this.domImg = document.createElement('img');
-        this.domImg.className = 'jxTreeIcon ' + (this.options.imageClass ? this.options.imageClass : '');
-        this.domImg.src = Jx.aPixel.src;
-
-        if (this.options.image) {
-            this.domImg.style.backgroundImage = 'url('+this.options.image+')';
-        }
-
-        var domLabel = new Element('span',{
-            'class': 'fusionLegendLabel',
-            html: this.options.label
-        });
-
-        domA.appendChild(this.domImg);
-        domA.appendChild(domLabel);
-
-        this.itemLabelobj = domA;
-
-        return domA;
-
-    },
-
-    renderFolderCheckbox: function() {
-        var domA = new Element('a',{
-            'class':this.options['class'],
-            href:'javascript:void(0)',
-            events: {
-                click: this.selected.bindWithEvent(this),
-                dblclick: this.selected.bindWithEvent(this),
-                contextmenu: this.options.contextMenu.show.bindWithEvent(this.options.contextMenu)
-            }
-        });
-
-        this.checkBox = document.createElement('input');
-        this.checkBox.type = 'checkbox';
-
-        this.domImg = document.createElement('img');
-        this.domImg.className = 'jxTreeIcon ' + (this.options.imageClass ? this.options.imageClass : '');
-        this.domImg.src = Jx.aPixel.src;
-
-        if (this.options.image) {
-            this.domImg.style.backgroundImage = 'url('+this.options.image+')';
-        }
-
-        var domLabel = new Element('span',{
-            'class': 'fusionLegendLabel',
-            html: this.options.label
-        });
-
-        domA.appendChild(this.checkBox);
-        domA.appendChild(this.domImg);
-        domA.appendChild(domLabel);
-
-        this.itemLabelobj = domA;
-
-        return domA;
-
-    },
-
-    renderItem: function() {
-
-        var domA = new Element('a', {
-            'class': 'fusionLegendItem',
-            href: 'javascript:void(0)',
-            events: {
-                click: this.selected.bindWithEvent(this),
-                dblclick: this.selected.bindWithEvent(this),
-                contextmenu: this.options.contextMenu.show.bindWithEvent(this.options.contextMenu)
-            }
-        });
-
-
-        this.domImg = document.createElement('img');
-        this.domImg.className = 'jxTreeIcon ' + (this.options.imageClass ? this.options.imageClass : '');
-        this.domImg.src = Jx.aPixel.src;
-
-        if (this.options.image) {
-            this.domImg.style.backgroundImage = 'url('+this.options.image+')';
-        }
-
-        var domLabel = new Element('span',{
-            'class': 'fusionLegendLabel',
-            html: this.options.label
-        });
-
-        domA.appendChild(this.domImg);
-        domA.appendChild(domLabel);
-        this.itemLabelobj = domA;
-
-        return domA;
-    },
-
-    renderItemCheckBox: function() {
-        var domA = new Element('a', {
-            'class': 'fusionLegendItemCheckbox',
-            'href':'javascript:void(0);',
-            events: {
-                click: this.selected.bindWithEvent(this),
-                dblclick: this.selected.bindWithEvent(this),
-                contextmenu: this.options.contextMenu.show.bindWithEvent(this.options.contextMenu)
-            }
-        });
-
-        this.checkBox = document.createElement('input');
-        this.checkBox.type = 'checkbox';
-
-        /* layer is set to "status default" set checkbox to checked , disabled , read only*/
-        if(this.options.statusIsDefault){
-            this.checkBox.checked = true;
-            this.checkBox.disabled = true;
-            this.checkBox.readOnly = true;
-        }
-
-        this.domImg = document.createElement('img');
-        this.domImg.className = 'jxTreeIcon ' + (this.options.imageClass ? this.options.imageClass : '');
-        this.domImg.src = Jx.aPixel.src;
-
-        if (this.options.image) {
-            this.domImg.style.backgroundImage = 'url('+this.options.image+')';
-        }
-
-        var domLabel = new Element('span',{
-            'class': 'fusionLegendLabel',
-            html: this.options.label
-        });
-
-
-        domA.appendChild(this.checkBox);
-        domA.appendChild(this.domImg);
-        domA.appendChild(domLabel);
-        this.itemLabelobj = domA;
-
-        return domA;
-    }
-
-});
+/**
+ * Fusion.Widget.Legend
+ *
+ * $Id$
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+ /********************************************************************
+ * Class: Fusion.Widget.Legend
+ *
+ * A widget to display a legend of all layers.
+ *
+ * **********************************************************************/
+
+Fusion.Widget.Legend = OpenLayers.Class(Fusion.Widget,  {
+
+    /**
+     * Constant: defaultLayerDWFIcon
+     * {String} The default image for DWF layer
+     */
+    defaultLayerDWFIcon: 'images/icons/legend-DWF.png',
+    
+    /**
+     * Constant: defaultLayerRasterIcon
+     * {String} The default image for Raster layer
+     */
+    defaultLayerRasterIcon: 'images/icons/legend-raster.png',
+    
+    /**
+     * Constant: defaultLayerThemeIcon
+     * {String} The default image for layers that are currently themed.
+     */
+    defaultLayerThemeIcon: 'images/icons/legend-theme.png',
+
+    /**
+     * Constant: defaultDisabledLayerIcon
+     * {String} The default image for layers that are out of scale.
+     */
+    defaultDisabledLayerIcon: 'images/icons/legend-layer.png',
+
+    /**
+     * Constant: defaultRootFolderIcon
+     * {String} The default image for the root folder
+     */
+    defaultRootFolderIcon: 'images/icons/legend-map.png',
+    
+    /**
+     * Constant: defaultLayerInfoIcon
+     * {String} The default image for layer info
+     */
+    defaultLayerInfoIcon: 'images/icons/layer-info.png',
+    
+    /**
+     * Constant: defaultGroupInfoIcon
+     * {String} The default image for groupd info
+     */
+    defaultGroupInfoIcon: 'images/icons/group-info.png',
+    
+    initializeWidget: function(widgetTag) {
+        // TODO: maybe it's a good idea to do a function like Fusion.Widget.BindRenderer.. for limit the code
+        // duplication if we plan to apply this pattern to others widgets
+        Fusion.addWidgetStyleSheet(widgetTag.location + 'Legend/Legend.css');
+        
+        // TODO: maybe it's a good idea to do a function like Fusion.Widget.BindRenderer.. for limit the code
+        //       duplication if we plan to apply this pattern to others widgets
+        var json = widgetTag.extension;
+        if (json.LegendRenderer)
+        {
+            var renderer = eval(json.LegendRenderer[0]);
+            if (renderer && renderer.prototype.CLASS_NAME
+                && renderer.prototype.CLASS_NAME == "Fusion.Widget.Legend.LegendRenderer") {
+                this.renderer = new renderer(this, widgetTag);
+            } else if (typeof renderer == "function") {
+                var renderFunction = renderer;
+                this.renderer = new Fusion.Widget.Legend.LegendRenderer(this);
+                this.renderer.mapLoaded = renderFunction;
+                this.renderer.mapReloaded = renderFunction;
+                this.renderer.mapLoading = false;
+            } else {
+                this.renderer = new Fusion.Widget.Legend.LegendRendererDefault(this, widgetTag);
+            }
+        } else {
+            this.renderer = new Fusion.Widget.Legend.LegendRendererDefault(this, widgetTag);
+        }
+
+        if (this.renderer.mapReloaded)
+            this.getMap().registerForEvent(Fusion.Event.MAP_RELOADED,
+                                           OpenLayers.Function.bind(this.renderer.mapReloaded, this.renderer));
+        if (this.renderer.mapLoading)
+            this.getMap().registerForEvent(Fusion.Event.MAP_LOADING,
+                                           OpenLayers.Function.bind(this.renderer.mapLoading,this.renderer));
+        if (this.renderer.mapLoaded)
+            this.getMap().registerForEvent(Fusion.Event.MAP_LOADED,
+                                           OpenLayers.Function.bind(this.renderer.mapLoaded, this.renderer));
+        if (this.renderer.scaleRangesLoaded)
+            this.getMap().registerForEvent(Fusion.Event.MAP_SCALE_RANGE_LOADED,
+                                           OpenLayers.Function.bind(this.renderer.scaleRangesLoaded, this.renderer));
+    }
+});
+
+/* Class: Fusion.Widget.Legend.LegendRenderer
+ * This is a class designed to help users to create their own renderer
+ * for customize the legend.
+ */
+Fusion.Widget.Legend.LegendRenderer = OpenLayers.Class(
+{
+     /**
+     * Property: oLegend
+     * {<Fusion.Widget.Legend>} The parent widget that uses
+     *                                  the renderer.
+     */
+    oLegend: null,
+
+    /**
+     * Property: layerRoot
+     * {Groups} The groups of all layers.
+     *
+     */
+    layerRoot: null,
+
+    initialize: function(legend) {
+        this.oLegend = legend;
+        this.layerRoot = this.getMap().layerRoot;
+    },
+
+    /**
+     * Method: renderLegend
+     * Abstract method that have the main purpose to draw the legend. This method
+     * should be implemented by all concrete class.
+     *
+     */
+    renderLegend: function() {},
+    
+    /**defaultDisabledLayerIcon
+     * Method: mapLoading
+     * Abstract method that handle the event: Fusion.Event.MAP_LOADING. This method
+     * is optional.
+     *
+     */
+    mapLoading: function() {},
+
+    /**
+     * Method: mapLoaded
+     * Abstract method that handle the event: Fusion.Event.MAP_LOADED. This method
+     * occur only at the first load of the map and should be implemented by all concrete class.
+     *
+     */
+    mapLoaded: function() {},
+
+     /**
+     * Method: mapReloaded
+     * Abstract method that handle the event: Fusion.Event.MAP_RELOADED. This method
+     * should be implemented by all concrete class.
+     *
+     */
+    mapReloaded: function() {},
+
+     /**
+     * Method: mapRefresh
+     * Abstract method that handle the event: Fusion.Event.MAP_LAYER_ORDER_CHANGED. This method
+     * should be implemented by all concrete class.
+     *
+     */
+    mapRefresh: function() {},
+
+    /**
+     * Method: getMap
+     * Helper method to obtains the map.
+     *
+     * Returns:
+     * {<Fusion.Maps>} The map that uses the SelectionPanel Widget.
+     */
+    getMap: function() {
+        return this.oLegend.getMap();
+    },
+
+    CLASS_NAME: "Fusion.Widget.Legend.LegendRenderer"
+});
+
+
+/* Class: Fusion.Widget.Legend.LegendRendererDefault
+ * This class provide a default legend as a collapsable tree.
+ *
+ */
+
+Fusion.Widget.Legend.LegendRendererDefault = OpenLayers.Class(Fusion.Widget.Legend.LegendRenderer,
+{
+    /**
+     * Property: showRootFolder
+     * {Boolean} This controls whether the tree will have a single root node that
+     * contains the name of the map as its label.  By default, the root node does
+     * not appear.  Set to "true" or "1" to make the root node appear.
+     */
+    showRootFolder: false,
+
+    /**
+     * Property: currentNode
+     * {Jx.TreeNode} The current selected node.
+     */
+    currentNode: null,
+    
+    /**
+     * Property: bIsDrawn
+     * {Boolean} Determine if the map is drawn.
+     */
+    bIsDrawn: false,
+
+    /**
+     * Property: updateDelay
+     * {Integer} number of milliseconds to delay the update of legend
+     */
+    updateDelay: 500,
+
+    /**
+     * Property: targetFolder
+     * {Jx.TreeFolder} The current TreeFolder that the mouse will interact with.
+     */
+    targetFolder: null,
+
+    /**
+     * Property: bIncludeVisToggle
+     * {Boolean} Determine if non-visible layer must be draw in the legend.
+     */
+    bIncludeVisToggle: true,
+    offsetsCalculated: false,
+   
+    initialize: function(legend, widgetTag) {   
+        Fusion.Widget.Legend.LegendRenderer.prototype.initialize.apply(this, [legend]);
+
+        var json = widgetTag.extension;
+        this.imgLayerDWFIcon = json.LayerDWFIcon ? json.LayerDWFIcon[0] : this.oLegend.defaultLayerDWFIcon;
+        this.imgLayerRasterIcon = json.LayerRasterIcon ? json.LayerRasterIcon[0] : this.oLegend.defaultLayerRasterIcon;
+        this.imgLayerThemeIcon = json.LayerThemeIcon ? json.LayerThemeIcon[0] : this.oLegend.defaultLayerThemeIcon;
+        this.imgDisabledLayerIcon = json.DisabledLayerIcon ? json.DisabledLayerIcon[0] : this.oLegend.defaultDisabledLayerIcon;
+        this.imgLayerInfoIcon = json.LayerInfoIcon ? json.LayerInfoIcon[0] : this.oLegend.defaultLayerInfoIcon;
+        this.imgGroupInfoIcon = json.GroupInfoIcon ? json.GroupInfoIcon[0] : this.oLegend.defaultGroupInfoIcon;
+       
+        //not used?
+        //this.layerInfoURL = json.LayerInfoURL ? json.LayerInfoURL[0] : '';
+        this.selectedLayer = null;
+        
+        this.selection = new Jx.Selection({
+            onSelect: function(item) {
+                var treeItem = item.retrieve('jxTreeItem');
+                var data = treeItem.options.data;
+                if (data instanceof Fusion.Layers.Group) {
+                    this.getMap().setActiveLayer(null);
+                } else {
+                    this.getMap().setActiveLayer(data);
+                }
+            }.bind(this)
+        });
+       
+        this.oTree = new Jx.Tree({
+            template: '<ul class="jxTreeRoot fusionLegendTreeRoot"></ul>',
+            selection:this.selection
+        }).addTo(this.oLegend.domObj);
+       
+        this.hideInvisibleLayers = (json.HideInvisibleLayers && json.HideInvisibleLayers[0]) == 'true' ? true : false;
+        //don't show the root folder by default
+        this.showRootFolder = (json.ShowRootFolder && json.ShowRootFolder[0] == 'true') ? true:false;
+        //do show the map folder by default
+        this.showMapFolder = (json.ShowMapFolder && json.ShowMapFolder[0] == 'false') ? false:true;
+        
+        if (!this.showRootFolder) {
+            //console.log('supressing root folder');
+            this.oRoot = this.oTree;
+        } else {
+           // console.log('showing root folder');
+            var opt = {
+                label: OpenLayers.i18n('defaultMapTitle'),
+                // contextMenu: this.getContextMenu(),
+                open: true
+            };
+            this.oRoot = new Jx.TreeFolder(opt);
+            this.oTree.add(this.oRoot);
+            // this.oRoot.options.contextMenu.add([
+            //     new Jx.Menu.Item({
+            //         label: OpenLayers.i18n('collapse'),
+            //         onClick: OpenLayers.Function.bind(this.collapseBranch, this, this.oRoot)
+            //     }),
+            //     new Jx.Menu.Item({
+            //         label: OpenLayers.i18n('expand'),
+            //         onClick: OpenLayers.Function.bind(this.expandBranch, this, this.oRoot)
+            //     })]
+            // );
+        }
+        
+        this.extentsChangedWatcher = this.update.bind(this);
+    },
+    
+    getContextMenu: function() {
+        return new Jx.Menu.Context(this.name).add([
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('refresh'),
+                onClick: OpenLayers.Function.bind(this.update, this)
+            }),
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('collapseAll'),
+                onClick: OpenLayers.Function.bind(this.collapseAll, this)
+            }),
+            new Jx.Menu.Item({
+                label: OpenLayers.i18n('expandAll'),
+                onClick: OpenLayers.Function.bind(this.expandAll, this)
+            })]
+        );
+    },
+    
+    expandAll: function(folder) {
+        this.oTree.items().each(function(item){
+            if (item instanceof Jx.TreeFolder) {
+                this.recurseTree('expand', item);
+            }
+        },this);
+        if (this.showRootFolder) {
+          this.oRoot.expand();
+        }
+    },
+    
+    collapseAll: function(folder) {
+        this.oTree.items().each(function(item){
+            if (item instanceof Jx.TreeFolder) {
+                this.recurseTree('collapse', item);
+            }
+        },this);
+        if (this.showRootFolder) {
+          this.oRoot.collapse();
+        }
+    },
+    
+    collapseBranch: function(folder) {
+        folder.collapse();
+    },
+    
+    expandBranch: function(folder) {
+        folder.expand();
+    },
+    
+  /**
+     * recursively descend the tree applying the request operation which is either 'collapse' or 'expand'
+     *
+     * @param op the operation to execute
+     * @param the folder to operate on
+     */
+    recurseTree: function(op, folder) {
+        folder.items().each(function(item){
+            if (item instanceof Jx.TreeFolder) {
+                this.recurseTree(op, item);
+                item[op]();
+            }
+        },this);
+    },
+   
+    scaleRangesLoaded: function() {
+        this.layerRoot = this.getMap().layerRoot;
+        this.renderLegend();
+    },
+    
+    mapLoading: function() {
+        this.getMap().deregisterForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.extentsChangedWatcher);
+        this.clear();
+    },
+   
+    mapLoaded: function() {
+        this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, this.extentsChangedWatcher);
+        var baseLayer = this.getMap().aMaps[0]; 
+        baseLayer.registerForEvent(Fusion.Event.MAP_LAYER_ORDER_CHANGED, OpenLayers.Function.bind(this.mapRefresh, this));
+        this.layerRoot = this.getMap().layerRoot;
+        //this.renderLegend();
+    },
+    
+    mapReloaded: function() {
+        this.layerRoot = this.getMap().layerRoot;
+        this.renderLegend();
+    },
+    
+    mapRefresh: function() {
+        var baseLayer = this.getMap().aMaps[0];
+        baseLayer.parseLayers();
+        this.layerRoot = this.getMap().layerRoot;
+        this.renderLegend();
+    },
+    
+    /**
+     * Callback for legend XML response. Creates a list of layers and sets up event
+     * handling. Create groups if applicable.
+     * TODO: error handling
+     *
+     * @param r Object the reponse xhr object
+     */
+    renderLegend: function(r) {
+        this.bIsDrawn = false;
+        this.clear();
+
+        if (this.showRootFolder) {
+            this.oRoot.setLabel(this.getMap().getMapTitle());
+        }
+        var startGroup = this.layerRoot;
+        if (!this.showMapFolder) {
+          startGroup = this.layerRoot.groups[0];
+        }
+        if (!startGroup.legend) {
+            startGroup.legend = {};
+            startGroup.legend.treeItem = this.oRoot;
+        }
+        for (var i=0; i<startGroup.groups.length; i++) {
+            //startGroup.groups[i].visible = true;
+            this.processMapGroup(startGroup.groups[i], this.oRoot);
+        }
+        for (var i=0; i<startGroup.layers.length; i++) {
+            this.processMapLayer(startGroup.layers[i], this.oRoot);
+        }
+        this.bIsDrawn = true;
+        this.update();
+    },
+
+    processMapGroup: function(group, folder) {
+        if (group.displayInLegend) {
+            /* make a 'namespace' on the group object to store legend-related info */
+            group.legend = {};
+            var opt = {
+                label: group.legendLabel,
+                open: group.expandInLegend,
+                // contextMenu: this.getContextMenu(),
+                checked: group.visible
+            };
+            var treeItem = new Fusion.Widget.Legend.TreeFolder(opt);
+            treeItem.options.data = group;
+            group.legend.treeItem = treeItem;
+            // treeItem.options.contextMenu.add(
+            //     new Jx.Menu.Item({
+            //         label: OpenLayers.i18n('collapse'),
+            //         onClick: OpenLayers.Function.bind(this.collapseBranch, this, treeItem)
+            //     }),
+            //     new Jx.Menu.Item({
+            //         label: OpenLayers.i18n('expand'),
+            //         onClick: OpenLayers.Function.bind(this.expandBranch, this, treeItem)
+            //     })
+            // );
+
+            folder.add(treeItem);
+
+            var groupInfo = group.oMap.getGroupInfoUrl(group.groupName);
+            if (groupInfo) {
+                treeItem.setGroupInfo(groupInfo, this.imgGroupInfoIcon);
+            }
+            for (var i=0; i<group.groups.length; i++) {
+                this.processMapGroup(group.groups[i], treeItem);
+            }
+            for (var i=0; i<group.layers.length; i++) {
+                this.processMapLayer(group.layers[i], treeItem);
+            }
+        }
+    },
+   
+    processMapLayer: function(layer, folder) {
+        /* make a 'namespace' on the layer object to store legend-related info */
+        layer.legend = {};
+        layer.legend.parentItem = folder;
+        layer.legend.currentRange = null;
+        layer.oMap.registerForEvent(Fusion.Event.LAYER_PROPERTY_CHANGED, OpenLayers.Function.bind(this.layerPropertyChanged, this));
+    },
+   
+    layerPropertyChanged: function(eventID, layer) {
+        layer.legend.treeItem.check(layer.isVisible());
+    },
+
+    updateTimer: null,
+    update: function() {
+        if (this.bIsDrawn) {
+          if (this.updateTimer) {
+            window.clearTimeout(this.updateTimer);
+            this.updateTimer = null;
+          }
+          this.updateTimer = window.setTimeout(OpenLayers.Function.bind(this._update, this), this.updateDelay);
+        }
+    },
+   
+    /**
+     * update the tree when the map scale changes
+     */
+    _update: function() {
+        this.updateTimer = null;
+        var map = this.getMap();
+        var currentScale = map.getScale();
+        for (var i=0; i<map.layerRoot.groups.length; i++) {
+            this.updateGroupLayers(map.layerRoot.groups[i], currentScale);
+        }
+        for (var i=0; i<map.layerRoot.layers.length; i++) {
+            this.updateLayer(map.layerRoot.layers[i], currentScale);
+        }
+    },
+   
+    /**
+     * remove the dom objects representing the legend layers and groups
+     */
+    clear: function() {
+        //console.log('clear legend');
+        var map = this.getMap();
+        for (var i=0; i<map.layerRoot.groups.length; i++) {
+            this.clearGroup(map.layerRoot.groups[i]);
+        }
+        for (var i=0; i<map.layerRoot.layers.length; i++) {
+          if (map.layerRoot.layers[i].legend) {
+            map.layerRoot.layers[i].legend.treeItem = null;
+            map.layerRoot.layers[i].legend.checkbox = null;
+            map.layerRoot.layers[i].legend.currentRange = null;
+          }
+        }
+        this.oRoot.empty();
+    },
+    
+    clearGroup: function(group) {
+      for (var i=0; i<group.groups.length; i++) {
+        this.clearGroup(group.groups[i]);
+      }
+      for (var i=0; i<group.layers.length; i++) {
+        if (group.layers[i].legend) {
+          group.layers[i].legend.treeItem = null;
+          group.layers[i].legend.currentRange = null;
+        }
+      }
+    },
+    
+    updateGroupLayers: function(group, fScale) {
+        for (var i=0; i<group.groups.length; i++) {
+            this.updateGroupLayers(group.groups[i], fScale);
+        }
+        for (var i=0; i<group.layers.length; i++) {
+            this.updateLayer(group.layers[i], fScale);
+        }
+    },
+    updateLayer: function(layer, fScale) {
+        /* no need to do anything if we are hiding the layer */
+        if (!layer.displayInLegend || !layer.legend) {
+            return;
+        }
+        /* check the layer's current scale range against the previous one
+         * if the range hasn't changed, don't do anything
+         */
+        var range = layer.getScaleRange(fScale);
+        if (range == layer.legend.currentRange && layer.legend.treeItem) {
+            return;
+        }
+        
+        /* remember the range we are now representing for the next update */
+        layer.legend.currentRange = range;
+        
+        /* if layer is in range and has at least one style */
+        if (range != null && range.styles && range.styles.length > 0) {
+            /* if it has more than one style, we represent it as a folder
+             * with classes as items in it
+             */
+            if (range.styles.length > 1) {
+                //tree item needs to be a folder
+                if (!layer.legend.treeItem) {
+                    layer.legend.treeItem = this.createFolderItem(layer);
+                    layer.parentGroup.legend.treeItem.add(layer.legend.treeItem);
+                } else if (layer.legend.treeItem instanceof Fusion.Widget.Legend.TreeItem) {
+                    this.clearTreeItem(layer);
+                    layer.legend.treeItem = this.createFolderItem(layer);
+                    layer.parentGroup.legend.treeItem.add(layer.legend.treeItem);
+                } else {
+                    layer.legend.treeItem.empty();
+                }
+                for (var i=0; i<range.styles.length; i++) {
+                    var item = this.createTreeItem(layer, range.styles[i], fScale, false);
+                    layer.legend.treeItem.add(item);
+                }
+            /* if there is only one style, we represent it as a tree item */
+            } else {
+                var style = range.styles[0];
+                if (!style.legendLabel) {
+                    style.legendLabel = layer.legendLabel;
+                }
+                if (!layer.legend.treeItem) {
+                    layer.legend.treeItem = this.createTreeItem(layer, style, fScale, true);
+                    layer.parentGroup.legend.treeItem.add(layer.legend.treeItem);
+                } else if (layer.legend.treeItem instanceof Fusion.Widget.Legend.TreeFolder) {
+                    this.clearTreeItem(layer);
+                    layer.legend.treeItem = this.createTreeItem(layer, style, fScale, true);
+                    layer.parentGroup.legend.treeItem.add(layer.legend.treeItem);
+                } else {
+                    if (range.styles.length > 0) {
+                        var url = layer.oMap.getLegendImageURL(fScale, layer, range.styles[0]);
+                        layer.legend.treeItem.setImage(url);
+                        layer.legend.treeItem.enable(true);
+                    } else {
+                        layer.legend.treeItem.enable(false);
+                    }
+                }
+            }
+        } else {
+            /* the layer is to be displayed but is not visible in the map
+             * at the current map scale so disable it and display as a tree
+             * item or hide it altogether if necessary;
+             */
+            if (this.hideInvisibleLayers) {
+                if (layer.legend.treeItem) {
+                    layer.parentGroup.legend.treeItem.remove(layer.legend.treeItem);
+                    layer.legend.treeItem = null;
+                }
+            } else {
+              var newTreeItem = this.createTreeItem(layer, {legendLabel: layer.legendLabel}, null, true);
+                if (layer.legend.treeItem) {
+                    layer.parentGroup.legend.treeItem.replace(newTreeItem, layer.legend.treeItem);
+                    layer.legend.treeItem.finalize();
+                } else {
+                    layer.parentGroup.legend.treeItem.add(newTreeItem);
+                }
+                layer.legend.treeItem = newTreeItem;
+            }
+        }
+        if (layer.legend.treeItem) {
+            layer.legend.treeItem.options.data = layer;
+            layer.legend.treeItem.check(layer.visible);
+        }
+    },
+    
+    createFolderItem: function(layer) {
+        var opt = {
+            label: layer.legendLabel == '' ? '&nbsp;' : layer.legendLabel,
+            isOpen: layer.expandInLegend,
+            // contextMenu: this.getContextMenu(),
+            image: this.imgLayerThemeIcon
+        };
+        if (layer.metadata) {
+          opt.selectable = !layer.metadata.jxUnselectable || (layer.metadata.jxUnselectable && layer.metadata.jxUnselectable != 'true');
+        } else {
+          opt.selectable = false;
+        }
+        var folder = new Fusion.Widget.Legend.TreeFolder(opt);
+        var img = folder.elements.get('jxTreeIcon');
+        img.style.backgroundPosition = '0px 0px';
+        // folder.options.contextMenu.add([
+        //     new Jx.Menu.Item({
+        //         label: OpenLayers.i18n('collapse'),
+        //         onClick: OpenLayers.Function.bind(this.collapseBranch, this, folder)
+        //     }),
+        //     new Jx.Menu.Item({
+        //         label: OpenLayers.i18n('expand'),
+        //         onClick: OpenLayers.Function.bind(this.expandBranch, this, folder)
+        //     })]
+        // );
+        
+        var layerInfo = layer.oMap.getLayerInfoUrl(layer.layerName);
+        if (layerInfo) {
+            folder.setLayerInfo(layerInfo, this.imgLayerInfoIcon);
+        }
+       
+        return folder;
+    },
+    
+    createTreeItem: function(layer, style, scale, checkbox) {
+        var opt = {};
+        opt.statusIsDefault = layer.statusDefault;
+        opt.label = style.legendLabel == '' ? '&nbsp;' : style.legendLabel;
+        if (layer.metadata) {
+          opt.selectable = !layer.metadata.jxUnselectable || (layer.metadata.jxUnselectable && layer.metadata.jxUnselectable != 'true');
+        } else {
+          opt.selectable = false;
+        }
+
+        if (!style) {
+            opt.image = this.imgDisabledLayerIcon;
+            opt.enabled = false;
+        } else {
+           var defaultIcon = this.imgDisabledLayerIcon;
+           if (layer.layerTypes[0] == 4) {
+               if (style.staticIcon == Fusion.Constant.LAYER_DWF_TYPE) {
+                 defaultIcon = this.imgLayerDWFIcon;
+              } else {
+                defaultIcon = this.imgLayerRasterIcon;
+              }
+            }
+            if (style.iconOpt && style.iconOpt.url) {
+                opt.image = style.iconOpt.url;
+            } else {
+                opt.image = layer.oMap.getLegendImageURL(scale, layer, style);
+            }
+        }
+
+        var item;
+        if (checkbox) {
+            // opt.contextMenu = this.getContextMenu();
+            item = new Fusion.Widget.Legend.TreeItem(opt);
+            /* only need to add layer info if it has a check box too */
+            var layerInfo = layer.oMap.getLayerInfoUrl(layer.layerName);
+            if (layerInfo) {
+                item.setLayerInfo(layerInfo, this.imgLayerInfoIcon);
+            }
+        }  else {
+            opt.selectable = false;
+            item = new Jx.TreeItem(opt);
+        }
+        
+        var iconX = 0;
+        var iconY = 0;
+        var img = item.elements.get('jxTreeIcon');
+        if (style && style.iconX >= 0 && style.iconY >= 0) {
+            /* calculate the size of the image that holds the icon
+             * only once and cache the values as it is an expensive operation
+             * We use the size to center the class/layer icon as a background
+             * image inside the image that holds it so that if the icon is
+             * not the same size it is represented in a reasonable way
+             */
+            if (!this.offsetsCalculated) {
+                var parent = img.parentNode;
+                var sibling = img.getPrevious();
+                var d = new Element('div', {'class':'fusionLegendTreeRoot'});
+                img.setStyle('visiblity','hidden');
+                img.inject(d);
+                //TODO: img.getStyle doesn't seem to work for IE, need another solution here
+                var w = 16;//img.getStyle('width').toInt();
+                var h = 16;//img.getStyle('height').toInt();
+                if (!sibling) {
+                    img.inject(parent,'top');
+                } else {
+                    img.inject(sibling, 'after');
+                }
+                img.setStyle('visibility','visible');
+                this.iconWidth = (style.iconOpt.width - w)/2;
+                this.iconHeight = (style.iconOpt.height - h)/2;
+                //alert(w+":"+h);
+                this.offsetsCalculated = true;
+            }
+            iconX = -1 * (style.iconX + this.iconWidth);
+            iconY = -1 * (style.iconY + this.iconHeight);
+        }
+        img.style.backgroundPosition = iconX + 'px ' + iconY + 'px';
+        
+        return item;
+    },
+    clearTreeItem: function(layer) {
+        if (layer.legend.treeItem && layer.legend.treeItem.owner) {
+            layer.legend.treeItem.owner.remove(layer.legend.treeItem);
+            // layer.legend.treeItem.finalize();
+            layer.legend.treeItem.destroy();
+            layer.legend.treeItem = null;
+        }
+    }
+});
+
+Fusion.Widget.Legend.TreeItem = new Class({
+    Extends: Jx.TreeItem,
+    options: {
+        template: '<li class="jxTreeContainer jxTreeLeaf"><img class="jxTreeImage" src="'+Jx.aPixel.src+'" alt="" title=""><span class="fusionLegendCheckboxContainer"><input type="checkbox" class="fusionLegendCheckboxInput"></span><a class="fusionLayerInfo" target="_blank"><img class="fusionLayerInfoIcon" src="'+Jx.aPixel.src+'"></a><a class="jxTreeItem fusionCheckboxItem" href="javascript:void(0);"><img class="jxTreeIcon" src="'+Jx.aPixel.src+'" alt="" title=""><span class="jxTreeLabel"></span></a></li>'
+    },
+    classes: new Hash({
+        domObj: 'jxTreeContainer', 
+        domA: 'jxTreeItem', 
+        domImg: 'jxTreeImage', 
+        domIcon: 'jxTreeIcon',
+        domLabel: 'jxTreeLabel',
+        checkbox: 'fusionLegendCheckboxInput',
+        layerInfo: 'fusionLayerInfo',
+        layerInfoIcon: 'fusionLayerInfoIcon'
+    }),
+    init: function() {
+      this.bound.onClick = function(e){
+          if (this.options.data) {
+              if (e.target.checked && this.options.data.show) {
+                  this.options.data.show();
+              } else if (!e.target.checked && this.options.data.hide) {
+                  this.options.data.hide();
+              }
+          }
+      }.bind(this);
+      this.bound.enabled = function() {
+          this.checkbox.disabled = false;
+      }.bind(this);
+      this.bound.disabled = function() {
+          this.checkbox.disabled = true;
+      }.bind(this);
+      this.parent();
+    },
+    render: function() {
+        this.parent();
+        if (this.checkbox) {
+            if ($defined(this.options.checked)) {
+                this.check(this.options.checked);
+            }
+            this.checkbox.addEvent('click', this.bound.onClick);
+            this.addEvents({
+              enabled: this.bound.enabled,
+              disabled: this.bound.disabled
+            });
+        }
+    },
+    cleanup: function() {
+      this.removeEvents({
+        enabled: this.bound.enabled,
+        disabled: this.bound.disabled
+      });
+      if (this.checkbox) {
+        this.checkbox.removeEvent('click', this.bound.onClick);
+      }
+      this.bound.onClick = null;
+      this.bound.enabled = null;
+      this.bound.disabled = null;
+      this.parent();
+    },
+    check: function(state) {
+        if (this.checkbox) {
+            this.checkbox.set('checked', state);
+        }
+    },
+    isChecked: function() {
+        return this.checkbox && this.checkbox.checked;
+    },
+    setLayerInfo: function(url, icon) {
+        //change class to make fusionLayerInfo display block
+        this.domObj.addClass('fusionShowLayerInfo');
+        if (this.layerInfo) {
+            this.layerInfo.set('href', url);
+        }
+        if (this.layerInfoIcon) {
+            this.layerInfoIcon.set('src', icon);
+        }
+    }
+});
+
+Fusion.Widget.Legend.TreeFolder = new Class({
+    Extends: Jx.TreeFolder,
+    options: {
+        template: '<li class="jxTreeContainer jxTreeBranch"><img class="jxTreeImage" src="'+Jx.aPixel.src+'" alt="" title=""><span class="fusionLegendCheckboxContainer"><input type="checkbox" class="fusionLegendCheckboxInput"></span><a class="jxTreeItem fusionCheckboxItem" href="javascript:void(0);"><img class="jxTreeIcon" src="'+Jx.aPixel.src+'" alt="" title=""><span class="jxTreeLabel"></span></a><a class="fusionGroupInfo"><img class="fusionGroupInfoIcon" src="'+Jx.aPixel.src+'"></a><a class="fusionLayerInfo"><img class="fusionLayerInfoIcon" src="'+Jx.aPixel.src+'"></a><ul class="jxTree"></ul></li>'
+    },
+    classes: new Hash({
+        domObj: 'jxTreeContainer', 
+        domA: 'jxTreeItem', 
+        domImg: 'jxTreeImage', 
+        domIcon: 'jxTreeIcon',
+        domLabel: 'jxTreeLabel',
+        domTree: 'jxTree',
+        checkbox: 'fusionLegendCheckboxInput',
+        layerInfo: 'fusionLayerInfo',
+        layerInfoIcon: 'fusionLayerInfoIcon',
+        groupInfo: 'fusionGroupInfo',
+        groupInfoIcon: 'fusionGroupInfoIcon'
+    }),
+    init: function() {
+      this.bound.onClick = function(e){
+          if (this.options.data) {
+              if (e.target.checked && this.options.data.show) {
+                  this.options.data.show();
+              } else if (!e.target.checked && this.options.data.hide) {
+                  this.options.data.hide();
+              }
+          }
+      }.bind(this);
+      this.bound.enabled = function() {
+          this.checkbox.disabled = false;
+      }.bind(this);
+      this.bound.disabled = function() {
+          this.checkbox.disabled = true;
+      }.bind(this);
+      this.parent();
+    },
+    
+    render: function() {
+        this.parent();
+        if (this.checkbox) {
+            if ($defined(this.options.checked)) {
+                this.check(this.options.checked);
+            }
+            this.checkbox.addEvent('click', this.bound.onClick);
+            this.addEvents({
+                enabled: this.bound.enabled,
+                disabled: this.bound.disabled
+            });
+        }
+    },
+    cleanup: function() {
+      this.removeEvents({
+        enabled: this.bound.enabled,
+        disabled: this.bound.disabled
+      });
+      if (this.checkbox) {
+        this.checkbox.removeEvent('click', this.bound.onClick);
+      }
+      this.bound.onClick = null;
+      this.bound.enabled = null;
+      this.bound.disabled = null;
+      this.parent();
+    },
+    check: function(state) {
+        if (this.checkbox) {
+            this.checkbox.set('checked', state);
+        }
+    },
+    isChecked: function() {
+        return this.checkbox && this.checkbox.checked;
+    },
+    setLayerInfo: function(url, icon) {
+        //change class to make fusionLayerInfo display block
+        this.domObj.addClass('fusionShowLayerInfo');
+        if (this.layerInfo) {
+            this.layerInfo.set('href', url);
+        }
+        if (this.layerInfoIcon) {
+            this.layerInfoIcon.set('src', icon);
+        }
+    },
+    
+    setGroupInfo: function(url, icon) {
+        //change class to make fusionGroupInfo display block
+        this.domObj.addClass('fusionShowGroupInfo');
+        if (this.groupInfo) {
+            this.groupInfo.set('href', url);
+        }
+        if (this.groupInfoIcon) {
+            this.groupInfoIcon.set('src', icon);
+        }
+    }
+});
\ No newline at end of file

Modified: trunk/widgets/MapMenu.js
===================================================================
--- trunk/widgets/MapMenu.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/MapMenu.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -45,6 +45,9 @@
         
         var json = this.widgetTag.extension;
         
+        this.loadMaxExtent = json.LoadMaxExtent ? 
+                      (json.LoadMaxExtent[0].toLowerCase() == 'true') : false;
+        
         //If no folder is specified for enumeration, build a menu
         //from the mapgroup alone. Folders are only supported with MapGuide.
         //Otherwise, create a hash of mapgroup resourceId to mapGroup data
@@ -80,9 +83,73 @@
             var params =  {parameters: {'folder': this.rootFolder},
                           onComplete: OpenLayers.Function.bind(this.processMapMenu, this)};
             Fusion.ajaxRequest(s, params);
+        } else if (this.arch == 'MapServer' && json.Folder) {
+            this.rootFolder = json.Folder ? json.Folder[0] : '/';
+            //var s = 'layers/' + this.arch + '/' + Fusion.getScriptLanguage() + '/MapMenu.' + Fusion.getScriptLanguage();
+            var s = json.ListURL ? json.ListURL[0] : '/platform/api/mapsherpa.php';
+            var options =  {
+                  parameters: {
+                    request: 'listpublishedmaps',
+                    depth: -1,
+                    folder: this.rootFolder
+                  },
+                  method: 'GET',
+                  onComplete: OpenLayers.Function.bind(this.processMSMapMenu, this)
+            };
+            var temp = new OpenLayers.Ajax.Request( s, options);
         };
     },
-
+    
+    processMSMapMenu: function(r) {
+        if (r.status == 200) {
+            var o;
+            eval("o="+r.responseText);
+            //var testData = '{"success":true,"errorMessages":[],"values":[{"sPath":"/ms4w/apps/gmap/cap/HamiltonLowIncome.map","sPermissions":"2","sResourceId":"/Hamilton/Hamilton Low Income","sMapResource":"/Hamilton/hamilton_low_income"},{"sPath":"/mnt/FGS_ENVIRONS/fgs-cap/apps/platform/data/home/root/Canada1.map","sPermissions":"2","sResourceId":"/Canada/Canada1","sMapResource":"/Canada/Canada"}],"request":"listpublishedmaps","version":1}';
+            //eval("o="+testData);
+            this.menus = {};
+            var list = o.values;
+            if (o.values) {
+              var basemap = this.getMap().aMaps[0];
+              for (var i=0; i<list.length; i++) {
+                  var resource = list[i];
+                  var mapId = resource.sResourceId;
+                  mapId = mapId.replace(this.rootFolder, '');
+                  var folders = mapId.split('/');
+                  var label = folders.pop();
+                  var path = folders.join('/');
+                  this.createFolders(path);
+                  
+                  // check for mapgroup data and if there is none,
+                  // create a maptag that will be passed to the map
+                  // widget constructor 
+                  data = {maps:[{'resourceId': resource.sResourceId,
+                          'singleTile':true,
+                          'type': this.arch,
+                          'extension':{
+                            'MapFile': [resource.sPath],
+                            'MapMetadata': [basemap.mapMetadataKeys],
+                            'LayerMetadata': [basemap.layerMetadataKeys]
+                          }
+                         }]};
+                  //set up needed accessor
+                  data.getInitialView = function() {
+                      return this.initialView;
+                  };
+                  var menuItem = new Jx.Menu.Item({
+                      label: label,
+                      onClick: OpenLayers.Function.bind(this.switchMap, this, data)
+                  });
+                  
+                  if (path == '') {
+                      this.uiObj.add(menuItem);
+                  }else {
+                      this.menus[path].add(menuItem);
+                  }
+              }
+            }
+        }
+    },
+    
     processMapMenu: function(r) {
         if (r.status == 200) {
             var o;
@@ -156,23 +223,29 @@
         
     //change the map, preserving current extents
     switchMap: function(data) {
-        var ce = this.getMap().getCurrentExtents();
-        var dest = null;
-        for (var i=0; i<data.maps.length; ++i) {
-          dest = new OpenLayers.Projection(data.maps[i].layerOptions.projection);
-          if (data.maps[i].layerOptions.isBaseLayer) {
-            break;
+        data.initialView = null;
+        if (!this.loadMaxExtent) {
+          var ce = this.getMap().getCurrentExtents();
+          var dest = null;
+          for (var i=0; i<data.maps.length; ++i) {
+            if (data.maps[i].layerOptions && data.maps[i].layerOptions.projection) {
+              dest = new OpenLayers.Projection(data.maps[i].layerOptions.projection);
+              if (data.maps[i].layerOptions.isBaseLayer) {
+                break;
+              }
+            }
           }
-        }
-        if (!dest) {
-          dest = new OpenLayers.Projection("EPSG:4326");
-        }
-        ce = ce.transform(this.oMap.oMapOL.getProjectionObject(), dest);
-        data.initialView = {minX:ce.left,
+          if (!dest) {
+            dest = new OpenLayers.Projection("EPSG:4326");
+          }
+          ce = ce.transform(this.oMap.oMapOL.getProjectionObject(), dest);
+          data.initialView = {minX:ce.left,
                             minY:ce.bottom,
                             maxX:ce.right,
-                            maxY:ce.top
+                            maxY:ce.top,
+                            projection: dest
                             };        
+        }
         this.getMap().loadMapGroup(data);
     }
 });

Copied: trunk/widgets/MapMetadata.js (from rev 2248, sandbox/jxlib-3.0/widgets/MapMetadata.js)
===================================================================
--- trunk/widgets/MapMetadata.js	                        (rev 0)
+++ trunk/widgets/MapMetadata.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,80 @@
+/**
+ * Fusion.Widget.About
+ *
+ * $Id: About.js 1656 2008-11-08 21:44:26Z madair $
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/********************************************************************
+* Class: Fusion.Widget.MapMetadata
+*
+* MapMetadata widget to inject map metadata elements into the interface
+*
+* **********************************************************************/
+
+Fusion.Widget.MapMetadata = OpenLayers.Class(Fusion.Widget, {
+    
+    content: 'text',
+    
+/*
+ * Constructor: MapMetadata
+ *
+ * Parameters:
+ *
+ * widgetTag - JSON node for this widget from the Application definition
+ *
+ */
+    initializeWidget: function(widgetTag) {
+        var json = widgetTag.extension;
+        if (json.MetadataId) {
+          this.metadataId =  json.MetadataId[0];
+        } else {
+          Fusion.reportError( new Fusion.Error(Fusion.Error.FATAL,"MetadataId is a required parameter for mapMetadata widget"));
+        }
+        if (json.Content) {
+          this.content = json.Content[0];
+        }
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.showMetadata, this));
+    },
+
+    /**
+     * Function: showMetadata
+     *
+     * puts a piece of map metadata into the interface
+     * 
+     */
+    showMetadata: function() {
+      var metadata = this.getMap().getMetadata(this.metadataId);
+      if (metadata) {
+        //this.domObj.appendChild(metadata);
+        if (this.content == 'markup') {             
+          //load in an HTML fragment
+          //this isn't working in IE
+          var domObj = $(this.domObj);
+          domObj.load(metadata);
+        } else if ((this.content == 'hyperlink') && this.domObj.tagName == 'A') {
+          this.domObj.href = metadata;
+        } else {
+          this.domObj.innerHTML = metadata;
+        }
+      }
+    }
+});

Copied: trunk/widgets/SaveMapImage.js (from rev 2248, sandbox/jxlib-3.0/widgets/SaveMapImage.js)
===================================================================
--- trunk/widgets/SaveMapImage.js	                        (rev 0)
+++ trunk/widgets/SaveMapImage.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,67 @@
+/**
+ * Fusion.Widget.SaveMapImage
+ *
+ * $Id: About.js 1656 2008-11-08 21:44:26Z madair $
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/********************************************************************
+* Class: Fusion.Widget.SaveMapImage
+*
+* SaveMapImage widget to display a pop-up window with an image of the map
+* that can be saved to the users disk
+*
+* **********************************************************************/
+
+Fusion.Widget.SaveMapImage = OpenLayers.Class(Fusion.Widget, {
+    uiClass: Jx.Button,
+    
+/*
+ * Constructor: SaveMapImage
+ *
+ * Parameters:
+ *
+ * widgetTag - JSON node for this widget from the Application definition
+ *
+ */
+    initializeWidget: function(widgetTag) {
+        var json = widgetTag.extension;
+        //this.enable();
+    },
+
+    /**
+     * Function: execute
+     *
+     * opens a pop-up window with the about information when invoked
+     * 
+     */
+    activate: function() {
+      var map = this.oMap.oMapOL;
+      var imageUrl = null;
+      if (map.baseLayer.singleTile) {
+        var imageUrl = map.baseLayer.grid[0][0].url;
+      } //TODO: how to get tiled and overlay images??
+      
+      if (imageUrl) {
+        window.open(imageUrl,'','');
+      }
+    }
+});

Modified: trunk/widgets/ScalebarDual.js
===================================================================
--- trunk/widgets/ScalebarDual.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/ScalebarDual.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -45,6 +45,9 @@
             bottomInUnits: bottomInUnits,
             bottomOutUnits: bottomOutUnits
         };
+        if (this.domObj) {
+            options.div = this.domObj;
+        }
         this.addControl(new OpenLayers.Control.ScaleLine(options));
     }
 });

Modified: trunk/widgets/Select.js
===================================================================
--- trunk/widgets/Select.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/Select.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -35,7 +35,7 @@
     isExclusive: true,
     uiClass: Jx.Button,
     selectionType: 'INTERSECTS',
-    nTolerance : 3,     //default pixel tolerance for a point click
+    nTolerance: 3,     //default pixel tolerance for a point click
     bActiveOnly: false, //only select feature(s) on the active layer?
     maxFeatures: 0,     //default of 0 selects all features (i.e. no maximum)
     pointClickSingleSelect: true, //default of true causes a point click always to select only a single feature

Copied: trunk/widgets/SelectAttribute.js (from rev 2248, sandbox/jxlib-3.0/widgets/SelectAttribute.js)
===================================================================
--- trunk/widgets/SelectAttribute.js	                        (rev 0)
+++ trunk/widgets/SelectAttribute.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,207 @@
+/**
+ * Fusion.Widget.SelectRadius
+ *
+ * $Id: SelectRadius.js 1816 2009-03-11 20:33:51Z pagameba $
+ *
+ * Copyright (c) 2007, DM Solutions Group Inc.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+ /********************************************************************
+ * Class: Fusion.Widget.SelectRadius
+ *
+ * perform a selection by radius from a point
+ * 
+ * **********************************************************************/
+Fusion.Event.RADIUS_WIDGET_ACTIVATED = Fusion.Event.lastEventId++;
+
+Fusion.Widget.SelectAttribute = OpenLayers.Class(Fusion.Widget, {
+    isExclusive: true,
+    uiClass: Jx.Button,
+    drawn: false,
+    
+    initializeWidget: function(widgetTag) {
+        var json = widgetTag.extension;
+        
+        if (json.Container) {
+          this.container = $(json.Container[0]);
+        }
+        this.workArea = document.createElement('div');
+        this.container.appendChild(this.workArea);
+        
+        //eventually there will be multiple rows for AND/OR ops on attributes
+        this.attrRow = document.createElement('div');
+        this.attrRow.className = 'selectAttrInputs';
+        this.workArea.appendChild(this.attrRow);
+        
+        var d = document.createElement('div');
+        d.innerHTML = "layer to query:";
+        this.layerList = document.createElement('select');
+        this.layerList.className = 'layerSelector';
+        d.appendChild(this.layerList);
+        this.attrRow.appendChild(d);
+
+        this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.listLayers, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_RELOADED, OpenLayers.Function.bind(this.listLayers, this));
+        this.getMap().registerForEvent(Fusion.Event.MAP_ACTIVE_LAYER_CHANGED, OpenLayers.Function.bind(this.setAttributeOptions, this));
+    },
+    
+    /**
+     * populate th list of seletable layers when the map loads
+     */
+    listLayers: function() {
+        //this.layerList.empty();
+        this.layerList.innerHTML = '';
+        var map = this.getMap().aMaps[0]; 
+        this.layerList.options[this.layerList.options.length] = new Option('--select--','');
+        for (var i=0; i<map.aLayers.length; ++i) {
+            var layer = map.aLayers[i];
+            if (layer.selectable) {
+              this.layerList.options[this.layerList.options.length] = new Option(layer.legendLabel,layer.layerName);
+            }
+        }
+        this.layerList.onchange = OpenLayers.Function.bind(this.activateLayer, this);
+        
+        var sl = Fusion.getScriptLanguage();
+        var queryScript = 'layers/' + map.arch + '/' + sl  + '/GetAttributes.' + sl;
+        var params = {
+            'mapname': map._sMapname,
+            'session': map.getSessionID()
+            //'layers': layers,
+        };
+        var ajaxOptions = {
+            onSuccess: OpenLayers.Function.bind(this.setAttributes, this),
+            method: 'GET',
+            parameters: params
+        };
+        Fusion.ajaxRequest(queryScript, ajaxOptions);
+        
+    },
+    
+    setAttributes: function(xhr) {
+      if (xhr.status < 400) {
+          eval('this.attrs='+xhr.responseText);
+      }
+    },
+    
+    setAttributeOptions: function(event) {
+      if (this.drawn) {
+        //this.propsList.empty();
+        this.propsList.innerHTML = '';
+        var layer = this.layerList[this.layerList.selectedIndex].value;
+        var attrs = this.attrs[layer].propertyvalues;
+        var props = this.attrs[layer].propertynames;
+        for (var i=0; i<attrs.length; ++i) {
+            this.propsList.options[this.propsList.options.length] = new Option(attrs[i],props[i]);
+        }
+      }
+    },
+    
+    activateLayer: function(event) {
+      if (this.drawn) {
+        var layer = this.layerList[this.layerList.selectedIndex].value;
+        var map = this.getMap();
+        for (var i=0; i<map.aMaps[0].aLayers.length; ++i) {
+          if (map.aMaps[0].aLayers[i].layerName == layer) {
+            map.setActiveLayer(map.aMaps[0].aLayers[i]);
+            break;
+          }
+        }
+      }
+    },
+    
+    /**
+     * activate the widget (listen to mouse events and change cursor)
+     * This function should be defined for all functions that register
+     * as a widget in the map
+     */
+    activate: function() {
+        this.container.style.display = 'block';
+        
+        if (!this.drawn) {
+          this.drawn = true;
+          this.propsList = document.createElement('select');
+          this.propsList.className = 'propsSelector';
+          this.attrRow.appendChild(this.propsList);
+          
+          this.operatorList = document.createElement('select');
+          this.operatorList.className = 'operatorSelector';
+          this.operatorList.options[this.operatorList.options.length] = new Option("=","eq",true);
+          this.operatorList.options[this.operatorList.options.length] = new Option("like","like");
+          this.operatorList.options[this.operatorList.options.length] = new Option(">","gt");
+          this.operatorList.options[this.operatorList.options.length] = new Option("<","lt");
+          this.operatorList.options[this.operatorList.options.length] = new Option("<=","le");
+          this.operatorList.options[this.operatorList.options.length] = new Option(">=","ge");
+          this.operatorList.options[this.operatorList.options.length] = new Option("!=","ne");
+          this.attrRow.appendChild(this.operatorList);
+          
+          this.attrValue = document.createElement('input');
+          this.attrValue.className = 'propsValue';
+          this.attrRow.appendChild(this.attrValue);
+          
+          new Jx.Button({
+              label: 'Query',
+              onClick: OpenLayers.Function.bind(this.execute, this)
+          }).addTo(this.attrRow);
+        }
+    },
+
+    /**
+     * deactivate the widget (listen to mouse events and change cursor)
+     * This function should be defined for all functions that register
+     * as a widget in the map
+     **/
+    deactivate: function() {
+        this.container.style.display = 'none';
+        //this.workArea.style.display = 'none';
+    },
+    
+    /**
+     *  set the extants of the map based on the pixel coordinates
+     * passed
+     * 
+     * @param center
+     * @param radius
+     **/
+    execute: function() {
+        var propertyName = this.propsList[this.propsList.selectedIndex].value;
+        var operator = this.operatorList[this.operatorList.selectedIndex].value;
+        var propertyValue = this.attrValue.value;
+        var numValue = parseFloat(propertyValue);
+        var filter = null;
+        if ( isNaN(numValue) ) {
+          //we have a string
+          if (operator == 'like') {
+            filter = "/.*"+propertyValue+".*/gi";
+          } else {
+            filter = "(["+propertyName+"] "+operator+" '"+propertyValue+"')";
+          }
+        } else {
+          //we have a number
+            filter = "(["+propertyName+"] "+operator+" "+propertyValue+")";
+        }
+        var options = {
+          layers: this.layerList[this.layerList.selectedIndex].value,
+          filterItem: propertyName,
+          filter: filter
+        };
+        
+        this.getMap().query(options);
+    }
+});

Modified: trunk/widgets/SelectPolygon.js
===================================================================
--- trunk/widgets/SelectPolygon.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/SelectPolygon.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -33,6 +33,7 @@
 Fusion.Widget.SelectPolygon = OpenLayers.Class(Fusion.Widget, {
     isExclusive: true,
     uiClass: Jx.Button,
+    bActiveOnly: false, //only select feature(s) on the active layer?
     selectionType: 'INTERSECTS',
     nTolerance: 3, //default pixel tolernace for a point click
     
@@ -45,6 +46,11 @@
         if (json.Tolerance && (parseInt(json.Tolerance[0]) > 0)) {
             nTolerance = parseInt(json.Tolerance[0]);
         }
+        
+        this.bActiveOnly = (json.QueryActiveLayer &&
+                           (json.QueryActiveLayer[0] == 'true' ||
+                            json.QueryActiveLayer[0] == '1')) ? true : false;
+
         this.bComputeMetadata = (json.ComputeMetadata &&
                            (json.ComputeMetadata[0] == 'true' ||
                             json.ComputeMetadata[0] == '1')) ? true : false;

Modified: trunk/widgets/SelectRadius.js
===================================================================
--- trunk/widgets/SelectRadius.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/SelectRadius.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -35,6 +35,7 @@
     isExclusive: true,
     uiClass: Jx.Button,
     selectionType: 'INTERSECTS',
+    bActiveOnly: false, //only select feature(s) on the active layer?
     nTolerance: 3, //default pixel tolernace for a point click
     defaultRadius: 20, //this is now in pixels
     
@@ -53,6 +54,9 @@
                            (json.ComputeMetadata[0] == 'true' ||
                             json.ComputeMetadata[0] == '1')) ? true : false;
         
+        this.bActiveOnly = (json.QueryActiveLayer &&
+                           (json.QueryActiveLayer[0] == 'true' ||
+                            json.QueryActiveLayer[0] == '1')) ? true : false;
         
         var container = json.RadiusTooltipContainer ? json.RadiusTooltipContainer[0] : '';
         if (container != '') {

Deleted: trunk/widgets/SelectionInfo/SelectionInfo.css
===================================================================
--- sandbox/jxlib-3.0/widgets/SelectionInfo/SelectionInfo.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/SelectionInfo/SelectionInfo.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,17 +0,0 @@
-.spanSelectionInfo {
-    display: -moz-inline-box;
-    display: inline-block;
-    padding: 2px;
-    margin: 0;
-    border: none;
-    width: 200px;
-    font-family: Arial, Helvetica, sans-serif;
-    font-size: 11px;
-    line-height: 16px;
-    background-color: transparent;
-    color: #000;
-}
-
-.jxToolItem .spanSelectionInfo {
-    padding: 6px 2px;
-}
\ No newline at end of file

Copied: trunk/widgets/SelectionInfo/SelectionInfo.css (from rev 2248, sandbox/jxlib-3.0/widgets/SelectionInfo/SelectionInfo.css)
===================================================================
--- trunk/widgets/SelectionInfo/SelectionInfo.css	                        (rev 0)
+++ trunk/widgets/SelectionInfo/SelectionInfo.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,17 @@
+.spanSelectionInfo {
+    display: -moz-inline-box;
+    display: inline-block;
+    padding: 2px;
+    margin: 0;
+    border: none;
+    width: 200px;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 11px;
+    line-height: 16px;
+    background-color: transparent;
+    color: #000;
+}
+
+.jxToolItem .spanSelectionInfo {
+    padding: 6px 2px;
+}
\ No newline at end of file

Modified: trunk/widgets/SelectionInfo.js
===================================================================
--- trunk/widgets/SelectionInfo.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/SelectionInfo.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -65,6 +65,9 @@
 
         this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_ON, OpenLayers.Function.bind(this.update, this));
         this.getMap().registerForEvent(Fusion.Event.MAP_SELECTION_OFF, OpenLayers.Function.bind(this.update, this));
+        
+        Fusion.addWidgetStyleSheet(widgetTag.location + '/SelectionInfo/SelectionInfo.css');
+        
     },
     
     setUiObject: function(uiObj) {

Modified: trunk/widgets/SelectionPanel.js
===================================================================
--- trunk/widgets/SelectionPanel.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/SelectionPanel.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -336,8 +336,19 @@
         var nLayers = this.oSelection.getNumLayers();
         for (var i=0; i<nLayers; i++) {
             var layerObj = this.oSelection.getLayer(i);
+            
             //find the legend label from the Map layer objects
-            var mapLayers = this.getMap().aMaps[0].aLayers; //TODO: allow multiple maps
+            //join up all layers from all maps into an array
+            var mapLayers = [];
+            var map = this.getMap();
+            for (j=0; j<map.aMaps.length; ++j) {
+              if (map.aMaps[j].aLayers) {
+                for (k=0; k<map.aMaps[j].aLayers.length; ++k) {
+                  mapLayers.push(map.aMaps[j].aLayers[k]);
+                }
+              }
+            }
+            
             var labelName = layerObj.getName();
             for (var j=0; j<mapLayers.length; ++j) {
               if (mapLayers[j].layerName == labelName) {

Modified: trunk/widgets/TaskPane.js
===================================================================
--- trunk/widgets/TaskPane.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/TaskPane.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -185,7 +185,7 @@
         
         this.aExecutedTasks.push(newUrl);
         ++this.nCurrentTask;
-        this.loadFrame(url);
+        this.loadFrame(newUrl);
     },
     
     loadFrame: function(url) {

Deleted: trunk/widgets/ViewSize/ViewSize.css
===================================================================
--- sandbox/jxlib-3.0/widgets/ViewSize/ViewSize.css	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/ViewSize/ViewSize.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -1,17 +0,0 @@
-.spanViewSize {
-    display: -moz-inline-box;
-    display: inline-block;
-    padding: 2px;
-    margin: 0;
-    border: none;
-    width: 200px;
-    font-family: Arial, Helvetica, sans-serif;
-    font-size: 11px;
-    line-height: 16px;
-    background-color: transparent;
-    color: #000;
-}
-
-.jxToolItem .spanViewSize {
-    padding: 6px 2px;
-}
\ No newline at end of file

Copied: trunk/widgets/ViewSize/ViewSize.css (from rev 2248, sandbox/jxlib-3.0/widgets/ViewSize/ViewSize.css)
===================================================================
--- trunk/widgets/ViewSize/ViewSize.css	                        (rev 0)
+++ trunk/widgets/ViewSize/ViewSize.css	2010-10-19 14:31:05 UTC (rev 2249)
@@ -0,0 +1,17 @@
+.spanViewSize {
+    display: -moz-inline-box;
+    display: inline-block;
+    padding: 2px;
+    margin: 0;
+    border: none;
+    width: 200px;
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 11px;
+    line-height: 16px;
+    background-color: transparent;
+    color: #000;
+}
+
+.jxToolItem .spanViewSize {
+    padding: 6px 2px;
+}
\ No newline at end of file

Modified: trunk/widgets/ViewSize.js
===================================================================
--- trunk/widgets/ViewSize.js	2010-10-14 20:02:58 UTC (rev 2248)
+++ trunk/widgets/ViewSize.js	2010-10-19 14:31:05 UTC (rev 2249)
@@ -56,6 +56,9 @@
         this.getMap().registerForEvent(Fusion.Event.MAP_LOADED, OpenLayers.Function.bind(this.setUnits, this));
         this.getMap().registerForEvent(Fusion.Event.MAP_EXTENTS_CHANGED, OpenLayers.Function.bind(this.updateViewSize, this));
         this.registerParameter('Units');
+        
+        Fusion.addWidgetStyleSheet(widgetTag.location + '/ViewSize/ViewSize.css');
+        
     },
     
     setUiObject: function(uiObj) {



More information about the fusion-commits mailing list