Checked in the most recent infusion library from nightly build of Dec 13.
authorcindy li <cli@ocad.ca>
Tue, 14 Dec 2010 19:39:36 +0000 (19:39 -0000)
committercindy li <cli@ocad.ca>
Tue, 14 Dec 2010 19:39:36 +0000 (19:39 -0000)
257 files changed:
docs/jscripts/infusion/InfusionAll.js [new file with mode: 0644]
docs/jscripts/infusion/README.txt [new file with mode: 0644]
docs/jscripts/infusion/components/inlineEdit/css/InlineEdit.css [new file with mode: 0644]
docs/jscripts/infusion/components/inlineEdit/images/inline_edit_edit_button_16x16.png [new file with mode: 0644]
docs/jscripts/infusion/components/inlineEdit/js/InlineEdit.js [new file with mode: 0644]
docs/jscripts/infusion/components/inlineEdit/js/InlineEditIntegrations.js [new file with mode: 0644]
docs/jscripts/infusion/components/progress/js/Progress.js [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/css/ImageReorderer.css [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/css/Reorderer.css [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Banana.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Blackberry.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Cherry.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Dragonfruit.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Fig.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Grapes.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Kiwano.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Kiwi.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Kumquat.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Lemon.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Mangosteen.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Orange.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/RedApple.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/images/Tamarillo.jpg [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/js/GeometricManager.js [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/js/ImageReorderer.js [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/js/LayoutReorderer.js [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/js/ModuleLayout.js [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/js/Reorderer.js [new file with mode: 0644]
docs/jscripts/infusion/components/reorderer/js/ReordererDOMUtilities.js [new file with mode: 0644]
docs/jscripts/infusion/components/tooltip/js/Tooltip.js [new file with mode: 0644]
docs/jscripts/infusion/components/undo/js/Undo.js [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/ReadMe.txt [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/css/Uploader.css [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/html/Uploader.html [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/add.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/browse.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/error.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/gradient-file-green.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/gradient-file-grey.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/gradient-total-green.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/gradient-total-grey.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/gradient-total-yellow.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/remove.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/images/tick.png [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/js/DemoUploadManager.js [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/js/FileQueue.js [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/js/FileQueueView.js [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/js/Flash9UploaderSupport.js [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/js/FlashUploaderSupport.js [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/js/HTML5UploaderSupport.js [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/js/Scroller.js [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/js/Uploader.js [new file with mode: 0644]
docs/jscripts/infusion/components/uploader/js/UploaderCompatibility-Infusion1.2.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/DataBinding.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/Fluid.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/FluidDOMUtilities.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/FluidDebugging.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/FluidDocument.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/FluidIoC.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/FluidRequests.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/FluidView.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/JavaProperties.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/ModelTransformations.js [new file with mode: 0644]
docs/jscripts/infusion/framework/core/js/jquery.keyboard-a11y.js [new file with mode: 0644]
docs/jscripts/infusion/framework/enhancement/js/ProgressiveEnhancement.js [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-JSR168Bridge.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-layout.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-mobile-layout.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-mobile-theme-android.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-mobile-theme-iphone.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-reset.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-text.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-theme-coal.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-theme-debug.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-theme-hc.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-theme-hci.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-theme-mist.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-theme-rust.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-theme-slate.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/css/fss-transitions.css [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/exclamation.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/gripper.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/_common/exclamation.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/_common/gloss_25_repeater.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/_common/gripper.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/android/backbutton_mask.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/android/button_bg_insetShadow.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_arrow.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.gif [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/android/navbar_back_button_insetShadow.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/android/navbar_normal_button_insetShadow.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-light-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-light-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/icon-menu-Delete.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Close.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Less.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-More.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-ShowSettings.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-gripper.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-container-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-content-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-container-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-content-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/coal/widget-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/iphone/backbutton_mask.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/iphone/button_bg_insetShadow.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_arrow.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.gif [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/iphone/navbar_back_button_insetShadow.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/iphone/navbar_normal_button_insetShadow.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-med-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-med-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/icon-menu-Delete.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/icon-options-ListOrGrid.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Close.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Less.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-More.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-ShowSettings.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-gripper.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-container-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-container-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/mist/widget-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/rust/gripper.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/rust/icon-menu-Delete.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/rust/icon-options-ListOrGrid.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Close.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-More.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Settings.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/rust/menu-hover.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/rust/widget-earmark.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-light-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-light-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Close.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Less.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-More.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-gripper.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/sprites.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-container-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-cap.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-container-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/widget-bg.png [new file with mode: 0644]
docs/jscripts/infusion/framework/fss/images/themes/slate/widget-earmark.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/core/js/jquery.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/plugins/tooltip/css/jquery.tooltip.css [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/plugins/tooltip/js/jquery.ui.tooltip.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/coal.css [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_0_000000_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_75_cccccc_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_25_575757_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_55_cccccc_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_65_000000_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_333333_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_666666_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_inset-soft_95_fef1ec_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_222222_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_333333_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_a3a3a3_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cccccc_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cd0a0a_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_ffffff_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/hc.css [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_000000_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_ffffff_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_000000_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_ffffff_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/hci.css [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_000000_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_999999_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_ffffff_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_000000_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_fffff_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_ffffff_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_000000_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_2e83ff_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_75_ffffff_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_65_ffffff_1x400.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_9dcaf6_1x400.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_d9e8f7_1x400.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_highlight-soft_55_9dcaf6_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_inset-soft_95_fef1ec_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_000000_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_222222_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_2e83ff_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_454545_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_888888_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_cd0a0a_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/mist.css [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_666666_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_999999_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_cccccc_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ebebeb_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ffffff_40x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_glass_75_666666_1x400.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-hard_100_ebebeb_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-soft_75_999999_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_inset-hard_100_ebebeb_1x100.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_000000_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_666666_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ebebeb_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ffffff_256x240.png [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/slate.css [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/css/jquery.ui.theme.css [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.accordion.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.core.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.dialog.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.draggable.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.mouse.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.position.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.slider.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.widget.js [new file with mode: 0644]
docs/jscripts/infusion/lib/jquery/ui/js/ui-FLUID-readme.txt [new file with mode: 0644]
docs/jscripts/infusion/lib/json/js/json2.js [new file with mode: 0644]
docs/jscripts/infusion/lib/swfobject/js/swfobject.js [new file with mode: 0644]
docs/jscripts/infusion/lib/swfupload/flash/swfupload.swf [new file with mode: 0644]
docs/jscripts/infusion/lib/swfupload/js/swfupload.js [new file with mode: 0644]
docs/jscripts/infusion/licenses/Infusion-LICENSE.txt [new file with mode: 0644]
docs/jscripts/infusion/licenses/fastXmlPull-LICENSE.txt [new file with mode: 0644]
docs/jscripts/infusion/licenses/jQuery-LICENSE.txt [new file with mode: 0644]
docs/jscripts/infusion/licenses/swfobject-LICENSE.txt [new file with mode: 0644]
docs/jscripts/infusion/licenses/swfupload-LICENSE.txt [new file with mode: 0644]

diff --git a/docs/jscripts/infusion/InfusionAll.js b/docs/jscripts/infusion/InfusionAll.js
new file mode 100644 (file)
index 0000000..6b7cbf1
--- /dev/null
@@ -0,0 +1,22489 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function( window, undefined ) {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+               // The jQuery object is actually just the init constructor 'enhanced'
+               return new jQuery.fn.init( selector, context );
+       },
+
+       // Map over jQuery in case of overwrite
+       _jQuery = window.jQuery,
+
+       // Map over the $ in case of overwrite
+       _$ = window.$,
+
+       // Use the correct document accordingly with window argument (sandbox)
+       document = window.document,
+
+       // A central reference to the root jQuery(document)
+       rootjQuery,
+
+       // A simple way to check for HTML strings or ID strings
+       // (both of which we optimize for)
+       quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
+
+       // Is it a simple selector
+       isSimple = /^.[^:#\[\.,]*$/,
+
+       // Check if a string has a non-whitespace character in it
+       rnotwhite = /\S/,
+
+       // Used for trimming whitespace
+       rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
+
+       // Match a standalone tag
+       rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+       // Keep a UserAgent string for use with jQuery.browser
+       userAgent = navigator.userAgent,
+
+       // For matching the engine and version of the browser
+       browserMatch,
+       
+       // Has the ready events already been bound?
+       readyBound = false,
+       
+       // The functions to execute on DOM ready
+       readyList = [],
+
+       // The ready event handler
+       DOMContentLoaded,
+
+       // Save a reference to some core methods
+       toString = Object.prototype.toString,
+       hasOwnProperty = Object.prototype.hasOwnProperty,
+       push = Array.prototype.push,
+       slice = Array.prototype.slice,
+       indexOf = Array.prototype.indexOf;
+
+jQuery.fn = jQuery.prototype = {
+       init: function( selector, context ) {
+               var match, elem, ret, doc;
+
+               // Handle $(""), $(null), or $(undefined)
+               if ( !selector ) {
+                       return this;
+               }
+
+               // Handle $(DOMElement)
+               if ( selector.nodeType ) {
+                       this.context = this[0] = selector;
+                       this.length = 1;
+                       return this;
+               }
+               
+               // The body element only exists once, optimize finding it
+               if ( selector === "body" && !context ) {
+                       this.context = document;
+                       this[0] = document.body;
+                       this.selector = "body";
+                       this.length = 1;
+                       return this;
+               }
+
+               // Handle HTML strings
+               if ( typeof selector === "string" ) {
+                       // Are we dealing with HTML string or an ID?
+                       match = quickExpr.exec( selector );
+
+                       // Verify a match, and that no context was specified for #id
+                       if ( match && (match[1] || !context) ) {
+
+                               // HANDLE: $(html) -> $(array)
+                               if ( match[1] ) {
+                                       doc = (context ? context.ownerDocument || context : document);
+
+                                       // If a single string is passed in and it's a single tag
+                                       // just do a createElement and skip the rest
+                                       ret = rsingleTag.exec( selector );
+
+                                       if ( ret ) {
+                                               if ( jQuery.isPlainObject( context ) ) {
+                                                       selector = [ document.createElement( ret[1] ) ];
+                                                       jQuery.fn.attr.call( selector, context, true );
+
+                                               } else {
+                                                       selector = [ doc.createElement( ret[1] ) ];
+                                               }
+
+                                       } else {
+                                               ret = buildFragment( [ match[1] ], [ doc ] );
+                                               selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
+                                       }
+                                       
+                                       return jQuery.merge( this, selector );
+                                       
+                               // HANDLE: $("#id")
+                               } else {
+                                       elem = document.getElementById( match[2] );
+
+                                       if ( elem ) {
+                                               // Handle the case where IE and Opera return items
+                                               // by name instead of ID
+                                               if ( elem.id !== match[2] ) {
+                                                       return rootjQuery.find( selector );
+                                               }
+
+                                               // Otherwise, we inject the element directly into the jQuery object
+                                               this.length = 1;
+                                               this[0] = elem;
+                                       }
+
+                                       this.context = document;
+                                       this.selector = selector;
+                                       return this;
+                               }
+
+                       // HANDLE: $("TAG")
+                       } else if ( !context && /^\w+$/.test( selector ) ) {
+                               this.selector = selector;
+                               this.context = document;
+                               selector = document.getElementsByTagName( selector );
+                               return jQuery.merge( this, selector );
+
+                       // HANDLE: $(expr, $(...))
+                       } else if ( !context || context.jquery ) {
+                               return (context || rootjQuery).find( selector );
+
+                       // HANDLE: $(expr, context)
+                       // (which is just equivalent to: $(context).find(expr)
+                       } else {
+                               return jQuery( context ).find( selector );
+                       }
+
+               // HANDLE: $(function)
+               // Shortcut for document ready
+               } else if ( jQuery.isFunction( selector ) ) {
+                       return rootjQuery.ready( selector );
+               }
+
+               if (selector.selector !== undefined) {
+                       this.selector = selector.selector;
+                       this.context = selector.context;
+               }
+
+               return jQuery.makeArray( selector, this );
+       },
+
+       // Start with an empty selector
+       selector: "",
+
+       // The current version of jQuery being used
+       jquery: "1.4.2",
+
+       // The default length of a jQuery object is 0
+       length: 0,
+
+       // The number of elements contained in the matched element set
+       size: function() {
+               return this.length;
+       },
+
+       toArray: function() {
+               return slice.call( this, 0 );
+       },
+
+       // Get the Nth element in the matched element set OR
+       // Get the whole matched element set as a clean array
+       get: function( num ) {
+               return num == null ?
+
+                       // Return a 'clean' array
+                       this.toArray() :
+
+                       // Return just the object
+                       ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
+       },
+
+       // Take an array of elements and push it onto the stack
+       // (returning the new matched element set)
+       pushStack: function( elems, name, selector ) {
+               // Build a new jQuery matched element set
+               var ret = jQuery();
+
+               if ( jQuery.isArray( elems ) ) {
+                       push.apply( ret, elems );
+               
+               } else {
+                       jQuery.merge( ret, elems );
+               }
+
+               // Add the old object onto the stack (as a reference)
+               ret.prevObject = this;
+
+               ret.context = this.context;
+
+               if ( name === "find" ) {
+                       ret.selector = this.selector + (this.selector ? " " : "") + selector;
+               } else if ( name ) {
+                       ret.selector = this.selector + "." + name + "(" + selector + ")";
+               }
+
+               // Return the newly-formed element set
+               return ret;
+       },
+
+       // Execute a callback for every element in the matched set.
+       // (You can seed the arguments with an array of args, but this is
+       // only used internally.)
+       each: function( callback, args ) {
+               return jQuery.each( this, callback, args );
+       },
+       
+       ready: function( fn ) {
+               // Attach the listeners
+               jQuery.bindReady();
+
+               // If the DOM is already ready
+               if ( jQuery.isReady ) {
+                       // Execute the function immediately
+                       fn.call( document, jQuery );
+
+               // Otherwise, remember the function for later
+               } else if ( readyList ) {
+                       // Add the function to the wait list
+                       readyList.push( fn );
+               }
+
+               return this;
+       },
+       
+       eq: function( i ) {
+               return i === -1 ?
+                       this.slice( i ) :
+                       this.slice( i, +i + 1 );
+       },
+
+       first: function() {
+               return this.eq( 0 );
+       },
+
+       last: function() {
+               return this.eq( -1 );
+       },
+
+       slice: function() {
+               return this.pushStack( slice.apply( this, arguments ),
+                       "slice", slice.call(arguments).join(",") );
+       },
+
+       map: function( callback ) {
+               return this.pushStack( jQuery.map(this, function( elem, i ) {
+                       return callback.call( elem, i, elem );
+               }));
+       },
+       
+       end: function() {
+               return this.prevObject || jQuery(null);
+       },
+
+       // For internal use only.
+       // Behaves like an Array's method, not like a jQuery method.
+       push: push,
+       sort: [].sort,
+       splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+       // copy reference to target object
+       var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
+
+       // Handle a deep copy situation
+       if ( typeof target === "boolean" ) {
+               deep = target;
+               target = arguments[1] || {};
+               // skip the boolean and the target
+               i = 2;
+       }
+
+       // Handle case when target is a string or something (possible in deep copy)
+       if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+               target = {};
+       }
+
+       // extend jQuery itself if only one argument is passed
+       if ( length === i ) {
+               target = this;
+               --i;
+       }
+
+       for ( ; i < length; i++ ) {
+               // Only deal with non-null/undefined values
+               if ( (options = arguments[ i ]) != null ) {
+                       // Extend the base object
+                       for ( name in options ) {
+                               src = target[ name ];
+                               copy = options[ name ];
+
+                               // Prevent never-ending loop
+                               if ( target === copy ) {
+                                       continue;
+                               }
+
+                               // Recurse if we're merging object literal values or arrays
+                               if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
+                                       var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
+                                               : jQuery.isArray(copy) ? [] : {};
+
+                                       // Never move original objects, clone them
+                                       target[ name ] = jQuery.extend( deep, clone, copy );
+
+                               // Don't bring in undefined values
+                               } else if ( copy !== undefined ) {
+                                       target[ name ] = copy;
+                               }
+                       }
+               }
+       }
+
+       // Return the modified object
+       return target;
+};
+
+jQuery.extend({
+       noConflict: function( deep ) {
+               window.$ = _$;
+
+               if ( deep ) {
+                       window.jQuery = _jQuery;
+               }
+
+               return jQuery;
+       },
+       
+       // Is the DOM ready to be used? Set to true once it occurs.
+       isReady: false,
+       
+       // Handle when the DOM is ready
+       ready: function() {
+               // Make sure that the DOM is not already loaded
+               if ( !jQuery.isReady ) {
+                       // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+                       if ( !document.body ) {
+                               return setTimeout( jQuery.ready, 13 );
+                       }
+
+                       // Remember that the DOM is ready
+                       jQuery.isReady = true;
+
+                       // If there are functions bound, to execute
+                       if ( readyList ) {
+                               // Execute all of them
+                               var fn, i = 0;
+                               while ( (fn = readyList[ i++ ]) ) {
+                                       fn.call( document, jQuery );
+                               }
+
+                               // Reset the list of functions
+                               readyList = null;
+                       }
+
+                       // Trigger any bound ready events
+                       if ( jQuery.fn.triggerHandler ) {
+                               jQuery( document ).triggerHandler( "ready" );
+                       }
+               }
+       },
+       
+       bindReady: function() {
+               if ( readyBound ) {
+                       return;
+               }
+
+               readyBound = true;
+
+               // Catch cases where $(document).ready() is called after the
+               // browser event has already occurred.
+               if ( document.readyState === "complete" ) {
+                       return jQuery.ready();
+               }
+
+               // Mozilla, Opera and webkit nightlies currently support this event
+               if ( document.addEventListener ) {
+                       // Use the handy event callback
+                       document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+                       
+                       // A fallback to window.onload, that will always work
+                       window.addEventListener( "load", jQuery.ready, false );
+
+               // If IE event model is used
+               } else if ( document.attachEvent ) {
+                       // ensure firing before onload,
+                       // maybe late but safe also for iframes
+                       document.attachEvent("onreadystatechange", DOMContentLoaded);
+                       
+                       // A fallback to window.onload, that will always work
+                       window.attachEvent( "onload", jQuery.ready );
+
+                       // If IE and not a frame
+                       // continually check to see if the document is ready
+                       var toplevel = false;
+
+                       try {
+                               toplevel = window.frameElement == null;
+                       } catch(e) {}
+
+                       if ( document.documentElement.doScroll && toplevel ) {
+                               doScrollCheck();
+                       }
+               }
+       },
+
+       // See test/unit/core.js for details concerning isFunction.
+       // Since version 1.3, DOM methods and functions like alert
+       // aren't supported. They return false on IE (#2968).
+       isFunction: function( obj ) {
+               return toString.call(obj) === "[object Function]";
+       },
+
+       isArray: function( obj ) {
+               return toString.call(obj) === "[object Array]";
+       },
+
+       isPlainObject: function( obj ) {
+               // Must be an Object.
+               // Because of IE, we also have to check the presence of the constructor property.
+               // Make sure that DOM nodes and window objects don't pass through, as well
+               if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
+                       return false;
+               }
+               
+               // Not own constructor property must be Object
+               if ( obj.constructor
+                       && !hasOwnProperty.call(obj, "constructor")
+                       && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
+                       return false;
+               }
+               
+               // Own properties are enumerated firstly, so to speed up,
+               // if last one is own, then all properties are own.
+       
+               var key;
+               for ( key in obj ) {}
+               
+               return key === undefined || hasOwnProperty.call( obj, key );
+       },
+
+       isEmptyObject: function( obj ) {
+               for ( var name in obj ) {
+                       return false;
+               }
+               return true;
+       },
+       
+       error: function( msg ) {
+               throw msg;
+       },
+       
+       parseJSON: function( data ) {
+               if ( typeof data !== "string" || !data ) {
+                       return null;
+               }
+
+               // Make sure leading/trailing whitespace is removed (IE can't handle it)
+               data = jQuery.trim( data );
+               
+               // Make sure the incoming data is actual JSON
+               // Logic borrowed from http://json.org/json2.js
+               if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
+                       .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
+                       .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
+
+                       // Try to use the native JSON parser first
+                       return window.JSON && window.JSON.parse ?
+                               window.JSON.parse( data ) :
+                               (new Function("return " + data))();
+
+               } else {
+                       jQuery.error( "Invalid JSON: " + data );
+               }
+       },
+
+       noop: function() {},
+
+       // Evalulates a script in a global context
+       globalEval: function( data ) {
+               if ( data && rnotwhite.test(data) ) {
+                       // Inspired by code by Andrea Giammarchi
+                       // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+                       var head = document.getElementsByTagName("head")[0] || document.documentElement,
+                               script = document.createElement("script");
+
+                       script.type = "text/javascript";
+
+                       if ( jQuery.support.scriptEval ) {
+                               script.appendChild( document.createTextNode( data ) );
+                       } else {
+                               script.text = data;
+                       }
+
+                       // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+                       // This arises when a base node is used (#2709).
+                       head.insertBefore( script, head.firstChild );
+                       head.removeChild( script );
+               }
+       },
+
+       nodeName: function( elem, name ) {
+               return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+       },
+
+       // args is for internal usage only
+       each: function( object, callback, args ) {
+               var name, i = 0,
+                       length = object.length,
+                       isObj = length === undefined || jQuery.isFunction(object);
+
+               if ( args ) {
+                       if ( isObj ) {
+                               for ( name in object ) {
+                                       if ( callback.apply( object[ name ], args ) === false ) {
+                                               break;
+                                       }
+                               }
+                       } else {
+                               for ( ; i < length; ) {
+                                       if ( callback.apply( object[ i++ ], args ) === false ) {
+                                               break;
+                                       }
+                               }
+                       }
+
+               // A special, fast, case for the most common use of each
+               } else {
+                       if ( isObj ) {
+                               for ( name in object ) {
+                                       if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+                                               break;
+                                       }
+                               }
+                       } else {
+                               for ( var value = object[0];
+                                       i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
+                       }
+               }
+
+               return object;
+       },
+
+       trim: function( text ) {
+               return (text || "").replace( rtrim, "" );
+       },
+
+       // results is for internal usage only
+       makeArray: function( array, results ) {
+               var ret = results || [];
+
+               if ( array != null ) {
+                       // The window, strings (and functions) also have 'length'
+                       // The extra typeof function check is to prevent crashes
+                       // in Safari 2 (See: #3039)
+                       if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
+                               push.call( ret, array );
+                       } else {
+                               jQuery.merge( ret, array );
+                       }
+               }
+
+               return ret;
+       },
+
+       inArray: function( elem, array ) {
+               if ( array.indexOf ) {
+                       return array.indexOf( elem );
+               }
+
+               for ( var i = 0, length = array.length; i < length; i++ ) {
+                       if ( array[ i ] === elem ) {
+                               return i;
+                       }
+               }
+
+               return -1;
+       },
+
+       merge: function( first, second ) {
+               var i = first.length, j = 0;
+
+               if ( typeof second.length === "number" ) {
+                       for ( var l = second.length; j < l; j++ ) {
+                               first[ i++ ] = second[ j ];
+                       }
+               
+               } else {
+                       while ( second[j] !== undefined ) {
+                               first[ i++ ] = second[ j++ ];
+                       }
+               }
+
+               first.length = i;
+
+               return first;
+       },
+
+       grep: function( elems, callback, inv ) {
+               var ret = [];
+
+               // Go through the array, only saving the items
+               // that pass the validator function
+               for ( var i = 0, length = elems.length; i < length; i++ ) {
+                       if ( !inv !== !callback( elems[ i ], i ) ) {
+                               ret.push( elems[ i ] );
+                       }
+               }
+
+               return ret;
+       },
+
+       // arg is for internal usage only
+       map: function( elems, callback, arg ) {
+               var ret = [], value;
+
+               // Go through the array, translating each of the items to their
+               // new value (or values).
+               for ( var i = 0, length = elems.length; i < length; i++ ) {
+                       value = callback( elems[ i ], i, arg );
+
+                       if ( value != null ) {
+                               ret[ ret.length ] = value;
+                       }
+               }
+
+               return ret.concat.apply( [], ret );
+       },
+
+       // A global GUID counter for objects
+       guid: 1,
+
+       proxy: function( fn, proxy, thisObject ) {
+               if ( arguments.length === 2 ) {
+                       if ( typeof proxy === "string" ) {
+                               thisObject = fn;
+                               fn = thisObject[ proxy ];
+                               proxy = undefined;
+
+                       } else if ( proxy && !jQuery.isFunction( proxy ) ) {
+                               thisObject = proxy;
+                               proxy = undefined;
+                       }
+               }
+
+               if ( !proxy && fn ) {
+                       proxy = function() {
+                               return fn.apply( thisObject || this, arguments );
+                       };
+               }
+
+               // Set the guid of unique handler to the same of original handler, so it can be removed
+               if ( fn ) {
+                       proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+               }
+
+               // So proxy can be declared as an argument
+               return proxy;
+       },
+
+       // Use of jQuery.browser is frowned upon.
+       // More details: http://docs.jquery.com/Utilities/jQuery.browser
+       uaMatch: function( ua ) {
+               ua = ua.toLowerCase();
+
+               var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+                       /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
+                       /(msie) ([\w.]+)/.exec( ua ) ||
+                       !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
+                       [];
+
+               return { browser: match[1] || "", version: match[2] || "0" };
+       },
+
+       browser: {}
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+       jQuery.browser[ browserMatch.browser ] = true;
+       jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+       jQuery.browser.safari = true;
+}
+
+if ( indexOf ) {
+       jQuery.inArray = function( elem, array ) {
+               return indexOf.call( array, elem );
+       };
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+       DOMContentLoaded = function() {
+               document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+               jQuery.ready();
+       };
+
+} else if ( document.attachEvent ) {
+       DOMContentLoaded = function() {
+               // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+               if ( document.readyState === "complete" ) {
+                       document.detachEvent( "onreadystatechange", DOMContentLoaded );
+                       jQuery.ready();
+               }
+       };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+       if ( jQuery.isReady ) {
+               return;
+       }
+
+       try {
+               // If IE is used, use the trick by Diego Perini
+               // http://javascript.nwbox.com/IEContentLoaded/
+               document.documentElement.doScroll("left");
+       } catch( error ) {
+               setTimeout( doScrollCheck, 1 );
+               return;
+       }
+
+       // and execute any waiting functions
+       jQuery.ready();
+}
+
+function evalScript( i, elem ) {
+       if ( elem.src ) {
+               jQuery.ajax({
+                       url: elem.src,
+                       async: false,
+                       dataType: "script"
+               });
+       } else {
+               jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+       }
+
+       if ( elem.parentNode ) {
+               elem.parentNode.removeChild( elem );
+       }
+}
+
+// Mutifunctional method to get and set values to a collection
+// The value/s can be optionally by executed if its a function
+function access( elems, key, value, exec, fn, pass ) {
+       var length = elems.length;
+       
+       // Setting many attributes
+       if ( typeof key === "object" ) {
+               for ( var k in key ) {
+                       access( elems, k, key[k], exec, fn, value );
+               }
+               return elems;
+       }
+       
+       // Setting one attribute
+       if ( value !== undefined ) {
+               // Optionally, function values get executed if exec is true
+               exec = !pass && exec && jQuery.isFunction(value);
+               
+               for ( var i = 0; i < length; i++ ) {
+                       fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+               }
+               
+               return elems;
+       }
+       
+       // Getting an attribute
+       return length ? fn( elems[0], key ) : undefined;
+}
+
+function now() {
+       return (new Date).getTime();
+}
+(function() {
+
+       jQuery.support = {};
+
+       var root = document.documentElement,
+               script = document.createElement("script"),
+               div = document.createElement("div"),
+               id = "script" + now();
+
+       div.style.display = "none";
+       div.innerHTML = "   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+       var all = div.getElementsByTagName("*"),
+               a = div.getElementsByTagName("a")[0];
+
+       // Can't get basic test support
+       if ( !all || !all.length || !a ) {
+               return;
+       }
+
+       jQuery.support = {
+               // IE strips leading whitespace when .innerHTML is used
+               leadingWhitespace: div.firstChild.nodeType === 3,
+
+               // Make sure that tbody elements aren't automatically inserted
+               // IE will insert them into empty tables
+               tbody: !div.getElementsByTagName("tbody").length,
+
+               // Make sure that link elements get serialized correctly by innerHTML
+               // This requires a wrapper element in IE
+               htmlSerialize: !!div.getElementsByTagName("link").length,
+
+               // Get the style information from getAttribute
+               // (IE uses .cssText insted)
+               style: /red/.test( a.getAttribute("style") ),
+
+               // Make sure that URLs aren't manipulated
+               // (IE normalizes it by default)
+               hrefNormalized: a.getAttribute("href") === "/a",
+
+               // Make sure that element opacity exists
+               // (IE uses filter instead)
+               // Use a regex to work around a WebKit issue. See #5145
+               opacity: /^0.55$/.test( a.style.opacity ),
+
+               // Verify style float existence
+               // (IE uses styleFloat instead of cssFloat)
+               cssFloat: !!a.style.cssFloat,
+
+               // Make sure that if no value is specified for a checkbox
+               // that it defaults to "on".
+               // (WebKit defaults to "" instead)
+               checkOn: div.getElementsByTagName("input")[0].value === "on",
+
+               // Make sure that a selected-by-default option has a working selected property.
+               // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+               optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
+
+               parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
+
+               // Will be defined later
+               deleteExpando: true,
+               checkClone: false,
+               scriptEval: false,
+               noCloneEvent: true,
+               boxModel: null
+       };
+
+       script.type = "text/javascript";
+       try {
+               script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+       } catch(e) {}
+
+       root.insertBefore( script, root.firstChild );
+
+       // Make sure that the execution of code works by injecting a script
+       // tag with appendChild/createTextNode
+       // (IE doesn't support this, fails, and uses .text instead)
+       if ( window[ id ] ) {
+               jQuery.support.scriptEval = true;
+               delete window[ id ];
+       }
+
+       // Test to see if it's possible to delete an expando from an element
+       // Fails in Internet Explorer
+       try {
+               delete script.test;
+       
+       } catch(e) {
+               jQuery.support.deleteExpando = false;
+       }
+
+       root.removeChild( script );
+
+       if ( div.attachEvent && div.fireEvent ) {
+               div.attachEvent("onclick", function click() {
+                       // Cloning a node shouldn't copy over any
+                       // bound event handlers (IE does this)
+                       jQuery.support.noCloneEvent = false;
+                       div.detachEvent("onclick", click);
+               });
+               div.cloneNode(true).fireEvent("onclick");
+       }
+
+       div = document.createElement("div");
+       div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
+
+       var fragment = document.createDocumentFragment();
+       fragment.appendChild( div.firstChild );
+
+       // WebKit doesn't clone checked state correctly in fragments
+       jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
+
+       // Figure out if the W3C box model works as expected
+       // document.body must exist before we can do this
+       jQuery(function() {
+               var div = document.createElement("div");
+               div.style.width = div.style.paddingLeft = "1px";
+
+               document.body.appendChild( div );
+               jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+               document.body.removeChild( div ).style.display = 'none';
+
+               div = null;
+       });
+
+       // Technique from Juriy Zaytsev
+       // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+       var eventSupported = function( eventName ) { 
+               var el = document.createElement("div"); 
+               eventName = "on" + eventName; 
+
+               var isSupported = (eventName in el); 
+               if ( !isSupported ) { 
+                       el.setAttribute(eventName, "return;"); 
+                       isSupported = typeof el[eventName] === "function"; 
+               } 
+               el = null; 
+
+               return isSupported; 
+       };
+       
+       jQuery.support.submitBubbles = eventSupported("submit");
+       jQuery.support.changeBubbles = eventSupported("change");
+
+       // release memory in IE
+       root = script = div = all = a = null;
+})();
+
+jQuery.props = {
+       "for": "htmlFor",
+       "class": "className",
+       readonly: "readOnly",
+       maxlength: "maxLength",
+       cellspacing: "cellSpacing",
+       rowspan: "rowSpan",
+       colspan: "colSpan",
+       tabindex: "tabIndex",
+       usemap: "useMap",
+       frameborder: "frameBorder"
+};
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+       cache: {},
+       
+       expando:expando,
+
+       // The following elements throw uncatchable exceptions if you
+       // attempt to add expando properties to them.
+       noData: {
+               "embed": true,
+               "object": true,
+               "applet": true
+       },
+
+       data: function( elem, name, data ) {
+               if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+                       return;
+               }
+
+               elem = elem == window ?
+                       windowData :
+                       elem;
+
+               var id = elem[ expando ], cache = jQuery.cache, thisCache;
+
+               if ( !id && typeof name === "string" && data === undefined ) {
+                       return null;
+               }
+
+               // Compute a unique ID for the element
+               if ( !id ) { 
+                       id = ++uuid;
+               }
+
+               // Avoid generating a new cache unless none exists and we
+               // want to manipulate it.
+               if ( typeof name === "object" ) {
+                       elem[ expando ] = id;
+                       thisCache = cache[ id ] = jQuery.extend(true, {}, name);
+
+               } else if ( !cache[ id ] ) {
+                       elem[ expando ] = id;
+                       cache[ id ] = {};
+               }
+
+               thisCache = cache[ id ];
+
+               // Prevent overriding the named cache with undefined values
+               if ( data !== undefined ) {
+                       thisCache[ name ] = data;
+               }
+
+               return typeof name === "string" ? thisCache[ name ] : thisCache;
+       },
+
+       removeData: function( elem, name ) {
+               if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+                       return;
+               }
+
+               elem = elem == window ?
+                       windowData :
+                       elem;
+
+               var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];
+
+               // If we want to remove a specific section of the element's data
+               if ( name ) {
+                       if ( thisCache ) {
+                               // Remove the section of cache data
+                               delete thisCache[ name ];
+
+                               // If we've removed all the data, remove the element's cache
+                               if ( jQuery.isEmptyObject(thisCache) ) {
+                                       jQuery.removeData( elem );
+                               }
+                       }
+
+               // Otherwise, we want to remove all of the element's data
+               } else {
+                       if ( jQuery.support.deleteExpando ) {
+                               delete elem[ jQuery.expando ];
+
+                       } else if ( elem.removeAttribute ) {
+                               elem.removeAttribute( jQuery.expando );
+                       }
+
+                       // Completely remove the data cache
+                       delete cache[ id ];
+               }
+       }
+});
+
+jQuery.fn.extend({
+       data: function( key, value ) {
+               if ( typeof key === "undefined" && this.length ) {
+                       return jQuery.data( this[0] );
+
+               } else if ( typeof key === "object" ) {
+                       return this.each(function() {
+                               jQuery.data( this, key );
+                       });
+               }
+
+               var parts = key.split(".");
+               parts[1] = parts[1] ? "." + parts[1] : "";
+
+               if ( value === undefined ) {
+                       var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+                       if ( data === undefined && this.length ) {
+                               data = jQuery.data( this[0], key );
+                       }
+                       return data === undefined && parts[1] ?
+                               this.data( parts[0] ) :
+                               data;
+               } else {
+                       return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
+                               jQuery.data( this, key, value );
+                       });
+               }
+       },
+
+       removeData: function( key ) {
+               return this.each(function() {
+                       jQuery.removeData( this, key );
+               });
+       }
+});
+jQuery.extend({
+       queue: function( elem, type, data ) {
+               if ( !elem ) {
+                       return;
+               }
+
+               type = (type || "fx") + "queue";
+               var q = jQuery.data( elem, type );
+
+               // Speed up dequeue by getting out quickly if this is just a lookup
+               if ( !data ) {
+                       return q || [];
+               }
+
+               if ( !q || jQuery.isArray(data) ) {
+                       q = jQuery.data( elem, type, jQuery.makeArray(data) );
+
+               } else {
+                       q.push( data );
+               }
+
+               return q;
+       },
+
+       dequeue: function( elem, type ) {
+               type = type || "fx";
+
+               var queue = jQuery.queue( elem, type ), fn = queue.shift();
+
+               // If the fx queue is dequeued, always remove the progress sentinel
+               if ( fn === "inprogress" ) {
+                       fn = queue.shift();
+               }
+
+               if ( fn ) {
+                       // Add a progress sentinel to prevent the fx queue from being
+                       // automatically dequeued
+                       if ( type === "fx" ) {
+                               queue.unshift("inprogress");
+                       }
+
+                       fn.call(elem, function() {
+                               jQuery.dequeue(elem, type);
+                       });
+               }
+       }
+});
+
+jQuery.fn.extend({
+       queue: function( type, data ) {
+               if ( typeof type !== "string" ) {
+                       data = type;
+                       type = "fx";
+               }
+
+               if ( data === undefined ) {
+                       return jQuery.queue( this[0], type );
+               }
+               return this.each(function( i, elem ) {
+                       var queue = jQuery.queue( this, type, data );
+
+                       if ( type === "fx" && queue[0] !== "inprogress" ) {
+                               jQuery.dequeue( this, type );
+                       }
+               });
+       },
+       dequeue: function( type ) {
+               return this.each(function() {
+                       jQuery.dequeue( this, type );
+               });
+       },
+
+       // Based off of the plugin by Clint Helfers, with permission.
+       // http://blindsignals.com/index.php/2009/07/jquery-delay/
+       delay: function( time, type ) {
+               time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+               type = type || "fx";
+
+               return this.queue( type, function() {
+                       var elem = this;
+                       setTimeout(function() {
+                               jQuery.dequeue( elem, type );
+                       }, time );
+               });
+       },
+
+       clearQueue: function( type ) {
+               return this.queue( type || "fx", [] );
+       }
+});
+var rclass = /[\n\t]/g,
+       rspace = /\s+/,
+       rreturn = /\r/g,
+       rspecialurl = /href|src|style/,
+       rtype = /(button|input)/i,
+       rfocusable = /(button|input|object|select|textarea)/i,
+       rclickable = /^(a|area)$/i,
+       rradiocheck = /radio|checkbox/;
+
+jQuery.fn.extend({
+       attr: function( name, value ) {
+               return access( this, name, value, true, jQuery.attr );
+       },
+
+       removeAttr: function( name, fn ) {
+               return this.each(function(){
+                       jQuery.attr( this, name, "" );
+                       if ( this.nodeType === 1 ) {
+                               this.removeAttribute( name );
+                       }
+               });
+       },
+
+       addClass: function( value ) {
+               if ( jQuery.isFunction(value) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               self.addClass( value.call(this, i, self.attr("class")) );
+                       });
+               }
+
+               if ( value && typeof value === "string" ) {
+                       var classNames = (value || "").split( rspace );
+
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               var elem = this[i];
+
+                               if ( elem.nodeType === 1 ) {
+                                       if ( !elem.className ) {
+                                               elem.className = value;
+
+                                       } else {
+                                               var className = " " + elem.className + " ", setClass = elem.className;
+                                               for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+                                                       if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
+                                                               setClass += " " + classNames[c];
+                                                       }
+                                               }
+                                               elem.className = jQuery.trim( setClass );
+                                       }
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       removeClass: function( value ) {
+               if ( jQuery.isFunction(value) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               self.removeClass( value.call(this, i, self.attr("class")) );
+                       });
+               }
+
+               if ( (value && typeof value === "string") || value === undefined ) {
+                       var classNames = (value || "").split(rspace);
+
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               var elem = this[i];
+
+                               if ( elem.nodeType === 1 && elem.className ) {
+                                       if ( value ) {
+                                               var className = (" " + elem.className + " ").replace(rclass, " ");
+                                               for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+                                                       className = className.replace(" " + classNames[c] + " ", " ");
+                                               }
+                                               elem.className = jQuery.trim( className );
+
+                                       } else {
+                                               elem.className = "";
+                                       }
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       toggleClass: function( value, stateVal ) {
+               var type = typeof value, isBool = typeof stateVal === "boolean";
+
+               if ( jQuery.isFunction( value ) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
+                       });
+               }
+
+               return this.each(function() {
+                       if ( type === "string" ) {
+                               // toggle individual class names
+                               var className, i = 0, self = jQuery(this),
+                                       state = stateVal,
+                                       classNames = value.split( rspace );
+
+                               while ( (className = classNames[ i++ ]) ) {
+                                       // check each className given, space seperated list
+                                       state = isBool ? state : !self.hasClass( className );
+                                       self[ state ? "addClass" : "removeClass" ]( className );
+                               }
+
+                       } else if ( type === "undefined" || type === "boolean" ) {
+                               if ( this.className ) {
+                                       // store className if set
+                                       jQuery.data( this, "__className__", this.className );
+                               }
+
+                               // toggle whole className
+                               this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
+                       }
+               });
+       },
+
+       hasClass: function( selector ) {
+               var className = " " + selector + " ";
+               for ( var i = 0, l = this.length; i < l; i++ ) {
+                       if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       },
+
+       val: function( value ) {
+               if ( value === undefined ) {
+                       var elem = this[0];
+
+                       if ( elem ) {
+                               if ( jQuery.nodeName( elem, "option" ) ) {
+                                       return (elem.attributes.value || {}).specified ? elem.value : elem.text;
+                               }
+
+                               // We need to handle select boxes special
+                               if ( jQuery.nodeName( elem, "select" ) ) {
+                                       var index = elem.selectedIndex,
+                                               values = [],
+                                               options = elem.options,
+                                               one = elem.type === "select-one";
+
+                                       // Nothing was selected
+                                       if ( index < 0 ) {
+                                               return null;
+                                       }
+
+                                       // Loop through all the selected options
+                                       for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+                                               var option = options[ i ];
+
+                                               if ( option.selected ) {
+                                                       // Get the specifc value for the option
+                                                       value = jQuery(option).val();
+
+                                                       // We don't need an array for one selects
+                                                       if ( one ) {
+                                                               return value;
+                                                       }
+
+                                                       // Multi-Selects return an array
+                                                       values.push( value );
+                                               }
+                                       }
+
+                                       return values;
+                               }
+
+                               // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+                               if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
+                                       return elem.getAttribute("value") === null ? "on" : elem.value;
+                               }
+                               
+
+                               // Everything else, we just grab the value
+                               return (elem.value || "").replace(rreturn, "");
+
+                       }
+
+                       return undefined;
+               }
+
+               var isFunction = jQuery.isFunction(value);
+
+               return this.each(function(i) {
+                       var self = jQuery(this), val = value;
+
+                       if ( this.nodeType !== 1 ) {
+                               return;
+                       }
+
+                       if ( isFunction ) {
+                               val = value.call(this, i, self.val());
+                       }
+
+                       // Typecast each time if the value is a Function and the appended
+                       // value is therefore different each time.
+                       if ( typeof val === "number" ) {
+                               val += "";
+                       }
+
+                       if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
+                               this.checked = jQuery.inArray( self.val(), val ) >= 0;
+
+                       } else if ( jQuery.nodeName( this, "select" ) ) {
+                               var values = jQuery.makeArray(val);
+
+                               jQuery( "option", this ).each(function() {
+                                       this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+                               });
+
+                               if ( !values.length ) {
+                                       this.selectedIndex = -1;
+                               }
+
+                       } else {
+                               this.value = val;
+                       }
+               });
+       }
+});
+
+jQuery.extend({
+       attrFn: {
+               val: true,
+               css: true,
+               html: true,
+               text: true,
+               data: true,
+               width: true,
+               height: true,
+               offset: true
+       },
+               
+       attr: function( elem, name, value, pass ) {
+               // don't set attributes on text and comment nodes
+               if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+                       return undefined;
+               }
+
+               if ( pass && name in jQuery.attrFn ) {
+                       return jQuery(elem)[name](value);
+               }
+
+               var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
+                       // Whether we are setting (or getting)
+                       set = value !== undefined;
+
+               // Try to normalize/fix the name
+               name = notxml && jQuery.props[ name ] || name;
+
+               // Only do all the following if this is a node (faster for style)
+               if ( elem.nodeType === 1 ) {
+                       // These attributes require special treatment
+                       var special = rspecialurl.test( name );
+
+                       // Safari mis-reports the default selected property of an option
+                       // Accessing the parent's selectedIndex property fixes it
+                       if ( name === "selected" && !jQuery.support.optSelected ) {
+                               var parent = elem.parentNode;
+                               if ( parent ) {
+                                       parent.selectedIndex;
+       
+                                       // Make sure that it also works with optgroups, see #5701
+                                       if ( parent.parentNode ) {
+                                               parent.parentNode.selectedIndex;
+                                       }
+                               }
+                       }
+
+                       // If applicable, access the attribute via the DOM 0 way
+                       if ( name in elem && notxml && !special ) {
+                               if ( set ) {
+                                       // We can't allow the type property to be changed (since it causes problems in IE)
+                                       if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
+                                               jQuery.error( "type property can't be changed" );
+                                       }
+
+                                       elem[ name ] = value;
+                               }
+
+                               // browsers index elements by id/name on forms, give priority to attributes.
+                               if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
+                                       return elem.getAttributeNode( name ).nodeValue;
+                               }
+
+                               // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+                               // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+                               if ( name === "tabIndex" ) {
+                                       var attributeNode = elem.getAttributeNode( "tabIndex" );
+
+                                       return attributeNode && attributeNode.specified ?
+                                               attributeNode.value :
+                                               rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+                                                       0 :
+                                                       undefined;
+                               }
+
+                               return elem[ name ];
+                       }
+
+                       if ( !jQuery.support.style && notxml && name === "style" ) {
+                               if ( set ) {
+                                       elem.style.cssText = "" + value;
+                               }
+
+                               return elem.style.cssText;
+                       }
+
+                       if ( set ) {
+                               // convert the value to a string (all browsers do this but IE) see #1070
+                               elem.setAttribute( name, "" + value );
+                       }
+
+                       var attr = !jQuery.support.hrefNormalized && notxml && special ?
+                                       // Some attributes require a special call on IE
+                                       elem.getAttribute( name, 2 ) :
+                                       elem.getAttribute( name );
+
+                       // Non-existent attributes return null, we normalize to undefined
+                       return attr === null ? undefined : attr;
+               }
+
+               // elem is actually elem.style ... set the style
+               // Using attr for specific style information is now deprecated. Use style instead.
+               return jQuery.style( elem, name, value );
+       }
+});
+var rnamespaces = /\.(.*)$/,
+       fcleanup = function( nm ) {
+               return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
+                       return "\\" + ch;
+               });
+       };
+
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+       // Bind an event to an element
+       // Original by Dean Edwards
+       add: function( elem, types, handler, data ) {
+               if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+                       return;
+               }
+
+               // For whatever reason, IE has trouble passing the window object
+               // around, causing it to be cloned in the process
+               if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
+                       elem = window;
+               }
+
+               var handleObjIn, handleObj;
+
+               if ( handler.handler ) {
+                       handleObjIn = handler;
+                       handler = handleObjIn.handler;
+               }
+
+               // Make sure that the function being executed has a unique ID
+               if ( !handler.guid ) {
+                       handler.guid = jQuery.guid++;
+               }
+
+               // Init the element's event structure
+               var elemData = jQuery.data( elem );
+
+               // If no elemData is found then we must be trying to bind to one of the
+               // banned noData elements
+               if ( !elemData ) {
+                       return;
+               }
+
+               var events = elemData.events = elemData.events || {},
+                       eventHandle = elemData.handle, eventHandle;
+
+               if ( !eventHandle ) {
+                       elemData.handle = eventHandle = function() {
+                               // Handle the second event of a trigger and when
+                               // an event is called after a page has unloaded
+                               return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+                                       jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+                                       undefined;
+                       };
+               }
+
+               // Add elem as a property of the handle function
+               // This is to prevent a memory leak with non-native events in IE.
+               eventHandle.elem = elem;
+
+               // Handle multiple events separated by a space
+               // jQuery(...).bind("mouseover mouseout", fn);
+               types = types.split(" ");
+
+               var type, i = 0, namespaces;
+
+               while ( (type = types[ i++ ]) ) {
+                       handleObj = handleObjIn ?
+                               jQuery.extend({}, handleObjIn) :
+                               { handler: handler, data: data };
+
+                       // Namespaced event handlers
+                       if ( type.indexOf(".") > -1 ) {
+                               namespaces = type.split(".");
+                               type = namespaces.shift();
+                               handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+                       } else {
+                               namespaces = [];
+                               handleObj.namespace = "";
+                       }
+
+                       handleObj.type = type;
+                       handleObj.guid = handler.guid;
+
+                       // Get the current list of functions bound to this event
+                       var handlers = events[ type ],
+                               special = jQuery.event.special[ type ] || {};
+
+                       // Init the event handler queue
+                       if ( !handlers ) {
+                               handlers = events[ type ] = [];
+
+                               // Check for a special event handler
+                               // Only use addEventListener/attachEvent if the special
+                               // events handler returns false
+                               if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+                                       // Bind the global event handler to the element
+                                       if ( elem.addEventListener ) {
+                                               elem.addEventListener( type, eventHandle, false );
+
+                                       } else if ( elem.attachEvent ) {
+                                               elem.attachEvent( "on" + type, eventHandle );
+                                       }
+                               }
+                       }
+                       
+                       if ( special.add ) { 
+                               special.add.call( elem, handleObj ); 
+
+                               if ( !handleObj.handler.guid ) {
+                                       handleObj.handler.guid = handler.guid;
+                               }
+                       }
+
+                       // Add the function to the element's handler list
+                       handlers.push( handleObj );
+
+                       // Keep track of which events have been used, for global triggering
+                       jQuery.event.global[ type ] = true;
+               }
+
+               // Nullify elem to prevent memory leaks in IE
+               elem = null;
+       },
+
+       global: {},
+
+       // Detach an event or set of events from an element
+       remove: function( elem, types, handler, pos ) {
+               // don't do events on text and comment nodes
+               if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+                       return;
+               }
+
+               var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+                       elemData = jQuery.data( elem ),
+                       events = elemData && elemData.events;
+
+               if ( !elemData || !events ) {
+                       return;
+               }
+
+               // types is actually an event object here
+               if ( types && types.type ) {
+                       handler = types.handler;
+                       types = types.type;
+               }
+
+               // Unbind all events for the element
+               if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+                       types = types || "";
+
+                       for ( type in events ) {
+                               jQuery.event.remove( elem, type + types );
+                       }
+
+                       return;
+               }
+
+               // Handle multiple events separated by a space
+               // jQuery(...).unbind("mouseover mouseout", fn);
+               types = types.split(" ");
+
+               while ( (type = types[ i++ ]) ) {
+                       origType = type;
+                       handleObj = null;
+                       all = type.indexOf(".") < 0;
+                       namespaces = [];
+
+                       if ( !all ) {
+                               // Namespaced event handlers
+                               namespaces = type.split(".");
+                               type = namespaces.shift();
+
+                               namespace = new RegExp("(^|\\.)" + 
+                                       jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
+                       }
+
+                       eventType = events[ type ];
+
+                       if ( !eventType ) {
+                               continue;
+                       }
+
+                       if ( !handler ) {
+                               for ( var j = 0; j < eventType.length; j++ ) {
+                                       handleObj = eventType[ j ];
+
+                                       if ( all || namespace.test( handleObj.namespace ) ) {
+                                               jQuery.event.remove( elem, origType, handleObj.handler, j );
+                                               eventType.splice( j--, 1 );
+                                       }
+                               }
+
+                               continue;
+                       }
+
+                       special = jQuery.event.special[ type ] || {};
+
+                       for ( var j = pos || 0; j < eventType.length; j++ ) {
+                               handleObj = eventType[ j ];
+
+                               if ( handler.guid === handleObj.guid ) {
+                                       // remove the given handler for the given type
+                                       if ( all || namespace.test( handleObj.namespace ) ) {
+                                               if ( pos == null ) {
+                                                       eventType.splice( j--, 1 );
+                                               }
+
+                                               if ( special.remove ) {
+                                                       special.remove.call( elem, handleObj );
+                                               }
+                                       }
+
+                                       if ( pos != null ) {
+                                               break;
+                                       }
+                               }
+                       }
+
+                       // remove generic event handler if no more handlers exist
+                       if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+                               if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+                                       removeEvent( elem, type, elemData.handle );
+                               }
+
+                               ret = null;
+                               delete events[ type ];
+                       }
+               }
+
+               // Remove the expando if it's no longer used
+               if ( jQuery.isEmptyObject( events ) ) {
+                       var handle = elemData.handle;
+                       if ( handle ) {
+                               handle.elem = null;
+                       }
+
+                       delete elemData.events;
+                       delete elemData.handle;
+
+                       if ( jQuery.isEmptyObject( elemData ) ) {
+                               jQuery.removeData( elem );
+                       }
+               }
+       },
+
+       // bubbling is internal
+       trigger: function( event, data, elem /*, bubbling */ ) {
+               // Event object or event type
+               var type = event.type || event,
+                       bubbling = arguments[3];
+
+               if ( !bubbling ) {
+                       event = typeof event === "object" ?
+                               // jQuery.Event object
+                               event[expando] ? event :
+                               // Object literal
+                               jQuery.extend( jQuery.Event(type), event ) :
+                               // Just the event type (string)
+                               jQuery.Event(type);
+
+                       if ( type.indexOf("!") >= 0 ) {
+                               event.type = type = type.slice(0, -1);
+                               event.exclusive = true;
+                       }
+
+                       // Handle a global trigger
+                       if ( !elem ) {
+                               // Don't bubble custom events when global (to avoid too much overhead)
+                               event.stopPropagation();
+
+                               // Only trigger if we've ever bound an event for it
+                               if ( jQuery.event.global[ type ] ) {
+                                       jQuery.each( jQuery.cache, function() {
+                                               if ( this.events && this.events[type] ) {
+                                                       jQuery.event.trigger( event, data, this.handle.elem );
+                                               }
+                                       });
+                               }
+                       }
+
+                       // Handle triggering a single element
+
+                       // don't do events on text and comment nodes
+                       if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+                               return undefined;
+                       }
+
+                       // Clean up in case it is reused
+                       event.result = undefined;
+                       event.target = elem;
+
+                       // Clone the incoming data, if any
+                       data = jQuery.makeArray( data );
+                       data.unshift( event );
+               }
+
+               event.currentTarget = elem;
+
+               // Trigger the event, it is assumed that "handle" is a function
+               var handle = jQuery.data( elem, "handle" );
+               if ( handle ) {
+                       handle.apply( elem, data );
+               }
+
+               var parent = elem.parentNode || elem.ownerDocument;
+
+               // Trigger an inline bound script
+               try {
+                       if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
+                               if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
+                                       event.result = false;
+                               }
+                       }
+
+               // prevent IE from throwing an error for some elements with some event types, see #3533
+               } catch (e) {}
+
+               if ( !event.isPropagationStopped() && parent ) {
+                       jQuery.event.trigger( event, data, parent, true );
+
+               } else if ( !event.isDefaultPrevented() ) {
+                       var target = event.target, old,
+                               isClick = jQuery.nodeName(target, "a") && type === "click",
+                               special = jQuery.event.special[ type ] || {};
+
+                       if ( (!special._default || special._default.call( elem, event ) === false) && 
+                               !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
+
+                               try {
+                                       if ( target[ type ] ) {
+                                               // Make sure that we don't accidentally re-trigger the onFOO events
+                                               old = target[ "on" + type ];
+
+                                               if ( old ) {
+                                                       target[ "on" + type ] = null;
+                                               }
+
+                                               jQuery.event.triggered = true;
+                                               target[ type ]();
+                                       }
+
+                               // prevent IE from throwing an error for some elements with some event types, see #3533
+                               } catch (e) {}
+
+                               if ( old ) {
+                                       target[ "on" + type ] = old;
+                               }
+
+                               jQuery.event.triggered = false;
+                       }
+               }
+       },
+
+       handle: function( event ) {
+               var all, handlers, namespaces, namespace, events;
+
+               event = arguments[0] = jQuery.event.fix( event || window.event );
+               event.currentTarget = this;
+
+               // Namespaced event handlers
+               all = event.type.indexOf(".") < 0 && !event.exclusive;
+
+               if ( !all ) {
+                       namespaces = event.type.split(".");
+                       event.type = namespaces.shift();
+                       namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
+               }
+
+               var events = jQuery.data(this, "events"), handlers = events[ event.type ];
+
+               if ( events && handlers ) {
+                       // Clone the handlers to prevent manipulation
+                       handlers = handlers.slice(0);
+
+                       for ( var j = 0, l = handlers.length; j < l; j++ ) {
+                               var handleObj = handlers[ j ];
+
+                               // Filter the functions by class
+                               if ( all || namespace.test( handleObj.namespace ) ) {
+                                       // Pass in a reference to the handler function itself
+                                       // So that we can later remove it
+                                       event.handler = handleObj.handler;
+                                       event.data = handleObj.data;
+                                       event.handleObj = handleObj;
+       
+                                       var ret = handleObj.handler.apply( this, arguments );
+
+                                       if ( ret !== undefined ) {
+                                               event.result = ret;
+                                               if ( ret === false ) {
+                                                       event.preventDefault();
+                                                       event.stopPropagation();
+                                               }
+                                       }
+
+                                       if ( event.isImmediatePropagationStopped() ) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               return event.result;
+       },
+
+       props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+       fix: function( event ) {
+               if ( event[ expando ] ) {
+                       return event;
+               }
+
+               // store a copy of the original event object
+               // and "clone" to set read-only properties
+               var originalEvent = event;
+               event = jQuery.Event( originalEvent );
+
+               for ( var i = this.props.length, prop; i; ) {
+                       prop = this.props[ --i ];
+                       event[ prop ] = originalEvent[ prop ];
+               }
+
+               // Fix target property, if necessary
+               if ( !event.target ) {
+                       event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+               }
+
+               // check if target is a textnode (safari)
+               if ( event.target.nodeType === 3 ) {
+                       event.target = event.target.parentNode;
+               }
+
+               // Add relatedTarget, if necessary
+               if ( !event.relatedTarget && event.fromElement ) {
+                       event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+               }
+
+               // Calculate pageX/Y if missing and clientX/Y available
+               if ( event.pageX == null && event.clientX != null ) {
+                       var doc = document.documentElement, body = document.body;
+                       event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+                       event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
+               }
+
+               // Add which for key events
+               if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
+                       event.which = event.charCode || event.keyCode;
+               }
+
+               // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+               if ( !event.metaKey && event.ctrlKey ) {
+                       event.metaKey = event.ctrlKey;
+               }
+
+               // Add which for click: 1 === left; 2 === middle; 3 === right
+               // Note: button is not normalized, so don't use it
+               if ( !event.which && event.button !== undefined ) {
+                       event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+               }
+
+               return event;
+       },
+
+       // Deprecated, use jQuery.guid instead
+       guid: 1E8,
+
+       // Deprecated, use jQuery.proxy instead
+       proxy: jQuery.proxy,
+
+       special: {
+               ready: {
+                       // Make sure the ready event is setup
+                       setup: jQuery.bindReady,
+                       teardown: jQuery.noop
+               },
+
+               live: {
+                       add: function( handleObj ) {
+                               jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); 
+                       },
+
+                       remove: function( handleObj ) {
+                               var remove = true,
+                                       type = handleObj.origType.replace(rnamespaces, "");
+                               
+                               jQuery.each( jQuery.data(this, "events").live || [], function() {
+                                       if ( type === this.origType.replace(rnamespaces, "") ) {
+                                               remove = false;
+                                               return false;
+                                       }
+                               });
+
+                               if ( remove ) {
+                                       jQuery.event.remove( this, handleObj.origType, liveHandler );
+                               }
+                       }
+
+               },
+
+               beforeunload: {
+                       setup: function( data, namespaces, eventHandle ) {
+                               // We only want to do this special case on windows
+                               if ( this.setInterval ) {
+                                       this.onbeforeunload = eventHandle;
+                               }
+
+                               return false;
+                       },
+                       teardown: function( namespaces, eventHandle ) {
+                               if ( this.onbeforeunload === eventHandle ) {
+                                       this.onbeforeunload = null;
+                               }
+                       }
+               }
+       }
+};
+
+var removeEvent = document.removeEventListener ?
+       function( elem, type, handle ) {
+               elem.removeEventListener( type, handle, false );
+       } : 
+       function( elem, type, handle ) {
+               elem.detachEvent( "on" + type, handle );
+       };
+
+jQuery.Event = function( src ) {
+       // Allow instantiation without the 'new' keyword
+       if ( !this.preventDefault ) {
+               return new jQuery.Event( src );
+       }
+
+       // Event object
+       if ( src && src.type ) {
+               this.originalEvent = src;
+               this.type = src.type;
+       // Event type
+       } else {
+               this.type = src;
+       }
+
+       // timeStamp is buggy for some events on Firefox(#3843)
+       // So we won't rely on the native value
+       this.timeStamp = now();
+
+       // Mark it as fixed
+       this[ expando ] = true;
+};
+
+function returnFalse() {
+       return false;
+}
+function returnTrue() {
+       return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+       preventDefault: function() {
+               this.isDefaultPrevented = returnTrue;
+
+               var e = this.originalEvent;
+               if ( !e ) {
+                       return;
+               }
+               
+               // if preventDefault exists run it on the original event
+               if ( e.preventDefault ) {
+                       e.preventDefault();
+               }
+               // otherwise set the returnValue property of the original event to false (IE)
+               e.returnValue = false;
+       },
+       stopPropagation: function() {
+               this.isPropagationStopped = returnTrue;
+
+               var e = this.originalEvent;
+               if ( !e ) {
+                       return;
+               }
+               // if stopPropagation exists run it on the original event
+               if ( e.stopPropagation ) {
+                       e.stopPropagation();
+               }
+               // otherwise set the cancelBubble property of the original event to true (IE)
+               e.cancelBubble = true;
+       },
+       stopImmediatePropagation: function() {
+               this.isImmediatePropagationStopped = returnTrue;
+               this.stopPropagation();
+       },
+       isDefaultPrevented: returnFalse,
+       isPropagationStopped: returnFalse,
+       isImmediatePropagationStopped: returnFalse
+};
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function( event ) {
+       // Check if mouse(over|out) are still within the same parent element
+       var parent = event.relatedTarget;
+
+       // Firefox sometimes assigns relatedTarget a XUL element
+       // which we cannot access the parentNode property of
+       try {
+               // Traverse up the tree
+               while ( parent && parent !== this ) {
+                       parent = parent.parentNode;
+               }
+
+               if ( parent !== this ) {
+                       // set the correct event type
+                       event.type = event.data;
+
+                       // handle event if we actually just moused on to a non sub-element
+                       jQuery.event.handle.apply( this, arguments );
+               }
+
+       // assuming we've left the element since we most likely mousedover a xul element
+       } catch(e) { }
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+       event.type = event.data;
+       jQuery.event.handle.apply( this, arguments );
+};
+
+// Create mouseenter and mouseleave events
+jQuery.each({
+       mouseenter: "mouseover",
+       mouseleave: "mouseout"
+}, function( orig, fix ) {
+       jQuery.event.special[ orig ] = {
+               setup: function( data ) {
+                       jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
+               },
+               teardown: function( data ) {
+                       jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+               }
+       };
+});
+
+// submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+       jQuery.event.special.submit = {
+               setup: function( data, namespaces ) {
+                       if ( this.nodeName.toLowerCase() !== "form" ) {
+                               jQuery.event.add(this, "click.specialSubmit", function( e ) {
+                                       var elem = e.target, type = elem.type;
+
+                                       if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+                                               return trigger( "submit", this, arguments );
+                                       }
+                               });
+        
+                               jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+                                       var elem = e.target, type = elem.type;
+
+                                       if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+                                               return trigger( "submit", this, arguments );
+                                       }
+                               });
+
+                       } else {
+                               return false;
+                       }
+               },
+
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialSubmit" );
+               }
+       };
+
+}
+
+// change delegation, happens here so we have bind.
+if ( !jQuery.support.changeBubbles ) {
+
+       var formElems = /textarea|input|select/i,
+
+       changeFilters,
+
+       getVal = function( elem ) {
+               var type = elem.type, val = elem.value;
+
+               if ( type === "radio" || type === "checkbox" ) {
+                       val = elem.checked;
+
+               } else if ( type === "select-multiple" ) {
+                       val = elem.selectedIndex > -1 ?
+                               jQuery.map( elem.options, function( elem ) {
+                                       return elem.selected;
+                               }).join("-") :
+                               "";
+
+               } else if ( elem.nodeName.toLowerCase() === "select" ) {
+                       val = elem.selectedIndex;
+               }
+
+               return val;
+       },
+
+       testChange = function testChange( e ) {
+               var elem = e.target, data, val;
+
+               if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
+                       return;
+               }
+
+               data = jQuery.data( elem, "_change_data" );
+               val = getVal(elem);
+
+               // the current data will be also retrieved by beforeactivate
+               if ( e.type !== "focusout" || elem.type !== "radio" ) {
+                       jQuery.data( elem, "_change_data", val );
+               }
+               
+               if ( data === undefined || val === data ) {
+                       return;
+               }
+
+               if ( data != null || val ) {
+                       e.type = "change";
+                       return jQuery.event.trigger( e, arguments[1], elem );
+               }
+       };
+
+       jQuery.event.special.change = {
+               filters: {
+                       focusout: testChange, 
+
+                       click: function( e ) {
+                               var elem = e.target, type = elem.type;
+
+                               if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
+                                       return testChange.call( this, e );
+                               }
+                       },
+
+                       // Change has to be called before submit
+                       // Keydown will be called before keypress, which is used in submit-event delegation
+                       keydown: function( e ) {
+                               var elem = e.target, type = elem.type;
+
+                               if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
+                                       (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+                                       type === "select-multiple" ) {
+                                       return testChange.call( this, e );
+                               }
+                       },
+
+                       // Beforeactivate happens also before the previous element is blurred
+                       // with this event you can't trigger a change event, but you can store
+                       // information/focus[in] is not needed anymore
+                       beforeactivate: function( e ) {
+                               var elem = e.target;
+                               jQuery.data( elem, "_change_data", getVal(elem) );
+                       }
+               },
+
+               setup: function( data, namespaces ) {
+                       if ( this.type === "file" ) {
+                               return false;
+                       }
+
+                       for ( var type in changeFilters ) {
+                               jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+                       }
+
+                       return formElems.test( this.nodeName );
+               },
+
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialChange" );
+
+                       return formElems.test( this.nodeName );
+               }
+       };
+
+       changeFilters = jQuery.event.special.change.filters;
+}
+
+function trigger( type, elem, args ) {
+       args[0].type = type;
+       return jQuery.event.handle.apply( elem, args );
+}
+
+// Create "bubbling" focus and blur events
+if ( document.addEventListener ) {
+       jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+               jQuery.event.special[ fix ] = {
+                       setup: function() {
+                               this.addEventListener( orig, handler, true );
+                       }, 
+                       teardown: function() { 
+                               this.removeEventListener( orig, handler, true );
+                       }
+               };
+
+               function handler( e ) { 
+                       e = jQuery.event.fix( e );
+                       e.type = fix;
+                       return jQuery.event.handle.call( this, e );
+               }
+       });
+}
+
+jQuery.each(["bind", "one"], function( i, name ) {
+       jQuery.fn[ name ] = function( type, data, fn ) {
+               // Handle object literals
+               if ( typeof type === "object" ) {
+                       for ( var key in type ) {
+                               this[ name ](key, data, type[key], fn);
+                       }
+                       return this;
+               }
+               
+               if ( jQuery.isFunction( data ) ) {
+                       fn = data;
+                       data = undefined;
+               }
+
+               var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
+                       jQuery( this ).unbind( event, handler );
+                       return fn.apply( this, arguments );
+               }) : fn;
+
+               if ( type === "unload" && name !== "one" ) {
+                       this.one( type, data, fn );
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.add( this[i], type, handler, data );
+                       }
+               }
+
+               return this;
+       };
+});
+
+jQuery.fn.extend({
+       unbind: function( type, fn ) {
+               // Handle object literals
+               if ( typeof type === "object" && !type.preventDefault ) {
+                       for ( var key in type ) {
+                               this.unbind(key, type[key]);
+                       }
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.remove( this[i], type, fn );
+                       }
+               }
+
+               return this;
+       },
+       
+       delegate: function( selector, types, data, fn ) {
+               return this.live( types, data, fn, selector );
+       },
+       
+       undelegate: function( selector, types, fn ) {
+               if ( arguments.length === 0 ) {
+                               return this.unbind( "live" );
+               
+               } else {
+                       return this.die( types, null, fn, selector );
+               }
+       },
+       
+       trigger: function( type, data ) {
+               return this.each(function() {
+                       jQuery.event.trigger( type, data, this );
+               });
+       },
+
+       triggerHandler: function( type, data ) {
+               if ( this[0] ) {
+                       var event = jQuery.Event( type );
+                       event.preventDefault();
+                       event.stopPropagation();
+                       jQuery.event.trigger( event, data, this[0] );
+                       return event.result;
+               }
+       },
+
+       toggle: function( fn ) {
+               // Save reference to arguments for access in closure
+               var args = arguments, i = 1;
+
+               // link all the functions, so any of them can unbind this click handler
+               while ( i < args.length ) {
+                       jQuery.proxy( fn, args[ i++ ] );
+               }
+
+               return this.click( jQuery.proxy( fn, function( event ) {
+                       // Figure out which function to execute
+                       var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+                       jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+                       // Make sure that clicks stop
+                       event.preventDefault();
+
+                       // and execute the function
+                       return args[ lastToggle ].apply( this, arguments ) || false;
+               }));
+       },
+
+       hover: function( fnOver, fnOut ) {
+               return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+       }
+});
+
+var liveMap = {
+       focus: "focusin",
+       blur: "focusout",
+       mouseenter: "mouseover",
+       mouseleave: "mouseout"
+};
+
+jQuery.each(["live", "die"], function( i, name ) {
+       jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+               var type, i = 0, match, namespaces, preType,
+                       selector = origSelector || this.selector,
+                       context = origSelector ? this : jQuery( this.context );
+
+               if ( jQuery.isFunction( data ) ) {
+                       fn = data;
+                       data = undefined;
+               }
+
+               types = (types || "").split(" ");
+
+               while ( (type = types[ i++ ]) != null ) {
+                       match = rnamespaces.exec( type );
+                       namespaces = "";
+
+                       if ( match )  {
+                               namespaces = match[0];
+                               type = type.replace( rnamespaces, "" );
+                       }
+
+                       if ( type === "hover" ) {
+                               types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+                               continue;
+                       }
+
+                       preType = type;
+
+                       if ( type === "focus" || type === "blur" ) {
+                               types.push( liveMap[ type ] + namespaces );
+                               type = type + namespaces;
+
+                       } else {
+                               type = (liveMap[ type ] || type) + namespaces;
+                       }
+
+                       if ( name === "live" ) {
+                               // bind live handler
+                               context.each(function(){
+                                       jQuery.event.add( this, liveConvert( type, selector ),
+                                               { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+                               });
+
+                       } else {
+                               // unbind live handler
+                               context.unbind( liveConvert( type, selector ), fn );
+                       }
+               }
+               
+               return this;
+       }
+});
+
+function liveHandler( event ) {
+       var stop, elems = [], selectors = [], args = arguments,
+               related, match, handleObj, elem, j, i, l, data,
+               events = jQuery.data( this, "events" );
+
+       // Make sure we avoid non-left-click bubbling in Firefox (#3861)
+       if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
+               return;
+       }
+
+       event.liveFired = this;
+
+       var live = events.live.slice(0);
+
+       for ( j = 0; j < live.length; j++ ) {
+               handleObj = live[j];
+
+               if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+                       selectors.push( handleObj.selector );
+
+               } else {
+                       live.splice( j--, 1 );
+               }
+       }
+
+       match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+       for ( i = 0, l = match.length; i < l; i++ ) {
+               for ( j = 0; j < live.length; j++ ) {
+                       handleObj = live[j];
+
+                       if ( match[i].selector === handleObj.selector ) {
+                               elem = match[i].elem;
+                               related = null;
+
+                               // Those two events require additional checking
+                               if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+                                       related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
+                               }
+
+                               if ( !related || related !== elem ) {
+                                       elems.push({ elem: elem, handleObj: handleObj });
+                               }
+                       }
+               }
+       }
+
+       for ( i = 0, l = elems.length; i < l; i++ ) {
+               match = elems[i];
+               event.currentTarget = match.elem;
+               event.data = match.handleObj.data;
+               event.handleObj = match.handleObj;
+
+               if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
+                       stop = false;
+                       break;
+               }
+       }
+
+       return stop;
+}
+
+function liveConvert( type, selector ) {
+       return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
+}
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+       "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+       "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
+
+       // Handle event binding
+       jQuery.fn[ name ] = function( fn ) {
+               return fn ? this.bind( name, fn ) : this.trigger( name );
+       };
+
+       if ( jQuery.attrFn ) {
+               jQuery.attrFn[ name ] = true;
+       }
+});
+
+// Prevent memory leaks in IE
+// Window isn't included so as not to unbind existing unload events
+// More info:
+//  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
+if ( window.attachEvent && !window.addEventListener ) {
+       window.attachEvent("onunload", function() {
+               for ( var id in jQuery.cache ) {
+                       if ( jQuery.cache[ id ].handle ) {
+                               // Try/Catch is to handle iframes being unloaded, see #4280
+                               try {
+                                       jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+                               } catch(e) {}
+                       }
+               }
+       });
+}
+/*!
+ * Sizzle CSS Selector Engine - v1.0
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+       done = 0,
+       toString = Object.prototype.toString,
+       hasDuplicate = false,
+       baseHasDuplicate = true;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+//   Thus far that includes Google Chrome.
+[0, 0].sort(function(){
+       baseHasDuplicate = false;
+       return 0;
+});
+
+var Sizzle = function(selector, context, results, seed) {
+       results = results || [];
+       var origContext = context = context || document;
+
+       if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+               return [];
+       }
+       
+       if ( !selector || typeof selector !== "string" ) {
+               return results;
+       }
+
+       var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
+               soFar = selector;
+       
+       // Reset the position of the chunker regexp (start from head)
+       while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
+               soFar = m[3];
+               
+               parts.push( m[1] );
+               
+               if ( m[2] ) {
+                       extra = m[3];
+                       break;
+               }
+       }
+
+       if ( parts.length > 1 && origPOS.exec( selector ) ) {
+               if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+                       set = posProcess( parts[0] + parts[1], context );
+               } else {
+                       set = Expr.relative[ parts[0] ] ?
+                               [ context ] :
+                               Sizzle( parts.shift(), context );
+
+                       while ( parts.length ) {
+                               selector = parts.shift();
+
+                               if ( Expr.relative[ selector ] ) {
+                                       selector += parts.shift();
+                               }
+                               
+                               set = posProcess( selector, set );
+                       }
+               }
+       } else {
+               // Take a shortcut and set the context if the root selector is an ID
+               // (but not if it'll be faster if the inner selector is an ID)
+               if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+                               Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+                       var ret = Sizzle.find( parts.shift(), context, contextXML );
+                       context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
+               }
+
+               if ( context ) {
+                       var ret = seed ?
+                               { expr: parts.pop(), set: makeArray(seed) } :
+                               Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+                       set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
+
+                       if ( parts.length > 0 ) {
+                               checkSet = makeArray(set);
+                       } else {
+                               prune = false;
+                       }
+
+                       while ( parts.length ) {
+                               var cur = parts.pop(), pop = cur;
+
+                               if ( !Expr.relative[ cur ] ) {
+                                       cur = "";
+                               } else {
+                                       pop = parts.pop();
+                               }
+
+                               if ( pop == null ) {
+                                       pop = context;
+                               }
+
+                               Expr.relative[ cur ]( checkSet, pop, contextXML );
+                       }
+               } else {
+                       checkSet = parts = [];
+               }
+       }
+
+       if ( !checkSet ) {
+               checkSet = set;
+       }
+
+       if ( !checkSet ) {
+               Sizzle.error( cur || selector );
+       }
+
+       if ( toString.call(checkSet) === "[object Array]" ) {
+               if ( !prune ) {
+                       results.push.apply( results, checkSet );
+               } else if ( context && context.nodeType === 1 ) {
+                       for ( var i = 0; checkSet[i] != null; i++ ) {
+                               if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+                                       results.push( set[i] );
+                               }
+                       }
+               } else {
+                       for ( var i = 0; checkSet[i] != null; i++ ) {
+                               if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+                                       results.push( set[i] );
+                               }
+                       }
+               }
+       } else {
+               makeArray( checkSet, results );
+       }
+
+       if ( extra ) {
+               Sizzle( extra, origContext, results, seed );
+               Sizzle.uniqueSort( results );
+       }
+
+       return results;
+};
+
+Sizzle.uniqueSort = function(results){
+       if ( sortOrder ) {
+               hasDuplicate = baseHasDuplicate;
+               results.sort(sortOrder);
+
+               if ( hasDuplicate ) {
+                       for ( var i = 1; i < results.length; i++ ) {
+                               if ( results[i] === results[i-1] ) {
+                                       results.splice(i--, 1);
+                               }
+                       }
+               }
+       }
+
+       return results;
+};
+
+Sizzle.matches = function(expr, set){
+       return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+       var set, match;
+
+       if ( !expr ) {
+               return [];
+       }
+
+       for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+               var type = Expr.order[i], match;
+               
+               if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+                       var left = match[1];
+                       match.splice(1,1);
+
+                       if ( left.substr( left.length - 1 ) !== "\\" ) {
+                               match[1] = (match[1] || "").replace(/\\/g, "");
+                               set = Expr.find[ type ]( match, context, isXML );
+                               if ( set != null ) {
+                                       expr = expr.replace( Expr.match[ type ], "" );
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if ( !set ) {
+               set = context.getElementsByTagName("*");
+       }
+
+       return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+       var old = expr, result = [], curLoop = set, match, anyFound,
+               isXMLFilter = set && set[0] && isXML(set[0]);
+
+       while ( expr && set.length ) {
+               for ( var type in Expr.filter ) {
+                       if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+                               var filter = Expr.filter[ type ], found, item, left = match[1];
+                               anyFound = false;
+
+                               match.splice(1,1);
+
+                               if ( left.substr( left.length - 1 ) === "\\" ) {
+                                       continue;
+                               }
+
+                               if ( curLoop === result ) {
+                                       result = [];
+                               }
+
+                               if ( Expr.preFilter[ type ] ) {
+                                       match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+                                       if ( !match ) {
+                                               anyFound = found = true;
+                                       } else if ( match === true ) {
+                                               continue;
+                                       }
+                               }
+
+                               if ( match ) {
+                                       for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+                                               if ( item ) {
+                                                       found = filter( item, match, i, curLoop );
+                                                       var pass = not ^ !!found;
+
+                                                       if ( inplace && found != null ) {
+                                                               if ( pass ) {
+                                                                       anyFound = true;
+                                                               } else {
+                                                                       curLoop[i] = false;
+                                                               }
+                                                       } else if ( pass ) {
+                                                               result.push( item );
+                                                               anyFound = true;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               if ( found !== undefined ) {
+                                       if ( !inplace ) {
+                                               curLoop = result;
+                                       }
+
+                                       expr = expr.replace( Expr.match[ type ], "" );
+
+                                       if ( !anyFound ) {
+                                               return [];
+                                       }
+
+                                       break;
+                               }
+                       }
+               }
+
+               // Improper expression
+               if ( expr === old ) {
+                       if ( anyFound == null ) {
+                               Sizzle.error( expr );
+                       } else {
+                               break;
+                       }
+               }
+
+               old = expr;
+       }
+
+       return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+       throw "Syntax error, unrecognized expression: " + msg;
+};
+
+var Expr = Sizzle.selectors = {
+       order: [ "ID", "NAME", "TAG" ],
+       match: {
+               ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+               CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+               NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
+               ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+               TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
+               CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+               POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+               PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+       },
+       leftMatch: {},
+       attrMap: {
+               "class": "className",
+               "for": "htmlFor"
+       },
+       attrHandle: {
+               href: function(elem){
+                       return elem.getAttribute("href");
+               }
+       },
+       relative: {
+               "+": function(checkSet, part){
+                       var isPartStr = typeof part === "string",
+                               isTag = isPartStr && !/\W/.test(part),
+                               isPartStrNotTag = isPartStr && !isTag;
+
+                       if ( isTag ) {
+                               part = part.toLowerCase();
+                       }
+
+                       for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+                               if ( (elem = checkSet[i]) ) {
+                                       while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+                                       checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+                                               elem || false :
+                                               elem === part;
+                               }
+                       }
+
+                       if ( isPartStrNotTag ) {
+                               Sizzle.filter( part, checkSet, true );
+                       }
+               },
+               ">": function(checkSet, part){
+                       var isPartStr = typeof part === "string";
+
+                       if ( isPartStr && !/\W/.test(part) ) {
+                               part = part.toLowerCase();
+
+                               for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+                                       var elem = checkSet[i];
+                                       if ( elem ) {
+                                               var parent = elem.parentNode;
+                                               checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+                                       }
+                               }
+                       } else {
+                               for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+                                       var elem = checkSet[i];
+                                       if ( elem ) {
+                                               checkSet[i] = isPartStr ?
+                                                       elem.parentNode :
+                                                       elem.parentNode === part;
+                                       }
+                               }
+
+                               if ( isPartStr ) {
+                                       Sizzle.filter( part, checkSet, true );
+                               }
+                       }
+               },
+               "": function(checkSet, part, isXML){
+                       var doneName = done++, checkFn = dirCheck;
+
+                       if ( typeof part === "string" && !/\W/.test(part) ) {
+                               var nodeCheck = part = part.toLowerCase();
+                               checkFn = dirNodeCheck;
+                       }
+
+                       checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+               },
+               "~": function(checkSet, part, isXML){
+                       var doneName = done++, checkFn = dirCheck;
+
+                       if ( typeof part === "string" && !/\W/.test(part) ) {
+                               var nodeCheck = part = part.toLowerCase();
+                               checkFn = dirNodeCheck;
+                       }
+
+                       checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+               }
+       },
+       find: {
+               ID: function(match, context, isXML){
+                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
+                               var m = context.getElementById(match[1]);
+                               return m ? [m] : [];
+                       }
+               },
+               NAME: function(match, context){
+                       if ( typeof context.getElementsByName !== "undefined" ) {
+                               var ret = [], results = context.getElementsByName(match[1]);
+
+                               for ( var i = 0, l = results.length; i < l; i++ ) {
+                                       if ( results[i].getAttribute("name") === match[1] ) {
+                                               ret.push( results[i] );
+                                       }
+                               }
+
+                               return ret.length === 0 ? null : ret;
+                       }
+               },
+               TAG: function(match, context){
+                       return context.getElementsByTagName(match[1]);
+               }
+       },
+       preFilter: {
+               CLASS: function(match, curLoop, inplace, result, not, isXML){
+                       match = " " + match[1].replace(/\\/g, "") + " ";
+
+                       if ( isXML ) {
+                               return match;
+                       }
+
+                       for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+                               if ( elem ) {
+                                       if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
+                                               if ( !inplace ) {
+                                                       result.push( elem );
+                                               }
+                                       } else if ( inplace ) {
+                                               curLoop[i] = false;
+                                       }
+                               }
+                       }
+
+                       return false;
+               },
+               ID: function(match){
+                       return match[1].replace(/\\/g, "");
+               },
+               TAG: function(match, curLoop){
+                       return match[1].toLowerCase();
+               },
+               CHILD: function(match){
+                       if ( match[1] === "nth" ) {
+                               // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+                               var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+                                       match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+                                       !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+                               // calculate the numbers (first)n+(last) including if they are negative
+                               match[2] = (test[1] + (test[2] || 1)) - 0;
+                               match[3] = test[3] - 0;
+                       }
+
+                       // TODO: Move to normal caching system
+                       match[0] = done++;
+
+                       return match;
+               },
+               ATTR: function(match, curLoop, inplace, result, not, isXML){
+                       var name = match[1].replace(/\\/g, "");
+                       
+                       if ( !isXML && Expr.attrMap[name] ) {
+                               match[1] = Expr.attrMap[name];
+                       }
+
+                       if ( match[2] === "~=" ) {
+                               match[4] = " " + match[4] + " ";
+                       }
+
+                       return match;
+               },
+               PSEUDO: function(match, curLoop, inplace, result, not){
+                       if ( match[1] === "not" ) {
+                               // If we're dealing with a complex expression, or a simple one
+                               if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+                                       match[3] = Sizzle(match[3], null, null, curLoop);
+                               } else {
+                                       var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+                                       if ( !inplace ) {
+                                               result.push.apply( result, ret );
+                                       }
+                                       return false;
+                               }
+                       } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+                               return true;
+                       }
+                       
+                       return match;
+               },
+               POS: function(match){
+                       match.unshift( true );
+                       return match;
+               }
+       },
+       filters: {
+               enabled: function(elem){
+                       return elem.disabled === false && elem.type !== "hidden";
+               },
+               disabled: function(elem){
+                       return elem.disabled === true;
+               },
+               checked: function(elem){
+                       return elem.checked === true;
+               },
+               selected: function(elem){
+                       // Accessing this property makes selected-by-default
+                       // options in Safari work properly
+                       elem.parentNode.selectedIndex;
+                       return elem.selected === true;
+               },
+               parent: function(elem){
+                       return !!elem.firstChild;
+               },
+               empty: function(elem){
+                       return !elem.firstChild;
+               },
+               has: function(elem, i, match){
+                       return !!Sizzle( match[3], elem ).length;
+               },
+               header: function(elem){
+                       return /h\d/i.test( elem.nodeName );
+               },
+               text: function(elem){
+                       return "text" === elem.type;
+               },
+               radio: function(elem){
+                       return "radio" === elem.type;
+               },
+               checkbox: function(elem){
+                       return "checkbox" === elem.type;
+               },
+               file: function(elem){
+                       return "file" === elem.type;
+               },
+               password: function(elem){
+                       return "password" === elem.type;
+               },
+               submit: function(elem){
+                       return "submit" === elem.type;
+               },
+               image: function(elem){
+                       return "image" === elem.type;
+               },
+               reset: function(elem){
+                       return "reset" === elem.type;
+               },
+               button: function(elem){
+                       return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
+               },
+               input: function(elem){
+                       return /input|select|textarea|button/i.test(elem.nodeName);
+               }
+       },
+       setFilters: {
+               first: function(elem, i){
+                       return i === 0;
+               },
+               last: function(elem, i, match, array){
+                       return i === array.length - 1;
+               },
+               even: function(elem, i){
+                       return i % 2 === 0;
+               },
+               odd: function(elem, i){
+                       return i % 2 === 1;
+               },
+               lt: function(elem, i, match){
+                       return i < match[3] - 0;
+               },
+               gt: function(elem, i, match){
+                       return i > match[3] - 0;
+               },
+               nth: function(elem, i, match){
+                       return match[3] - 0 === i;
+               },
+               eq: function(elem, i, match){
+                       return match[3] - 0 === i;
+               }
+       },
+       filter: {
+               PSEUDO: function(elem, match, i, array){
+                       var name = match[1], filter = Expr.filters[ name ];
+
+                       if ( filter ) {
+                               return filter( elem, i, match, array );
+                       } else if ( name === "contains" ) {
+                               return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+                       } else if ( name === "not" ) {
+                               var not = match[3];
+
+                               for ( var i = 0, l = not.length; i < l; i++ ) {
+                                       if ( not[i] === elem ) {
+                                               return false;
+                                       }
+                               }
+
+                               return true;
+                       } else {
+                               Sizzle.error( "Syntax error, unrecognized expression: " + name );
+                       }
+               },
+               CHILD: function(elem, match){
+                       var type = match[1], node = elem;
+                       switch (type) {
+                               case 'only':
+                               case 'first':
+                                       while ( (node = node.previousSibling) )  {
+                                               if ( node.nodeType === 1 ) { 
+                                                       return false; 
+                                               }
+                                       }
+                                       if ( type === "first" ) { 
+                                               return true; 
+                                       }
+                                       node = elem;
+                               case 'last':
+                                       while ( (node = node.nextSibling) )      {
+                                               if ( node.nodeType === 1 ) { 
+                                                       return false; 
+                                               }
+                                       }
+                                       return true;
+                               case 'nth':
+                                       var first = match[2], last = match[3];
+
+                                       if ( first === 1 && last === 0 ) {
+                                               return true;
+                                       }
+                                       
+                                       var doneName = match[0],
+                                               parent = elem.parentNode;
+       
+                                       if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+                                               var count = 0;
+                                               for ( node = parent.firstChild; node; node = node.nextSibling ) {
+                                                       if ( node.nodeType === 1 ) {
+                                                               node.nodeIndex = ++count;
+                                                       }
+                                               } 
+                                               parent.sizcache = doneName;
+                                       }
+                                       
+                                       var diff = elem.nodeIndex - last;
+                                       if ( first === 0 ) {
+                                               return diff === 0;
+                                       } else {
+                                               return ( diff % first === 0 && diff / first >= 0 );
+                                       }
+                       }
+               },
+               ID: function(elem, match){
+                       return elem.nodeType === 1 && elem.getAttribute("id") === match;
+               },
+               TAG: function(elem, match){
+                       return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+               },
+               CLASS: function(elem, match){
+                       return (" " + (elem.className || elem.getAttribute("class")) + " ")
+                               .indexOf( match ) > -1;
+               },
+               ATTR: function(elem, match){
+                       var name = match[1],
+                               result = Expr.attrHandle[ name ] ?
+                                       Expr.attrHandle[ name ]( elem ) :
+                                       elem[ name ] != null ?
+                                               elem[ name ] :
+                                               elem.getAttribute( name ),
+                               value = result + "",
+                               type = match[2],
+                               check = match[4];
+
+                       return result == null ?
+                               type === "!=" :
+                               type === "=" ?
+                               value === check :
+                               type === "*=" ?
+                               value.indexOf(check) >= 0 :
+                               type === "~=" ?
+                               (" " + value + " ").indexOf(check) >= 0 :
+                               !check ?
+                               value && result !== false :
+                               type === "!=" ?
+                               value !== check :
+                               type === "^=" ?
+                               value.indexOf(check) === 0 :
+                               type === "$=" ?
+                               value.substr(value.length - check.length) === check :
+                               type === "|=" ?
+                               value === check || value.substr(0, check.length + 1) === check + "-" :
+                               false;
+               },
+               POS: function(elem, match, i, array){
+                       var name = match[2], filter = Expr.setFilters[ name ];
+
+                       if ( filter ) {
+                               return filter( elem, i, match, array );
+                       }
+               }
+       }
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+       Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+       Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){
+               return "\\" + (num - 0 + 1);
+       }));
+}
+
+var makeArray = function(array, results) {
+       array = Array.prototype.slice.call( array, 0 );
+
+       if ( results ) {
+               results.push.apply( results, array );
+               return results;
+       }
+       
+       return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+       Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch(e){
+       makeArray = function(array, results) {
+               var ret = results || [];
+
+               if ( toString.call(array) === "[object Array]" ) {
+                       Array.prototype.push.apply( ret, array );
+               } else {
+                       if ( typeof array.length === "number" ) {
+                               for ( var i = 0, l = array.length; i < l; i++ ) {
+                                       ret.push( array[i] );
+                               }
+                       } else {
+                               for ( var i = 0; array[i]; i++ ) {
+                                       ret.push( array[i] );
+                               }
+                       }
+               }
+
+               return ret;
+       };
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+       sortOrder = function( a, b ) {
+               if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+                       if ( a == b ) {
+                               hasDuplicate = true;
+                       }
+                       return a.compareDocumentPosition ? -1 : 1;
+               }
+
+               var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+               if ( ret === 0 ) {
+                       hasDuplicate = true;
+               }
+               return ret;
+       };
+} else if ( "sourceIndex" in document.documentElement ) {
+       sortOrder = function( a, b ) {
+               if ( !a.sourceIndex || !b.sourceIndex ) {
+                       if ( a == b ) {
+                               hasDuplicate = true;
+                       }
+                       return a.sourceIndex ? -1 : 1;
+               }
+
+               var ret = a.sourceIndex - b.sourceIndex;
+               if ( ret === 0 ) {
+                       hasDuplicate = true;
+               }
+               return ret;
+       };
+} else if ( document.createRange ) {
+       sortOrder = function( a, b ) {
+               if ( !a.ownerDocument || !b.ownerDocument ) {
+                       if ( a == b ) {
+                               hasDuplicate = true;
+                       }
+                       return a.ownerDocument ? -1 : 1;
+               }
+
+               var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+               aRange.setStart(a, 0);
+               aRange.setEnd(a, 0);
+               bRange.setStart(b, 0);
+               bRange.setEnd(b, 0);
+               var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+               if ( ret === 0 ) {
+                       hasDuplicate = true;
+               }
+               return ret;
+       };
+}
+
+// Utility function for retreiving the text value of an array of DOM nodes
+function getText( elems ) {
+       var ret = "", elem;
+
+       for ( var i = 0; elems[i]; i++ ) {
+               elem = elems[i];
+
+               // Get the text from text nodes and CDATA nodes
+               if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+                       ret += elem.nodeValue;
+
+               // Traverse everything else, except comment nodes
+               } else if ( elem.nodeType !== 8 ) {
+                       ret += getText( elem.childNodes );
+               }
+       }
+
+       return ret;
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+       // We're going to inject a fake input element with a specified name
+       var form = document.createElement("div"),
+               id = "script" + (new Date).getTime();
+       form.innerHTML = "<a name='" + id + "'/>";
+
+       // Inject it into the root element, check its status, and remove it quickly
+       var root = document.documentElement;
+       root.insertBefore( form, root.firstChild );
+
+       // The workaround has to do additional checks after a getElementById
+       // Which slows things down for other browsers (hence the branching)
+       if ( document.getElementById( id ) ) {
+               Expr.find.ID = function(match, context, isXML){
+                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
+                               var m = context.getElementById(match[1]);
+                               return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+                       }
+               };
+
+               Expr.filter.ID = function(elem, match){
+                       var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+                       return elem.nodeType === 1 && node && node.nodeValue === match;
+               };
+       }
+
+       root.removeChild( form );
+       root = form = null; // release memory in IE
+})();
+
+(function(){
+       // Check to see if the browser returns only elements
+       // when doing getElementsByTagName("*")
+
+       // Create a fake element
+       var div = document.createElement("div");
+       div.appendChild( document.createComment("") );
+
+       // Make sure no comments are found
+       if ( div.getElementsByTagName("*").length > 0 ) {
+               Expr.find.TAG = function(match, context){
+                       var results = context.getElementsByTagName(match[1]);
+
+                       // Filter out possible comments
+                       if ( match[1] === "*" ) {
+                               var tmp = [];
+
+                               for ( var i = 0; results[i]; i++ ) {
+                                       if ( results[i].nodeType === 1 ) {
+                                               tmp.push( results[i] );
+                                       }
+                               }
+
+                               results = tmp;
+                       }
+
+                       return results;
+               };
+       }
+
+       // Check to see if an attribute returns normalized href attributes
+       div.innerHTML = "<a href='#'></a>";
+       if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+                       div.firstChild.getAttribute("href") !== "#" ) {
+               Expr.attrHandle.href = function(elem){
+                       return elem.getAttribute("href", 2);
+               };
+       }
+
+       div = null; // release memory in IE
+})();
+
+if ( document.querySelectorAll ) {
+       (function(){
+               var oldSizzle = Sizzle, div = document.createElement("div");
+               div.innerHTML = "<p class='TEST'></p>";
+
+               // Safari can't handle uppercase or unicode characters when
+               // in quirks mode.
+               if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+                       return;
+               }
+       
+               Sizzle = function(query, context, extra, seed){
+                       context = context || document;
+
+                       // Only use querySelectorAll on non-XML documents
+                       // (ID selectors don't work in non-HTML documents)
+                       if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+                               try {
+                                       return makeArray( context.querySelectorAll(query), extra );
+                               } catch(e){}
+                       }
+               
+                       return oldSizzle(query, context, extra, seed);
+               };
+
+               for ( var prop in oldSizzle ) {
+                       Sizzle[ prop ] = oldSizzle[ prop ];
+               }
+
+               div = null; // release memory in IE
+       })();
+}
+
+(function(){
+       var div = document.createElement("div");
+
+       div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+       // Opera can't find a second classname (in 9.6)
+       // Also, make sure that getElementsByClassName actually exists
+       if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+               return;
+       }
+
+       // Safari caches class attributes, doesn't catch changes (in 3.2)
+       div.lastChild.className = "e";
+
+       if ( div.getElementsByClassName("e").length === 1 ) {
+               return;
+       }
+       
+       Expr.order.splice(1, 0, "CLASS");
+       Expr.find.CLASS = function(match, context, isXML) {
+               if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+                       return context.getElementsByClassName(match[1]);
+               }
+       };
+
+       div = null; // release memory in IE
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+               var elem = checkSet[i];
+               if ( elem ) {
+                       elem = elem[dir];
+                       var match = false;
+
+                       while ( elem ) {
+                               if ( elem.sizcache === doneName ) {
+                                       match = checkSet[elem.sizset];
+                                       break;
+                               }
+
+                               if ( elem.nodeType === 1 && !isXML ){
+                                       elem.sizcache = doneName;
+                                       elem.sizset = i;
+                               }
+
+                               if ( elem.nodeName.toLowerCase() === cur ) {
+                                       match = elem;
+                                       break;
+                               }
+
+                               elem = elem[dir];
+                       }
+
+                       checkSet[i] = match;
+               }
+       }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+               var elem = checkSet[i];
+               if ( elem ) {
+                       elem = elem[dir];
+                       var match = false;
+
+                       while ( elem ) {
+                               if ( elem.sizcache === doneName ) {
+                                       match = checkSet[elem.sizset];
+                                       break;
+                               }
+
+                               if ( elem.nodeType === 1 ) {
+                                       if ( !isXML ) {
+                                               elem.sizcache = doneName;
+                                               elem.sizset = i;
+                                       }
+                                       if ( typeof cur !== "string" ) {
+                                               if ( elem === cur ) {
+                                                       match = true;
+                                                       break;
+                                               }
+
+                                       } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+                                               match = elem;
+                                               break;
+                                       }
+                               }
+
+                               elem = elem[dir];
+                       }
+
+                       checkSet[i] = match;
+               }
+       }
+}
+
+var contains = document.compareDocumentPosition ? function(a, b){
+       return !!(a.compareDocumentPosition(b) & 16);
+} : function(a, b){
+       return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+       // documentElement is verified for cases where it doesn't yet exist
+       // (such as loading iframes in IE - #4833) 
+       var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+       return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function(selector, context){
+       var tmpSet = [], later = "", match,
+               root = context.nodeType ? [context] : context;
+
+       // Position selectors must be done after the filter
+       // And so must :not(positional) so we move all PSEUDOs to the end
+       while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+               later += match[0];
+               selector = selector.replace( Expr.match.PSEUDO, "" );
+       }
+
+       selector = Expr.relative[selector] ? selector + "*" : selector;
+
+       for ( var i = 0, l = root.length; i < l; i++ ) {
+               Sizzle( selector, root[i], tmpSet );
+       }
+
+       return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = getText;
+jQuery.isXMLDoc = isXML;
+jQuery.contains = contains;
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+var runtil = /Until$/,
+       rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+       // Note: This RegExp should be improved, or likely pulled from Sizzle
+       rmultiselector = /,/,
+       slice = Array.prototype.slice;
+
+// Implement the identical functionality for filter and not
+var winnow = function( elements, qualifier, keep ) {
+       if ( jQuery.isFunction( qualifier ) ) {
+               return jQuery.grep(elements, function( elem, i ) {
+                       return !!qualifier.call( elem, i, elem ) === keep;
+               });
+
+       } else if ( qualifier.nodeType ) {
+               return jQuery.grep(elements, function( elem, i ) {
+                       return (elem === qualifier) === keep;
+               });
+
+       } else if ( typeof qualifier === "string" ) {
+               var filtered = jQuery.grep(elements, function( elem ) {
+                       return elem.nodeType === 1;
+               });
+
+               if ( isSimple.test( qualifier ) ) {
+                       return jQuery.filter(qualifier, filtered, !keep);
+               } else {
+                       qualifier = jQuery.filter( qualifier, filtered );
+               }
+       }
+
+       return jQuery.grep(elements, function( elem, i ) {
+               return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+       });
+};
+
+jQuery.fn.extend({
+       find: function( selector ) {
+               var ret = this.pushStack( "", "find", selector ), length = 0;
+
+               for ( var i = 0, l = this.length; i < l; i++ ) {
+                       length = ret.length;
+                       jQuery.find( selector, this[i], ret );
+
+                       if ( i > 0 ) {
+                               // Make sure that the results are unique
+                               for ( var n = length; n < ret.length; n++ ) {
+                                       for ( var r = 0; r < length; r++ ) {
+                                               if ( ret[r] === ret[n] ) {
+                                                       ret.splice(n--, 1);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               return ret;
+       },
+
+       has: function( target ) {
+               var targets = jQuery( target );
+               return this.filter(function() {
+                       for ( var i = 0, l = targets.length; i < l; i++ ) {
+                               if ( jQuery.contains( this, targets[i] ) ) {
+                                       return true;
+                               }
+                       }
+               });
+       },
+
+       not: function( selector ) {
+               return this.pushStack( winnow(this, selector, false), "not", selector);
+       },
+
+       filter: function( selector ) {
+               return this.pushStack( winnow(this, selector, true), "filter", selector );
+       },
+       
+       is: function( selector ) {
+               return !!selector && jQuery.filter( selector, this ).length > 0;
+       },
+
+       closest: function( selectors, context ) {
+               if ( jQuery.isArray( selectors ) ) {
+                       var ret = [], cur = this[0], match, matches = {}, selector;
+
+                       if ( cur && selectors.length ) {
+                               for ( var i = 0, l = selectors.length; i < l; i++ ) {
+                                       selector = selectors[i];
+
+                                       if ( !matches[selector] ) {
+                                               matches[selector] = jQuery.expr.match.POS.test( selector ) ? 
+                                                       jQuery( selector, context || this.context ) :
+                                                       selector;
+                                       }
+                               }
+
+                               while ( cur && cur.ownerDocument && cur !== context ) {
+                                       for ( selector in matches ) {
+                                               match = matches[selector];
+
+                                               if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
+                                                       ret.push({ selector: selector, elem: cur });
+                                                       delete matches[selector];
+                                               }
+                                       }
+                                       cur = cur.parentNode;
+                               }
+                       }
+
+                       return ret;
+               }
+
+               var pos = jQuery.expr.match.POS.test( selectors ) ? 
+                       jQuery( selectors, context || this.context ) : null;
+
+               return this.map(function( i, cur ) {
+                       while ( cur && cur.ownerDocument && cur !== context ) {
+                               if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
+                                       return cur;
+                               }
+                               cur = cur.parentNode;
+                       }
+                       return null;
+               });
+       },
+       
+       // Determine the position of an element within
+       // the matched set of elements
+       index: function( elem ) {
+               if ( !elem || typeof elem === "string" ) {
+                       return jQuery.inArray( this[0],
+                               // If it receives a string, the selector is used
+                               // If it receives nothing, the siblings are used
+                               elem ? jQuery( elem ) : this.parent().children() );
+               }
+               // Locate the position of the desired element
+               return jQuery.inArray(
+                       // If it receives a jQuery object, the first element is used
+                       elem.jquery ? elem[0] : elem, this );
+       },
+
+       add: function( selector, context ) {
+               var set = typeof selector === "string" ?
+                               jQuery( selector, context || this.context ) :
+                               jQuery.makeArray( selector ),
+                       all = jQuery.merge( this.get(), set );
+
+               return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+                       all :
+                       jQuery.unique( all ) );
+       },
+
+       andSelf: function() {
+               return this.add( this.prevObject );
+       }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+       return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+       parent: function( elem ) {
+               var parent = elem.parentNode;
+               return parent && parent.nodeType !== 11 ? parent : null;
+       },
+       parents: function( elem ) {
+               return jQuery.dir( elem, "parentNode" );
+       },
+       parentsUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "parentNode", until );
+       },
+       next: function( elem ) {
+               return jQuery.nth( elem, 2, "nextSibling" );
+       },
+       prev: function( elem ) {
+               return jQuery.nth( elem, 2, "previousSibling" );
+       },
+       nextAll: function( elem ) {
+               return jQuery.dir( elem, "nextSibling" );
+       },
+       prevAll: function( elem ) {
+               return jQuery.dir( elem, "previousSibling" );
+       },
+       nextUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "nextSibling", until );
+       },
+       prevUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "previousSibling", until );
+       },
+       siblings: function( elem ) {
+               return jQuery.sibling( elem.parentNode.firstChild, elem );
+       },
+       children: function( elem ) {
+               return jQuery.sibling( elem.firstChild );
+       },
+       contents: function( elem ) {
+               return jQuery.nodeName( elem, "iframe" ) ?
+                       elem.contentDocument || elem.contentWindow.document :
+                       jQuery.makeArray( elem.childNodes );
+       }
+}, function( name, fn ) {
+       jQuery.fn[ name ] = function( until, selector ) {
+               var ret = jQuery.map( this, fn, until );
+               
+               if ( !runtil.test( name ) ) {
+                       selector = until;
+               }
+
+               if ( selector && typeof selector === "string" ) {
+                       ret = jQuery.filter( selector, ret );
+               }
+
+               ret = this.length > 1 ? jQuery.unique( ret ) : ret;
+
+               if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+                       ret = ret.reverse();
+               }
+
+               return this.pushStack( ret, name, slice.call(arguments).join(",") );
+       };
+});
+
+jQuery.extend({
+       filter: function( expr, elems, not ) {
+               if ( not ) {
+                       expr = ":not(" + expr + ")";
+               }
+
+               return jQuery.find.matches(expr, elems);
+       },
+       
+       dir: function( elem, dir, until ) {
+               var matched = [], cur = elem[dir];
+               while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+                       if ( cur.nodeType === 1 ) {
+                               matched.push( cur );
+                       }
+                       cur = cur[dir];
+               }
+               return matched;
+       },
+
+       nth: function( cur, result, dir, elem ) {
+               result = result || 1;
+               var num = 0;
+
+               for ( ; cur; cur = cur[dir] ) {
+                       if ( cur.nodeType === 1 && ++num === result ) {
+                               break;
+                       }
+               }
+
+               return cur;
+       },
+
+       sibling: function( n, elem ) {
+               var r = [];
+
+               for ( ; n; n = n.nextSibling ) {
+                       if ( n.nodeType === 1 && n !== elem ) {
+                               r.push( n );
+                       }
+               }
+
+               return r;
+       }
+});
+var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+       rleadingWhitespace = /^\s+/,
+       rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
+       rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
+       rtagName = /<([\w:]+)/,
+       rtbody = /<tbody/i,
+       rhtml = /<|&#?\w+;/,
+       rnocache = /<script|<object|<embed|<option|<style/i,
+       rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,  // checked="checked" or checked (html5)
+       fcloseTag = function( all, front, tag ) {
+               return rselfClosing.test( tag ) ?
+                       all :
+                       front + "></" + tag + ">";
+       },
+       wrapMap = {
+               option: [ 1, "<select multiple='multiple'>", "</select>" ],
+               legend: [ 1, "<fieldset>", "</fieldset>" ],
+               thead: [ 1, "<table>", "</table>" ],
+               tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+               td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+               col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+               area: [ 1, "<map>", "</map>" ],
+               _default: [ 0, "", "" ]
+       };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+       wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+       text: function( text ) {
+               if ( jQuery.isFunction(text) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               self.text( text.call(this, i, self.text()) );
+                       });
+               }
+
+               if ( typeof text !== "object" && text !== undefined ) {
+                       return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+               }
+
+               return jQuery.text( this );
+       },
+
+       wrapAll: function( html ) {
+               if ( jQuery.isFunction( html ) ) {
+                       return this.each(function(i) {
+                               jQuery(this).wrapAll( html.call(this, i) );
+                       });
+               }
+
+               if ( this[0] ) {
+                       // The elements to wrap the target around
+                       var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+                       if ( this[0].parentNode ) {
+                               wrap.insertBefore( this[0] );
+                       }
+
+                       wrap.map(function() {
+                               var elem = this;
+
+                               while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+                                       elem = elem.firstChild;
+                               }
+
+                               return elem;
+                       }).append(this);
+               }
+
+               return this;
+       },
+
+       wrapInner: function( html ) {
+               if ( jQuery.isFunction( html ) ) {
+                       return this.each(function(i) {
+                               jQuery(this).wrapInner( html.call(this, i) );
+                       });
+               }
+
+               return this.each(function() {
+                       var self = jQuery( this ), contents = self.contents();
+
+                       if ( contents.length ) {
+                               contents.wrapAll( html );
+
+                       } else {
+                               self.append( html );
+                       }
+               });
+       },
+
+       wrap: function( html ) {
+               return this.each(function() {
+                       jQuery( this ).wrapAll( html );
+               });
+       },
+
+       unwrap: function() {
+               return this.parent().each(function() {
+                       if ( !jQuery.nodeName( this, "body" ) ) {
+                               jQuery( this ).replaceWith( this.childNodes );
+                       }
+               }).end();
+       },
+
+       append: function() {
+               return this.domManip(arguments, true, function( elem ) {
+                       if ( this.nodeType === 1 ) {
+                               this.appendChild( elem );
+                       }
+               });
+       },
+
+       prepend: function() {
+               return this.domManip(arguments, true, function( elem ) {
+                       if ( this.nodeType === 1 ) {
+                               this.insertBefore( elem, this.firstChild );
+                       }
+               });
+       },
+
+       before: function() {
+               if ( this[0] && this[0].parentNode ) {
+                       return this.domManip(arguments, false, function( elem ) {
+                               this.parentNode.insertBefore( elem, this );
+                       });
+               } else if ( arguments.length ) {
+                       var set = jQuery(arguments[0]);
+                       set.push.apply( set, this.toArray() );
+                       return this.pushStack( set, "before", arguments );
+               }
+       },
+
+       after: function() {
+               if ( this[0] && this[0].parentNode ) {
+                       return this.domManip(arguments, false, function( elem ) {
+                               this.parentNode.insertBefore( elem, this.nextSibling );
+                       });
+               } else if ( arguments.length ) {
+                       var set = this.pushStack( this, "after", arguments );
+                       set.push.apply( set, jQuery(arguments[0]).toArray() );
+                       return set;
+               }
+       },
+       
+       // keepData is for internal use only--do not document
+       remove: function( selector, keepData ) {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+                               if ( !keepData && elem.nodeType === 1 ) {
+                                       jQuery.cleanData( elem.getElementsByTagName("*") );
+                                       jQuery.cleanData( [ elem ] );
+                               }
+
+                               if ( elem.parentNode ) {
+                                        elem.parentNode.removeChild( elem );
+                               }
+                       }
+               }
+               
+               return this;
+       },
+
+       empty: function() {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       // Remove element nodes and prevent memory leaks
+                       if ( elem.nodeType === 1 ) {
+                               jQuery.cleanData( elem.getElementsByTagName("*") );
+                       }
+
+                       // Remove any remaining nodes
+                       while ( elem.firstChild ) {
+                               elem.removeChild( elem.firstChild );
+                       }
+               }
+               
+               return this;
+       },
+
+       clone: function( events ) {
+               // Do the clone
+               var ret = this.map(function() {
+                       if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+                               // IE copies events bound via attachEvent when
+                               // using cloneNode. Calling detachEvent on the
+                               // clone will also remove the events from the orignal
+                               // In order to get around this, we use innerHTML.
+                               // Unfortunately, this means some modifications to
+                               // attributes in IE that are actually only stored
+                               // as properties will not be copied (such as the
+                               // the name attribute on an input).
+                               var html = this.outerHTML, ownerDocument = this.ownerDocument;
+                               if ( !html ) {
+                                       var div = ownerDocument.createElement("div");
+                                       div.appendChild( this.cloneNode(true) );
+                                       html = div.innerHTML;
+                               }
+
+                               return jQuery.clean([html.replace(rinlinejQuery, "")
+                                       // Handle the case in IE 8 where action=/test/> self-closes a tag
+                                       .replace(/=([^="'>\s]+\/)>/g, '="$1">')
+                                       .replace(rleadingWhitespace, "")], ownerDocument)[0];
+                       } else {
+                               return this.cloneNode(true);
+                       }
+               });
+
+               // Copy the events from the original to the clone
+               if ( events === true ) {
+                       cloneCopyEvent( this, ret );
+                       cloneCopyEvent( this.find("*"), ret.find("*") );
+               }
+
+               // Return the cloned set
+               return ret;
+       },
+
+       html: function( value ) {
+               if ( value === undefined ) {
+                       return this[0] && this[0].nodeType === 1 ?
+                               this[0].innerHTML.replace(rinlinejQuery, "") :
+                               null;
+
+               // See if we can take a shortcut and just use innerHTML
+               } else if ( typeof value === "string" && !rnocache.test( value ) &&
+                       (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+                       !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
+
+                       value = value.replace(rxhtmlTag, fcloseTag);
+
+                       try {
+                               for ( var i = 0, l = this.length; i < l; i++ ) {
+                                       // Remove element nodes and prevent memory leaks
+                                       if ( this[i].nodeType === 1 ) {
+                                               jQuery.cleanData( this[i].getElementsByTagName("*") );
+                                               this[i].innerHTML = value;
+                                       }
+                               }
+
+                       // If using innerHTML throws an exception, use the fallback method
+                       } catch(e) {
+                               this.empty().append( value );
+                       }
+
+               } else if ( jQuery.isFunction( value ) ) {
+                       this.each(function(i){
+                               var self = jQuery(this), old = self.html();
+                               self.empty().append(function(){
+                                       return value.call( this, i, old );
+                               });
+                       });
+
+               } else {
+                       this.empty().append( value );
+               }
+
+               return this;
+       },
+
+       replaceWith: function( value ) {
+               if ( this[0] && this[0].parentNode ) {
+                       // Make sure that the elements are removed from the DOM before they are inserted
+                       // this can help fix replacing a parent with child elements
+                       if ( jQuery.isFunction( value ) ) {
+                               return this.each(function(i) {
+                                       var self = jQuery(this), old = self.html();
+                                       self.replaceWith( value.call( this, i, old ) );
+                               });
+                       }
+
+                       if ( typeof value !== "string" ) {
+                               value = jQuery(value).detach();
+                       }
+
+                       return this.each(function() {
+                               var next = this.nextSibling, parent = this.parentNode;
+
+                               jQuery(this).remove();
+
+                               if ( next ) {
+                                       jQuery(next).before( value );
+                               } else {
+                                       jQuery(parent).append( value );
+                               }
+                       });
+               } else {
+                       return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
+               }
+       },
+
+       detach: function( selector ) {
+               return this.remove( selector, true );
+       },
+
+       domManip: function( args, table, callback ) {
+               var results, first, value = args[0], scripts = [], fragment, parent;
+
+               // We can't cloneNode fragments that contain checked, in WebKit
+               if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+                       return this.each(function() {
+                               jQuery(this).domManip( args, table, callback, true );
+                       });
+               }
+
+               if ( jQuery.isFunction(value) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               args[0] = value.call(this, i, table ? self.html() : undefined);
+                               self.domManip( args, table, callback );
+                       });
+               }
+
+               if ( this[0] ) {
+                       parent = value && value.parentNode;
+
+                       // If we're in a fragment, just use that instead of building a new one
+                       if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+                               results = { fragment: parent };
+
+                       } else {
+                               results = buildFragment( args, this, scripts );
+                       }
+                       
+                       fragment = results.fragment;
+                       
+                       if ( fragment.childNodes.length === 1 ) {
+                               first = fragment = fragment.firstChild;
+                       } else {
+                               first = fragment.firstChild;
+                       }
+
+                       if ( first ) {
+                               table = table && jQuery.nodeName( first, "tr" );
+
+                               for ( var i = 0, l = this.length; i < l; i++ ) {
+                                       callback.call(
+                                               table ?
+                                                       root(this[i], first) :
+                                                       this[i],
+                                               i > 0 || results.cacheable || this.length > 1  ?
+                                                       fragment.cloneNode(true) :
+                                                       fragment
+                                       );
+                               }
+                       }
+
+                       if ( scripts.length ) {
+                               jQuery.each( scripts, evalScript );
+                       }
+               }
+
+               return this;
+
+               function root( elem, cur ) {
+                       return jQuery.nodeName(elem, "table") ?
+                               (elem.getElementsByTagName("tbody")[0] ||
+                               elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+                               elem;
+               }
+       }
+});
+
+function cloneCopyEvent(orig, ret) {
+       var i = 0;
+
+       ret.each(function() {
+               if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
+                       return;
+               }
+
+               var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;
+
+               if ( events ) {
+                       delete curData.handle;
+                       curData.events = {};
+
+                       for ( var type in events ) {
+                               for ( var handler in events[ type ] ) {
+                                       jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+                               }
+                       }
+               }
+       });
+}
+
+function buildFragment( args, nodes, scripts ) {
+       var fragment, cacheable, cacheresults,
+               doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
+
+       // Only cache "small" (1/2 KB) strings that are associated with the main document
+       // Cloning options loses the selected state, so don't cache them
+       // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+       // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+       if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+               !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+
+               cacheable = true;
+               cacheresults = jQuery.fragments[ args[0] ];
+               if ( cacheresults ) {
+                       if ( cacheresults !== 1 ) {
+                               fragment = cacheresults;
+                       }
+               }
+       }
+
+       if ( !fragment ) {
+               fragment = doc.createDocumentFragment();
+               jQuery.clean( args, doc, fragment, scripts );
+       }
+
+       if ( cacheable ) {
+               jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+       }
+
+       return { fragment: fragment, cacheable: cacheable };
+}
+
+jQuery.fragments = {};
+
+jQuery.each({
+       appendTo: "append",
+       prependTo: "prepend",
+       insertBefore: "before",
+       insertAfter: "after",
+       replaceAll: "replaceWith"
+}, function( name, original ) {
+       jQuery.fn[ name ] = function( selector ) {
+               var ret = [], insert = jQuery( selector ),
+                       parent = this.length === 1 && this[0].parentNode;
+               
+               if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+                       insert[ original ]( this[0] );
+                       return this;
+                       
+               } else {
+                       for ( var i = 0, l = insert.length; i < l; i++ ) {
+                               var elems = (i > 0 ? this.clone(true) : this).get();
+                               jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+                               ret = ret.concat( elems );
+                       }
+               
+                       return this.pushStack( ret, name, insert.selector );
+               }
+       };
+});
+
+jQuery.extend({
+       clean: function( elems, context, fragment, scripts ) {
+               context = context || document;
+
+               // !context.createElement fails in IE with an error but returns typeof 'object'
+               if ( typeof context.createElement === "undefined" ) {
+                       context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+               }
+
+               var ret = [];
+
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       if ( typeof elem === "number" ) {
+                               elem += "";
+                       }
+
+                       if ( !elem ) {
+                               continue;
+                       }
+
+                       // Convert html string into DOM nodes
+                       if ( typeof elem === "string" && !rhtml.test( elem ) ) {
+                               elem = context.createTextNode( elem );
+
+                       } else if ( typeof elem === "string" ) {
+                               // Fix "XHTML"-style tags in all browsers
+                               elem = elem.replace(rxhtmlTag, fcloseTag);
+
+                               // Trim whitespace, otherwise indexOf won't work as expected
+                               var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+                                       wrap = wrapMap[ tag ] || wrapMap._default,
+                                       depth = wrap[0],
+                                       div = context.createElement("div");
+
+                               // Go to html and back, then peel off extra wrappers
+                               div.innerHTML = wrap[1] + elem + wrap[2];
+
+                               // Move to the right depth
+                               while ( depth-- ) {
+                                       div = div.lastChild;
+                               }
+
+                               // Remove IE's autoinserted <tbody> from table fragments
+                               if ( !jQuery.support.tbody ) {
+
+                                       // String was a <table>, *may* have spurious <tbody>
+                                       var hasBody = rtbody.test(elem),
+                                               tbody = tag === "table" && !hasBody ?
+                                                       div.firstChild && div.firstChild.childNodes :
+
+                                                       // String was a bare <thead> or <tfoot>
+                                                       wrap[1] === "<table>" && !hasBody ?
+                                                               div.childNodes :
+                                                               [];
+
+                                       for ( var j = tbody.length - 1; j >= 0 ; --j ) {
+                                               if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+                                                       tbody[ j ].parentNode.removeChild( tbody[ j ] );
+                                               }
+                                       }
+
+                               }
+
+                               // IE completely kills leading whitespace when innerHTML is used
+                               if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+                                       div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+                               }
+
+                               elem = div.childNodes;
+                       }
+
+                       if ( elem.nodeType ) {
+                               ret.push( elem );
+                       } else {
+                               ret = jQuery.merge( ret, elem );
+                       }
+               }
+
+               if ( fragment ) {
+                       for ( var i = 0; ret[i]; i++ ) {
+                               if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+                                       scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+                               
+                               } else {
+                                       if ( ret[i].nodeType === 1 ) {
+                                               ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+                                       }
+                                       fragment.appendChild( ret[i] );
+                               }
+                       }
+               }
+
+               return ret;
+       },
+       
+       cleanData: function( elems ) {
+               var data, id, cache = jQuery.cache,
+                       special = jQuery.event.special,
+                       deleteExpando = jQuery.support.deleteExpando;
+               
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       id = elem[ jQuery.expando ];
+                       
+                       if ( id ) {
+                               data = cache[ id ];
+                               
+                               if ( data.events ) {
+                                       for ( var type in data.events ) {
+                                               if ( special[ type ] ) {
+                                                       jQuery.event.remove( elem, type );
+
+                                               } else {
+                                                       removeEvent( elem, type, data.handle );
+                                               }
+                                       }
+                               }
+                               
+                               if ( deleteExpando ) {
+                                       delete elem[ jQuery.expando ];
+
+                               } else if ( elem.removeAttribute ) {
+                                       elem.removeAttribute( jQuery.expando );
+                               }
+                               
+                               delete cache[ id ];
+                       }
+               }
+       }
+});
+// exclude the following css properties to add px
+var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+       ralpha = /alpha\([^)]*\)/,
+       ropacity = /opacity=([^)]*)/,
+       rfloat = /float/i,
+       rdashAlpha = /-([a-z])/ig,
+       rupper = /([A-Z])/g,
+       rnumpx = /^-?\d+(?:px)?$/i,
+       rnum = /^-?\d/,
+
+       cssShow = { position: "absolute", visibility: "hidden", display:"block" },
+       cssWidth = [ "Left", "Right" ],
+       cssHeight = [ "Top", "Bottom" ],
+
+       // cache check for defaultView.getComputedStyle
+       getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
+       // normalize float css property
+       styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
+       fcamelCase = function( all, letter ) {
+               return letter.toUpperCase();
+       };
+
+jQuery.fn.css = function( name, value ) {
+       return access( this, name, value, true, function( elem, name, value ) {
+               if ( value === undefined ) {
+                       return jQuery.curCSS( elem, name );
+               }
+               
+               if ( typeof value === "number" && !rexclude.test(name) ) {
+                       value += "px";
+               }
+
+               jQuery.style( elem, name, value );
+       });
+};
+
+jQuery.extend({
+       style: function( elem, name, value ) {
+               // don't set styles on text and comment nodes
+               if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+                       return undefined;
+               }
+
+               // ignore negative width and height values #1599
+               if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
+                       value = undefined;
+               }
+
+               var style = elem.style || elem, set = value !== undefined;
+
+               // IE uses filters for opacity
+               if ( !jQuery.support.opacity && name === "opacity" ) {
+                       if ( set ) {
+                               // IE has trouble with opacity if it does not have layout
+                               // Force it by setting the zoom level
+                               style.zoom = 1;
+
+                               // Set the alpha filter to set the opacity
+                               var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
+                               var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
+                               style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
+                       }
+
+                       return style.filter && style.filter.indexOf("opacity=") >= 0 ?
+                               (parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
+                               "";
+               }
+
+               // Make sure we're using the right name for getting the float value
+               if ( rfloat.test( name ) ) {
+                       name = styleFloat;
+               }
+
+               name = name.replace(rdashAlpha, fcamelCase);
+
+               if ( set ) {
+                       style[ name ] = value;
+               }
+
+               return style[ name ];
+       },
+
+       css: function( elem, name, force, extra ) {
+               if ( name === "width" || name === "height" ) {
+                       var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;
+
+                       function getWH() {
+                               val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
+
+                               if ( extra === "border" ) {
+                                       return;
+                               }
+
+                               jQuery.each( which, function() {
+                                       if ( !extra ) {
+                                               val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+                                       }
+
+                                       if ( extra === "margin" ) {
+                                               val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+                                       } else {
+                                               val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+                                       }
+                               });
+                       }
+
+                       if ( elem.offsetWidth !== 0 ) {
+                               getWH();
+                       } else {
+                               jQuery.swap( elem, props, getWH );
+                       }
+
+                       return Math.max(0, Math.round(val));
+               }
+
+               return jQuery.curCSS( elem, name, force );
+       },
+
+       curCSS: function( elem, name, force ) {
+               var ret, style = elem.style, filter;
+
+               // IE uses filters for opacity
+               if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
+                       ret = ropacity.test(elem.currentStyle.filter || "") ?
+                               (parseFloat(RegExp.$1) / 100) + "" :
+                               "";
+
+                       return ret === "" ?
+                               "1" :
+                               ret;
+               }
+
+               // Make sure we're using the right name for getting the float value
+               if ( rfloat.test( name ) ) {
+                       name = styleFloat;
+               }
+
+               if ( !force && style && style[ name ] ) {
+                       ret = style[ name ];
+
+               } else if ( getComputedStyle ) {
+
+                       // Only "float" is needed here
+                       if ( rfloat.test( name ) ) {
+                               name = "float";
+                       }
+
+                       name = name.replace( rupper, "-$1" ).toLowerCase();
+
+                       var defaultView = elem.ownerDocument.defaultView;
+
+                       if ( !defaultView ) {
+                               return null;
+                       }
+
+                       var computedStyle = defaultView.getComputedStyle( elem, null );
+
+                       if ( computedStyle ) {
+                               ret = computedStyle.getPropertyValue( name );
+                       }
+
+                       // We should always get a number back from opacity
+                       if ( name === "opacity" && ret === "" ) {
+                               ret = "1";
+                       }
+
+               } else if ( elem.currentStyle ) {
+                       var camelCase = name.replace(rdashAlpha, fcamelCase);
+
+                       ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+                       // From the awesome hack by Dean Edwards
+                       // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+                       // If we're not dealing with a regular pixel number
+                       // but a number that has a weird ending, we need to convert it to pixels
+                       if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+                               // Remember the original values
+                               var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+                               // Put in the new values to get a computed value out
+                               elem.runtimeStyle.left = elem.currentStyle.left;
+                               style.left = camelCase === "fontSize" ? "1em" : (ret || 0);
+                               ret = style.pixelLeft + "px";
+
+                               // Revert the changed values
+                               style.left = left;
+                               elem.runtimeStyle.left = rsLeft;
+                       }
+               }
+
+               return ret;
+       },
+
+       // A method for quickly swapping in/out CSS properties to get correct calculations
+       swap: function( elem, options, callback ) {
+               var old = {};
+
+               // Remember the old values, and insert the new ones
+               for ( var name in options ) {
+                       old[ name ] = elem.style[ name ];
+                       elem.style[ name ] = options[ name ];
+               }
+
+               callback.call( elem );
+
+               // Revert the old values
+               for ( var name in options ) {
+                       elem.style[ name ] = old[ name ];
+               }
+       }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+       jQuery.expr.filters.hidden = function( elem ) {
+               var width = elem.offsetWidth, height = elem.offsetHeight,
+                       skip = elem.nodeName.toLowerCase() === "tr";
+
+               return width === 0 && height === 0 && !skip ?
+                       true :
+                       width > 0 && height > 0 && !skip ?
+                               false :
+                               jQuery.curCSS(elem, "display") === "none";
+       };
+
+       jQuery.expr.filters.visible = function( elem ) {
+               return !jQuery.expr.filters.hidden( elem );
+       };
+}
+var jsc = now(),
+       rscript = /<script(.|\s)*?\/script>/gi,
+       rselectTextarea = /select|textarea/i,
+       rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
+       jsre = /=\?(&|$)/,
+       rquery = /\?/,
+       rts = /(\?|&)_=.*?(&|$)/,
+       rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+       r20 = /%20/g,
+
+       // Keep a copy of the old load method
+       _load = jQuery.fn.load;
+
+jQuery.fn.extend({
+       load: function( url, params, callback ) {
+               if ( typeof url !== "string" ) {
+                       return _load.call( this, url );
+
+               // Don't do a request if no elements are being requested
+               } else if ( !this.length ) {
+                       return this;
+               }
+
+               var off = url.indexOf(" ");
+               if ( off >= 0 ) {
+                       var selector = url.slice(off, url.length);
+                       url = url.slice(0, off);
+               }
+
+               // Default to a GET request
+               var type = "GET";
+
+               // If the second parameter was provided
+               if ( params ) {
+                       // If it's a function
+                       if ( jQuery.isFunction( params ) ) {
+                               // We assume that it's the callback
+                               callback = params;
+                               params = null;
+
+                       // Otherwise, build a param string
+                       } else if ( typeof params === "object" ) {
+                               params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+                               type = "POST";
+                       }
+               }
+
+               var self = this;
+
+               // Request the remote document
+               jQuery.ajax({
+                       url: url,
+                       type: type,
+                       dataType: "html",
+                       data: params,
+                       complete: function( res, status ) {
+                               // If successful, inject the HTML into all the matched elements
+                               if ( status === "success" || status === "notmodified" ) {
+                                       // See if a selector was specified
+                                       self.html( selector ?
+                                               // Create a dummy div to hold the results
+                                               jQuery("<div />")
+                                                       // inject the contents of the document in, removing the scripts
+                                                       // to avoid any 'Permission Denied' errors in IE
+                                                       .append(res.responseText.replace(rscript, ""))
+
+                                                       // Locate the specified elements
+                                                       .find(selector) :
+
+                                               // If not, just inject the full result
+                                               res.responseText );
+                               }
+
+                               if ( callback ) {
+                                       self.each( callback, [res.responseText, status, res] );
+                               }
+                       }
+               });
+
+               return this;
+       },
+
+       serialize: function() {
+               return jQuery.param(this.serializeArray());
+       },
+       serializeArray: function() {
+               return this.map(function() {
+                       return this.elements ? jQuery.makeArray(this.elements) : this;
+               })
+               .filter(function() {
+                       return this.name && !this.disabled &&
+                               (this.checked || rselectTextarea.test(this.nodeName) ||
+                                       rinput.test(this.type));
+               })
+               .map(function( i, elem ) {
+                       var val = jQuery(this).val();
+
+                       return val == null ?
+                               null :
+                               jQuery.isArray(val) ?
+                                       jQuery.map( val, function( val, i ) {
+                                               return { name: elem.name, value: val };
+                                       }) :
+                                       { name: elem.name, value: val };
+               }).get();
+       }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
+       jQuery.fn[o] = function( f ) {
+               return this.bind(o, f);
+       };
+});
+
+jQuery.extend({
+
+       get: function( url, data, callback, type ) {
+               // shift arguments if data argument was omited
+               if ( jQuery.isFunction( data ) ) {
+                       type = type || callback;
+                       callback = data;
+                       data = null;
+               }
+
+               return jQuery.ajax({
+                       type: "GET",
+                       url: url,
+                       data: data,
+                       success: callback,
+                       dataType: type
+               });
+       },
+
+       getScript: function( url, callback ) {
+               return jQuery.get(url, null, callback, "script");
+       },
+
+       getJSON: function( url, data, callback ) {
+               return jQuery.get(url, data, callback, "json");
+       },
+
+       post: function( url, data, callback, type ) {
+               // shift arguments if data argument was omited
+               if ( jQuery.isFunction( data ) ) {
+                       type = type || callback;
+                       callback = data;
+                       data = {};
+               }
+
+               return jQuery.ajax({
+                       type: "POST",
+                       url: url,
+                       data: data,
+                       success: callback,
+                       dataType: type
+               });
+       },
+
+       ajaxSetup: function( settings ) {
+               jQuery.extend( jQuery.ajaxSettings, settings );
+       },
+
+       ajaxSettings: {
+               url: location.href,
+               global: true,
+               type: "GET",
+               contentType: "application/x-www-form-urlencoded",
+               processData: true,
+               async: true,
+               /*
+               timeout: 0,
+               data: null,
+               username: null,
+               password: null,
+               traditional: false,
+               */
+               // Create the request object; Microsoft failed to properly
+               // implement the XMLHttpRequest in IE7 (can't request local files),
+               // so we use the ActiveXObject when it is available
+               // This function can be overriden by calling jQuery.ajaxSetup
+               xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
+                       function() {
+                               return new window.XMLHttpRequest();
+                       } :
+                       function() {
+                               try {
+                                       return new window.ActiveXObject("Microsoft.XMLHTTP");
+                               } catch(e) {}
+                       },
+               accepts: {
+                       xml: "application/xml, text/xml",
+                       html: "text/html",
+                       script: "text/javascript, application/javascript",
+                       json: "application/json, text/javascript",
+                       text: "text/plain",
+                       _default: "*/*"
+               }
+       },
+
+       // Last-Modified header cache for next request
+       lastModified: {},
+       etag: {},
+
+       ajax: function( origSettings ) {
+               var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
+               
+               var jsonp, status, data,
+                       callbackContext = origSettings && origSettings.context || s,
+                       type = s.type.toUpperCase();
+
+               // convert data if not already a string
+               if ( s.data && s.processData && typeof s.data !== "string" ) {
+                       s.data = jQuery.param( s.data, s.traditional );
+               }
+
+               // Handle JSONP Parameter Callbacks
+               if ( s.dataType === "jsonp" ) {
+                       if ( type === "GET" ) {
+                               if ( !jsre.test( s.url ) ) {
+                                       s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+                               }
+                       } else if ( !s.data || !jsre.test(s.data) ) {
+                               s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+                       }
+                       s.dataType = "json";
+               }
+
+               // Build temporary JSONP function
+               if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
+                       jsonp = s.jsonpCallback || ("jsonp" + jsc++);
+
+                       // Replace the =? sequence both in the query string and the data
+                       if ( s.data ) {
+                               s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+                       }
+
+                       s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+                       // We need to make sure
+                       // that a JSONP style response is executed properly
+                       s.dataType = "script";
+
+                       // Handle JSONP-style loading
+                       window[ jsonp ] = window[ jsonp ] || function( tmp ) {
+                               data = tmp;
+                               success();
+                               complete();
+                               // Garbage collect
+                               window[ jsonp ] = undefined;
+
+                               try {
+                                       delete window[ jsonp ];
+                               } catch(e) {}
+
+                               if ( head ) {
+                                       head.removeChild( script );
+                               }
+                       };
+               }
+
+               if ( s.dataType === "script" && s.cache === null ) {
+                       s.cache = false;
+               }
+
+               if ( s.cache === false && type === "GET" ) {
+                       var ts = now();
+
+                       // try replacing _= if it is there
+                       var ret = s.url.replace(rts, "$1_=" + ts + "$2");
+
+                       // if nothing was replaced, add timestamp to the end
+                       s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
+               }
+
+               // If data is available, append data to url for get requests
+               if ( s.data && type === "GET" ) {
+                       s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
+               }
+
+               // Watch for a new set of requests
+               if ( s.global && ! jQuery.active++ ) {
+                       jQuery.event.trigger( "ajaxStart" );
+               }
+
+               // Matches an absolute URL, and saves the domain
+               var parts = rurl.exec( s.url ),
+                       remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
+
+               // If we're requesting a remote document
+               // and trying to load JSON or Script with a GET
+               if ( s.dataType === "script" && type === "GET" && remote ) {
+                       var head = document.getElementsByTagName("head")[0] || document.documentElement;
+                       var script = document.createElement("script");
+                       script.src = s.url;
+                       if ( s.scriptCharset ) {
+                               script.charset = s.scriptCharset;
+                       }
+
+                       // Handle Script loading
+                       if ( !jsonp ) {
+                               var done = false;
+
+                               // Attach handlers for all browsers
+                               script.onload = script.onreadystatechange = function() {
+                                       if ( !done && (!this.readyState ||
+                                                       this.readyState === "loaded" || this.readyState === "complete") ) {
+                                               done = true;
+                                               success();
+                                               complete();
+
+                                               // Handle memory leak in IE
+                                               script.onload = script.onreadystatechange = null;
+                                               if ( head && script.parentNode ) {
+                                                       head.removeChild( script );
+                                               }
+                                       }
+                               };
+                       }
+
+                       // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
+                       // This arises when a base node is used (#2709 and #4378).
+                       head.insertBefore( script, head.firstChild );
+
+                       // We handle everything using the script element injection
+                       return undefined;
+               }
+
+               var requestDone = false;
+
+               // Create the request object
+               var xhr = s.xhr();
+
+               if ( !xhr ) {
+                       return;
+               }
+
+               // Open the socket
+               // Passing null username, generates a login popup on Opera (#2865)
+               if ( s.username ) {
+                       xhr.open(type, s.url, s.async, s.username, s.password);
+               } else {
+                       xhr.open(type, s.url, s.async);
+               }
+
+               // Need an extra try/catch for cross domain requests in Firefox 3
+               try {
+                       // Set the correct header, if data is being sent
+                       if ( s.data || origSettings && origSettings.contentType ) {
+                               xhr.setRequestHeader("Content-Type", s.contentType);
+                       }
+
+                       // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+                       if ( s.ifModified ) {
+                               if ( jQuery.lastModified[s.url] ) {
+                                       xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
+                               }
+
+                               if ( jQuery.etag[s.url] ) {
+                                       xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
+                               }
+                       }
+
+                       // Set header so the called script knows that it's an XMLHttpRequest
+                       // Only send the header if it's not a remote XHR
+                       if ( !remote ) {
+                               xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+                       }
+
+                       // Set the Accepts header for the server, depending on the dataType
+                       xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+                               s.accepts[ s.dataType ] + ", */*" :
+                               s.accepts._default );
+               } catch(e) {}
+
+               // Allow custom headers/mimetypes and early abort
+               if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {
+                       // Handle the global AJAX counter
+                       if ( s.global && ! --jQuery.active ) {
+                               jQuery.event.trigger( "ajaxStop" );
+                       }
+
+                       // close opended socket
+                       xhr.abort();
+                       return false;
+               }
+
+               if ( s.global ) {
+                       trigger("ajaxSend", [xhr, s]);
+               }
+
+               // Wait for a response to come back
+               var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
+                       // The request was aborted
+                       if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
+                               // Opera doesn't call onreadystatechange before this point
+                               // so we simulate the call
+                               if ( !requestDone ) {
+                                       complete();
+                               }
+
+                               requestDone = true;
+                               if ( xhr ) {
+                                       xhr.onreadystatechange = jQuery.noop;
+                               }
+
+                       // The transfer is complete and the data is available, or the request timed out
+                       } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
+                               requestDone = true;
+                               xhr.onreadystatechange = jQuery.noop;
+
+                               status = isTimeout === "timeout" ?
+                                       "timeout" :
+                                       !jQuery.httpSuccess( xhr ) ?
+                                               "error" :
+                                               s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
+                                                       "notmodified" :
+                                                       "success";
+
+                               var errMsg;
+
+                               if ( status === "success" ) {
+                                       // Watch for, and catch, XML document parse errors
+                                       try {
+                                               // process the data (runs the xml through httpData regardless of callback)
+                                               data = jQuery.httpData( xhr, s.dataType, s );
+                                       } catch(err) {
+                                               status = "parsererror";
+                                               errMsg = err;
+                                       }
+                               }
+
+                               // Make sure that the request was successful or notmodified
+                               if ( status === "success" || status === "notmodified" ) {
+                                       // JSONP handles its own success callback
+                                       if ( !jsonp ) {
+                                               success();
+                                       }
+                               } else {
+                                       jQuery.handleError(s, xhr, status, errMsg);
+                               }
+
+                               // Fire the complete handlers
+                               complete();
+
+                               if ( isTimeout === "timeout" ) {
+                                       xhr.abort();
+                               }
+
+                               // Stop memory leaks
+                               if ( s.async ) {
+                                       xhr = null;
+                               }
+                       }
+               };
+
+               // Override the abort handler, if we can (IE doesn't allow it, but that's OK)
+               // Opera doesn't fire onreadystatechange at all on abort
+               try {
+                       var oldAbort = xhr.abort;
+                       xhr.abort = function() {
+                               if ( xhr ) {
+                                       oldAbort.call( xhr );
+                               }
+
+                               onreadystatechange( "abort" );
+                       };
+               } catch(e) { }
+
+               // Timeout checker
+               if ( s.async && s.timeout > 0 ) {
+                       setTimeout(function() {
+                               // Check to see if the request is still happening
+                               if ( xhr && !requestDone ) {
+                                       onreadystatechange( "timeout" );
+                               }
+                       }, s.timeout);
+               }
+
+               // Send the data
+               try {
+                       xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
+               } catch(e) {
+                       jQuery.handleError(s, xhr, null, e);
+                       // Fire the complete handlers
+                       complete();
+               }
+
+               // firefox 1.5 doesn't fire statechange for sync requests
+               if ( !s.async ) {
+                       onreadystatechange();
+               }
+
+               function success() {
+                       // If a local callback was specified, fire it and pass it the data
+                       if ( s.success ) {
+                               s.success.call( callbackContext, data, status, xhr );
+                       }
+
+                       // Fire the global callback
+                       if ( s.global ) {
+                               trigger( "ajaxSuccess", [xhr, s] );
+                       }
+               }
+
+               function complete() {
+                       // Process result
+                       if ( s.complete ) {
+                               s.complete.call( callbackContext, xhr, status);
+                       }
+
+                       // The request was completed
+                       if ( s.global ) {
+                               trigger( "ajaxComplete", [xhr, s] );
+                       }
+
+                       // Handle the global AJAX counter
+                       if ( s.global && ! --jQuery.active ) {
+                               jQuery.event.trigger( "ajaxStop" );
+                       }
+               }
+               
+               function trigger(type, args) {
+                       (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
+               }
+
+               // return XMLHttpRequest to allow aborting the request etc.
+               return xhr;
+       },
+
+       handleError: function( s, xhr, status, e ) {
+               // If a local callback was specified, fire it
+               if ( s.error ) {
+                       s.error.call( s.context || s, xhr, status, e );
+               }
+
+               // Fire the global callback
+               if ( s.global ) {
+                       (s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
+               }
+       },
+
+       // Counter for holding the number of active queries
+       active: 0,
+
+       // Determines if an XMLHttpRequest was successful or not
+       httpSuccess: function( xhr ) {
+               try {
+                       // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+                       return !xhr.status && location.protocol === "file:" ||
+                               // Opera returns 0 when status is 304
+                               ( xhr.status >= 200 && xhr.status < 300 ) ||
+                               xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
+               } catch(e) {}
+
+               return false;
+       },
+
+       // Determines if an XMLHttpRequest returns NotModified
+       httpNotModified: function( xhr, url ) {
+               var lastModified = xhr.getResponseHeader("Last-Modified"),
+                       etag = xhr.getResponseHeader("Etag");
+
+               if ( lastModified ) {
+                       jQuery.lastModified[url] = lastModified;
+               }
+
+               if ( etag ) {
+                       jQuery.etag[url] = etag;
+               }
+
+               // Opera returns 0 when status is 304
+               return xhr.status === 304 || xhr.status === 0;
+       },
+
+       httpData: function( xhr, type, s ) {
+               var ct = xhr.getResponseHeader("content-type") || "",
+                       xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
+                       data = xml ? xhr.responseXML : xhr.responseText;
+
+               if ( xml && data.documentElement.nodeName === "parsererror" ) {
+                       jQuery.error( "parsererror" );
+               }
+
+               // Allow a pre-filtering function to sanitize the response
+               // s is checked to keep backwards compatibility
+               if ( s && s.dataFilter ) {
+                       data = s.dataFilter( data, type );
+               }
+
+               // The filter can actually parse the response
+               if ( typeof data === "string" ) {
+                       // Get the JavaScript object, if JSON is used.
+                       if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
+                               data = jQuery.parseJSON( data );
+
+                       // If the type is "script", eval it in global context
+                       } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
+                               jQuery.globalEval( data );
+                       }
+               }
+
+               return data;
+       },
+
+       // Serialize an array of form elements or a set of
+       // key/values into a query string
+       param: function( a, traditional ) {
+               var s = [];
+               
+               // Set traditional to true for jQuery <= 1.3.2 behavior.
+               if ( traditional === undefined ) {
+                       traditional = jQuery.ajaxSettings.traditional;
+               }
+               
+               // If an array was passed in, assume that it is an array of form elements.
+               if ( jQuery.isArray(a) || a.jquery ) {
+                       // Serialize the form elements
+                       jQuery.each( a, function() {
+                               add( this.name, this.value );
+                       });
+                       
+               } else {
+                       // If traditional, encode the "old" way (the way 1.3.2 or older
+                       // did it), otherwise encode params recursively.
+                       for ( var prefix in a ) {
+                               buildParams( prefix, a[prefix] );
+                       }
+               }
+
+               // Return the resulting serialization
+               return s.join("&").replace(r20, "+");
+
+               function buildParams( prefix, obj ) {
+                       if ( jQuery.isArray(obj) ) {
+                               // Serialize array item.
+                               jQuery.each( obj, function( i, v ) {
+                                       if ( traditional || /\[\]$/.test( prefix ) ) {
+                                               // Treat each array item as a scalar.
+                                               add( prefix, v );
+                                       } else {
+                                               // If array item is non-scalar (array or object), encode its
+                                               // numeric index to resolve deserialization ambiguity issues.
+                                               // Note that rack (as of 1.0.0) can't currently deserialize
+                                               // nested arrays properly, and attempting to do so may cause
+                                               // a server error. Possible fixes are to modify rack's
+                                               // deserialization algorithm or to provide an option or flag
+                                               // to force array serialization to be shallow.
+                                               buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v );
+                                       }
+                               });
+                                       
+                       } else if ( !traditional && obj != null && typeof obj === "object" ) {
+                               // Serialize object item.
+                               jQuery.each( obj, function( k, v ) {
+                                       buildParams( prefix + "[" + k + "]", v );
+                               });
+                                       
+                       } else {
+                               // Serialize scalar item.
+                               add( prefix, obj );
+                       }
+               }
+
+               function add( key, value ) {
+                       // If value is a function, invoke it and return its value
+                       value = jQuery.isFunction(value) ? value() : value;
+                       s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
+               }
+       }
+});
+var elemdisplay = {},
+       rfxtypes = /toggle|show|hide/,
+       rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
+       timerId,
+       fxAttrs = [
+               // height animations
+               [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+               // width animations
+               [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+               // opacity animations
+               [ "opacity" ]
+       ];
+
+jQuery.fn.extend({
+       show: function( speed, callback ) {
+               if ( speed || speed === 0) {
+                       return this.animate( genFx("show", 3), speed, callback);
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               var old = jQuery.data(this[i], "olddisplay");
+
+                               this[i].style.display = old || "";
+
+                               if ( jQuery.css(this[i], "display") === "none" ) {
+                                       var nodeName = this[i].nodeName, display;
+
+                                       if ( elemdisplay[ nodeName ] ) {
+                                               display = elemdisplay[ nodeName ];
+
+                                       } else {
+                                               var elem = jQuery("<" + nodeName + " />").appendTo("body");
+
+                                               display = elem.css("display");
+
+                                               if ( display === "none" ) {
+                                                       display = "block";
+                                               }
+
+                                               elem.remove();
+
+                                               elemdisplay[ nodeName ] = display;
+                                       }
+
+                                       jQuery.data(this[i], "olddisplay", display);
+                               }
+                       }
+
+                       // Set the display of the elements in a second loop
+                       // to avoid the constant reflow
+                       for ( var j = 0, k = this.length; j < k; j++ ) {
+                               this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
+                       }
+
+                       return this;
+               }
+       },
+
+       hide: function( speed, callback ) {
+               if ( speed || speed === 0 ) {
+                       return this.animate( genFx("hide", 3), speed, callback);
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               var old = jQuery.data(this[i], "olddisplay");
+                               if ( !old && old !== "none" ) {
+                                       jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+                               }
+                       }
+
+                       // Set the display of the elements in a second loop
+                       // to avoid the constant reflow
+                       for ( var j = 0, k = this.length; j < k; j++ ) {
+                               this[j].style.display = "none";
+                       }
+
+                       return this;
+               }
+       },
+
+       // Save the old toggle function
+       _toggle: jQuery.fn.toggle,
+
+       toggle: function( fn, fn2 ) {
+               var bool = typeof fn === "boolean";
+
+               if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+                       this._toggle.apply( this, arguments );
+
+               } else if ( fn == null || bool ) {
+                       this.each(function() {
+                               var state = bool ? fn : jQuery(this).is(":hidden");
+                               jQuery(this)[ state ? "show" : "hide" ]();
+                       });
+
+               } else {
+                       this.animate(genFx("toggle", 3), fn, fn2);
+               }
+
+               return this;
+       },
+
+       fadeTo: function( speed, to, callback ) {
+               return this.filter(":hidden").css("opacity", 0).show().end()
+                                       .animate({opacity: to}, speed, callback);
+       },
+
+       animate: function( prop, speed, easing, callback ) {
+               var optall = jQuery.speed(speed, easing, callback);
+
+               if ( jQuery.isEmptyObject( prop ) ) {
+                       return this.each( optall.complete );
+               }
+
+               return this[ optall.queue === false ? "each" : "queue" ](function() {
+                       var opt = jQuery.extend({}, optall), p,
+                               hidden = this.nodeType === 1 && jQuery(this).is(":hidden"),
+                               self = this;
+
+                       for ( p in prop ) {
+                               var name = p.replace(rdashAlpha, fcamelCase);
+
+                               if ( p !== name ) {
+                                       prop[ name ] = prop[ p ];
+                                       delete prop[ p ];
+                                       p = name;
+                               }
+
+                               if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
+                                       return opt.complete.call(this);
+                               }
+
+                               if ( ( p === "height" || p === "width" ) && this.style ) {
+                                       // Store display property
+                                       opt.display = jQuery.css(this, "display");
+
+                                       // Make sure that nothing sneaks out
+                                       opt.overflow = this.style.overflow;
+                               }
+
+                               if ( jQuery.isArray( prop[p] ) ) {
+                                       // Create (if needed) and add to specialEasing
+                                       (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
+                                       prop[p] = prop[p][0];
+                               }
+                       }
+
+                       if ( opt.overflow != null ) {
+                               this.style.overflow = "hidden";
+                       }
+
+                       opt.curAnim = jQuery.extend({}, prop);
+
+                       jQuery.each( prop, function( name, val ) {
+                               var e = new jQuery.fx( self, opt, name );
+
+                               if ( rfxtypes.test(val) ) {
+                                       e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+
+                               } else {
+                                       var parts = rfxnum.exec(val),
+                                               start = e.cur(true) || 0;
+
+                                       if ( parts ) {
+                                               var end = parseFloat( parts[2] ),
+                                                       unit = parts[3] || "px";
+
+                                               // We need to compute starting value
+                                               if ( unit !== "px" ) {
+                                                       self.style[ name ] = (end || 1) + unit;
+                                                       start = ((end || 1) / e.cur(true)) * start;
+                                                       self.style[ name ] = start + unit;
+                                               }
+
+                                               // If a +=/-= token was provided, we're doing a relative animation
+                                               if ( parts[1] ) {
+                                                       end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
+                                               }
+
+                                               e.custom( start, end, unit );
+
+                                       } else {
+                                               e.custom( start, val, "" );
+                                       }
+                               }
+                       });
+
+                       // For JS strict compliance
+                       return true;
+               });
+       },
+
+       stop: function( clearQueue, gotoEnd ) {
+               var timers = jQuery.timers;
+
+               if ( clearQueue ) {
+                       this.queue([]);
+               }
+
+               this.each(function() {
+                       // go in reverse order so anything added to the queue during the loop is ignored
+                       for ( var i = timers.length - 1; i >= 0; i-- ) {
+                               if ( timers[i].elem === this ) {
+                                       if (gotoEnd) {
+                                               // force the next step to be the last
+                                               timers[i](true);
+                                       }
+
+                                       timers.splice(i, 1);
+                               }
+                       }
+               });
+
+               // start the next in the queue if the last step wasn't forced
+               if ( !gotoEnd ) {
+                       this.dequeue();
+               }
+
+               return this;
+       }
+
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+       slideDown: genFx("show", 1),
+       slideUp: genFx("hide", 1),
+       slideToggle: genFx("toggle", 1),
+       fadeIn: { opacity: "show" },
+       fadeOut: { opacity: "hide" }
+}, function( name, props ) {
+       jQuery.fn[ name ] = function( speed, callback ) {
+               return this.animate( props, speed, callback );
+       };
+});
+
+jQuery.extend({
+       speed: function( speed, easing, fn ) {
+               var opt = speed && typeof speed === "object" ? speed : {
+                       complete: fn || !fn && easing ||
+                               jQuery.isFunction( speed ) && speed,
+                       duration: speed,
+                       easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+               };
+
+               opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+                       jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
+               // Queueing
+               opt.old = opt.complete;
+               opt.complete = function() {
+                       if ( opt.queue !== false ) {
+                               jQuery(this).dequeue();
+                       }
+                       if ( jQuery.isFunction( opt.old ) ) {
+                               opt.old.call( this );
+                       }
+               };
+
+               return opt;
+       },
+
+       easing: {
+               linear: function( p, n, firstNum, diff ) {
+                       return firstNum + diff * p;
+               },
+               swing: function( p, n, firstNum, diff ) {
+                       return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+               }
+       },
+
+       timers: [],
+
+       fx: function( elem, options, prop ) {
+               this.options = options;
+               this.elem = elem;
+               this.prop = prop;
+
+               if ( !options.orig ) {
+                       options.orig = {};
+               }
+       }
+
+});
+
+jQuery.fx.prototype = {
+       // Simple function for setting a style value
+       update: function() {
+               if ( this.options.step ) {
+                       this.options.step.call( this.elem, this.now, this );
+               }
+
+               (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+               // Set display property to block for height/width animations
+               if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) {
+                       this.elem.style.display = "block";
+               }
+       },
+
+       // Get the current size
+       cur: function( force ) {
+               if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+                       return this.elem[ this.prop ];
+               }
+
+               var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+               return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+       },
+
+       // Start an animation from one number to another
+       custom: function( from, to, unit ) {
+               this.startTime = now();
+               this.start = from;
+               this.end = to;
+               this.unit = unit || this.unit || "px";
+               this.now = this.start;
+               this.pos = this.state = 0;
+
+               var self = this;
+               function t( gotoEnd ) {
+                       return self.step(gotoEnd);
+               }
+
+               t.elem = this.elem;
+
+               if ( t() && jQuery.timers.push(t) && !timerId ) {
+                       timerId = setInterval(jQuery.fx.tick, 13);
+               }
+       },
+
+       // Simple 'show' function
+       show: function() {
+               // Remember where we started, so that we can go back to it later
+               this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+               this.options.show = true;
+
+               // Begin the animation
+               // Make sure that we start at a small width/height to avoid any
+               // flash of content
+               this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+
+               // Start by showing the element
+               jQuery( this.elem ).show();
+       },
+
+       // Simple 'hide' function
+       hide: function() {
+               // Remember where we started, so that we can go back to it later
+               this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+               this.options.hide = true;
+
+               // Begin the animation
+               this.custom(this.cur(), 0);
+       },
+
+       // Each step of an animation
+       step: function( gotoEnd ) {
+               var t = now(), done = true;
+
+               if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+                       this.now = this.end;
+                       this.pos = this.state = 1;
+                       this.update();
+
+                       this.options.curAnim[ this.prop ] = true;
+
+                       for ( var i in this.options.curAnim ) {
+                               if ( this.options.curAnim[i] !== true ) {
+                                       done = false;
+                               }
+                       }
+
+                       if ( done ) {
+                               if ( this.options.display != null ) {
+                                       // Reset the overflow
+                                       this.elem.style.overflow = this.options.overflow;
+
+                                       // Reset the display
+                                       var old = jQuery.data(this.elem, "olddisplay");
+                                       this.elem.style.display = old ? old : this.options.display;
+
+                                       if ( jQuery.css(this.elem, "display") === "none" ) {
+                                               this.elem.style.display = "block";
+                                       }
+                               }
+
+                               // Hide the element if the "hide" operation was done
+                               if ( this.options.hide ) {
+                                       jQuery(this.elem).hide();
+                               }
+
+                               // Reset the properties, if the item has been hidden or shown
+                               if ( this.options.hide || this.options.show ) {
+                                       for ( var p in this.options.curAnim ) {
+                                               jQuery.style(this.elem, p, this.options.orig[p]);
+                                       }
+                               }
+
+                               // Execute the complete function
+                               this.options.complete.call( this.elem );
+                       }
+
+                       return false;
+
+               } else {
+                       var n = t - this.startTime;
+                       this.state = n / this.options.duration;
+
+                       // Perform the easing function, defaults to swing
+                       var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
+                       var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
+                       this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
+                       this.now = this.start + ((this.end - this.start) * this.pos);
+
+                       // Perform the next step of the animation
+                       this.update();
+               }
+
+               return true;
+       }
+};
+
+jQuery.extend( jQuery.fx, {
+       tick: function() {
+               var timers = jQuery.timers;
+
+               for ( var i = 0; i < timers.length; i++ ) {
+                       if ( !timers[i]() ) {
+                               timers.splice(i--, 1);
+                       }
+               }
+
+               if ( !timers.length ) {
+                       jQuery.fx.stop();
+               }
+       },
+               
+       stop: function() {
+               clearInterval( timerId );
+               timerId = null;
+       },
+       
+       speeds: {
+               slow: 600,
+               fast: 200,
+               // Default speed
+               _default: 400
+       },
+
+       step: {
+               opacity: function( fx ) {
+                       jQuery.style(fx.elem, "opacity", fx.now);
+               },
+
+               _default: function( fx ) {
+                       if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+                               fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+                       } else {
+                               fx.elem[ fx.prop ] = fx.now;
+                       }
+               }
+       }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+       jQuery.expr.filters.animated = function( elem ) {
+               return jQuery.grep(jQuery.timers, function( fn ) {
+                       return elem === fn.elem;
+               }).length;
+       };
+}
+
+function genFx( type, num ) {
+       var obj = {};
+
+       jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+               obj[ this ] = type;
+       });
+
+       return obj;
+}
+if ( "getBoundingClientRect" in document.documentElement ) {
+       jQuery.fn.offset = function( options ) {
+               var elem = this[0];
+
+               if ( options ) { 
+                       return this.each(function( i ) {
+                               jQuery.offset.setOffset( this, options, i );
+                       });
+               }
+
+               if ( !elem || !elem.ownerDocument ) {
+                       return null;
+               }
+
+               if ( elem === elem.ownerDocument.body ) {
+                       return jQuery.offset.bodyOffset( elem );
+               }
+
+               var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,
+                       clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+                       top  = box.top  + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
+                       left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
+
+               return { top: top, left: left };
+       };
+
+} else {
+       jQuery.fn.offset = function( options ) {
+               var elem = this[0];
+
+               if ( options ) { 
+                       return this.each(function( i ) {
+                               jQuery.offset.setOffset( this, options, i );
+                       });
+               }
+
+               if ( !elem || !elem.ownerDocument ) {
+                       return null;
+               }
+
+               if ( elem === elem.ownerDocument.body ) {
+                       return jQuery.offset.bodyOffset( elem );
+               }
+
+               jQuery.offset.initialize();
+
+               var offsetParent = elem.offsetParent, prevOffsetParent = elem,
+                       doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
+                       body = doc.body, defaultView = doc.defaultView,
+                       prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+                       top = elem.offsetTop, left = elem.offsetLeft;
+
+               while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+                       if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+                               break;
+                       }
+
+                       computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+                       top  -= elem.scrollTop;
+                       left -= elem.scrollLeft;
+
+                       if ( elem === offsetParent ) {
+                               top  += elem.offsetTop;
+                               left += elem.offsetLeft;
+
+                               if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
+                                       top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
+                                       left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+                               }
+
+                               prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
+                       }
+
+                       if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+                               top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
+                               left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+                       }
+
+                       prevComputedStyle = computedStyle;
+               }
+
+               if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+                       top  += body.offsetTop;
+                       left += body.offsetLeft;
+               }
+
+               if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+                       top  += Math.max( docElem.scrollTop, body.scrollTop );
+                       left += Math.max( docElem.scrollLeft, body.scrollLeft );
+               }
+
+               return { top: top, left: left };
+       };
+}
+
+jQuery.offset = {
+       initialize: function() {
+               var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0,
+                       html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+
+               jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
+
+               container.innerHTML = html;
+               body.insertBefore( container, body.firstChild );
+               innerDiv = container.firstChild;
+               checkDiv = innerDiv.firstChild;
+               td = innerDiv.nextSibling.firstChild.firstChild;
+
+               this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+               this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+               checkDiv.style.position = "fixed", checkDiv.style.top = "20px";
+               // safari subtracts parent border width here which is 5px
+               this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
+               checkDiv.style.position = checkDiv.style.top = "";
+
+               innerDiv.style.overflow = "hidden", innerDiv.style.position = "relative";
+               this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+               this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
+
+               body.removeChild( container );
+               body = container = innerDiv = checkDiv = table = td = null;
+               jQuery.offset.initialize = jQuery.noop;
+       },
+
+       bodyOffset: function( body ) {
+               var top = body.offsetTop, left = body.offsetLeft;
+
+               jQuery.offset.initialize();
+
+               if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+                       top  += parseFloat( jQuery.curCSS(body, "marginTop",  true) ) || 0;
+                       left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
+               }
+
+               return { top: top, left: left };
+       },
+       
+       setOffset: function( elem, options, i ) {
+               // set position first, in-case top/left are set even on static elem
+               if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
+                       elem.style.position = "relative";
+               }
+               var curElem   = jQuery( elem ),
+                       curOffset = curElem.offset(),
+                       curTop    = parseInt( jQuery.curCSS( elem, "top",  true ), 10 ) || 0,
+                       curLeft   = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;
+
+               if ( jQuery.isFunction( options ) ) {
+                       options = options.call( elem, i, curOffset );
+               }
+
+               var props = {
+                       top:  (options.top  - curOffset.top)  + curTop,
+                       left: (options.left - curOffset.left) + curLeft
+               };
+               
+               if ( "using" in options ) {
+                       options.using.call( elem, props );
+               } else {
+                       curElem.css( props );
+               }
+       }
+};
+
+
+jQuery.fn.extend({
+       position: function() {
+               if ( !this[0] ) {
+                       return null;
+               }
+
+               var elem = this[0],
+
+               // Get *real* offsetParent
+               offsetParent = this.offsetParent(),
+
+               // Get correct offsets
+               offset       = this.offset(),
+               parentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+               // Subtract element margins
+               // note: when an element has margin: auto the offsetLeft and marginLeft
+               // are the same in Safari causing offset.left to incorrectly be 0
+               offset.top  -= parseFloat( jQuery.curCSS(elem, "marginTop",  true) ) || 0;
+               offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;
+
+               // Add offsetParent borders
+               parentOffset.top  += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth",  true) ) || 0;
+               parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0;
+
+               // Subtract the two offsets
+               return {
+                       top:  offset.top  - parentOffset.top,
+                       left: offset.left - parentOffset.left
+               };
+       },
+
+       offsetParent: function() {
+               return this.map(function() {
+                       var offsetParent = this.offsetParent || document.body;
+                       while ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+                               offsetParent = offsetParent.offsetParent;
+                       }
+                       return offsetParent;
+               });
+       }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+       var method = "scroll" + name;
+
+       jQuery.fn[ method ] = function(val) {
+               var elem = this[0], win;
+               
+               if ( !elem ) {
+                       return null;
+               }
+
+               if ( val !== undefined ) {
+                       // Set the scroll offset
+                       return this.each(function() {
+                               win = getWindow( this );
+
+                               if ( win ) {
+                                       win.scrollTo(
+                                               !i ? val : jQuery(win).scrollLeft(),
+                                                i ? val : jQuery(win).scrollTop()
+                                       );
+
+                               } else {
+                                       this[ method ] = val;
+                               }
+                       });
+               } else {
+                       win = getWindow( elem );
+
+                       // Return the scroll offset
+                       return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
+                               jQuery.support.boxModel && win.document.documentElement[ method ] ||
+                                       win.document.body[ method ] :
+                               elem[ method ];
+               }
+       };
+});
+
+function getWindow( elem ) {
+       return ("scrollTo" in elem && elem.document) ?
+               elem :
+               elem.nodeType === 9 ?
+                       elem.defaultView || elem.parentWindow :
+                       false;
+}
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+       var type = name.toLowerCase();
+
+       // innerHeight and innerWidth
+       jQuery.fn["inner" + name] = function() {
+               return this[0] ?
+                       jQuery.css( this[0], type, false, "padding" ) :
+                       null;
+       };
+
+       // outerHeight and outerWidth
+       jQuery.fn["outer" + name] = function( margin ) {
+               return this[0] ?
+                       jQuery.css( this[0], type, false, margin ? "margin" : "border" ) :
+                       null;
+       };
+
+       jQuery.fn[ type ] = function( size ) {
+               // Get window width or height
+               var elem = this[0];
+               if ( !elem ) {
+                       return size == null ? null : this;
+               }
+               
+               if ( jQuery.isFunction( size ) ) {
+                       return this.each(function( i ) {
+                               var self = jQuery( this );
+                               self[ type ]( size.call( this, i, self[ type ]() ) );
+                       });
+               }
+
+               return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
+                       // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+                       elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
+                       elem.document.body[ "client" + name ] :
+
+                       // Get document width or height
+                       (elem.nodeType === 9) ? // is it a document
+                               // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+                               Math.max(
+                                       elem.documentElement["client" + name],
+                                       elem.body["scroll" + name], elem.documentElement["scroll" + name],
+                                       elem.body["offset" + name], elem.documentElement["offset" + name]
+                               ) :
+
+                               // Get or set width or height on the element
+                               size === undefined ?
+                                       // Get width or height on the element
+                                       jQuery.css( elem, type ) :
+
+                                       // Set the width or height on the element (default to pixels if value is unitless)
+                                       this.css( type, typeof size === "string" ? size : size + "px" );
+       };
+
+});
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+})(window);
+/*!
+ * jQuery UI 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
+;jQuery.ui || (function($) {
+
+//Helper functions and ui object
+$.ui = {
+       version: "1.8",
+
+       // $.ui.plugin is deprecated.  Use the proxy pattern instead.
+       plugin: {
+               add: function(module, option, set) {
+                       var proto = $.ui[module].prototype;
+                       for(var i in set) {
+                               proto.plugins[i] = proto.plugins[i] || [];
+                               proto.plugins[i].push([option, set[i]]);
+                       }
+               },
+               call: function(instance, name, args) {
+                       var set = instance.plugins[name];
+                       if(!set || !instance.element[0].parentNode) { return; }
+
+                       for (var i = 0; i < set.length; i++) {
+                               if (instance.options[set[i][0]]) {
+                                       set[i][1].apply(instance.element, args);
+                               }
+                       }
+               }
+       },
+
+       contains: function(a, b) {
+               return document.compareDocumentPosition
+                       ? a.compareDocumentPosition(b) & 16
+                       : a !== b && a.contains(b);
+       },
+
+       hasScroll: function(el, a) {
+
+               //If overflow is hidden, the element might have extra content, but the user wants to hide it
+               if ($(el).css('overflow') == 'hidden') { return false; }
+
+               var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop',
+                       has = false;
+
+               if (el[scroll] > 0) { return true; }
+
+               // TODO: determine which cases actually cause this to happen
+               // if the element doesn't have the scroll set, see if it's possible to
+               // set the scroll
+               el[scroll] = 1;
+               has = (el[scroll] > 0);
+               el[scroll] = 0;
+               return has;
+       },
+
+       isOverAxis: function(x, reference, size) {
+               //Determines when x coordinate is over "b" element axis
+               return (x > reference) && (x < (reference + size));
+       },
+
+       isOver: function(y, x, top, left, height, width) {
+               //Determines when x, y coordinates is over "b" element
+               return $.ui.isOverAxis(y, top, height) && $.ui.isOverAxis(x, left, width);
+       },
+
+       keyCode: {
+               BACKSPACE: 8,
+               CAPS_LOCK: 20,
+               COMMA: 188,
+               CONTROL: 17,
+               DELETE: 46,
+               DOWN: 40,
+               END: 35,
+               ENTER: 13,
+               ESCAPE: 27,
+               HOME: 36,
+               INSERT: 45,
+               LEFT: 37,
+               NUMPAD_ADD: 107,
+               NUMPAD_DECIMAL: 110,
+               NUMPAD_DIVIDE: 111,
+               NUMPAD_ENTER: 108,
+               NUMPAD_MULTIPLY: 106,
+               NUMPAD_SUBTRACT: 109,
+               PAGE_DOWN: 34,
+               PAGE_UP: 33,
+               PERIOD: 190,
+               RIGHT: 39,
+               SHIFT: 16,
+               SPACE: 32,
+               TAB: 9,
+               UP: 38
+       }
+};
+
+//jQuery plugins
+$.fn.extend({
+       _focus: $.fn.focus,
+       focus: function(delay, fn) {
+               return typeof delay === 'number'
+                       ? this.each(function() {
+                               var elem = this;
+                               setTimeout(function() {
+                                       $(elem).focus();
+                                       (fn && fn.call(elem));
+                               }, delay);
+                       })
+                       : this._focus.apply(this, arguments);
+       },
+       
+       enableSelection: function() {
+               return this
+                       .attr('unselectable', 'off')
+                       .css('MozUserSelect', '')
+                       .unbind('selectstart.ui');
+       },
+
+       disableSelection: function() {
+               return this
+                       .attr('unselectable', 'on')
+                       .css('MozUserSelect', 'none')
+                       .bind('selectstart.ui', function() { return false; });
+       },
+
+       scrollParent: function() {
+               var scrollParent;
+               if(($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
+                       scrollParent = this.parents().filter(function() {
+                               return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+                       }).eq(0);
+               } else {
+                       scrollParent = this.parents().filter(function() {
+                               return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+                       }).eq(0);
+               }
+
+               return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
+       },
+
+       zIndex: function(zIndex) {
+               if (zIndex !== undefined) {
+                       return this.css('zIndex', zIndex);
+               }
+               
+               if (this.length) {
+                       var elem = $(this[0]), position, value;
+                       while (elem.length && elem[0] !== document) {
+                               // Ignore z-index if position is set to a value where z-index is ignored by the browser
+                               // This makes behavior of this function consistent across browsers
+                               // WebKit always returns auto if the element is positioned
+                               position = elem.css('position');
+                               if (position == 'absolute' || position == 'relative' || position == 'fixed')
+                               {
+                                       // IE returns 0 when zIndex is not specified
+                                       // other browsers return a string
+                                       // we ignore the case of nested elements with an explicit value of 0
+                                       // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
+                                       value = parseInt(elem.css('zIndex'));
+                                       if (!isNaN(value) && value != 0) {
+                                               return value;
+                                       }
+                               }
+                               elem = elem.parent();
+                       }
+               }
+
+               return 0;
+       }
+});
+
+
+//Additional selectors
+$.extend($.expr[':'], {
+       data: function(elem, i, match) {
+               return !!$.data(elem, match[3]);
+       },
+
+       focusable: function(element) {
+               var nodeName = element.nodeName.toLowerCase(),
+                       tabIndex = $.attr(element, 'tabindex');
+               return (/input|select|textarea|button|object/.test(nodeName)
+                       ? !element.disabled
+                       : 'a' == nodeName || 'area' == nodeName
+                               ? element.href || !isNaN(tabIndex)
+                               : !isNaN(tabIndex))
+                       // the element and all of its ancestors must be visible
+                       // the browser may report that the area is hidden
+                       && !$(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length;
+       },
+
+       tabbable: function(element) {
+               var tabIndex = $.attr(element, 'tabindex');
+               return (isNaN(tabIndex) || tabIndex >= 0) && $(element).is(':focusable');
+       }
+});
+
+})(jQuery);
+/*!
+ * jQuery UI Widget 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function( $ ) {
+
+var _remove = $.fn.remove;
+
+$.fn.remove = function( selector, keepData ) {
+       return this.each(function() {
+               if ( !keepData ) {
+                       if ( !selector || $.filter( selector, [ this ] ).length ) {
+                               $( "*", this ).add( this ).each(function() {
+                                       $( this ).triggerHandler( "remove" );
+                               });
+                       }
+               }
+               return _remove.call( $(this), selector, keepData );
+       });
+};
+
+$.widget = function( name, base, prototype ) {
+       var namespace = name.split( "." )[ 0 ],
+               fullName;
+       name = name.split( "." )[ 1 ];
+       fullName = namespace + "-" + name;
+
+       if ( !prototype ) {
+               prototype = base;
+               base = $.Widget;
+       }
+
+       // create selector for plugin
+       $.expr[ ":" ][ fullName ] = function( elem ) {
+               return !!$.data( elem, name );
+       };
+
+       $[ namespace ] = $[ namespace ] || {};
+       $[ namespace ][ name ] = function( options, element ) {
+               // allow instantiation without initializing for simple inheritance
+               if ( arguments.length ) {
+                       this._createWidget( options, element );
+               }
+       };
+
+       var basePrototype = new base();
+       // we need to make the options hash a property directly on the new instance
+       // otherwise we'll modify the options hash on the prototype that we're
+       // inheriting from
+//     $.each( basePrototype, function( key, val ) {
+//             if ( $.isPlainObject(val) ) {
+//                     basePrototype[ key ] = $.extend( {}, val );
+//             }
+//     });
+       basePrototype.options = $.extend( {}, basePrototype.options );
+       $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+               namespace: namespace,
+               widgetName: name,
+               widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+               widgetBaseClass: fullName
+       }, prototype );
+
+       $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+       $.fn[ name ] = function( options ) {
+               var isMethodCall = typeof options === "string",
+                       args = Array.prototype.slice.call( arguments, 1 ),
+                       returnValue = this;
+
+               // allow multiple hashes to be passed on init
+               options = !isMethodCall && args.length ?
+                       $.extend.apply( null, [ true, options ].concat(args) ) :
+                       options;
+
+               // prevent calls to internal methods
+               if ( isMethodCall && options.substring( 0, 1 ) === "_" ) {
+                       return returnValue;
+               }
+
+               if ( isMethodCall ) {
+                       this.each(function() {
+                               var instance = $.data( this, name ),
+                                       methodValue = instance && $.isFunction( instance[options] ) ?
+                                               instance[ options ].apply( instance, args ) :
+                                               instance;
+                               if ( methodValue !== instance && methodValue !== undefined ) {
+                                       returnValue = methodValue;
+                                       return false;
+                               }
+                       });
+               } else {
+                       this.each(function() {
+                               var instance = $.data( this, name );
+                               if ( instance ) {
+                                       if ( options ) {
+                                               instance.option( options );
+                                       }
+                                       instance._init();
+                               } else {
+                                       $.data( this, name, new object( options, this ) );
+                               }
+                       });
+               }
+
+               return returnValue;
+       };
+};
+
+$.Widget = function( options, element ) {
+       // allow instantiation without initializing for simple inheritance
+       if ( arguments.length ) {
+               this._createWidget( options, element );
+       }
+};
+
+$.Widget.prototype = {
+       widgetName: "widget",
+       widgetEventPrefix: "",
+       options: {
+               disabled: false
+       },
+       _createWidget: function( options, element ) {
+               // $.widget.bridge stores the plugin instance, but we do it anyway
+               // so that it's stored even before the _create function runs
+               this.element = $( element ).data( this.widgetName, this );
+               this.options = $.extend( true, {},
+                       this.options,
+                       $.metadata && $.metadata.get( element )[ this.widgetName ],
+                       options );
+
+               var self = this;
+               this.element.bind( "remove." + this.widgetName, function() {
+                       self.destroy();
+               });
+
+               this._create();
+               this._init();
+       },
+       _create: function() {},
+       _init: function() {},
+
+       destroy: function() {
+               this.element
+                       .unbind( "." + this.widgetName )
+                       .removeData( this.widgetName );
+               this.widget()
+                       .unbind( "." + this.widgetName )
+                       .removeAttr( "aria-disabled" )
+                       .removeClass(
+                               this.widgetBaseClass + "-disabled " +
+                               this.namespace + "-state-disabled" );
+       },
+
+       widget: function() {
+               return this.element;
+       },
+
+       option: function( key, value ) {
+               var options = key,
+                       self = this;
+
+               if ( arguments.length === 0 ) {
+                       // don't return a reference to the internal hash
+                       return $.extend( {}, self.options );
+               }
+
+               if  (typeof key === "string" ) {
+                       if ( value === undefined ) {
+                               return this.options[ key ];
+                       }
+                       options = {};
+                       options[ key ] = value;
+               }
+
+               $.each( options, function( key, value ) {
+                       self._setOption( key, value );
+               });
+
+               return self;
+       },
+       _setOption: function( key, value ) {
+               this.options[ key ] = value;
+
+               if ( key === "disabled" ) {
+                       this.widget()
+                               [ value ? "addClass" : "removeClass"](
+                                       this.widgetBaseClass + "-disabled" + " " +
+                                       this.namespace + "-state-disabled" )
+                               .attr( "aria-disabled", value );
+               }
+
+               return this;
+       },
+
+       enable: function() {
+               return this._setOption( "disabled", false );
+       },
+       disable: function() {
+               return this._setOption( "disabled", true );
+       },
+
+       _trigger: function( type, event, data ) {
+               var callback = this.options[ type ];
+
+               event = $.Event( event );
+               event.type = ( type === this.widgetEventPrefix ?
+                       type :
+                       this.widgetEventPrefix + type ).toLowerCase();
+               data = data || {};
+
+               // copy original event properties over to the new event
+               // this would happen if we could call $.event.fix instead of $.Event
+               // but we don't have a way to force an event to be fixed multiple times
+               if ( event.originalEvent ) {
+                       for ( var i = $.event.props.length, prop; i; ) {
+                               prop = $.event.props[ --i ];
+                               event[ prop ] = event.originalEvent[ prop ];
+                       }
+               }
+
+               this.element.trigger( event, data );
+
+               return !( $.isFunction(callback) &&
+                       callback.call( this.element[0], event, data ) === false ||
+                       event.isDefaultPrevented() );
+       }
+};
+
+})( jQuery );
+/*!
+ * jQuery UI Mouse 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ *     jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.mouse", {
+       options: {
+               cancel: ':input,option',
+               distance: 1,
+               delay: 0
+       },
+       _mouseInit: function() {
+               var self = this;
+
+               this.element
+                       .bind('mousedown.'+this.widgetName, function(event) {
+                               return self._mouseDown(event);
+                       })
+                       .bind('click.'+this.widgetName, function(event) {
+                               if(self._preventClickEvent) {
+                                       self._preventClickEvent = false;
+                                       event.stopImmediatePropagation();
+                                       return false;
+                               }
+                       });
+
+               this.started = false;
+       },
+
+       // TODO: make sure destroying one instance of mouse doesn't mess with
+       // other instances of mouse
+       _mouseDestroy: function() {
+               this.element.unbind('.'+this.widgetName);
+       },
+
+       _mouseDown: function(event) {
+               // don't let more than one widget handle mouseStart
+               // TODO: figure out why we have to use originalEvent
+               event.originalEvent = event.originalEvent || {};
+               if (event.originalEvent.mouseHandled) { return; }
+
+               // we may have missed mouseup (out of window)
+               (this._mouseStarted && this._mouseUp(event));
+
+               this._mouseDownEvent = event;
+
+               var self = this,
+                       btnIsLeft = (event.which == 1),
+                       elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
+               if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
+                       return true;
+               }
+
+               this.mouseDelayMet = !this.options.delay;
+               if (!this.mouseDelayMet) {
+                       this._mouseDelayTimer = setTimeout(function() {
+                               self.mouseDelayMet = true;
+                       }, this.options.delay);
+               }
+
+               if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+                       this._mouseStarted = (this._mouseStart(event) !== false);
+                       if (!this._mouseStarted) {
+                               event.preventDefault();
+                               return true;
+                       }
+               }
+
+               // these delegates are required to keep context
+               this._mouseMoveDelegate = function(event) {
+                       return self._mouseMove(event);
+               };
+               this._mouseUpDelegate = function(event) {
+                       return self._mouseUp(event);
+               };
+               $(document)
+                       .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+                       .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+               // preventDefault() is used to prevent the selection of text here -
+               // however, in Safari, this causes select boxes not to be selectable
+               // anymore, so this fix is needed
+               ($.browser.safari || event.preventDefault());
+
+               event.originalEvent.mouseHandled = true;
+               return true;
+       },
+
+       _mouseMove: function(event) {
+               // IE mouseup check - mouseup happened when mouse was out of window
+               if ($.browser.msie && !event.button) {
+                       return this._mouseUp(event);
+               }
+
+               if (this._mouseStarted) {
+                       this._mouseDrag(event);
+                       return event.preventDefault();
+               }
+
+               if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+                       this._mouseStarted =
+                               (this._mouseStart(this._mouseDownEvent, event) !== false);
+                       (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
+               }
+
+               return !this._mouseStarted;
+       },
+
+       _mouseUp: function(event) {
+               $(document)
+                       .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+                       .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+               if (this._mouseStarted) {
+                       this._mouseStarted = false;
+                       this._preventClickEvent = (event.target == this._mouseDownEvent.target);
+                       this._mouseStop(event);
+               }
+
+               return false;
+       },
+
+       _mouseDistanceMet: function(event) {
+               return (Math.max(
+                               Math.abs(this._mouseDownEvent.pageX - event.pageX),
+                               Math.abs(this._mouseDownEvent.pageY - event.pageY)
+                       ) >= this.options.distance
+               );
+       },
+
+       _mouseDelayMet: function(event) {
+               return this.mouseDelayMet;
+       },
+
+       // These are placeholder methods, to be overriden by extending plugin
+       _mouseStart: function(event) {},
+       _mouseDrag: function(event) {},
+       _mouseStop: function(event) {},
+       _mouseCapture: function(event) { return true; }
+});
+
+})(jQuery);
+/*
+ * jQuery UI Position 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function( $ ) {
+
+$.ui = $.ui || {};
+
+var horizontalPositions = /left|center|right/,
+       horizontalDefault = "center",
+       verticalPositions = /top|center|bottom/,
+       verticalDefault = "center",
+       _position = $.fn.position,
+       _offset = $.fn.offset;
+
+$.fn.position = function( options ) {
+       if ( !options || !options.of ) {
+               return _position.apply( this, arguments );
+       }
+
+       // make a copy, we don't want to modify arguments
+       options = $.extend( {}, options );
+
+       var target = $( options.of ),
+               collision = ( options.collision || "flip" ).split( " " ),
+               offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
+               targetWidth,
+               targetHeight,
+               basePosition;
+
+       if ( options.of.nodeType === 9 ) {
+               targetWidth = target.width();
+               targetHeight = target.height();
+               basePosition = { top: 0, left: 0 };
+       } else if ( options.of.scrollTo && options.of.document ) {
+               targetWidth = target.width();
+               targetHeight = target.height();
+               basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
+       } else if ( options.of.preventDefault ) {
+               // force left top to allow flipping
+               options.at = "left top";
+               targetWidth = targetHeight = 0;
+               basePosition = { top: options.of.pageY, left: options.of.pageX };
+       } else {
+               targetWidth = target.outerWidth();
+               targetHeight = target.outerHeight();
+               basePosition = target.offset();
+       }
+
+       // force my and at to have valid horizontal and veritcal positions
+       // if a value is missing or invalid, it will be converted to center 
+       $.each( [ "my", "at" ], function() {
+               var pos = ( options[this] || "" ).split( " " );
+               if ( pos.length === 1) {
+                       pos = horizontalPositions.test( pos[0] ) ?
+                               pos.concat( [verticalDefault] ) :
+                               verticalPositions.test( pos[0] ) ?
+                                       [ horizontalDefault ].concat( pos ) :
+                                       [ horizontalDefault, verticalDefault ];
+               }
+               pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : horizontalDefault;
+               pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : verticalDefault;
+               options[ this ] = pos;
+       });
+
+       // normalize collision option
+       if ( collision.length === 1 ) {
+               collision[ 1 ] = collision[ 0 ];
+       }
+
+       // normalize offset option
+       offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
+       if ( offset.length === 1 ) {
+               offset[ 1 ] = offset[ 0 ];
+       }
+       offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
+
+       if ( options.at[0] === "right" ) {
+               basePosition.left += targetWidth;
+       } else if (options.at[0] === horizontalDefault ) {
+               basePosition.left += targetWidth / 2;
+       }
+
+       if ( options.at[1] === "bottom" ) {
+               basePosition.top += targetHeight;
+       } else if ( options.at[1] === verticalDefault ) {
+               basePosition.top += targetHeight / 2;
+       }
+
+       basePosition.left += offset[ 0 ];
+       basePosition.top += offset[ 1 ];
+
+       return this.each(function() {
+               var elem = $( this ),
+                       elemWidth = elem.outerWidth(),
+                       elemHeight = elem.outerHeight(),
+                       position = $.extend( {}, basePosition );
+
+               if ( options.my[0] === "right" ) {
+                       position.left -= elemWidth;
+               } else if ( options.my[0] === horizontalDefault ) {
+                       position.left -= elemWidth / 2;
+               }
+
+               if ( options.my[1] === "bottom" ) {
+                       position.top -= elemHeight;
+               } else if ( options.my[1] === verticalDefault ) {
+                       position.top -= elemHeight / 2;
+               }
+
+               $.each( [ "left", "top" ], function( i, dir ) {
+                       if ( $.ui.position[ collision[i] ] ) {
+                               $.ui.position[ collision[i] ][ dir ]( position, {
+                                       targetWidth: targetWidth,
+                                       targetHeight: targetHeight,
+                                       elemWidth: elemWidth,
+                                       elemHeight: elemHeight,
+                                       offset: offset,
+                                       my: options.my,
+                                       at: options.at
+                               });
+                       }
+               });
+
+               if ( $.fn.bgiframe ) {
+                       elem.bgiframe();
+               }
+               elem.offset( $.extend( position, { using: options.using } ) );
+       });
+};
+
+$.ui.position = {
+       fit: {
+               left: function( position, data ) {
+                       var win = $( window ),
+                               over = position.left + data.elemWidth - win.width() - win.scrollLeft();
+                       position.left = over > 0 ? position.left - over : Math.max( 0, position.left );
+               },
+               top: function( position, data ) {
+                       var win = $( window ),
+                               over = position.top + data.elemHeight - win.height() - win.scrollTop();
+                       position.top = over > 0 ? position.top - over : Math.max( 0, position.top );
+               }
+       },
+
+       flip: {
+               left: function( position, data ) {
+                       if ( data.at[0] === "center" ) {
+                               return;
+                       }
+                       var win = $( window ),
+                               over = position.left + data.elemWidth - win.width() - win.scrollLeft(),
+                               myOffset = data.my[ 0 ] === "left" ?
+                                       -data.elemWidth :
+                                       data.my[ 0 ] === "right" ?
+                                               data.elemWidth :
+                                               0,
+                               offset = -2 * data.offset[ 0 ];
+                       position.left += position.left < 0 ?
+                               myOffset + data.targetWidth + offset :
+                               over > 0 ?
+                                       myOffset - data.targetWidth + offset :
+                                       0;
+               },
+               top: function( position, data ) {
+                       if ( data.at[1] === "center" ) {
+                               return;
+                       }
+                       var win = $( window ),
+                               over = position.top + data.elemHeight - win.height() - win.scrollTop(),
+                               myOffset = data.my[ 1 ] === "top" ?
+                                       -data.elemHeight :
+                                       data.my[ 1 ] === "bottom" ?
+                                               data.elemHeight :
+                                               0,
+                               atOffset = data.at[ 1 ] === "top" ?
+                                       data.targetHeight :
+                                       -data.targetHeight,
+                               offset = -2 * data.offset[ 1 ];
+                       position.top += position.top < 0 ?
+                               myOffset + data.targetHeight + offset :
+                               over > 0 ?
+                                       myOffset + atOffset + offset :
+                                       0;
+               }
+       }
+};
+
+// offset setter from jQuery 1.4
+if ( !$.offset.setOffset ) {
+       $.offset.setOffset = function( elem, options ) {
+               // set position first, in-case top/left are set even on static elem
+               if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
+                       elem.style.position = "relative";
+               }
+               var curElem   = $( elem ),
+                       curOffset = curElem.offset(),
+                       curTop    = parseInt( $.curCSS( elem, "top",  true ), 10 ) || 0,
+                       curLeft   = parseInt( $.curCSS( elem, "left", true ), 10)  || 0,
+                       props     = {
+                               top:  (options.top  - curOffset.top)  + curTop,
+                               left: (options.left - curOffset.left) + curLeft
+                       };
+               
+               if ( 'using' in options ) {
+                       options.using.call( elem, props );
+               } else {
+                       curElem.css( props );
+               }
+       };
+
+       $.fn.offset = function( options ) {
+               var elem = this[ 0 ];
+               if ( !elem || !elem.ownerDocument ) { return null; }
+               if ( options ) { 
+                       return this.each(function() {
+                               $.offset.setOffset( this, options );
+                       });
+               }
+               return _offset.call( this );
+       };
+}
+
+}( jQuery ));
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2007-2009 University of Toronto
+Copyright 2007-2009 University of California, Berkeley
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery, YAHOO, opera*/
+
+var fluid_1_3 = fluid_1_3 || {};
+var fluid = fluid || fluid_1_3;
+
+(function ($, fluid) {
+    
+    fluid.version = "Infusion 1.3";
+    
+    fluid.environment = {
+        fluid: fluid
+    };
+    var globalObject = window || {};
+    
+    /**
+     * Causes an error message to be logged to the console and a real runtime error to be thrown.
+     * 
+     * @param {String|Error} message the error message to log
+     */
+    fluid.fail = function (message) {
+        fluid.setLogging(true);
+        fluid.log(message.message? message.message : message);
+        throw new Error(message);
+        //message.fail(); // Intentionally cause a browser error by invoking a nonexistent function.
+    };
+    
+    // Logging
+    var logging;
+    /** method to allow user to enable logging (off by default) */
+    fluid.setLogging = function (enabled) {
+        if (typeof enabled === "boolean") {
+            logging = enabled;
+        } else {
+            logging = false;
+        }
+    };
+
+    /** Log a message to a suitable environmental console. If the standard "console" 
+     * stream is available, the message will be sent there - otherwise either the
+     * YAHOO logger or the Opera "postError" stream will be used. Logging must first
+     * be enabled with a call fo the fluid.setLogging(true) function.
+     */
+    fluid.log = function (str) {
+        if (logging) {
+            str = fluid.renderTimestamp(new Date()) + ":  " + str;
+            if (typeof(console) !== "undefined") {
+                if (console.debug) {
+                    console.debug(str);
+                } else {
+                    console.log(str);
+                }
+            }
+            else if (typeof(YAHOO) !== "undefined") {
+                YAHOO.log(str);
+            }
+            else if (typeof(opera) !== "undefined") {
+                opera.postError(str);
+            }
+        }
+    };
+    
+    /**
+     * Wraps an object in a jQuery if it isn't already one. This function is useful since
+     * it ensures to wrap a null or otherwise falsy argument to itself, rather than the
+     * often unhelpful jQuery default of returning the overall document node.
+     * 
+     * @param {Object} obj the object to wrap in a jQuery
+     */
+    fluid.wrap = function (obj) {
+        return ((!obj || obj.jquery) ? obj : $(obj)); 
+    };
+    
+    /**
+     * If obj is a jQuery, this function will return the first DOM element within it.
+     * 
+     * @param {jQuery} obj the jQuery instance to unwrap into a pure DOM element
+     */
+    fluid.unwrap = function (obj) {
+        return obj && obj.jquery && obj.length === 1 ? obj[0] : obj; // Unwrap the element if it's a jQuery.
+    };
+    
+    // Functional programming utilities.
+            
+    /** Return an empty container as the same type as the argument (either an
+     * array or hash */
+    fluid.freshContainer = function(tocopy) {
+        return fluid.isArrayable(tocopy)? [] : {};   
+    };
+    
+    /** Performs a deep copy (clone) of its argument **/
+    
+    fluid.copy = function (tocopy) {
+        if (fluid.isPrimitive(tocopy)) {
+            return tocopy;
+        }
+        return $.extend(true, fluid.freshContainer(tocopy), tocopy);
+    };
+    
+    /** A basic utility that returns its argument unchanged */
+    
+    fluid.identity = function(arg) {
+        return arg;
+    };
+    
+    // Framework and instantiation functions.
+
+    
+    /** Returns true if the argument is a primitive type **/
+    fluid.isPrimitive = function (value) {
+        var valueType = typeof(value);
+        return !value || valueType === "string" || valueType === "boolean" || valueType === "number" || valueType === "function";
+    };
+    
+    /** Determines whether the supplied object can be treated as an array, by 
+     * iterating an index towards its length. The test functions by detecting
+     * a property named "length" which is of type "number", but excluding objects
+     * which are themselves of primitive types (in particular functions and strings)
+     */
+    fluid.isArrayable = function(totest) {
+        return totest && !fluid.isPrimitive(totest) && typeof(totest.length) === "number";
+    };
+    
+            
+    /** Corrected version of jQuery makearray that returns an empty array on undefined rather than crashing **/
+    fluid.makeArray = function(arg) {
+        if (arg === null || arg === undefined) {
+            return [];
+        }
+        else {
+            return $.makeArray(arg);
+        }
+    };
+    
+    function transformInternal(source, togo, key, args) {
+        var transit = source[key];
+        for (var j = 0; j < args.length - 1; ++ j) {
+            transit = args[j + 1](transit, key);
+        }
+        togo[key] = transit; 
+    }
+    
+    /** Return a list or hash of objects, transformed by one or more functions. Similar to
+     * jQuery.map, only will accept an arbitrary list of transformation functions and also
+     * works on non-arrays.
+     * @param source {Array or Object} The initial container of objects to be transformed.
+     * @param fn1, fn2, etc. {Function} An arbitrary number of optional further arguments,
+     * all of type Function, accepting the signature (object, index), where object is the
+     * list member to be transformed, and index is its list index. Each function will be
+     * applied in turn to each list member, which will be replaced by the return value
+     * from the function.
+     * @return The finally transformed list, where each member has been replaced by the
+     * original member acted on by the function or functions.
+     */
+    fluid.transform = function (source) {
+        var togo = fluid.freshContainer(source);
+        if (fluid.isArrayable(source)) {
+            for (var i = 0; i < source.length; ++ i) {
+                transformInternal(source, togo, i, arguments);
+            }
+        }
+        else {
+            for (var key in source) {
+                transformInternal(source, togo, key, arguments);
+            }
+        }  
+        return togo;
+    };
+    
+    /** Better jQuery.each which works on hashes as well as having the arguments
+     * the right way round. 
+     * @param source {Arrayable or Object} The container to be iterated over
+     * @param func {Function} A function accepting (value, key) for each iterated
+     * object. This function may return a value to terminate the iteration
+     */
+    fluid.each = function (source, func) {
+        if (fluid.isArrayable(source)) {
+            for (var i = 0; i < source.length; ++ i) {
+                func(source[i], i);
+            }
+        }
+        else {
+            for (var key in source) {
+                func(source[key], key);
+            }
+        }
+    };
+    
+    /** Scan through a list or hash of objects, terminating on the first member which
+     * matches a predicate function.
+     * @param source {Arrayable or Object} The list or hash of objects to be searched.
+     * @param func {Function} A predicate function, acting on a member. A predicate which
+     * returns any value which is not <code>null</code> or <code>undefined</code> will terminate
+     * the search. The function accepts (object, index).
+     * @param deflt {Object} A value to be returned in the case no predicate function matches
+     * a list member. The default will be the natural value of <code>undefined</code>
+     * @return The first return value from the predicate function which is not <code>null</code>
+     * or <code>undefined</code>
+     */
+    fluid.find = function (source, func, deflt) {
+        if (fluid.isArrayable(source)) {
+            for (var i = 0; i < source.length; ++ i) {
+                var disp = func(source[i], i);
+                if (disp !== undefined) { return disp;}
+            }
+        }
+        else {
+            for (var key in source) {
+                var disp = func(source[key], key);
+                if (disp !== undefined) { return disp;}
+            }
+        }
+        return deflt;
+    };
+    
+    /** Scan through a list of objects, "accumulating" a value over them 
+     * (may be a straightforward "sum" or some other chained computation).
+     * @param list {Array} The list of objects to be accumulated over.
+     * @param fn {Function} An "accumulation function" accepting the signature (object, total, index) where
+     * object is the list member, total is the "running total" object (which is the return value from the previous function),
+     * and index is the index number.
+     * @param arg {Object} The initial value for the "running total" object.
+     * @return {Object} the final running total object as returned from the final invocation of the function on the last list member.
+     */
+    fluid.accumulate = function (list, fn, arg) {
+        for (var i = 0; i < list.length; ++ i) {
+            arg = fn(list[i], arg, i);
+        }
+        return arg;
+    };
+    
+    /** Can through a list of objects, removing those which match a predicate. Similar to
+     * jQuery.grep, only acts on the list in-place by removal, rather than by creating
+     * a new list by inclusion.
+     * @param source {Array|Object} The list of objects to be scanned over.
+     * @param fn {Function} A predicate function determining whether an element should be
+     * removed. This accepts the standard signature (object, index) and returns a "truthy"
+     * result in order to determine that the supplied object should be removed from the list.
+     * @return The list, transformed by the operation of removing the matched elements. The
+     * supplied list is modified by this operation.
+     */
+    fluid.remove_if = function (source, fn) {
+        if (fluid.isArrayable(source)) {
+            for (var i = 0; i < source.length; ++i) {
+                if (fn(source[i], i)) {
+                    source.splice(i, 1);
+                    --i;
+                }
+            }
+        }
+        else {
+            for (var key in source) {
+                if (fn(source[key], key)) {
+                    delete source[key];
+                }
+            }
+        }
+        return source;
+    };
+    
+    
+    /** 
+     * Searches through the supplied object for the first value which matches the one supplied.
+     * @param obj {Object} the Object to be searched through
+     * @param value {Object} the value to be found. This will be compared against the object's
+     * member using === equality.
+     * @return {String} The first key whose value matches the one supplied, or <code>null</code> if no
+     * such key is found.
+     */
+    fluid.keyForValue = function (obj, value) {
+        return fluid.find(obj, function(thisValue, key) {
+            if (value === thisValue) {
+                return key;
+            }
+        });
+    };
+    
+    /**
+     * This method is now deprecated and will be removed in a future release of Infusion. 
+     * See fluid.keyForValue instead.
+     */
+    fluid.findKeyInObject = fluid.keyForValue;
+    
+    /** 
+     * Clears an object or array of its contents. For objects, each property is deleted.
+     * 
+     * @param {Object|Array} target the target to be cleared
+     */
+    fluid.clear = function (target) {
+        if (target instanceof Array) {
+            target.length = 0;
+        }
+        else {
+            for (var i in target) {
+                delete target[i];
+            }
+        }
+    };
+        
+    // Model functions
+    fluid.model = {}; // cannot call registerNamespace yet since it depends on fluid.model
+       
+    /** Another special "marker object" representing that a distinguished 
+     * (probably context-dependent) value should be substituted.
+     */
+    fluid.VALUE = {type: "fluid.marker", value: "VALUE"};
+    
+    /** Another special "marker object" representing that no value is present (where
+     * signalling using the value "undefined" is not possible) */
+    fluid.NO_VALUE = {type: "fluid.marker", value: "NO_VALUE"};
+    
+    /** Determine whether an object is any marker, or a particular marker - omit the
+     * 2nd argument to detect any marker
+     */
+    fluid.isMarker = function(totest, type) {
+        if (!totest || typeof (totest) !== 'object' || totest.type !== "fluid.marker") return false;
+        if (!type) return true;
+        return totest.value === type || totest.value === type.value;
+    };
+   
+    /** Copy a source "model" onto a target **/
+    fluid.model.copyModel = function (target, source) {
+        fluid.clear(target);
+        $.extend(true, target, source);
+    };
+    
+    /** Parse an EL expression separated by periods (.) into its component segments.
+     * @param {String} EL The EL expression to be split
+     * @return {Array of String} the component path expressions.
+     * TODO: This needs to be upgraded to handle (the same) escaping rules (as RSF), so that
+     * path segments containing periods and backslashes etc. can be processed.
+     */
+    fluid.model.parseEL = function (EL) {
+        return String(EL).split('.');
+    };
+    
+    /** Compose an EL expression from two separate EL expressions. The returned 
+     * expression will be the one that will navigate the first expression, and then
+     * the second, from the value reached by the first. Either prefix or suffix may be
+     * the empty string **/
+    
+    fluid.model.composePath = function (prefix, suffix) {
+        return prefix === ""? suffix : (suffix === ""? prefix : prefix + "." + suffix);
+    };
+    
+    /** Compose any number of path segments, none of which may be empty **/
+    fluid.model.composeSegments = function () {
+        return $.makeArray(arguments).join(".");
+    };
+
+    /** Standard strategies for resolving path segments **/
+    fluid.model.environmentStrategy = function(initEnvironment) {
+        return {
+            init: function() {
+                var environment = initEnvironment;
+                return function(root, segment, index) {
+                    if (environment && environment[segment]) {
+                        var togo = environment[segment];
+                    }
+                    environment = null;
+                    return togo; 
+                };
+            }
+        };
+    };
+
+    fluid.model.defaultCreatorStrategy = function(root, segment) {
+        if (root[segment] === undefined) {
+            return root[segment] = {};
+            }
+        };
+    
+    fluid.model.defaultFetchStrategy = function(root, segment) {
+        return root[segment];
+        };
+        
+    fluid.model.funcResolverStrategy = function(root, segment) {
+        if (root.resolvePathSegment) {
+            return root.resolvePathSegment(segment);
+        }
+    };
+    
+    fluid.model.makeResolver = function(root, EL, config) {
+        var that = {
+            segs: fluid.model.parseEL(EL),
+            root: root,
+            index: 0,
+            strategies: fluid.transform(config, function(figel) {
+                return figel.init? figel.init() : figel;
+            })
+        };
+        that.next = function() {
+            if (!that.root) {return;}
+            var accepted;
+            for (var i = 0; i < that.strategies.length; ++ i) {
+                var value = that.strategies[i](that.root, that.segs[that.index], that.index);
+                if (accepted === undefined) {
+                    accepted = value;
+                }
+            }
+            if (accepted === fluid.NO_VALUE) {
+                accepted = undefined;
+            }
+            that.root = accepted;
+            ++that.index;
+        };
+        that.step = function(limit) {
+            for (var i = 0; i < limit; ++ i) {
+                that.next();
+            }
+            that.last = that.segs[that.index];
+        };
+        return that;
+    }    
+
+    fluid.model.defaultSetConfig = [fluid.model.funcResolverStrategy, fluid.model.defaultFetchStrategy, fluid.model.defaultCreatorStrategy];
+    
+    fluid.model.getPenultimate = function(root, EL, config) {
+        config = config || fluid.model.defaultGetConfig;
+        var resolver = fluid.model.makeResolver(root, EL, config);
+        resolver.step(resolver.segs.length - 1);
+        return resolver;
+    }
+    
+    fluid.set = function(root, EL, newValue, config) {
+        config = config || fluid.model.defaultSetConfig;
+        var resolver = fluid.model.getPenultimate(root, EL, config);
+        resolver.root[resolver.last] = newValue;
+    };
+    
+    fluid.model.defaultGetConfig = [fluid.model.funcResolverStrategy, fluid.model.defaultFetchStrategy];
+    
+    /** Evaluates an EL expression by fetching a dot-separated list of members
+     * recursively from a provided root.
+     * @param root The root data structure in which the EL expression is to be evaluated
+     * @param {string} EL The EL expression to be evaluated
+     * @param environment An optional "environment" which, if it contains any members
+     * at top level, will take priority over the root data structure.
+     * @return The fetched data value.
+     */
+    
+    fluid.get = function (root, EL, config) {
+        if (EL === "" || EL === null || EL === undefined) {
+            return root;
+        }
+        config = config || fluid.model.defaultGetConfig;
+        var resolver = fluid.model.makeResolver(root, EL, config);
+        resolver.step(resolver.segs.length);
+        return resolver.root;
+    };
+
+    // This backward compatibility will be maintained for a number of releases, probably until Fluid 2.0
+    fluid.model.setBeanValue = fluid.set;
+    fluid.model.getBeanValue = fluid.get;
+    
+    fluid.getGlobalValue = function(path, env) {
+        if (path) {
+            env = env || fluid.environment;
+            var envFetcher = fluid.model.environmentStrategy(env);
+            return fluid.get(globalObject, path, [envFetcher].concat(fluid.model.defaultGetConfig));
+        }
+    };
+    
+    /**
+     * Allows for the calling of a function from an EL expression "functionPath", with the arguments "args", scoped to an framework version "environment".
+     * @param {Object} functionPath - An EL expression
+     * @param {Object} args - An array of arguments to be applied to the function, specified in functionPath
+     * @param {Object} environment - (optional) The object to scope the functionPath to  (typically the framework root for version control)
+     */
+    fluid.invokeGlobalFunction = function (functionPath, args, environment) {
+        var func = fluid.getGlobalValue(functionPath, environment);
+        if (!func) {
+            fluid.fail("Error invoking global function: " + functionPath + " could not be located");
+        } else {
+            return func.apply(null, args);
+        }
+    };
+    
+    /** Registers a new global function at a given path (currently assumes that
+     * it lies within the fluid namespace)
+     */
+    
+    fluid.registerGlobalFunction = function (functionPath, func, env) {
+        env = env || fluid.environment;
+        var envFetcher = fluid.model.environmentStrategy(env);
+        fluid.set(globalObject, functionPath, func, [envFetcher].concat(fluid.model.defaultSetConfig));
+    };
+    
+    fluid.setGlobalValue = fluid.registerGlobalFunction;
+    
+    /** Ensures that an entry in the global namespace exists **/
+    fluid.registerNamespace = function (naimspace, env) {
+        env = env || fluid.environment;
+        var existing = fluid.getGlobalValue(naimspace, env);
+        if (!existing) {
+            existing = {};
+            fluid.setGlobalValue(naimspace, existing, env);
+        }
+        return existing;
+    };
+    
+    fluid.registerNamespace("fluid.event");
+    
+    fluid.event.addListenerToFirer = function(firer, value, namespace) {
+        if (typeof(value) === "function") {
+            firer.addListener(value, namespace);
+        }
+        else if (value && typeof(value) === "object") {
+            firer.addListener(value.listener, namespace, value.predicate, value.priority);
+        }
+    };
+    /**
+     * Attaches the user's listeners to a set of events.
+     * 
+     * @param {Object} events a collection of named event firers
+     * @param {Object} listeners optional listeners to add
+     */
+    fluid.mergeListeners = function (events, listeners) {
+        fluid.each(listeners, function(value, key) {
+            var keydot = key.indexOf(".");
+            var namespace;
+            if (keydot !== -1) {
+                namespace = key.substring(keydot + 1);
+                key = key.substring(0, keydot);
+            }
+            if (!events[key]) {
+                events[key] = fluid.event.getEventFirer();
+            }
+            var firer = events[key];
+            if (fluid.isArrayable(value)) {
+                for (var i = 0; i < value.length; ++ i) {
+                    fluid.event.addListenerToFirer(firer, value[i], namespace); 
+                }
+            }
+            else {
+                fluid.event.addListenerToFirer(firer, value, namespace);
+            } 
+        });
+    };
+    
+    /**
+     * Sets up a component's declared events.
+     * Events are specified in the options object by name. There are three different types of events that can be
+     * specified: 
+     * 1. an ordinary multicast event, specified by "null". 
+     * 2. a unicast event, which allows only one listener to be registered
+     * 3. a preventable event
+     * 
+     * @param {Object} that the component
+     * @param {Object} options the component's options structure, containing the declared event names and types
+     */
+    fluid.instantiateFirers = function (that, options) {
+        that.events = {};
+        if (options.events) {
+            for (var event in options.events) {
+                var eventType = options.events[event];
+                that.events[event] = fluid.event.getEventFirer(eventType === "unicast", eventType === "preventable");
+            }
+        }
+        fluid.mergeListeners(that.events, options.listeners);
+    };
+    
+        
+    // stubs for two functions in FluidDebugging.js
+    fluid.dumpEl = fluid.identity;
+    fluid.renderTimestamp = fluid.identity;
+    
+    /**
+     * Retreives and stores a component's default settings centrally.
+     * @param {boolean} (options) if true, manipulate a global option (for the head
+     *   component) rather than instance options.
+     * @param {String} componentName the name of the component
+     * @param {Object} (optional) an container of key/value pairs to set
+     * 
+     */
+    var defaultsStore = {};
+    var globalDefaultsStore = {};
+    fluid.defaults = function () {
+        var offset = 0;
+        var store = defaultsStore;
+        if (typeof arguments[0] === "boolean") {
+            store = globalDefaultsStore;
+            offset = 1;
+        }
+        var componentName = arguments[offset];
+        var defaultsObject = arguments[offset + 1];
+        if (defaultsObject !== undefined) {
+            store[componentName] = defaultsObject;   
+            return defaultsObject;
+        }
+        
+        return store[componentName];
+    };
+    
+                
+    fluid.mergePolicyIs = function(policy, test) {
+        return typeof(policy) === "string" && policy.indexOf(test) !== -1;
+    };
+    
+    function mergeImpl(policy, basePath, target, source, thisPolicy) {
+        if (typeof(thisPolicy) === "function") {
+            thisPolicy.apply(null, target, source);
+            return target;
+        }
+        if (fluid.mergePolicyIs(thisPolicy, "replace")) {
+            fluid.clear(target);
+        }
+      
+        for (var name in source) {
+            var path = (basePath? basePath + ".": "") + name;
+            var newPolicy = policy && typeof(policy) !== "string"? policy[path] : policy;
+            var thisTarget = target[name];
+            var thisSource = source[name];
+            var primitiveTarget = fluid.isPrimitive(thisTarget);
+    
+            if (thisSource !== undefined) {
+                if (thisSource !== null && typeof thisSource === 'object' &&
+                      !thisSource.nodeType && !thisSource.jquery && thisSource !== fluid.VALUE 
+                       && !fluid.mergePolicyIs(newPolicy, "preserve")) {
+                    if (primitiveTarget) {
+                        target[name] = thisTarget = thisSource instanceof Array? [] : {};
+                    }
+                    mergeImpl(policy, path, thisTarget, thisSource, newPolicy);
+                }
+                else {
+                    if (typeof(newPolicy) === "function") {
+                        newPolicy.call(null, target, source, name);
+                    }
+                    else if (thisTarget === null || thisTarget === undefined || !fluid.mergePolicyIs(newPolicy, "reverse")) {
+                        // TODO: When "grades" are implemented, grandfather in any paired applier to perform these operations
+                        // NB: mergePolicy of "preserve" now creates dependency on DataBinding.js
+                        target[name] = fluid.mergePolicyIs(newPolicy, "preserve")? fluid.model.mergeModel(thisTarget, thisSource) : thisSource;
+                    }
+                }
+            }
+        }
+        return target;
+    }
+    
+    /** Merge a collection of options structures onto a target, following an optional policy.
+     * This function is typically called automatically, as a result of an invocation of
+     * <code>fluid.iniView</code>. The behaviour of this function is explained more fully on
+     * the page http://wiki.fluidproject.org/display/fluid/Options+Merging+for+Fluid+Components .
+     * @param policy {Object/String} A "policy object" specifiying the type of merge to be performed.
+     * If policy is of type {String} it should take on the value "reverse" or "replace" representing
+     * a static policy. If it is an
+     * Object, it should contain a mapping of EL paths onto these String values, representing a
+     * fine-grained policy. If it is an Object, the values may also themselves be EL paths 
+     * representing that a default value is to be taken from that path.
+     * @param target {Object} The options structure which is to be modified by receiving the merge results.
+     * @param options1, options2, .... {Object} an arbitrary list of options structure which are to
+     * be merged "on top of" the <code>target</code>. These will not be modified.    
+     */
+    
+    fluid.merge = function (policy, target) {
+        var path = "";
+        
+        for (var i = 2; i < arguments.length; ++i) {
+            var source = arguments[i];
+            if (source !== null && source !== undefined) {
+                mergeImpl(policy, path, target, source, policy ? policy[""] : null);
+            }
+        }
+        if (policy && typeof(policy) !== "string") {
+            for (var key in policy) {
+                var elrh = policy[key];
+                if (typeof(elrh) === 'string' && elrh !== "replace") {
+                    var oldValue = fluid.get(target, key);
+                    if (oldValue === null || oldValue === undefined) {
+                        var value = fluid.get(target, elrh);
+                        fluid.set(target, key, value);
+                    }
+                }
+            }
+        }
+        return target;     
+    };
+
+    /**
+     * Merges the component's declared defaults, as obtained from fluid.defaults(),
+     * with the user's specified overrides.
+     * 
+     * @param {Object} that the instance to attach the options to
+     * @param {String} componentName the unique "name" of the component, which will be used
+     * to fetch the default options from store. By recommendation, this should be the global
+     * name of the component's creator function.
+     * @param {Object} userOptions the user-specified configuration options for this component
+     */
+    fluid.mergeComponentOptions = function (that, componentName, userOptions) {
+        var defaults = fluid.defaults(componentName); 
+        if (fluid.expandOptions) {
+            defaults = fluid.expandOptions(fluid.copy(defaults), that);
+        }
+        that.options = fluid.merge(defaults? defaults.mergePolicy: null, {}, defaults, userOptions);    
+    };
+    
+        
+    /** A special "marker object" which is recognised as one of the arguments to 
+     * fluid.initSubcomponents. This object is recognised by reference equality - 
+     * where it is found, it is replaced in the actual argument position supplied
+     * to the specific subcomponent instance, with the particular options block
+     * for that instance attached to the overall "that" object.
+     */
+    fluid.COMPONENT_OPTIONS = {type: "fluid.marker", value: "COMPONENT_OPTIONS"};
+    
+    /** Construct a dummy or "placeholder" subcomponent, that optionally provides empty
+     * implementations for a set of methods.
+     */
+    fluid.emptySubcomponent = function (options) {
+        var that = {};
+        options = $.makeArray(options);
+        var empty = function () {};
+        for (var i = 0; i < options.length; ++ i) {
+            that[options[i]] = empty;
+        }
+        return that;
+    };
+    
+    /** Compute a "nickname" given a fully qualified typename, by returning the last path
+     * segment.
+     */
+    
+    fluid.computeNickName = function(typeName) {
+        var segs = fluid.model.parseEL(typeName);
+        return segs[segs.length - 1];
+    };
+    
+    /**
+     * Creates a new "little component": a that-ist object with options merged into it by the framework.
+     * This method is a convenience for creating small objects that have options but don't require full
+     * View-like features such as the DOM Binder or events
+     * 
+     * @param {Object} name the name of the little component to create
+     * @param {Object} options user-supplied options to merge with the defaults
+     */
+    fluid.initLittleComponent = function(name, options) {
+        var that = {typeName: name, id: fluid.allocateGuid()};
+        // TODO: nickName must be available earlier than other merged options so that component may resolve to itself
+        that.nickName = options && options.nickName? options.nickName: fluid.computeNickName(that.typeName);
+        fluid.mergeComponentOptions(that, name, options);
+        return that;
+    };
+
+
+    // The Model Events system.
+    
+    var fluid_guid = 1;
+    
+    /** Allocate an integer value that will be unique for this session **/
+    
+    fluid.allocateGuid = function() {
+        return fluid_guid++;
+    };
+    
+    fluid.event.identifyListener = function(listener) {
+        if (!listener.$$guid) {
+            listener.$$guid = fluid.allocateGuid();
+        }
+        return listener.$$guid;
+    };
+    
+    fluid.event.mapPriority = function(priority) {
+        return (priority === null || priority === undefined? 0 : 
+           (priority === "last" ? -Number.MAX_VALUE :
+              (priority === "first" ? Number.MAX_VALUE : priority)));
+    };
+    
+    fluid.event.listenerComparator = function(recA, recB) {
+        return recB.priority - recA.priority;
+    };
+    
+    fluid.event.sortListeners = function(listeners) {
+        var togo = [];
+        fluid.each(listeners, function(listener) {
+            togo.push(listener);
+        });
+        return togo.sort(fluid.event.listenerComparator);
+    };
+    /** Construct an "event firer" object which can be used to register and deregister 
+     * listeners, to which "events" can be fired. These events consist of an arbitrary
+     * function signature. General documentation on the Fluid events system is at
+     * http://wiki.fluidproject.org/display/fluid/The+Fluid+Event+System .
+     * @param {Boolean} unicast If <code>true</code>, this is a "unicast" event which may only accept
+     * a single listener.
+     * @param {Boolean} preventable If <code>true</code> the return value of each handler will 
+     * be checked for <code>false</code> in which case further listeners will be shortcircuited, and this
+     * will be the return value of fire()
+     */
+    
+    fluid.event.getEventFirer = function (unicast, preventable) {
+        var listeners = {};
+        var sortedListeners = [];
+        
+        function fireToListeners(listeners, args, wrapper) {
+            for (var i in listeners) {
+                var lisrec = listeners[i];
+                var listener = lisrec.listener;
+                if (lisrec.predicate && !lisrec.predicate(listener, args)) {
+                    continue;
+                }
+                try {
+                    var ret = (wrapper? wrapper(listener) : listener).apply(null, args);
+                    if (preventable && ret === false) {
+                        return false;
+                    }
+                }
+                catch (e) {
+                    fluid.log("FireEvent received exception " + e.message + " e " + e + " firing to listener " + i);
+                    throw (e);       
+                }
+            }
+        }
+        
+        return {
+           addListener: function (listener, namespace, predicate, priority) {
+                if (!listener) {
+                    return;
+                }
+                if (unicast) {
+                    namespace = "unicast";
+                }
+                if (!namespace) {
+                    namespace = fluid.event.identifyListener(listener);
+                }
+
+                listeners[namespace] = {listener: listener, predicate: predicate, priority: 
+                    fluid.event.mapPriority(priority)};
+                sortedListeners = fluid.event.sortListeners(listeners);
+            },
+
+            removeListener: function (listener) {
+                if (typeof(listener) === 'string') {
+                    delete listeners[listener];
+                }
+                else if (listener.$$guid) {
+                    delete listeners[listener.$$guid];
+                }
+                sortedListeners = fluid.event.sortListeners(listeners);
+            },
+            // NB - this method exists currently solely for the convenience of the new,
+            // transactional changeApplier. As it exists it is hard to imagine the function
+            // being helpful to any other client. We need to get more experience on the kinds
+            // of listeners that are useful, and ultimately factor this method away.
+            fireToListeners: function (listeners, args, wrapper) {
+                return fireToListeners(listeners, args, wrapper);
+            },
+            fire: function () {
+                return fireToListeners(sortedListeners, arguments);
+            }
+        };
+    };
+
+  // **** VIEW-DEPENDENT DEFINITIONS BELOW HERE
+
+    /**
+     * Fetches a single container element and returns it as a jQuery.
+     * 
+     * @param {String||jQuery||element} containerSpec an id string, a single-element jQuery, or a DOM element specifying a unique container
+     * @param {Boolean} fallible <code>true</code> if an empty container is to be reported as a valid condition
+     * @return a single-element jQuery of container
+     */
+    fluid.container = function (containerSpec, fallible) {
+        var container = fluid.wrap(containerSpec);
+        if (fallible && !container || container.length === 0) {
+            return null;
+        }
+        
+        // Throw an exception if we've got more or less than one element.
+        if (!container || !container.jquery || container.length !== 1) {
+            if (typeof(containerSpec) !== "string") {
+                containerSpec = container.selector;
+            }
+            var count = container.length !== undefined? container.length : 0;
+            fluid.fail({
+                name: "NotOne",
+                message: count > 1? "More than one (" + count + ") container elements were "
+                : "No container element was found for selector " + containerSpec
+            });
+        }
+        
+        return container;
+    };
+    
+    /**
+     * Creates a new DOM Binder instance, used to locate elements in the DOM by name.
+     * 
+     * @param {Object} container the root element in which to locate named elements
+     * @param {Object} selectors a collection of named jQuery selectors
+     */
+    fluid.createDomBinder = function (container, selectors) {
+        var cache = {}, that = {};
+        
+        function cacheKey(name, thisContainer) {
+            return fluid.allocateSimpleId(thisContainer) + "-" + name;
+        }
+
+        function record(name, thisContainer, result) {
+            cache[cacheKey(name, thisContainer)] = result;
+        }
+
+        that.locate = function (name, localContainer) {
+            var selector, thisContainer, togo;
+            
+            selector = selectors[name];
+            thisContainer = localContainer? localContainer: container;
+            if (!thisContainer) {
+                fluid.fail("DOM binder invoked for selector " + name + " without container");
+            }
+
+            if (!selector) {
+                return thisContainer;
+            }
+
+            if (typeof(selector) === "function") {
+                togo = $(selector.call(null, fluid.unwrap(thisContainer)));
+            } else {
+                togo = $(selector, thisContainer);
+            }
+            if (togo.get(0) === document) {
+                togo = [];
+                //fluid.fail("Selector " + name + " with value " + selectors[name] +
+                //            " did not find any elements with container " + fluid.dumpEl(container));
+            }
+            if (!togo.selector) {
+                togo.selector = selector;
+                togo.context = thisContainer;
+            }
+            togo.selectorName = name;
+            record(name, thisContainer, togo);
+            return togo;
+        };
+        that.fastLocate = function (name, localContainer) {
+            var thisContainer = localContainer? localContainer: container;
+            var key = cacheKey(name, thisContainer);
+            var togo = cache[key];
+            return togo? togo : that.locate(name, localContainer);
+        };
+        that.clear = function () {
+            cache = {};
+        };
+        that.refresh = function (names, localContainer) {
+            var thisContainer = localContainer? localContainer: container;
+            if (typeof names === "string") {
+                names = [names];
+            }
+            if (thisContainer.length === undefined) {
+                thisContainer = [thisContainer];
+            }
+            for (var i = 0; i < names.length; ++ i) {
+                for (var j = 0; j < thisContainer.length; ++ j) {
+                    that.locate(names[i], thisContainer[j]);
+                }
+            }
+        };
+        that.resolvePathSegment = that.locate;
+        
+        return that;
+    };
+    
+    /** Expect that an output from the DOM binder has resulted in a non-empty set of 
+     * results. If none are found, this function will fail with a diagnostic message, 
+     * with the supplied message prepended.
+     */
+    fluid.expectFilledSelector = function (result, message) {
+        if (result && result.length === 0 && result.jquery) {
+            fluid.fail(message + ": selector \"" + result.selector + "\" with name " + result.selectorName +
+                       " returned no results in context " + fluid.dumpEl(result.context));
+        }
+    };
+    
+    /** 
+     * The central initialiation method called as the first act of every Fluid
+     * component. This function automatically merges user options with defaults,
+     * attaches a DOM Binder to the instance, and configures events.
+     * 
+     * @param {String} componentName The unique "name" of the component, which will be used
+     * to fetch the default options from store. By recommendation, this should be the global
+     * name of the component's creator function.
+     * @param {jQueryable} container A specifier for the single root "container node" in the
+     * DOM which will house all the markup for this component.
+     * @param {Object} userOptions The configuration options for this component.
+     */
+    fluid.initView = function (componentName, container, userOptions) {
+        fluid.expectFilledSelector(container, "Error instantiating component with name \"" + componentName);
+        container = fluid.container(container, true);
+        if (!container) {
+            return null;
+        }
+        var that = fluid.initLittleComponent(componentName, userOptions); 
+        that.container = container;
+        fluid.initDomBinder(that);
+        
+        fluid.instantiateFirers(that, that.options);
+
+        return that;
+    };
+
+    
+    fluid.initSubcomponent = function (that, className, args) {
+        return fluid.initSubcomponents(that, className, args)[0];
+    };
+    
+    fluid.initSubcomponentImpl = function(that, entry, args) {
+        var togo;
+        if (typeof(entry) !== "function") {
+            var entryType = typeof(entry) === "string"? entry : entry.type;
+            var globDef = fluid.defaults(true, entryType);
+            fluid.merge("reverse", that.options, globDef);
+            togo = entryType === "fluid.emptySubcomponent"?
+               fluid.emptySubcomponent(entry.options) : 
+               fluid.invokeGlobalFunction(entryType, args);
+        }
+        else {
+            togo = entry.apply(null, args);
+        }
+
+        var returnedOptions = togo? togo.returnedOptions : null;
+        if (returnedOptions) {
+            fluid.merge(that.options.mergePolicy, that.options, returnedOptions);
+            if (returnedOptions.listeners) {
+                fluid.mergeListeners(that.events, returnedOptions.listeners);
+            }
+        }
+        return togo;
+    };
+    
+    /** Initialise all the "subcomponents" which are configured to be attached to 
+     * the supplied top-level component, which share a particular "class name".
+     * @param {Component} that The top-level component for which sub-components are
+     * to be instantiated. It contains specifications for these subcomponents in its
+     * <code>options</code> structure.
+     * @param {String} className The "class name" or "category" for the subcomponents to
+     * be instantiated. A class name specifies an overall "function" for a class of 
+     * subcomponents and represents a category which accept the same signature of
+     * instantiation arguments.
+     * @param {Array of Object} args The instantiation arguments to be passed to each 
+     * constructed subcomponent. These will typically be members derived from the
+     * top-level <code>that</code> or perhaps globally discovered from elsewhere. One
+     * of these arguments may be <code>fluid.COMPONENT_OPTIONS</code> in which case this
+     * placeholder argument will be replaced by instance-specific options configured
+     * into the member of the top-level <code>options</code> structure named for the
+     * <code>className</code>
+     * @return {Array of Object} The instantiated subcomponents, one for each member
+     * of <code>that.options[className]</code>.
+     */
+    
+    fluid.initSubcomponents = function (that, className, args) {
+        var entry = that.options[className];
+        if (!entry) {
+            return;
+        }
+        var entries = $.makeArray(entry);
+        var optindex = -1;
+        var togo = [];
+        args = $.makeArray(args);
+        for (var i = 0; i < args.length; ++ i) {
+            if (args[i] === fluid.COMPONENT_OPTIONS) {
+                optindex = i;
+            }
+        }
+        for (i = 0; i < entries.length; ++ i) {
+            entry = entries[i];
+            if (optindex !== -1) {
+                args[optindex] = entry.options;
+            }
+            togo[i] = fluid.initSubcomponentImpl(that, entry, args);
+        }
+        return togo;
+    };
+    
+    /**
+     * Creates a new DOM Binder instance for the specified component and mixes it in.
+     * 
+     * @param {Object} that the component instance to attach the new DOM Binder to
+     */
+    fluid.initDomBinder = function (that) {
+        that.dom = fluid.createDomBinder(that.container, that.options.selectors);
+        that.locate = that.dom.locate;      
+    };
+
+
+
+    // DOM Utilities.
+    
+    /**
+     * Finds the nearest ancestor of the element that passes the test
+     * @param {Element} element DOM element
+     * @param {Function} test A function which takes an element as a parameter and return true or false for some test
+     */
+    fluid.findAncestor = function (element, test) {
+        element = fluid.unwrap(element);
+        while (element) {
+            if (test(element)) {
+                return element;
+            }
+            element = element.parentNode;
+        }
+    };
+    
+    /**
+     * Returns a jQuery object given the id of a DOM node. In the case the element
+     * is not found, will return an empty list.
+     */
+    fluid.jById = function (id, dokkument) {
+        dokkument = dokkument && dokkument.nodeType === 9? dokkument : document;
+        var element = fluid.byId(id, dokkument);
+        var togo = element? $(element) : [];
+        togo.selector = "#" + id;
+        togo.context = dokkument;
+        return togo;
+    };
+    
+    /**
+     * Returns an DOM element quickly, given an id
+     * 
+     * @param {Object} id the id of the DOM node to find
+     * @param {Document} dokkument the document in which it is to be found (if left empty, use the current document)
+     * @return The DOM element with this id, or null, if none exists in the document.
+     */
+    fluid.byId = function (id, dokkument) {
+        dokkument = dokkument && dokkument.nodeType === 9? dokkument : document;
+        var el = dokkument.getElementById(id);
+        if (el) {
+            if (el.getAttribute("id") !== id) {
+                fluid.fail("Problem in document structure - picked up element " +
+                    fluid.dumpEl(el) + " for id " + id +
+                    " without this id - most likely the element has a name which conflicts with this id");
+            }
+            return el;
+        }
+        else {
+            return null;
+        }
+    };
+    
+    /**
+     * Returns the id attribute from a jQuery or pure DOM element.
+     * 
+     * @param {jQuery||Element} element the element to return the id attribute for
+     */
+    fluid.getId = function (element) {
+        return fluid.unwrap(element).getAttribute("id");
+    };
+    
+    /** 
+     * Allocate an id to the supplied element if it has none already, by a simple
+     * scheme resulting in ids "fluid-id-nnnn" where nnnn is an increasing integer.
+     */
+    
+    fluid.allocateSimpleId = function (element) {
+        element = fluid.unwrap(element);
+        if (!element.id) {
+            element.id = "fluid-id-" + fluid.allocateGuid(); 
+        }
+        return element.id;
+    };
+    
+
+    // Message resolution and templating
+    
+    /**
+     * Simple string template system. 
+     * Takes a template string containing tokens in the form of "%value".
+     * Returns a new string with the tokens replaced by the specified values.
+     * Keys and values can be of any data type that can be coerced into a string. Arrays will work here as well.
+     * 
+     * @param {String}    template    a string (can be HTML) that contains tokens embedded into it
+     * @param {object}    values        a collection of token keys and values
+     */
+    fluid.stringTemplate = function (template, values) {
+        var newString = template;
+        for (var key in values) {
+            var searchStr = "%" + key;
+            newString = newString.replace(searchStr, values[key]);
+        }
+        return newString;
+    };
+    
+
+    fluid.messageResolver = function (options) {
+        var that = fluid.initLittleComponent("fluid.messageResolver", options);
+        that.messageBase = that.options.parseFunc(that.options.messageBase);
+        
+        that.lookup = function(messagecodes) {
+            var resolved = fluid.messageResolver.resolveOne(that.messageBase, messagecodes);
+            if (resolved === undefined) {
+                return fluid.find(that.options.parents, function(parent) {
+                    return parent.lookup(messagecodes);
+                });
+            }
+            else {
+                return {template: resolved, resolveFunc: that.options.resolveFunc};
+            }
+        };
+        that.resolve = function(messagecodes, args) {
+            if (!messagecodes) {
+                return "[No messagecodes provided]";
+            }
+            messagecodes = fluid.makeArray(messagecodes);
+            var looked = that.lookup(messagecodes);
+            return looked? looked.resolveFunc(looked.template, args)
+                :"[Message string for key " + messagecodes[0] + " not found]" 
+        };
+        
+        return that;  
+    };
+    
+    fluid.defaults("fluid.messageResolver", {
+        mergePolicy: {
+            messageBase: "preserve"  
+        },
+        resolveFunc: fluid.stringTemplate,
+        parseFunc: fluid.identity,
+        messageBase: {},
+        parents: []
+    });
+    
+    fluid.messageResolver.resolveOne = function(messageBase, messagecodes) {
+        for (var i = 0; i < messagecodes.length; ++ i) {
+            var code = messagecodes[i];
+            var message = messageBase[code];
+            if (message !== undefined) {
+                return message;
+            }
+        }
+    };
+          
+    /** Converts a data structure consisting of a mapping of keys to message strings,
+     * into a "messageLocator" function which maps an array of message codes, to be 
+     * tried in sequence until a key is found, and an array of substitution arguments,
+     * into a substituted message string.
+     */
+    fluid.messageLocator = function (messageBase, resolveFunc) {
+        var resolver = fluid.messageResolver({messageBase: messageBase, resolveFunc: resolveFunc});
+        return function(messagecodes, args) {
+            return resolver.resolve(messagecodes, args);
+        };
+    };
+
+})(jQuery, fluid_1_3);
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2007-2009 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/** This file contains functions which depend on the presence of a DOM document
+ * but which do not depend on the contents of Fluid.js **/
+
+// Declare dependencies.
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    // Private constants.
+    var NAMESPACE_KEY = "fluid-scoped-data";
+
+    /**
+     * Gets stored state from the jQuery instance's data map.
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.getScopedData = function(target, key) {
+        var data = $(target).data(NAMESPACE_KEY);
+        return data ? data[key] : undefined;
+    };
+
+    /**
+     * Stores state in the jQuery instance's data map. Unlike jQuery's version,
+     * accepts multiple-element jQueries.
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.setScopedData = function(target, key, value) {
+        $(target).each(function() {
+            var data = $.data(this, NAMESPACE_KEY) || {};
+            data[key] = value;
+
+            $.data(this, NAMESPACE_KEY, data);
+        });
+    };
+
+    /** Global focus manager - makes use of "focusin" event supported in jquery 1.4.2 or later.
+     */
+
+    var lastFocusedElement = null;
+    
+    $(document).bind("focusin", function(event){
+        lastFocusedElement = event.target;
+    });
+    
+    fluid.getLastFocusedElement = function () {
+        return lastFocusedElement;
+    }
+
+
+    var ENABLEMENT_KEY = "enablement";
+
+    /** Queries or sets the enabled status of a control. An activatable node
+     * may be "disabled" in which case its keyboard bindings will be inoperable
+     * (but still stored) until it is reenabled again.
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+     
+    fluid.enabled = function(target, state) {
+        target = $(target);
+        if (state === undefined) {
+            return fluid.getScopedData(target, ENABLEMENT_KEY) !== false;
+        }
+        else {
+            $("*", target).add(target).each(function() {
+                if (fluid.getScopedData(this, ENABLEMENT_KEY) !== undefined) {
+                    fluid.setScopedData(this, ENABLEMENT_KEY, state);
+                }
+                else if (/select|textarea|input/i.test(this.nodeName)) {
+                    $(this).attr("disabled", !state);
+                }
+            });
+            fluid.setScopedData(target, ENABLEMENT_KEY, state);
+        }
+    };
+    
+    fluid.initEnablement = function(target) {
+        fluid.setScopedData(target, ENABLEMENT_KEY, true);
+    }
+    
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2009 University of Toronto
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery */
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.dom = fluid.dom || {};
+    
+    // Node walker function for iterateDom.
+    var getNextNode = function (iterator) {
+        if (iterator.node.firstChild) {
+            iterator.node = iterator.node.firstChild;
+            iterator.depth += 1;
+            return iterator;
+        }
+        while (iterator.node) {
+            if (iterator.node.nextSibling) {
+                iterator.node = iterator.node.nextSibling;
+                return iterator;
+            }
+            iterator.node = iterator.node.parentNode;
+            iterator.depth -= 1;
+        }
+        return iterator;
+    };
+    
+    /**
+     * Walks the DOM, applying the specified acceptor function to each element.
+     * There is a special case for the acceptor, allowing for quick deletion of elements and their children.
+     * Return "delete" from your acceptor function if you want to delete the element in question.
+     * Return "stop" to terminate iteration.
+     * 
+     * @param {Element} node the node to start walking from
+     * @param {Function} acceptor the function to invoke with each DOM element
+     * @param {Boolean} allnodes Use <code>true</code> to call acceptor on all nodes, 
+     * rather than just element nodes (type 1)
+     */
+    fluid.dom.iterateDom = function (node, acceptor, allNodes) {
+        var currentNode = {node: node, depth: 0};
+        var prevNode = node;
+        var condition;
+        while (currentNode.node !== null && currentNode.depth >= 0 && currentNode.depth < fluid.dom.iterateDom.DOM_BAIL_DEPTH) {
+            condition = null;
+            if (currentNode.node.nodeType === 1 || allNodes) {
+                condition = acceptor(currentNode.node, currentNode.depth);
+            }
+            if (condition) {
+                if (condition === "delete") {
+                    currentNode.node.parentNode.removeChild(currentNode.node);
+                    currentNode.node = prevNode;
+                }
+                else if (condition === "stop") {
+                    return currentNode.node;
+                }
+            }
+            prevNode = currentNode.node;
+            currentNode = getNextNode(currentNode);
+        }
+    };
+    
+    // Work around IE circular DOM issue. This is the default max DOM depth on IE.
+    // http://msdn2.microsoft.com/en-us/library/ms761392(VS.85).aspx
+    fluid.dom.iterateDom.DOM_BAIL_DEPTH = 256;
+    
+    /**
+     * Checks if the sepcified container is actually the parent of containee.
+     * 
+     * @param {Element} container the potential parent
+     * @param {Element} containee the child in question
+     */
+    fluid.dom.isContainer = function (container, containee) {
+        for (; containee; containee = containee.parentNode) {
+            if (container === containee) {
+                return true;
+            }
+        }
+        return false;
+    };
+       
+    /** Return the element text from the supplied DOM node as a single String */
+    fluid.dom.getElementText = function(element) {
+        var nodes = element.childNodes;
+        var text = "";
+        for (var i = 0; i < nodes.length; ++ i) {
+          var child = nodes[i];
+          if (child.nodeType == 3) {
+            text = text + child.nodeValue;
+            }
+          }
+        return text; 
+    };
+    
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2009 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+      
+  var unUnicode = /(\\u[\dabcdef]{4}|\\x[\dabcdef]{2})/g;
+  
+  fluid.unescapeProperties = function (string) {
+    string = string.replace(unUnicode, function(match) {
+      var code = match.substring(2);
+      var parsed = parseInt(code, 16);
+      return String.fromCharCode(parsed);
+      }
+    );
+    var pos = 0;
+    while (true) {
+        var backpos = string.indexOf("\\", pos);
+        if (backpos === -1) {
+            break;
+        }
+        if (backpos === string.length - 1) {
+          return [string.substring(0, string.length - 1), true];
+        }
+        var replace = string.charAt(backpos + 1);
+        if (replace === "n") replace = "\n";
+        if (replace === "r") replace = "\r";
+        if (replace === "t") replace = "\t";
+        string = string.substring(0, backpos) + replace + string.substring(backpos + 2);
+        pos = backpos + 1;
+    }
+    return [string, false];
+  };
+  
+  var breakPos = /[^\\][\s:=]/;
+  
+  fluid.parseJavaProperties = function(text) {
+    // File format described at http://java.sun.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)
+    var togo = {};
+    text = text.replace(/\r\n/g, "\n");
+    text = text.replace(/\r/g, "\n");
+    lines = text.split("\n");
+    var contin, key, valueComp, valueRaw, valueEsc;
+    for (var i = 0; i < lines.length; ++ i) {
+      var line = $.trim(lines[i]);
+      if (!line || line.charAt(0) === "#" || line.charAt(0) === '!') {
+          continue;
+      }
+      if (!contin) {
+        valueComp = "";
+        var breakpos = line.search(breakPos);
+        if (breakpos === -1) {
+          key = line;
+          valueRaw = "";
+          }
+        else {
+          key = $.trim(line.substring(0, breakpos + 1)); // +1 since first char is escape exclusion
+          valueRaw = $.trim(line.substring(breakpos + 2));
+          if (valueRaw.charAt(0) === ":" || valueRaw.charAt(0) === "=") {
+            valueRaw = $.trim(valueRaw.substring(1));
+          }
+        }
+      
+        key = fluid.unescapeProperties(key)[0];
+        valueEsc = fluid.unescapeProperties(valueRaw);
+      }
+      else {
+        valueEsc = fluid.unescapeProperties(line);
+      }
+
+      contin = valueEsc[1];
+      if (!valueEsc[1]) { // this line was not a continuation line - store the value
+        togo[key] = valueComp + valueEsc[0];
+      }
+      else {
+        valueComp += valueEsc[0];
+      }
+    }
+    return togo;
+  };
+      
+    /** 
+     * Expand a message string with respect to a set of arguments, following a basic
+     * subset of the Java MessageFormat rules. 
+     * http://java.sun.com/j2se/1.4.2/docs/api/java/text/MessageFormat.html
+     * 
+     * The message string is expected to contain replacement specifications such
+     * as {0}, {1}, {2}, etc.
+     * @param messageString {String} The message key to be expanded
+     * @param args {String/Array of String} An array of arguments to be substituted into the message.
+     * @return The expanded message string. 
+     */
+    fluid.formatMessage = function (messageString, args) {
+        if (!args) {
+            return messageString;
+        } 
+        if (typeof(args) === "string") {
+            args = [args];
+        }
+        for (var i = 0; i < args.length; ++ i) {
+            messageString = messageString.replace("{" + i + "}", args[i]);
+        }
+        return messageString;
+    };
+      
+})(jQuery, fluid_1_3);
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2007-2009 University of Toronto
+Copyright 2007-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery, YAHOO, opera*/
+
+var fluid_1_3 = fluid_1_3 || {};
+var fluid = fluid || fluid_1_3;
+
+(function ($, fluid) {
+       
+    fluid.renderTimestamp = function (date) {
+        var zeropad = function (num, width) {
+             if (!width) width = 2;
+             var numstr = (num == undefined? "" : num.toString());
+             return "00000".substring(5 - width + numstr.length) + numstr;
+             }
+        return zeropad(date.getHours()) + ":" + zeropad(date.getMinutes()) + ":" + zeropad(date.getSeconds()) + "." + zeropad(date.getMilliseconds(), 3);
+    };
+    
+    function generate(c, count) {
+        var togo = "";
+        for (var i = 0; i < count; ++ i) {
+            togo += c;
+        }
+        return togo;
+    }
+    
+    function printImpl(obj, small, options) {
+        var big = small + options.indentChars;
+        if (obj === null) {
+            return "null";
+        }
+        else if (fluid.isPrimitive(obj)) {
+            return JSON.stringify(obj);
+        }
+        else {
+            var j = [];
+            if (fluid.isArrayable(obj)) {
+                if (obj.length === 0) {
+                    return "[]";
+                }
+                for (var i = 0; i < obj.length; ++ i) {
+                    j[i] = printImpl(obj[i], big, options);
+                }
+                return "[\n" + big + j.join(",\n" + big) + "\n" + small + "]";
+                }
+            else {
+                var i = 0;
+                fluid.each(obj, function(value, key) {
+                    j[i++] = JSON.stringify(key) + ": " + printImpl(value, big, options);
+                });
+                return "{\n" + big + j.join(",\n" + big) + "\n" + small + "}"; 
+            }
+        }
+    }
+    
+    fluid.prettyPrintJSON = function(obj, options) {
+        options = $.extend({indent: 4}, options);
+        options.indentChars = generate(" ", options.indent);
+        return printImpl(obj, "", options);
+    }
+        
+    /** 
+     * Dumps a DOM element into a readily recognisable form for debugging - produces a
+     * "semi-selector" summarising its tag name, class and id, whichever are set.
+     * 
+     * @param {jQueryable} element The element to be dumped
+     * @return A string representing the element.
+     */
+    fluid.dumpEl = function (element) {
+        var togo;
+        
+        if (!element) {
+            return "null";
+        }
+        if (element.nodeType === 3 || element.nodeType === 8) {
+            return "[data: " + element.data + "]";
+        } 
+        if (element.nodeType === 9) {
+            return "[document: location " + element.location + "]";
+        }
+        if (!element.nodeType && fluid.isArrayable(element)) {
+            togo = "[";
+            for (var i = 0; i < element.length; ++ i) {
+                togo += fluid.dumpEl(element[i]);
+                if (i < element.length - 1) {
+                    togo += ", ";
+                }
+            }
+            return togo + "]";
+        }
+        element = $(element);
+        togo = element.get(0).tagName;
+        if (element.attr("id")) {
+            togo += "#" + element.attr("id");
+        }
+        if (element.attr("class")) {
+            togo += "." + element.attr("class");
+        }
+        return togo;
+    };
+        
+})(jQuery, fluid_1_3);
+    /*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2009 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.BINDING_ROOT_KEY = "fluid-binding-root";
+    
+    /** Recursively find any data stored under a given name from a node upwards
+     * in its DOM hierarchy **/
+     
+    fluid.findData = function(elem, name) {
+        while (elem) {
+            var data = $.data(elem, name);
+            if (data) {return data;}
+            elem = elem.parentNode;
+            }
+        };
+  
+    fluid.bindFossils = function(node, data, fossils) {
+        $.data(node, fluid.BINDING_ROOT_KEY, {data: data, fossils: fossils});
+        };
+        
+    fluid.boundPathForNode = function(node, fossils) {
+        node = fluid.unwrap(node);
+        var key = node.name || node.id;
+        var record = fossils[key];
+        return record? record.EL: null;
+    };
+  
+    fluid.findForm = function (node) {
+      return fluid.findAncestor(node, 
+          function(element) {return element.nodeName.toLowerCase() === "form";});
+    };
+    
+    /** A generalisation of jQuery.val to correctly handle the case of acquiring and
+     * setting the value of clustered radio button/checkbox sets, potentially, given
+     * a node corresponding to just one element.
+     */
+    fluid.value = function (nodeIn, newValue) {
+        var node = fluid.unwrap(nodeIn);
+        var multiple = false;
+        if (node.nodeType === undefined && node.length > 1) {
+            node = node[0];
+            multiple = true;
+        }
+        if ("input" !== node.nodeName.toLowerCase()
+           || ! /radio|checkbox/.test(node.type)) {return $(node).val(newValue);}
+        var name = node.name;
+        if (name === undefined) {
+            fluid.fail("Cannot acquire value from node " + fluid.dumpEl(node) + " which does not have name attribute set");
+        }
+        var elements;
+        if (multiple) {
+            elements = nodeIn;
+        }
+        else {
+            elements = document.getElementsByName(name);
+            var scope = fluid.findForm(node);
+            elements = $.grep(elements, 
+              function(element) {
+                if (element.name !== name) {return false;}
+                return !scope || fluid.dom.isContainer(scope, element);
+              });
+        }
+        if (newValue !== undefined) {
+            if (typeof(newValue) === "boolean") {
+                newValue = (newValue? "true" : "false");
+            }
+          // jQuery gets this partially right, but when dealing with radio button array will
+          // set all of their values to "newValue" rather than setting the checked property
+          // of the corresponding control. 
+            $.each(elements, function() {
+               this.checked = (newValue instanceof Array? 
+                 $.inArray(this.value, newValue) !== -1 : newValue === this.value);
+            });
+        }
+        else { // this part jQuery will not do - extracting value from <input> array
+            var checked = $.map(elements, function(element) {
+                return element.checked? element.value : null;
+            });
+            return node.type === "radio"? checked[0] : checked;
+            }
+       };
+    
+    /** "Automatically" apply to whatever part of the data model is
+     * relevant, the changed value received at the given DOM node*/
+    fluid.applyChange = function(node, newValue, applier) {
+        node = fluid.unwrap(node);
+        if (newValue === undefined) {
+            newValue = fluid.value(node);
+        }
+        if (node.nodeType === undefined && node.length > 0) {node = node[0];} // assume here that they share name and parent
+        var root = fluid.findData(node, fluid.BINDING_ROOT_KEY);
+        if (!root) {
+            fluid.fail("Bound data could not be discovered in any node above " + fluid.dumpEl(node));
+        }
+        var name = node.name;
+        var fossil = root.fossils[name];
+        if (!fossil) {
+            fluid.fail("No fossil discovered for name " + name + " in fossil record above " + fluid.dumpEl(node));
+        }
+        if (typeof(fossil.oldvalue) === "boolean") { // deal with the case of an "isolated checkbox"
+            newValue = newValue[0]? true: false;
+        }
+        var EL = root.fossils[name].EL;
+        if (applier) {
+            applier.fireChangeRequest({path: EL, value: newValue, source: node.id});
+        }
+        else {
+            fluid.set(root.data, EL, newValue);
+        }    
+        };
+   
+    fluid.pathUtil = {};
+   
+    var getPathSegmentImpl = function(accept, path, i) {
+        var segment = null; // TODO: rewrite this with regexes and replaces
+        if (accept) {
+            segment = "";
+        }
+        var escaped = false;
+        var limit = path.length;
+        for (; i < limit; ++i) {
+            var c = path.charAt(i);
+            if (!escaped) {
+                if (c === '.') {
+                    break;
+                    }
+                else if (c === '\\') {
+                    escaped = true;
+                    }
+                else if (segment !== null) {
+                    segment += c;
+                }
+            }
+            else {
+                escaped = false;
+                if (segment !== null) {
+                    accept += c;
+                }
+            }
+        }
+        if (segment !== null) {
+            accept[0] = segment;
+        }
+        return i;
+        };
+    
+    var globalAccept = []; // TODO: serious reentrancy risk here, why is this impl like this?
+    
+    fluid.pathUtil.getPathSegment = function(path, i) {
+        getPathSegmentImpl(globalAccept, path, i);
+        return globalAccept[0];
+        }; 
+  
+    fluid.pathUtil.getHeadPath = function(path) {
+        return fluid.pathUtil.getPathSegment(path, 0);
+        };
+  
+    fluid.pathUtil.getFromHeadPath = function(path) {
+        var firstdot = getPathSegmentImpl(null, path, 0);
+        return firstdot === path.length ? null
+            : path.substring(firstdot + 1);
+        };
+    
+    function lastDotIndex(path) {
+        // TODO: proper escaping rules
+        return path.lastIndexOf(".");
+        }
+    
+    fluid.pathUtil.getToTailPath = function(path) {
+        var lastdot = lastDotIndex(path);
+        return lastdot == -1 ? null : path.substring(0, lastdot);
+        };
+
+  /** Returns the very last path component of a bean path */
+    fluid.pathUtil.getTailPath = function(path) {
+        var lastdot = lastDotIndex(path);
+        return fluid.pathUtil.getPathSegment(path, lastdot + 1);
+        };
+    
+    var composeSegment = function(prefix, toappend) {
+        for (var i = 0; i < toappend.length; ++i) {
+            var c = toappend.charAt(i);
+            if (c === '.' || c === '\\' || c === '}') {
+                prefix += '\\';
+            }
+            prefix += c;
+        }
+        return prefix;
+    };
+    
+    /**
+     * Compose a prefix and suffix EL path, where the prefix is already escaped.
+     * Prefix may be empty, but not null. The suffix will become escaped.
+     */
+    fluid.pathUtil.composePath = function(prefix, suffix) {
+        if (prefix.length !== 0) {
+            prefix += '.';
+        }
+        return composeSegment(prefix, suffix);
+        };    
+   
+    fluid.pathUtil.matchPath = function(spec, path) {
+        var togo = "";
+        while (true) {
+          if (!spec || path === "") {break;}
+          if (!path) {return null;}
+          var spechead = fluid.pathUtil.getHeadPath(spec);
+          var pathhead = fluid.pathUtil.getHeadPath(path);
+          // if we fail to match on a specific component, fail.
+          if (spechead !== "*" && spechead !== pathhead) {
+              return null;
+          }
+          togo = fluid.pathUtil.composePath(togo, pathhead);
+          spec = fluid.pathUtil.getFromHeadPath(spec);
+          path = fluid.pathUtil.getFromHeadPath(path);
+        }
+        return togo;
+      };
+    
+    fluid.model.mergeModel = function(target, source, applier) {
+        var copySource = fluid.copy(source);
+        applier = applier || fluid.makeChangeApplier(source);
+        applier.fireChangeRequest({type: "ADD", path: "", value: target});
+        applier.fireChangeRequest({type: "MERGE", path: "", value: copySource});
+        return source; 
+    };
+        
+      
+    fluid.model.isNullChange = function(model, request, resolverGetConfig) {
+        if (request.type === "ADD") {
+            var existing = fluid.get(model, request.path, resolverGetConfig);
+            if (existing === request.value) {
+                return true;
+            }
+        }
+    };
+    /** Applies the supplied ChangeRequest object directly to the supplied model.
+     */
+    fluid.model.applyChangeRequest = function(model, request, resolverSetConfig) {
+        var pen = fluid.model.getPenultimate(model, request.path, resolverSetConfig || fluid.model.defaultSetConfig);
+        
+        if (request.type === "ADD" || request.type === "MERGE") {
+            if (pen.last === "" || request.type === "MERGE") {
+               if (request.type === "ADD") {
+                   fluid.clear(pen.root);
+               }
+               $.extend(true, pen.last === ""? pen.root: pen.root[pen.last], request.value);
+            }
+            else {
+                pen.root[pen.last] = request.value;
+            }
+        }
+        else if (request.type === "DELETE") {
+            if (pen.last === "") {
+                fluid.clear(pen.root);
+            }
+            else {
+                delete pen.root[pen.last];
+            }
+        }
+    };
+    
+    // Utility shared between changeApplier and superApplier
+    
+    function bindRequestChange(that) {
+        that.requestChange = function(path, value, type) {
+            var changeRequest = {
+                path: path,
+                value: value,
+                type: type
+            };
+        that.fireChangeRequest(changeRequest);
+        };
+    }
+    
+  
+    fluid.makeChangeApplier = function(model, options) {
+        options = options || {};
+        var baseEvents = {
+            guards: fluid.event.getEventFirer(false, true),
+            postGuards: fluid.event.getEventFirer(false, true),
+            modelChanged: fluid.event.getEventFirer(false, false)
+        };
+        var that = {
+            model: model
+        };
+        
+        function makeGuardWrapper(cullUnchanged) {
+            if (!cullUnchanged) {
+                return null;
+            }
+            var togo = function(guard) {
+                return function(model, changeRequest, internalApplier) {
+                    var oldRet = guard(model, changeRequest, internalApplier);
+                    if (oldRet === false) { return false;}
+                    else {
+                        if (fluid.model.isNullChange(model, changeRequest)) {
+                            togo.culled = true;
+                            return false;
+                        }
+                    }
+                };
+            };
+            return togo;
+        }
+
+        function wrapListener(listener, spec) {
+             var pathSpec = spec;
+             var transactional = false;
+             var priority = Number.MAX_VALUE;
+             if (typeof (spec) !== "string") {
+                 pathSpec = spec.path;
+                 transactional = spec.transactional;
+                 if (spec.priority !== undefined) {
+                     priority = spec.priority;
+                     }
+                 }
+             else {
+                 if (pathSpec.charAt(0) === "!") {
+                     transactional = true;
+                     pathSpec = pathSpec.substring(1);
+                 }
+             }
+             return function(changePath, fireSpec, accum) {
+                 var guid = fluid.event.identifyListener(listener);
+                 var exist = fireSpec.guids[guid];
+                 if (!exist) {
+                     var match = fluid.pathUtil.matchPath(pathSpec, changePath);
+                     if (match !== null) {
+                         var record = {
+                             changePath: changePath,
+                             pathSpec: pathSpec,
+                             listener: listener,
+                             priority: priority,
+                             transactional: transactional
+                             };
+                         if (accum) {
+                             record.accumulate = [accum];
+                         }
+                         fireSpec.guids[guid] = record;
+                         var collection = transactional? "transListeners": "listeners";
+                         fireSpec[collection].push(record);
+                         fireSpec.all.push(record);
+                     }
+                 }
+                 else if (accum) {
+                     if (!exist.accumulate) {
+                        exist.accumulate = [];
+                     }
+                     exist.accumulate.push(accum);
+                 }
+           };
+        }
+        
+        function fireFromSpec(name, fireSpec, args, category, wrapper) {
+            return baseEvents[name].fireToListeners(fireSpec[category], args, wrapper);
+        }
+        
+        function fireComparator(recA, recB) {
+            return recA.priority - recB.priority;
+        }
+
+        function prepareFireEvent(name, changePath, fireSpec, accum) {
+            baseEvents[name].fire(changePath, fireSpec, accum);
+            fireSpec.all.sort(fireComparator);
+            fireSpec.listeners.sort(fireComparator);
+            fireSpec.transListeners.sort(fireComparator);
+        }
+        
+        function makeFireSpec() {
+            return {guids: {}, all: [], listeners: [], transListeners: []};
+        }
+        
+        function getFireSpec(name, changePath) {
+            var fireSpec = makeFireSpec();
+            prepareFireEvent(name, changePath, fireSpec);
+            return fireSpec;
+        }
+        
+        function fireEvent(name, changePath, args, wrapper) {
+            var fireSpec = getFireSpec(name, changePath);
+            return fireFromSpec(name, fireSpec, args, "all", wrapper);
+        }
+        
+        function adaptListener(that, name) {
+            that[name] = {
+                addListener: function(spec, listener, namespace) {
+                    baseEvents[name].addListener(wrapListener(listener, spec), namespace);
+                },
+                removeListener: function(listener) {
+                    baseEvents[name].removeListener(listener);
+                }
+            };
+        }
+        adaptListener(that, "guards");
+        adaptListener(that, "postGuards");
+        adaptListener(that, "modelChanged");
+        
+        function preFireChangeRequest(changeRequest) {
+            if (!changeRequest.type) {
+                changeRequest.type = "ADD";
+            }
+        }
+
+        var bareApplier = {
+            fireChangeRequest: function(changeRequest) {
+                that.fireChangeRequest(changeRequest, true);
+            }
+        };
+        bindRequestChange(bareApplier);
+
+        that.fireChangeRequest = function(changeRequest, defeatGuards) {
+            preFireChangeRequest(changeRequest);
+            var guardFireSpec = defeatGuards? null : getFireSpec("guards", changeRequest.path);
+            if (guardFireSpec && guardFireSpec.transListeners.length > 0) {
+                var ation = that.initiate();
+                ation.fireChangeRequest(changeRequest, guardFireSpec);
+                ation.commit();
+            }
+            else {
+                if (!defeatGuards) {
+                    // TODO: this use of "listeners" seems pointless since we have just verified that there are no transactional listeners
+                    var prevent = fireFromSpec("guards", guardFireSpec, [model, changeRequest, bareApplier], "listeners");
+                    if (prevent === false) {
+                        return false;
+                    }
+                }
+                var oldModel = model;
+                if (!options.thin) {
+                    oldModel = {};
+                    fluid.model.copyModel(oldModel, model);                    
+                }
+                fluid.model.applyChangeRequest(model, changeRequest, options.resolverSetConfig);
+                fireEvent("modelChanged", changeRequest.path, [model, oldModel, [changeRequest]]);
+            }
+        };
+        
+        bindRequestChange(that);
+
+        function fireAgglomerated(eventName, formName, changes, args, accpos) {
+            var fireSpec = makeFireSpec();
+            for (var i = 0; i < changes.length; ++ i) {
+                prepareFireEvent(eventName, changes[i].path, fireSpec, changes[i]);
+                }
+            for (var i = 0; i < fireSpec[formName].length; ++ i) {
+                var spec = fireSpec[formName][i];
+                if (accpos) {
+                    args[accpos] = spec.accumulate;
+                }
+                var ret = spec.listener.apply(null, args);
+                if (ret === false) {
+                    return false;
+                }
+            }
+        }
+
+        that.initiate = function(newModel) {
+            var cancelled = false;
+            var changes = [];
+            if (options.thin) {
+                newModel = model;
+            }
+            else {
+                newModel = newModel || {};
+                fluid.model.copyModel(newModel, model);
+            }
+            // the guard in the inner world is given a private applier to "fast track"
+            // and glob collateral changes it requires
+            var internalApplier = 
+              {fireChangeRequest: function(changeRequest) {
+                    preFireChangeRequest(changeRequest);
+                    fluid.model.applyChangeRequest(newModel, changeRequest, options.resolverSetConfig);
+                    changes.push(changeRequest);
+                }};
+            bindRequestChange(internalApplier);
+            var ation = {
+                commit: function() {
+                    var oldModel;
+                    if (cancelled) {
+                        return false;
+                    }
+                    var ret = fireAgglomerated("postGuards", "transListeners", changes, [newModel, null, internalApplier], 1);
+                    if (ret === false) {
+                        return false;
+                    }
+                    if (options.thin) {
+                        oldModel = model;
+                    }
+                    else {
+                        oldModel = {};
+                        fluid.model.copyModel(oldModel, model);
+                        fluid.clear(model);
+                        fluid.model.copyModel(model, newModel);
+                    }
+                    fireAgglomerated("modelChanged", "all", changes, [model, oldModel, null], 2);
+                },
+                fireChangeRequest: function(changeRequest) {
+                     preFireChangeRequest(changeRequest);
+                     if (options.cullUnchanged && fluid.model.isNullChange(model, changeRequest, options.resolverGetConfig)) {
+                         return;
+                     } 
+                     var wrapper = makeGuardWrapper(options.cullUnchanged);
+                     var prevent = fireEvent("guards", changeRequest.path, [newModel, changeRequest, internalApplier], wrapper);
+                     if (prevent === false && !(wrapper && wrapper.culled)) {
+                         cancelled = true;
+                     }
+                     if (!cancelled) {
+                         if (!(wrapper && wrapper.culled)) {
+                             fluid.model.applyChangeRequest(newModel, changeRequest, options.resolverSetConfig);
+                             changes.push(changeRequest);
+                         }
+                     }
+                }
+            };
+            bindRequestChange(ation);
+
+            return ation;
+        };
+        
+        return that;
+    };
+    
+    fluid.makeSuperApplier = function() {
+        var subAppliers = [];
+        var that = {};
+        that.addSubApplier = function(path, subApplier) {
+            subAppliers.push({path: path, subApplier: subApplier});
+        };
+        that.fireChangeRequest = function(request) {
+            for (var i = 0; i < subAppliers.length; ++ i) {
+                var path = subAppliers[i].path;
+                if (request.path.indexOf(path) === 0) {
+                    var subpath = request.path.substring(path.length + 1);
+                    var subRequest = fluid.copy(request);
+                    subRequest.path = subpath;
+                    // TODO: Deal with the as yet unsupported case of an EL rvalue DAR
+                    subAppliers[i].subApplier.fireChangeRequest(subRequest);
+                }
+            }
+        };
+        bindRequestChange(that);
+        return that;
+    };
+    
+    fluid.attachModel = function(baseModel, path, model) {
+        var segs = fluid.model.parseEL(path);
+        for (var i = 0; i < segs.length - 1; ++ i) {
+            var seg = segs[i];
+            var subModel = baseModel[seg];
+            if (!subModel) {
+                baseModel[seg] = subModel = {};
+            }
+            baseModel = subModel;
+        }
+        baseModel[segs[segs.length - 1]] = model;
+    };
+    
+    fluid.assembleModel = function (modelSpec) {
+       var model = {};
+       var superApplier = fluid.makeSuperApplier();
+       var togo = {model: model, applier: superApplier};
+       for (var path in modelSpec) {
+           var rec = modelSpec[path];
+           fluid.attachModel(model, path, rec.model);
+           if (rec.applier) {
+              superApplier.addSubApplier(path, rec.applier);
+           }
+       }
+       return togo;
+    };
+
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+var fluid = fluid || fluid_1_3;
+
+(function ($, fluid) {
+
+    // $().fluid("selectable", args)
+    // $().fluid("selectable".that()
+    // $().fluid("pager.pagerBar", args)
+    // $().fluid("reorderer", options)
+
+/** Create a "bridge" from code written in the Fluid standard "that-ist" style,
+ *  to the standard JQuery UI plugin architecture specified at http://docs.jquery.com/UI/Guidelines .
+ *  Every Fluid component corresponding to the top-level standard signature (JQueryable, options)
+ *  will automatically convert idiomatically to the JQuery UI standard via this adapter. 
+ *  Any return value which is a primitive or array type will become the return value
+ *  of the "bridged" function - however, where this function returns a general hash
+ *  (object) this is interpreted as forming part of the Fluid "return that" pattern,
+ *  and the function will instead be bridged to "return this" as per JQuery standard,
+ *  permitting chaining to occur. However, as a courtesy, the particular "this" returned
+ *  will be augmented with a function that() which will allow the original return
+ *  value to be retrieved if desired.
+ *  @param {String} name The name under which the "plugin space" is to be injected into
+ *  JQuery
+ *  @param {Object} peer The root of the namespace corresponding to the peer object.
+ */
+
+    fluid.thatistBridge = function (name, peer) {
+
+        var togo = function(funcname) {
+            var segs = funcname.split(".");
+            var move = peer;
+            for (var i = 0; i < segs.length; ++i) {
+                move = move[segs[i]];
+            }
+            var args = [this];
+            if (arguments.length === 2) {
+                args = args.concat($.makeArray(arguments[1]));
+            }
+            var ret = move.apply(null, args);
+            this.that = function() {
+                return ret;
+            }
+            var type = typeof(ret);
+            return !ret || type === "string" || type === "number" || type === "boolean"
+              || ret && ret.length !== undefined? ret: this;
+        };
+        $.fn[name] = togo;
+        return togo;
+    };
+
+    fluid.thatistBridge("fluid", fluid);
+    fluid.thatistBridge("fluid_1_3", fluid_1_3);
+
+/*************************************************************************
+ * Tabindex normalization - compensate for browser differences in naming
+ * and function of "tabindex" attribute and tabbing order.
+ */
+
+    // -- Private functions --
+    
+    
+    var normalizeTabindexName = function() {
+        return $.browser.msie ? "tabIndex" : "tabindex";
+    };
+
+    var canHaveDefaultTabindex = function(elements) {
+       if (elements.length <= 0) {
+           return false;
+       }
+
+       return $(elements[0]).is("a, input, button, select, area, textarea, object");
+    };
+    
+    var getValue = function(elements) {
+        if (elements.length <= 0) {
+            return undefined;
+        }
+
+        if (!fluid.tabindex.hasAttr(elements)) {
+            return canHaveDefaultTabindex(elements) ? Number(0) : undefined;
+        }
+
+        // Get the attribute and return it as a number value.
+        var value = elements.attr(normalizeTabindexName());
+        return Number(value);
+    };
+
+    var setValue = function(elements, toIndex) {
+        return elements.each(function(i, item) {
+            $(item).attr(normalizeTabindexName(), toIndex);
+        });
+    };
+    
+    // -- Public API --
+    
+    /**
+     * Gets the value of the tabindex attribute for the first item, or sets the tabindex value of all elements
+     * if toIndex is specified.
+     * 
+     * @param {String|Number} toIndex
+     */
+    fluid.tabindex = function(target, toIndex) {
+        target = $(target);
+        if (toIndex !== null && toIndex !== undefined) {
+            return setValue(target, toIndex);
+        } else {
+            return getValue(target);
+        }
+    };
+
+    /**
+     * Removes the tabindex attribute altogether from each element.
+     */
+    fluid.tabindex.remove = function(target) {
+        target = $(target);
+        return target.each(function(i, item) {
+            $(item).removeAttr(normalizeTabindexName());
+        });
+    };
+
+    /**
+     * Determines if an element actually has a tabindex attribute present.
+     */
+    fluid.tabindex.hasAttr = function(target) {
+        target = $(target);
+        if (target.length <= 0) {
+            return false;
+        }
+        var togo = target.map(
+            function() {
+                var attributeNode = this.getAttributeNode(normalizeTabindexName());
+                return attributeNode ? attributeNode.specified : false;
+            }
+            );
+        return togo.length === 1? togo[0] : togo;
+    };
+
+    /**
+     * Determines if an element either has a tabindex attribute or is naturally tab-focussable.
+     */
+    fluid.tabindex.has = function(target) {
+        target = $(target);
+        return fluid.tabindex.hasAttr(target) || canHaveDefaultTabindex(target);
+    };
+
+    // Keyboard navigation
+    // Public, static constants needed by the rest of the library.
+    fluid.a11y = $.a11y || {};
+
+    fluid.a11y.orientation = {
+        HORIZONTAL: 0,
+        VERTICAL: 1,
+        BOTH: 2
+    };
+
+    var UP_DOWN_KEYMAP = {
+        next: $.ui.keyCode.DOWN,
+        previous: $.ui.keyCode.UP
+    };
+
+    var LEFT_RIGHT_KEYMAP = {
+        next: $.ui.keyCode.RIGHT,
+        previous: $.ui.keyCode.LEFT
+    };
+
+    // Private functions.
+    var unwrap = function(element) {
+        return element.jquery ? element[0] : element; // Unwrap the element if it's a jQuery.
+    };
+
+
+    var makeElementsTabFocussable = function(elements) {
+        // If each element doesn't have a tabindex, or has one set to a negative value, set it to 0.
+        elements.each(function(idx, item) {
+            item = $(item);
+            if (!item.fluid("tabindex.has") || item.fluid("tabindex") < 0) {
+                item.fluid("tabindex", 0);
+            }
+        });
+    };
+
+    // Public API.
+    /**
+     * Makes all matched elements available in the tab order by setting their tabindices to "0".
+     */
+    fluid.tabbable = function(target) {
+        target = $(target);
+        makeElementsTabFocussable(target);
+    };
+
+    /*********************************************************************** 
+     * Selectable functionality - geometrising a set of nodes such that they
+     * can be navigated (by setting focus) using a set of directional keys
+     */
+
+    var CONTEXT_KEY = "selectionContext";
+    var NO_SELECTION = -32768;
+
+    var cleanUpWhenLeavingContainer = function(selectionContext) {
+        if (selectionContext.activeItemIndex !== NO_SELECTION) {
+            if (selectionContext.options.onLeaveContainer) {
+                selectionContext.options.onLeaveContainer(
+                  selectionContext.selectables[selectionContext.activeItemIndex]);
+            } else if (selectionContext.options.onUnselect) {
+                selectionContext.options.onUnselect(
+                selectionContext.selectables[selectionContext.activeItemIndex]);
+            }
+        }
+
+        if (!selectionContext.options.rememberSelectionState) {
+            selectionContext.activeItemIndex = NO_SELECTION;
+        }
+    };
+
+    /**
+     * Does the work of selecting an element and delegating to the client handler.
+     */
+    var drawSelection = function(elementToSelect, handler) {
+        if (handler) {
+            handler(elementToSelect);
+        }
+    };
+
+    /**
+     * Does does the work of unselecting an element and delegating to the client handler.
+     */
+    var eraseSelection = function(selectedElement, handler) {
+        if (handler && selectedElement) {
+            handler(selectedElement);
+        }
+    };
+
+    var unselectElement = function(selectedElement, selectionContext) {
+        eraseSelection(selectedElement, selectionContext.options.onUnselect);
+    };
+
+    var selectElement = function(elementToSelect, selectionContext) {
+        // It's possible that we're being called programmatically, in which case we should clear any previous selection.
+        unselectElement(selectionContext.selectedElement(), selectionContext);
+
+        elementToSelect = unwrap(elementToSelect);
+        var newIndex = selectionContext.selectables.index(elementToSelect);
+
+        // Next check if the element is a known selectable. If not, do nothing.
+        if (newIndex === -1) {
+           return;
+        }
+
+        // Select the new element.
+        selectionContext.activeItemIndex = newIndex;
+        drawSelection(elementToSelect, selectionContext.options.onSelect);
+    };
+
+    var selectableFocusHandler = function(selectionContext) {
+        return function(evt) {
+            // FLUID-3590: newer browsers (FF 3.6, Webkit 4) have a form of "bug" in that they will go bananas
+            // on attempting to move focus off an element which has tabindex dynamically set to -1.
+            $(evt.target).fluid("tabindex", 0);
+            selectElement(evt.target, selectionContext);
+
+            // Force focus not to bubble on some browsers.
+            return evt.stopPropagation();
+        };
+    };
+
+    var selectableBlurHandler = function(selectionContext) {
+        return function(evt) {
+            $(evt.target).fluid("tabindex", selectionContext.options.selectablesTabindex);
+            unselectElement(evt.target, selectionContext);
+
+            // Force blur not to bubble on some browsers.
+            return evt.stopPropagation();
+        };
+    };
+
+    var reifyIndex = function(sc_that) {
+        var elements = sc_that.selectables;
+        if (sc_that.activeItemIndex >= elements.length) {
+            sc_that.activeItemIndex = 0;
+        }
+        if (sc_that.activeItemIndex < 0 && sc_that.activeItemIndex !== NO_SELECTION) {
+            sc_that.activeItemIndex = elements.length - 1;
+        }
+        if (sc_that.activeItemIndex >= 0) {
+            $(elements[sc_that.activeItemIndex]).focus();
+        }
+    };
+
+    var prepareShift = function(selectionContext) {
+        // FLUID-3590: FF 3.6 and Safari 4.x won't fire blur() when programmatically moving focus.
+        var selElm = selectionContext.selectedElement();
+        if (selElm) {
+            selElm.blur();
+        }
+
+        unselectElement(selectionContext.selectedElement(), selectionContext);
+        if (selectionContext.activeItemIndex === NO_SELECTION) {
+          selectionContext.activeItemIndex = -1;
+        }
+    };
+
+    var focusNextElement = function(selectionContext) {
+        prepareShift(selectionContext);
+        ++selectionContext.activeItemIndex;
+        reifyIndex(selectionContext);
+    };
+
+    var focusPreviousElement = function(selectionContext) {
+        prepareShift(selectionContext);
+        --selectionContext.activeItemIndex;
+        reifyIndex(selectionContext);
+    };
+
+    var arrowKeyHandler = function(selectionContext, keyMap, userHandlers) {
+        return function(evt) {
+            if (evt.which === keyMap.next) {
+                focusNextElement(selectionContext);
+                evt.preventDefault();
+            } else if (evt.which === keyMap.previous) {
+                focusPreviousElement(selectionContext);
+                evt.preventDefault();
+            }
+        };
+    };
+
+    var getKeyMapForDirection = function(direction) {
+        // Determine the appropriate mapping for next and previous based on the specified direction.
+        var keyMap;
+        if (direction === fluid.a11y.orientation.HORIZONTAL) {
+            keyMap = LEFT_RIGHT_KEYMAP;
+        } 
+        else if (direction === fluid.a11y.orientation.VERTICAL) {
+            // Assume vertical in any other case.
+            keyMap = UP_DOWN_KEYMAP;
+        }
+
+        return keyMap;
+    };
+
+    var tabKeyHandler = function(selectionContext) {
+        return function(evt) {
+            if (evt.which !== $.ui.keyCode.TAB) {
+                return;
+            }
+            cleanUpWhenLeavingContainer(selectionContext);
+
+            // Catch Shift-Tab and note that focus is on its way out of the container.
+            if (evt.shiftKey) {
+                selectionContext.focusIsLeavingContainer = true;
+            }
+        };
+    };
+
+    var containerFocusHandler = function(selectionContext) {
+        return function(evt) {
+            var shouldOrig = selectionContext.options.autoSelectFirstItem;
+            var shouldSelect = typeof(shouldOrig) === "function" ? 
+                 shouldOrig() : shouldOrig;
+
+            // Override the autoselection if we're on the way out of the container.
+            if (selectionContext.focusIsLeavingContainer) {
+                shouldSelect = false;
+            }
+
+            // This target check works around the fact that sometimes focus bubbles, even though it shouldn't.
+            if (shouldSelect && evt.target === selectionContext.container.get(0)) {
+                if (selectionContext.activeItemIndex === NO_SELECTION) {
+                    selectionContext.activeItemIndex = 0;
+                }
+                $(selectionContext.selectables[selectionContext.activeItemIndex]).focus();
+            }
+
+           // Force focus not to bubble on some browsers.
+           return evt.stopPropagation();
+        };
+    };
+
+    var containerBlurHandler = function(selectionContext) {
+        return function(evt) {
+            selectionContext.focusIsLeavingContainer = false;
+
+            // Force blur not to bubble on some browsers.
+            return evt.stopPropagation();
+        };
+    };
+
+    var makeElementsSelectable = function(container, defaults, userOptions) {
+
+        var options = $.extend(true, {}, defaults, userOptions);
+
+        var keyMap = getKeyMapForDirection(options.direction);
+
+        var selectableElements = options.selectableElements? options.selectableElements :
+              container.find(options.selectableSelector);
+          
+        // Context stores the currently active item(undefined to start) and list of selectables.
+        var that = {
+            container: container,
+            activeItemIndex: NO_SELECTION,
+            selectables: selectableElements,
+            focusIsLeavingContainer: false,
+            options: options
+        };
+
+        that.selectablesUpdated = function(focusedItem) {
+          // Remove selectables from the tab order and add focus/blur handlers
+            if (typeof(that.options.selectablesTabindex) === "number") {
+                that.selectables.fluid("tabindex", that.options.selectablesTabindex);
+            }
+            that.selectables.unbind("focus." + CONTEXT_KEY);
+            that.selectables.unbind("blur." + CONTEXT_KEY);
+            that.selectables.bind("focus."+ CONTEXT_KEY, selectableFocusHandler(that));
+            that.selectables.bind("blur." + CONTEXT_KEY, selectableBlurHandler(that));
+            if (keyMap && that.options.noBubbleListeners) {
+                that.selectables.unbind("keydown."+CONTEXT_KEY);
+                that.selectables.bind("keydown."+CONTEXT_KEY, arrowKeyHandler(that, keyMap));
+            }
+            if (focusedItem) {
+                selectElement(focusedItem, that);
+            }
+            else {
+                reifyIndex(that);
+            }
+        };
+
+        that.refresh = function() {
+            if (!that.options.selectableSelector) {
+                throw("Cannot refresh selectable context which was not initialised by a selector");
+            }
+            that.selectables = container.find(options.selectableSelector);
+            that.selectablesUpdated();
+        };
+        
+        that.selectedElement = function() {
+            return that.activeItemIndex < 0? null : that.selectables[that.activeItemIndex];
+        };
+        
+        // Add various handlers to the container.
+        if (keyMap && !that.options.noBubbleListeners) {
+            container.keydown(arrowKeyHandler(that, keyMap));
+        }
+        container.keydown(tabKeyHandler(that));
+        container.focus(containerFocusHandler(that));
+        container.blur(containerBlurHandler(that));
+        
+        that.selectablesUpdated();
+
+        return that;
+    };
+
+    /**
+     * Makes all matched elements selectable with the arrow keys.
+     * Supply your own handlers object with onSelect: and onUnselect: properties for custom behaviour.
+     * Options provide configurability, including direction: and autoSelectFirstItem:
+     * Currently supported directions are jQuery.a11y.directions.HORIZONTAL and VERTICAL.
+     */
+    fluid.selectable = function(target, options) {
+        target = $(target);
+        var that = makeElementsSelectable(target, fluid.selectable.defaults, options);
+        fluid.setScopedData(target, CONTEXT_KEY, that);
+        return that;
+    };
+
+    /**
+     * Selects the specified element.
+     */
+    fluid.selectable.select = function(target, toSelect) {
+        $(toSelect).focus();
+    };
+
+    /**
+     * Selects the next matched element.
+     */
+    fluid.selectable.selectNext = function(target) {
+        target = $(target);
+        focusNextElement(fluid.getScopedData(target, CONTEXT_KEY));
+    };
+
+    /**
+     * Selects the previous matched element.
+     */
+    fluid.selectable.selectPrevious = function(target) {
+        target = $(target);
+        focusPreviousElement(fluid.getScopedData(target, CONTEXT_KEY));
+    };
+
+    /**
+     * Returns the currently selected item wrapped as a jQuery object.
+     */
+    fluid.selectable.currentSelection = function(target) {
+        target = $(target);
+        var that = fluid.getScopedData(target, CONTEXT_KEY);
+        return $(that.selectedElement());
+    };
+
+    fluid.selectable.defaults = {
+        direction: fluid.a11y.orientation.VERTICAL,
+        selectablesTabindex: -1,
+        autoSelectFirstItem: true,
+        rememberSelectionState: true,
+        selectableSelector: ".selectable",
+        selectableElements: null,
+        onSelect: null,
+        onUnselect: null,
+        onLeaveContainer: null
+    };
+
+    /********************************************************************
+     *  Activation functionality - declaratively associating actions with 
+     * a set of keyboard bindings.
+     */
+
+    var checkForModifier = function(binding, evt) {
+        // If no modifier was specified, just return true.
+        if (!binding.modifier) {
+            return true;
+        }
+
+        var modifierKey = binding.modifier;
+        var isCtrlKeyPresent = modifierKey && evt.ctrlKey;
+        var isAltKeyPresent = modifierKey && evt.altKey;
+        var isShiftKeyPresent = modifierKey && evt.shiftKey;
+
+        return isCtrlKeyPresent || isAltKeyPresent || isShiftKeyPresent;
+    };
+
+    /** Constructs a raw "keydown"-facing handler, given a binding entry. This
+     *  checks whether the key event genuinely triggers the event and forwards it
+     *  to any "activateHandler" registered in the binding. 
+     */
+    var makeActivationHandler = function(binding) {
+        return function(evt) {
+            var target = evt.target;
+            if (!fluid.enabled(evt.target)) {
+                return;
+            }
+// The following 'if' clause works in the real world, but there's a bug in the jQuery simulation
+// that causes keyboard simulation to fail in Safari, causing our tests to fail:
+//     http://ui.jquery.com/bugs/ticket/3229
+// The replacement 'if' clause works around this bug.
+// When this issue is resolved, we should revert to the original clause.
+//            if (evt.which === binding.key && binding.activateHandler && checkForModifier(binding, evt)) {
+            var code = evt.which? evt.which : evt.keyCode;
+            if (code === binding.key && binding.activateHandler && checkForModifier(binding, evt)) {
+                var event = $.Event("fluid-activate");
+                $(evt.target).trigger(event, [binding.activateHandler]);
+                if (event.isDefaultPrevented()) {
+                    evt.preventDefault();
+                }
+            }
+        };
+    };
+
+    var makeElementsActivatable = function(elements, onActivateHandler, defaultKeys, options) {
+        // Create bindings for each default key.
+        var bindings = [];
+        $(defaultKeys).each(function(index, key) {
+            bindings.push({
+                modifier: null,
+                key: key,
+                activateHandler: onActivateHandler
+            });
+        });
+
+        // Merge with any additional key bindings.
+        if (options && options.additionalBindings) {
+            bindings = bindings.concat(options.additionalBindings);
+        }
+
+        fluid.initEnablement(elements);
+
+        // Add listeners for each key binding.
+        for (var i = 0; i < bindings.length; ++ i) {
+            var binding = bindings[i];
+            elements.keydown(makeActivationHandler(binding));
+        }
+        elements.bind("fluid-activate", function(evt, handler) {
+            handler = handler || onActivateHandler;
+            return handler? handler(evt): null;
+        });
+    };
+
+    /**
+     * Makes all matched elements activatable with the Space and Enter keys.
+     * Provide your own handler function for custom behaviour.
+     * Options allow you to provide a list of additionalActivationKeys.
+     */
+    fluid.activatable = function(target, fn, options) {
+        target = $(target);
+        makeElementsActivatable(target, fn, fluid.activatable.defaults.keys, options);
+    };
+
+    /**
+     * Activates the specified element.
+     */
+    fluid.activate = function(target) {
+        $(target).trigger("fluid-activate");
+    };
+
+    // Public Defaults.
+    fluid.activatable.defaults = {
+        keys: [$.ui.keyCode.ENTER, $.ui.keyCode.SPACE]
+    };
+
+  
+  })(jQuery, fluid_1_3);
+/*
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/** This file contains functions which depend on the presence of a DOM document
+ *  and which depend on the contents of Fluid.js **/
+
+// Declare dependencies.
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    fluid.defaults("fluid.ariaLabeller", {
+        labelAttribute: "aria-label",
+        liveRegionMarkup: "<div class=\"liveRegion fl-offScreen-hidden\" aria-live=\"polite\"></div>",
+        liveRegionId: "fluid-ariaLabeller-liveRegion",
+        invokers: {
+            generateLiveElement: {funcName: "fluid.ariaLabeller.generateLiveElement", args: ["{ariaLabeller}"]}
+        }
+    });
+    fluid.ariaLabeller = function(element, options) {
+        var that = fluid.initView("fluid.ariaLabeller", element, options);
+        fluid.initDependents(that);
+
+        that.update = function(newOptions) {
+            newOptions = newOptions || that.options;
+            that.container.attr(that.options.labelAttribute, newOptions.text);
+            if (newOptions.dynamicLabel) {
+                var live = fluid.jById(that.options.liveRegionId); 
+                if (live.length === 0) {
+                    live = that.generateLiveElement();
+                }
+                live.text(newOptions.text);
+            }
+        }
+        that.update();
+        return that;
+    };
+    
+    fluid.ariaLabeller.generateLiveElement = function(that) {
+        var liveEl = $(that.options.liveRegionMarkup);
+        liveEl.attr("id", that.options.liveRegionId);
+        $("body").append(liveEl);
+        return liveEl;
+    };
+    
+    var LABEL_KEY = "aria-labelling";
+    
+    fluid.getAriaLabeller = function(element) {
+        element = $(element);
+        var that = fluid.getScopedData(element, LABEL_KEY);
+        return that;      
+    };
+    
+    /** Manages an ARIA-mediated label attached to a given DOM element. An
+     * aria-labelledby attribute and target node is fabricated in the document
+     * if they do not exist already, and a "little component" is returned exposing a method
+     * "update" that allows the text to be updated. */
+    
+    fluid.updateAriaLabel = function(element, text, options) {
+        var options = $.extend({}, options || {}, {text: text});
+        var that = fluid.getAriaLabeller(element);
+        if (!that) {
+            that = fluid.ariaLabeller(element, options);
+            fluid.setScopedData(element, LABEL_KEY, that);
+        }
+        else that.update(options);
+        return that;
+    };
+    
+    /** Sets an interation on a target control, which morally manages a "blur" for
+     * a possibly composite region.
+     * A timed blur listener is set on the control, which waits for a short period of
+     * time (options.delay, defaults to 150ms) to discover whether the reason for the 
+     * blur interaction is that either a focus or click is being serviced on a nominated
+     * set of "exclusions" (options.exclusions, a free hash of elements or jQueries). 
+     * If no such event is received within the window, options.handler will be called
+     * with the argument "control", to service whatever interaction is required of the
+     * blur.
+     */
+    
+    fluid.deadMansBlur = function (control, options) {
+        var that = fluid.initLittleComponent(control, options);
+        that.blurPending = false;
+        $(control).blur(function () {
+            that.blurPending = true;
+            setTimeout(function () {
+                if (that.blurPending) {
+                    that.options.handler(control);
+                }
+            }, that.options.delay);
+        });
+        that.canceller = function () {
+            that.blurPending = false; 
+        };
+        fluid.each(that.options.exclusions, function(exclusion) {
+            var exclusion = $(exclusion);
+            exclusion.focusin(that.canceller);
+            exclusion.click(that.canceller);
+        });
+        return that;
+    };
+
+    fluid.defaults("fluid.deadMansBlur", {
+        delay: 150,
+    });
+    
+})(jQuery, fluid_1_3);
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    /** The Fluid "IoC System proper" - resolution of references and 
+     * completely automated instantiation of declaratively defined
+     * component trees */ 
+    
+    var inCreationMarker = "__CURRENTLY_IN_CREATION__";
+    
+    var findMatchingComponent = function(that, visitor, except) {
+        for (var name in that) {
+            var component = that[name];
+            if (!component || component === except || !component.typeName) {continue;}
+            if (visitor(component, name)) {
+                return true;
+            }
+            findMatchingComponent(component, visitor);
+         }
+    };
+    
+    // thatStack contains an increasing list of MORE SPECIFIC thats.
+    var visitComponents = function(thatStack, visitor) {
+        var lastDead;
+        for (var i = thatStack.length - 1; i >= 0; -- i) {
+            var that = thatStack[i];
+            if (that.options && that.options.fireBreak) { // TODO: formalise this
+               return;
+            }
+            if (that.typeName) {
+                if (visitor(that, "")) {
+                    return;
+                }
+            }
+            if (findMatchingComponent(that, visitor, lastDead)) {
+                return;
+            }
+            lastDead = that;
+        }
+    };
+    
+    // An EL segment resolver strategy that will attempt to trigger creation of
+    // components that it discovers along the EL path, if they have been defined but not yet
+    // constructed. Spring, eat your heart out! Wot no SPR-2048?
+    
+    function makeGingerStrategy(thatStack) {
+        return function(component, thisSeg) {
+            var atval = component[thisSeg];
+            if (atval !== undefined) {
+                if (atval[inCreationMarker] && atval !== thatStack[0]) {
+                    fluid.fail("Component of type " + 
+                    atval.typeName + " cannot be used for lookup of path " + segs.join(".") +
+                    " since it is still in creation. Please reorganise your dependencies so that they no longer contain circular references");
+                }
+            }
+            else {
+                if (component.options && component.options.components && component.options.components[thisSeg]) {
+                    fluid.initDependent(component, thisSeg, thatStack);
+                    atval = component[thisSeg];
+                }
+            };
+            return atval;
+        }
+    };
+
+    function makeStackFetcher(thatStack, directModel) {
+        var fetchStrategies = [fluid.model.funcResolverStrategy, makeGingerStrategy(thatStack)]; 
+        var fetcher = function(parsed) {
+            var context = parsed.context;
+            var foundComponent;
+            visitComponents(thatStack, function(component, name) {
+                if (context === name || context === component.typeName || context === component.nickName) {
+                    foundComponent = component;
+                    return true; // YOUR VISIT IS AT AN END!!
+                }
+                if (component.options && component.options.components && component.options.components[context] && !component[context]) {
+                    foundComponent = fluid.get(component, context, fetchStrategies);
+                    return true;
+                }
+            });
+                // TODO: we used to get a helpful diagnostic when we failed to match a context name before we fell back
+                // to the environment for FLUID-3818
+                //fluid.fail("No context matched for name " + context + " from root of type " + thatStack[0].typeName);
+            return fluid.get(foundComponent, parsed.path, fetchStrategies);
+        };
+        return fetcher;
+    }
+     
+    function makeStackResolverOptions(thatStack, directModel) {
+        return $.extend({}, fluid.defaults("fluid.resolveEnvironment"), {
+            noCopy: true,
+            fetcher: makeStackFetcher(thatStack, directModel)
+            }); 
+    } 
+     
+    function resolveRvalue(thatStack, arg, initArgs, componentOptions) {
+        var directModel = thatStack[0].model; // TODO: this convention may not always be helpful
+        var options = makeStackResolverOptions(thatStack, directModel);
+        options.model = directModel;
+        
+        if (fluid.isMarker(arg, fluid.COMPONENT_OPTIONS)) {
+            arg = fluid.expander.expandLight(componentOptions, options);
+        }
+        else {
+            if (typeof(arg) === "string" && arg.charAt(0) === "@") { // Test cases for i) single-args, ii) composite args
+                var argpos = arg.substring(1);
+                arg = initArgs[argpos];
+            }
+            else {
+                arg = fluid.expander.expandLight(arg, options); // fluid.resolveEnvironment(arg, directModel, options);
+            }
+        }
+        return arg;
+    }
+    
+    
+    /** Given a concrete argument list and/or options, determine the final concrete
+     * "invocation specification" which is coded by the supplied demandspec in the 
+     * environment "thatStack" - the return is a package of concrete global function name
+     * and argument list which is suitable to be executed directly by fluid.invokeGlobalFunction.
+     */
+    fluid.embodyDemands = function(thatStack, demandspec, initArgs, options) {
+        var demands = $.makeArray(demandspec.args);
+        options = options || {};
+        if (demands.length === 0) {
+            if (options.componentOptions) { // Guess that it is meant to be a subcomponent TODO: component grades
+               demands = [fluid.COMPONENT_OPTIONS];
+            }
+            else if (options.passArgs) {
+                demands = fluid.transform(initArgs, function(arg, index) {
+                    return "@"+index;
+                });
+            }
+        }
+        var args = [];
+        if (demands) {
+            for (var i = 0; i < demands.length; ++ i) {
+                var arg = demands[i];
+                if (typeof(arg) === "object" && !fluid.isMarker(arg)) {
+                    var resolvedOptions = {};
+                    for (var key in arg) {
+                        var ref = arg[key];
+                        var rvalue = resolveRvalue(thatStack, ref, initArgs, options.componentOptions);
+                        fluid.set(resolvedOptions, key, rvalue);
+                    }
+                    args[i] = resolvedOptions;
+                }
+                else {
+                    var resolvedArg = resolveRvalue(thatStack, arg, initArgs, options.componentOptions);
+                    args[i] = resolvedArg;
+                }
+                if (i === demands.length - 1 && args[i] && typeof(args[i]) === "object" && !args[i].typeName && !args[i].targetTypeName) {
+                    args[i].targetTypeName = demandspec.funcName; // TODO: investigate the general sanity of this
+                }
+            }
+        }
+        else {
+            args = initArgs? initArgs: [];
+        }
+
+        var togo = {
+            args: args,
+            funcName: demandspec.funcName
+        };
+        return togo;
+    };
+   
+    var dependentStore = {};
+    
+    function searchDemands(demandingName, contextNames) {
+        var exist = dependentStore[demandingName] || [];
+        outer: for (var i = 0; i < exist.length; ++ i) {
+            var rec = exist[i];
+            for (var j = 0; j < contextNames.length; ++ j) {
+                 if (rec.contexts[j] !== contextNames[j]) {
+                     continue outer;
+                 }
+            }
+            return rec.spec;   
+        }
+    }
+    
+    fluid.demands = function(demandingName, contextName, spec) {
+        var contextNames = $.makeArray(contextName).sort(); 
+        if (!spec) {
+            return searchDemands(demandingName, contextNames);
+        }
+        else if (spec.length) {
+            spec = {args: spec};
+        }
+        var exist = dependentStore[demandingName];
+        if (!exist) {
+            exist = [];
+            dependentStore[demandingName] = exist;
+        }
+        exist.push({contexts: contextNames, spec: spec});
+    };
+    
+    fluid.getEnvironmentalThatStack = function() {
+         return [fluid.staticEnvironment];
+    };
+    
+    fluid.getDynamicEnvironmentalThatStack = function() {
+        var root = fluid.threadLocal();
+        var dynamic = root["fluid.initDependents"]
+        return dynamic? dynamic : fluid.getEnvironmentalThatStack();
+    };
+
+    fluid.locateDemands = function(demandingNames, thatStack) {
+        var searchStack = fluid.getEnvironmentalThatStack().concat(thatStack); // TODO: put in ThreadLocal "instance" too, and also accelerate lookup
+        var contextNames = {};
+        visitComponents(searchStack, function(component) {
+            contextNames[component.typeName] = true;
+        });
+        var matches = [];
+        for (var i = 0; i < demandingNames.length; ++ i) {
+            var rec = dependentStore[demandingNames[i]] || [];
+            for (var j = 0; j < rec.length; ++ j) {
+                var spec = rec[j];
+                var record = {spec: spec.spec, intersect: 0, uncess: 0};
+                for (var k = 0; k < spec.contexts.length; ++ k) {
+                    record[contextNames[spec.contexts[k]]? "intersect" : "uncess"] += 2;
+                }
+                if (spec.contexts.length === 0) { // allow weak priority for contextless matches
+                    record.intersect ++;
+                }
+                // TODO: Potentially more subtle algorithm here - also ambiguity reports  
+                matches.push(record); 
+            }
+        }
+        matches.sort(function(speca, specb) {
+            var p1 = specb.intersect - speca.intersect; 
+            return p1 === 0? speca.uncess - specb.uncess : p1;
+            });
+        return matches.length === 0 || matches[0].intersect === 0? null : matches[0].spec;
+    };
+    
+    /** Determine the appropriate demand specification held in the fluid.demands environment 
+     * relative to "thatStack" for the function name(s) funcNames.
+     */
+    fluid.determineDemands = function (thatStack, funcNames) {
+        var that = thatStack[thatStack.length - 1];
+        funcNames = $.makeArray(funcNames);
+        var demandspec = fluid.locateDemands(funcNames, thatStack);
+   
+        if (!demandspec) {
+            demandspec = {};
+        }
+        var newFuncName = funcNames[0];
+        if (demandspec.funcName) {
+            newFuncName = demandspec.funcName;
+           /**    TODO: "redirects" disabled pending further thought
+            var demandspec2 = fluid.fetchDirectDemands(funcNames[0], that.typeName);
+            if (demandspec2) {
+                demandspec = demandspec2; // follow just one redirect
+            } **/
+        }
+        var mergeArgs = [];
+        if (demandspec.parent) {
+            var parent = searchDemands(funcNames[0], $.makeArray(demandspec.parent).sort());
+            if (parent) {
+                mergeArgs = parent.args; // TODO: is this really a necessary feature?
+            }
+        }
+        var args = [];
+        fluid.merge(null, args, $.makeArray(mergeArgs), $.makeArray(demandspec.args)); // TODO: avoid so much copying
+        return {funcName: newFuncName, args: args};
+    };
+    
+    fluid.resolveDemands = function (thatStack, funcNames, initArgs, options) {
+        var demandspec = fluid.determineDemands(thatStack, funcNames);
+        return fluid.embodyDemands(thatStack, demandspec, initArgs, options);
+    };
+    
+    // TODO: make a *slightly* more performant version of fluid.invoke that perhaps caches the demands
+    // after the first successful invocation
+    fluid.invoke = function(functionName, args, that, environment) {
+        args = fluid.makeArray(args);
+        return fluid.withNewComponent(that || {typeName: functionName}, function(thatStack) {
+            var invokeSpec = fluid.resolveDemands(thatStack, functionName, args, {passArgs: true});
+            return fluid.invokeGlobalFunction(invokeSpec.funcName, invokeSpec.args, environment);
+        });
+    };
+    
+    /** Make a function which performs only "static redispatch" of the supplied function name - 
+     * that is, taking only account of the contents of the "static environment". Since the static
+     * environment is assumed to be constant, the dispatch of the call will be evaluated at the
+     * time this call is made, as an optimisation.
+     */
+    
+    fluid.makeFreeInvoker = function(functionName, environment) {
+        var demandSpec = fluid.determineDemands([fluid.staticEnvironment], functionName);
+        return function() {
+            var invokeSpec = fluid.embodyDemands(fluid.staticEnvironment, demandSpec, arguments);
+            return fluid.invokeGlobalFunction(invokeSpec.funcName, invokeSpec.args, environment);
+        };
+    };
+    
+    fluid.makeInvoker = function(thatStack, demandspec, functionName, environment) {
+        demandspec = demandspec || fluid.determineDemands(thatStack, functionName);
+        thatStack = $.makeArray(thatStack); // take a copy of this since it will most likely go away
+        return function() {
+            var invokeSpec = fluid.embodyDemands(thatStack, demandspec, arguments);
+            return fluid.invokeGlobalFunction(invokeSpec.funcName, invokeSpec.args, environment);
+        };
+    };
+    
+    fluid.addBoiledListener = function(thatStack, eventName, listener, namespace, predicate) {
+        thatStack = $.makeArray(thatStack);
+        var topThat = thatStack[thatStack.length - 1];
+        topThat.events[eventName].addListener(function(args) {
+            var resolved = fluid.resolveDemands(thatStack, eventName, args);
+            listener.apply(null, resolved.args);
+        }, namespace, predicate);
+    };
+    
+        
+    fluid.registerNamespace("fluid.expander");
+    
+    /** rescue that part of a component's options which should not be subject to
+     * options expansion via IoC - this initially consists of "components" and "mergePolicy" 
+     * but will be expanded by the set of paths specified as "noexpand" within "mergePolicy" 
+     */
+    
+    fluid.expander.preserveFromExpansion = function(options) {
+        var preserve = {};
+        var preserveList = ["mergePolicy", "components", "invokers"];
+        fluid.each(options.mergePolicy, function(value, key) {
+            if (fluid.mergePolicyIs(value, "noexpand")) {
+                preserveList.push(key);
+            }
+        });
+        fluid.each(preserveList, function(path) {
+            var pen = fluid.model.getPenultimate(options, path);
+            var value = pen.root[pen.last];
+            delete pen.root[pen.last];
+            fluid.set(preserve, path, value);  
+        });
+        return {
+            restore: function(target) {
+                fluid.each(preserveList, function(path) {
+                    var preserved = fluid.get(preserve, path);
+                    if (preserved !== undefined) {
+                        fluid.set(target, path, preserved)
+                    }
+                });
+            }
+        };
+    };
+    
+    /** Expand a set of component options with respect to a set of "expanders" (essentially only
+     *  deferredCall) -  This substitution is destructive since it is assumed that the options are already "live" as the
+     *  result of environmental substitutions. Note that options contained inside "components" will not be expanded
+     *  by this call directly to avoid linearly increasing expansion depth if this call is occuring as a result of
+     *  "initDependents" */
+     // TODO: This needs to be integrated with "embodyDemands" above which makes a call to "resolveEnvironment" directly
+     // but with very similarly derived options (makeStackResolverOptions)
+     // The whole merge/expansion pipeline needs an overhaul once we have "grades" to allow merging and
+     // defaulting to occur smoothly across a "demands" stack - right now, "demanded" options have exactly
+     // the same status as user options whereas they should slot into the right place between 
+     // "earlyDefaults"/"defaults"/"demands"/"user options". Demands should be allowed to say whether they
+     // integrate with or override defaults.
+    fluid.expandOptions = function(args, that) {
+        if (fluid.isPrimitive(args)) {
+            return args;
+        }
+        return fluid.withNewComponent(that, function(thatStack) {
+            var expandOptions = makeStackResolverOptions(thatStack);
+            expandOptions.noCopy = true; // It is still possible a model may be fetched even though it is preserved
+            if (!fluid.isArrayable(args)) {
+                var pres = fluid.expander.preserveFromExpansion(args);
+            }
+            var expanded = fluid.expander.expandLight(args, expandOptions);
+            if (pres) {
+                pres.restore(expanded);
+            }
+            return expanded;
+        });
+    };
+    
+    fluid.initDependent = function(that, name, thatStack) {
+        if (!that || that[name]) { return; }
+        var component = that.options.components[name];
+        var invokeSpec = fluid.resolveDemands(thatStack, [component.type, name], [], {componentOptions: component.options});
+        // TODO: only want to expand "options" or all args? See "component rescuing" in expandOptions above
+        //invokeSpec.args = fluid.expandOptions(invokeSpec.args, thatStack, true); 
+        var instance = fluid.initSubcomponentImpl(that, {type: invokeSpec.funcName}, invokeSpec.args);
+        if (instance) { // TODO: more fallibility
+            that[name] = instance;
+        }
+    };
+    
+    // NON-API function    
+    fluid.withNewComponent = function(that, func) {
+        if (that) {
+            that[inCreationMarker] = true;
+        }
+        // push a dynamic stack of "currently resolving components" onto the current thread
+        var root = fluid.threadLocal();
+        var thatStack = root["fluid.initDependents"];
+        if (that) {
+            if (!thatStack) {
+                thatStack = [that];
+                root["fluid.initDependents"] = thatStack;
+            }
+            else {
+                thatStack.push(that);
+            }
+        }
+        var fullStack = [fluid.staticEnvironment, fluid.threadLocal()].concat(fluid.makeArray(thatStack));
+        try {
+            return func(fullStack);
+        }
+        finally {
+            thatStack.pop();
+            delete that[inCreationMarker];
+        }              
+    };
+        
+    fluid.initDependents = function(that) {
+        var options = that.options;
+        fluid.withNewComponent(that, function(thatStack) {
+            var components = options.components || {};
+            for (var name in components) {
+                fluid.initDependent(that, name, thatStack);
+            }
+            var invokers = options.invokers || {};
+            for (var name in invokers) {
+                var invokerec = invokers[name];
+                var funcName = typeof(invokerec) === "string"? invokerec : null;
+                that[name] = fluid.makeInvoker(thatStack, funcName? null : invokerec, funcName);
+            }
+        });
+    };
+    
+    // Standard Fluid component types
+    
+    fluid.typeTag = function(name) {
+        return {
+            typeName: name
+        };
+    };
+    
+    fluid.standardComponent = function(name) {
+        return function(container, options) {
+            var that = fluid.initView(name, container, options);
+            fluid.initDependents(that);
+            return that;
+        };
+    };
+    
+    fluid.littleComponent = function(name) {
+        return function(options) {
+            var that = fluid.initLittleComponent(name, options);
+            fluid.initDependents(that);
+            return that;
+        };
+    };
+    
+    fluid.makeComponents = function(components, env) {
+        if (!env) {
+            env = fluid.environment;
+        }
+        for (var name in components) {
+            fluid.setGlobalValue(name, 
+                fluid.invokeGlobalFunction(components[name], [name], env), env);
+        }
+    };
+    
+        
+    fluid.staticEnvironment = fluid.typeTag("fluid.staticEnvironment");
+    
+    fluid.staticEnvironment.environmentClass = fluid.typeTag("fluid.browser");
+    
+    // fluid.environmentalRoot.environmentClass = fluid.typeTag("fluid.rhino");
+    
+    fluid.demands("fluid.threadLocal", "fluid.browser", {funcName: "fluid.singleThreadLocal"});
+
+    var singleThreadLocal = fluid.typeTag("fluid.dynamicEnvironment");
+    
+    fluid.singleThreadLocal = function() {
+        return singleThreadLocal;
+    };
+
+    fluid.threadLocal = fluid.makeFreeInvoker("fluid.threadLocal");
+
+    fluid.withEnvironment = function(envAdd, func) {
+        var root = fluid.threadLocal();
+        try {
+            $.extend(root, envAdd);
+            return func();
+        }
+        finally {
+            for (var key in envAdd) {
+               delete root[key];
+            }
+        }
+    };
+    
+    fluid.extractEL = function(string, options) {
+        if (options.ELstyle === "ALL") {
+            return string;
+        }
+        else if (options.ELstyle.length === 1) {
+            if (string.charAt(0) === options.ELstyle) {
+                return string.substring(1);
+            }
+        }
+        else if (options.ELstyle === "${}") {
+            var i1 = string.indexOf("${");
+            var i2 = string.lastIndexOf("}");
+            if (i1 === 0 && i2 !== -1) {
+                return string.substring(2, i2);
+            }
+        }
+    };
+    
+    fluid.extractELWithContext = function(string, options) {
+        var EL = fluid.extractEL(string, options);
+        if (EL && EL.charAt(0) === "{") {
+            return fluid.parseContextReference(EL, 0);
+        }
+        return EL? {path: EL} : EL;
+    };
+
+    /* An EL extraction utility suitable for context expressions which occur in 
+     * expanding component trees. It assumes that any context expressions refer
+     * to EL paths that are to be referred to the "true (direct) model" - since
+     * the context during expansion may not agree with the context during rendering.
+     * It satisfies the same contract as fluid.extractEL, in that it will either return
+     * an EL path, or undefined if the string value supplied cannot be interpreted
+     * as an EL path with respect to the supplied options.
+     */
+        
+    fluid.extractContextualPath = function (string, options, env) {
+        var parsed = fluid.extractELWithContext(string, options);
+        if (parsed) {
+            if (parsed.context) {
+                var fetched = env[parsed.context];
+                if (typeof(fetched) !== "string") {
+                    fluid.fail("Could not look up context path named " + parsed.context + " to string value");
+                }
+                return fluid.model.composePath(fetched, parsed.path);
+            }
+            else {
+                return parsed.path;
+            }
+        }
+    };
+
+    fluid.parseContextReference = function(reference, index, delimiter) {
+        var endcpos = reference.indexOf("}", index + 1);
+        if (endcpos === -1) {
+            fluid.fail("Malformed context reference without }");
+        }
+        var context = reference.substring(index + 1, endcpos);
+        var endpos = delimiter? reference.indexOf(delimiter, endcpos + 1) : reference.length;
+        var path = reference.substring(endcpos + 1, endpos);
+        if (path.charAt(0) === ".") {
+            path = path.substring(1);
+        }
+        return {context: context, path: path, endpos: endpos};
+    };
+    
+    fluid.fetchContextReference = function(parsed, directModel, env) {
+        var base = parsed.context? env[parsed.context] : directModel;
+        if (!base) {
+            return base;
+        }
+        return fluid.get(base, parsed.path);
+    };
+    
+    fluid.resolveContextValue = function(string, options) {
+        if (options.bareContextRefs && string.charAt(0) === "{") {
+            var parsed = fluid.parseContextReference(string, 0);
+            return options.fetcher(parsed);        
+        }
+        else if (options.ELstyle && options.ELstyle !== "${}") {
+            var parsed = fluid.extractELWithContext(string, options);
+            if (parsed) {
+                return options.fetcher(parsed);
+            }
+        }
+        while (typeof(string) === "string") {
+            var i1 = string.indexOf("${");
+            var i2 = string.indexOf("}", i1 + 2);
+            var all = (i1 === 0 && i2 === string.length - 1); 
+            if (i1 !== -1 && i2 !== -1) {
+                var parsed;
+                if (string.charAt(i1 + 2) === "{") {
+                    parsed = fluid.parseContextReference(string, i1 + 2, "}");
+                    i2 = parsed.endpos;
+                }
+                else {
+                    parsed = {path: string.substring(i1 + 2, i2)};
+                }
+                var subs = options.fetcher(parsed);
+                // TODO: test case for all undefined substitution
+                if (subs === undefined || subs === null) {
+                    return subs;
+                    }
+                string = all? subs : string.substring(0, i1) + subs + string.substring(i2 + 1);
+            }
+            else {
+                break;
+            }
+        }
+        return string;
+    };
+    
+    function resolveEnvironmentImpl(obj, options) {
+        function recurse(arg) {
+            return resolveEnvironmentImpl(arg, options);
+        }
+        if (typeof(obj) === "string" && !options.noValue) {
+            return fluid.resolveContextValue(obj, options);
+        }
+        else if (fluid.isPrimitive(obj) || obj.nodeType !== undefined || obj.jquery) {
+            return obj;
+        }
+        else if (options.filter) {
+            return options.filter(obj, recurse, options);
+        }
+        else {
+            return (options.noCopy? fluid.each : fluid.transform)(obj, function(value, key) {
+                return resolveEnvironmentImpl(value, options);
+            });
+        }
+    }
+    
+    fluid.defaults("fluid.resolveEnvironment", 
+        {ELstyle:     "${}",
+         bareContextRefs: true});
+    
+    fluid.environmentFetcher = function(directModel) {
+        var env = fluid.threadLocal();
+        return function(parsed) {
+            return fluid.fetchContextReference(parsed, directModel, env);
+        };
+    };
+    
+    fluid.resolveEnvironment = function(obj, directModel, userOptions) {
+        directModel = directModel || {};
+        var options = fluid.merge(null, {}, fluid.defaults("fluid.resolveEnvironment"), userOptions);
+        if (!options.fetcher) {
+            options.fetcher = fluid.environmentFetcher(directModel);
+        }
+        return resolveEnvironmentImpl(obj, options);
+    };
+
+    /** "light" expanders, starting with support functions for the "deferredFetcher" expander **/
+
+    fluid.expander.deferredCall = function(target, source, recurse) {
+        var expander = source.expander;
+        var args = (!expander.args || fluid.isArrayable(expander.args))? expander.args : $.makeArray(expander.args);
+        args = recurse(args); 
+        return fluid.invokeGlobalFunction(expander.func, args);
+    };
+    
+    fluid.deferredCall = fluid.expander.deferredCall; // put in top namespace for convenience
+    
+    fluid.deferredInvokeCall = function(target, source, recurse) {
+        var expander = source.expander;
+        var args = (!expander.args || fluid.isArrayable(expander.args))? expander.args : $.makeArray(expander.args);
+        args = recurse(args);  
+        return fluid.invoke(expander.func, args);
+    };
+    
+    // The "noexpand" expander which simply unwraps one level of expansion and ceases.
+    fluid.expander.noexpand = function(target, source) {
+        return $.extend(target, source.expander.tree);
+    };
+  
+    fluid.noexpand = fluid.expander.noexpand; // TODO: check naming and namespacing
+  
+    fluid.expander.lightFilter = function (obj, recurse, options) {
+          var togo;
+          if (fluid.isArrayable(obj)) {
+              togo = options.noCopy? obj : [];
+              fluid.each(obj, function(value, key) {togo[key] = recurse(value);});
+          }
+          else {
+              togo = options.noCopy? obj : {};
+              for (var key in obj) {
+                  var value = obj[key];
+                  var expander;
+                  if (key === "expander" && !(options.expandOnly && options.expandOnly[value.type])){
+                      expander = fluid.getGlobalValue(value.type);  
+                      if (expander) {
+                          return expander.call(null, togo, obj, recurse);
+                      }
+                  }
+                  if (key !== "expander" || !expander) {
+                      togo[key] = recurse(value);
+                  }
+              }
+          }
+          return options.noCopy? obj : togo;
+      };
+      
+    fluid.expander.expandLight = function (source, expandOptions) {
+        var options = $.extend({}, expandOptions);
+        options.filter = fluid.expander.lightFilter;
+        return fluid.resolveEnvironment(source, options.model, options);       
+    };
+          
+})(jQuery, fluid_1_3);
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2007-2009 University of Toronto
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    /** Framework-global caching state for fluid.fetchResources **/
+
+    var resourceCache = {};
+  
+    var pendingClass = {};
+    /** Accepts a hash of structures with free keys, where each entry has either
+     * href/url or nodeId set - on completion, callback will be called with the populated
+     * structure with fetched resource text in the field "resourceText" for each
+     * entry. Each structure may contain "options" holding raw options to be forwarded
+     * to jQuery.ajax().
+     */
+  
+    fluid.fetchResources = function(resourceSpecs, callback, options) {
+        var that = fluid.initLittleComponent("fluid.fetchResources", options);
+        that.resourceSpecs = resourceSpecs;
+        that.callback = callback;
+        that.operate = function() {
+            fluid.fetchResources.fetchResourcesImpl(that);
+        };
+        fluid.each(resourceSpecs, function(resourceSpec) {
+             resourceSpec.recurseFirer = fluid.event.getEventFirer();
+             resourceSpec.recurseFirer.addListener(that.operate);
+             if (resourceSpec.url && !resourceSpec.href) {
+                resourceSpec.href = resourceSpec.url;
+             }
+        });
+        if (that.options.amalgamateClasses) {
+            fluid.fetchResources.amalgamateClasses(resourceSpecs, that.options.amalgamateClasses, that.operate);
+        }
+        that.operate();
+        return that;
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    // Add "synthetic" elements of *this* resourceSpec list corresponding to any
+    // still pending elements matching the PROLEPTICK CLASS SPECIFICATION supplied 
+    fluid.fetchResources.amalgamateClasses = function(specs, classes, operator) {
+        fluid.each(classes, function(clazz) {
+            var pending = pendingClass[clazz];
+            fluid.each(pending, function(pendingrec, canon) {
+                specs[clazz+"!"+canon] = pendingrec;
+                pendingrec.recurseFirer.addListener(operator);
+            });
+        });
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.timeSuccessCallback = function(resourceSpec) {
+        if (resourceSpec.timeSuccess && resourceSpec.options && resourceSpec.options.success) {
+            var success = resourceSpec.options.success;
+            resourceSpec.options.success = function() {
+            var startTime = new Date();
+            var ret = success.apply(null, arguments);
+            fluid.log("External callback for URL " + resourceSpec.href + " completed - callback time: " + 
+                    (new Date().getTime() - startTime.getTime()) + "ms");
+            return ret;
+            };
+        }
+    };
+    
+    // TODO: Integrate punch-through from old Engage implementation
+    function canonUrl(url) {
+        return url;
+    }
+    
+    fluid.fetchResources.clearResourceCache = function(url) {
+        if (url) {
+            delete resourceCache[canonUrl(url)];
+        }
+        else {
+            fluid.clear(resourceCache);
+        }  
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.handleCachedRequest = function(resourceSpec, response) {
+         var canon = canonUrl(resourceSpec.href);
+         var cached = resourceCache[canon];
+         if (cached.$$firer$$) {
+             fluid.log("Handling request for " + canon + " from cache");
+             var fetchClass = resourceSpec.fetchClass;
+             if (fetchClass && pendingClass[fetchClass]) {
+                 fluid.log("Clearing pendingClass entry for class " + fetchClass);
+                 delete pendingClass[fetchClass][canon];
+             }
+             resourceCache[canon] = response;      
+             cached.fire(response);
+         }
+    };
+    
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.completeRequest = function(thisSpec, recurseCall) {
+        thisSpec.queued = false;
+        thisSpec.completeTime = new Date();
+        fluid.log("Request to URL " + thisSpec.href + " completed - total elapsed time: " + 
+            (thisSpec.completeTime.getTime() - thisSpec.initTime.getTime()) + "ms");
+        thisSpec.recurseFirer.fire();
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.makeResourceCallback = function(thisSpec) {
+        return {
+            success: function(response) {
+                thisSpec.resourceText = response;
+                thisSpec.resourceKey = thisSpec.href;
+                if (thisSpec.forceCache) {
+                    fluid.fetchResources.handleCachedRequest(thisSpec, response);
+                }
+                fluid.fetchResources.completeRequest(thisSpec);
+            },
+            error: function(response, textStatus, errorThrown) {
+                thisSpec.fetchError = {
+                    status: response.status,
+                    textStatus: response.textStatus,
+                    errorThrown: errorThrown
+                };
+                fluid.fetchResources.completeRequest(thisSpec);
+            }
+            
+        };
+    };
+    
+        
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.issueCachedRequest = function(resourceSpec, options) {
+         var canon = canonUrl(resourceSpec.href);
+         var cached = resourceCache[canon];
+         if (!cached) {
+             fluid.log("First request for cached resource with url " + canon);
+             cached = fluid.event.getEventFirer();
+             cached.$$firer$$ = true;
+             resourceCache[canon] = cached;
+             var fetchClass = resourceSpec.fetchClass;
+             if (fetchClass) {
+                 if (!pendingClass[fetchClass]) {
+                     pendingClass[fetchClass] = {};
+                 }
+                 pendingClass[fetchClass][canon] = resourceSpec;
+             }
+             options.cache = false; // TODO: Getting weird "not modified" issues on Firefox
+             $.ajax(options);
+         }
+         else {
+             if (!cached.$$firer$$) {
+                 options.success(cached);
+             }
+             else {
+                 fluid.log("Request for cached resource which is in flight: url " + canon);
+                 cached.addListener(function(response) {
+                     options.success(response);
+                 });
+             }
+         }
+    };
+    
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    // Compose callbacks in such a way that the 2nd, marked "external" will be applied
+    // first if it exists, but in all cases, the first, marked internal, will be 
+    // CALLED WITHOUT FAIL
+    fluid.fetchResources.composeCallbacks = function(internal, external) {
+        return external? function() {
+            try {
+                external.apply(null, arguments);
+            }
+            catch (e) {
+                fluid.log("Exception applying external fetchResources callback: " + e);
+            }
+            internal.apply(null, arguments); // call the internal callback without fail
+        } : internal;
+    };
+    
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.composePolicy = function(target, source, key) {
+        target[key] = fluid.fetchResources.composeCallbacks(target[key], source[key]);
+    };
+    
+    fluid.defaults("fluid.fetchResources.issueRequest", {
+        mergePolicy: {
+            success: fluid.fetchResources.composePolicy,
+            error: fluid.fetchResources.composePolicy,
+            url: "reverse"
+        }
+    });
+    
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.issueRequest = function(resourceSpec, key) {
+        var thisCallback = fluid.fetchResources.makeResourceCallback(resourceSpec);
+        var options = {  
+             url:     resourceSpec.href,
+             success: thisCallback.success, 
+             error:   thisCallback.error};
+        fluid.fetchResources.timeSuccessCallback(resourceSpec);
+        fluid.merge(fluid.defaults("fluid.fetchResources.issueRequest").mergePolicy,
+                      options, resourceSpec.options);
+        resourceSpec.queued = true;
+        resourceSpec.initTime = new Date();
+        fluid.log("Request with key " + key + " queued for " + resourceSpec.href);
+
+        if (resourceSpec.forceCache) {
+            fluid.fetchResources.issueCachedRequest(resourceSpec, options);
+        }
+        else {
+            $.ajax(options);
+        }
+    };
+    
+    fluid.fetchResources.fetchResourcesImpl = function(that) {
+        var complete = true;
+        var allSync = true;
+        var resourceSpecs = that.resourceSpecs;
+        for (var key in resourceSpecs) {
+            var resourceSpec = resourceSpecs[key];
+            if (!resourceSpec.options || resourceSpec.options.async) {
+                allSync = false;
+            }
+            if (resourceSpec.href && !resourceSpec.completeTime) {
+                 if (!resourceSpec.queued) {
+                     fluid.fetchResources.issueRequest(resourceSpec, key);  
+                 }
+                 if (resourceSpec.queued) {
+                     complete = false;
+                 }
+            }
+            else if (resourceSpec.nodeId && !resourceSpec.resourceText) {
+                var node = document.getElementById(resourceSpec.nodeId);
+                // upgrade this to somehow detect whether node is "armoured" somehow
+                // with comment or CDATA wrapping
+                resourceSpec.resourceText = fluid.dom.getElementText(node);
+                resourceSpec.resourceKey = resourceSpec.nodeId;
+            }
+        }
+        if (complete && that.callback && !that.callbackCalled) {
+            that.callbackCalled = true;
+            if ($.browser.mozilla && !allSync) {
+                // Defer this callback to avoid debugging problems on Firefox
+                setTimeout(function() {
+                    that.callback(resourceSpecs);
+                    }, 1);
+            }
+            else {
+                that.callback(resourceSpecs);
+            }
+        }
+    };
+    
+    fluid.fetchResources.primeCacheFromResources = function(componentName) {
+        var resources = fluid.defaults(componentName).resources;
+        var expanded = (fluid.expandOptions? fluid.expandOptions : fluid.identity)(fluid.copy(resources));
+        fluid.fetchResources(expanded);
+    };
+    
+    /** Utilities invoking requests for expansion **/
+    fluid.registerNamespace("fluid.expander");
+      
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.expander.makeDefaultFetchOptions = function (successdisposer, failid, options) {
+        return $.extend(true, {dataType: "text"}, options, {
+            success: function(response, environmentdisposer) {
+                var json = JSON.parse(response);
+                environmentdisposer(successdisposer(json));
+            },
+            error: function(response, textStatus) {
+                fluid.log("Error fetching " + failid + ": " + textStatus);
+            }
+        });
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.expander.makeFetchExpander = function (options) {
+        return { expander: {
+            type: "fluid.expander.deferredFetcher",
+            href: options.url,
+            options: fluid.expander.makeDefaultFetchOptions(options.disposer, options.url, options.options),
+            resourceSpecCollector: "{resourceSpecCollector}",
+            fetchKey: options.fetchKey
+        }};
+    };
+    
+    fluid.expander.deferredFetcher = function(target, source) {
+        var expander = source.expander;
+        var spec = fluid.copy(expander);
+        // fetch the "global" collector specified in the external environment to receive
+        // this resourceSpec
+        var collector = fluid.resolveEnvironment(expander.resourceSpecCollector);
+        delete spec.type;
+        delete spec.resourceSpecCollector;
+        delete spec.fetchKey;
+        var environmentdisposer = function(disposed) {
+            $.extend(target, disposed);
+        };
+        // replace the callback which is there (taking 2 arguments) with one which
+        // directly responds to the request, passing in the result and OUR "disposer" - 
+        // which once the user has processed the response (say, parsing JSON and repackaging)
+        // finally deposits it in the place of the expander in the tree to which this reference
+        // has been stored at the point this expander was evaluated.
+        spec.options.success = function(response) {
+             expander.options.success(response, environmentdisposer);
+        };
+        var key = expander.fetchKey || fluid.allocateGuid();
+        collector[key] = spec;
+        return target;
+    };
+    
+    
+})(jQuery, fluid_1_3);
+/*
+ * jQuery UI Tooltip @VERSION
+ *
+ * Copyright 2010, AUTHORS.txt
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tooltip
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.widget.js
+ *     jquery.ui.position.js
+ */
+(function($) {
+
+var increments = 0;
+
+$.widget("ui.tooltip", {
+       options: {
+               items: "[title]",
+               content: function() {
+                       return $(this).attr("title");
+               },
+               position: {
+                       my: "left center",
+                       at: "right center",
+                       offset: "15 0"
+               }
+       },
+       _create: function() {
+               var self = this;
+               this.tooltip = $("<div></div>")
+                       .attr("id", "ui-tooltip-" + increments++)
+                       .attr("role", "tooltip")
+                       .attr("aria-hidden", "true")
+                       .addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content")
+                       .appendTo(document.body)
+                       .hide();
+               this.tooltipContent = $("<div></div>")
+                       .addClass("ui-tooltip-content")
+                       .appendTo(this.tooltip);
+               this.opacity = this.tooltip.css("opacity");
+               this.element
+                       .bind("focus.tooltip mouseover.tooltip", function(event) {
+                               self.open( event );
+                       })
+                       .bind("blur.tooltip mouseout.tooltip", function(event) {
+                               self.close( event );
+                       });
+       },
+       
+       enable: function() {
+               this.options.disabled = false;
+       },
+       
+       disable: function() {
+               this.options.disabled = true;
+       },
+       
+       destroy: function() {
+               this.tooltip.remove();
+               $.Widget.prototype.destroy.apply(this, arguments);
+       },
+       
+       widget: function() {
+               return this.element.pushStack(this.tooltip.get());
+       },
+       
+       open: function(event) {
+               var target = $(event && event.target || this.element).closest(this.options.items);
+               // already visible? possible when both focus and mouseover events occur
+               if (this.current && this.current[0] == target[0])
+                       return;
+               var self = this;
+               this.current = target;
+               this.currentTitle = target.attr("title");
+               var content = this.options.content.call(target[0], function(response) {
+                       // IE may instantly serve a cached response, need to give it a chance to finish with _show before that
+                       setTimeout(function() {
+                               // ignore async responses that come in after the tooltip is already hidden
+                               if (self.current == target)
+                                       self._show(event, target, response);
+                       }, 13);
+               });
+               if (content) {
+                       self._show(event, target, content);
+               }
+       },
+       
+       _show: function(event, target, content) {
+               if (!content)
+                       return;
+               
+               target.attr("title", "");
+               
+               if (this.options.disabled)
+                       return;
+                       
+               this.tooltipContent.html(content);
+               this.tooltip.css({
+                       top: 0,
+                       left: 0
+               }).show().position( $.extend({
+                       of: target
+               }, this.options.position )).hide();
+               
+               this.tooltip.attr("aria-hidden", "false");
+               target.attr("aria-describedby", this.tooltip.attr("id"));
+
+               this.tooltip.stop(false, true).fadeIn();
+
+               this._trigger( "open", event );
+       },
+       
+       close: function(event) {
+               if (!this.current)
+                       return;
+               
+               var current = this.current.attr("title", this.currentTitle);
+               this.current = null;
+               
+               if (this.options.disabled)
+                       return;
+               
+               current.removeAttr("aria-describedby");
+               this.tooltip.attr("aria-hidden", "true");
+               
+               this.tooltip.stop(false, true).fadeOut();
+               
+               this._trigger( "close", event );
+       }
+       
+});
+
+})(jQuery);/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+  // The three states of the undo component
+    var STATE_INITIAL = "state_initial", 
+        STATE_CHANGED = "state_changed",
+        STATE_REVERTED = "state_reverted";
+  
+    function defaultRenderer(that, targetContainer) {
+        var str = that.options.strings;
+        var markup = "<span class='flc-undo'>" + 
+            "<a href='#' class='flc-undo-undoControl'>" + str.undo + "</a>" + 
+            "<a href='#' class='flc-undo-redoControl'>" + str.redo + "</a>" + 
+            "</span>";
+        var markupNode = $(markup).attr({
+            "role": "region",  
+            "aria-live": "polite", 
+            "aria-relevant": "all"
+        });
+        targetContainer.append(markupNode);
+        return markupNode;
+    }
+    
+    function refreshView(that) {
+        if (that.state === STATE_INITIAL) {
+            that.locate("undoContainer").hide();
+            that.locate("redoContainer").hide();
+        }
+        else if (that.state === STATE_CHANGED) {
+            that.locate("undoContainer").show();
+            that.locate("redoContainer").hide();
+        }
+        else if (that.state === STATE_REVERTED) {
+            that.locate("undoContainer").hide();
+            that.locate("redoContainer").show();          
+        }
+    }
+   
+    
+    var bindHandlers = function (that) { 
+        that.locate("undoControl").click( 
+            function () {
+                if (that.state !== STATE_REVERTED) {
+                    fluid.model.copyModel(that.extremalModel, that.component.model);
+                    that.component.updateModel(that.initialModel, that);
+                    that.state = STATE_REVERTED;
+                    refreshView(that);
+                    that.locate("redoControl").focus();
+                }
+                return false;
+            }
+        );
+        that.locate("redoControl").click( 
+            function () {
+                if (that.state !== STATE_CHANGED) {
+                    that.component.updateModel(that.extremalModel, that);
+                    that.state = STATE_CHANGED;
+                    refreshView(that);
+                    that.locate("undoControl").focus();
+                }
+                return false;
+            }
+        );
+        return {
+            modelChanged: function (newModel, oldModel, source) {
+                if (source !== that) {
+                    that.state = STATE_CHANGED;
+                
+                    fluid.model.copyModel(that.initialModel, oldModel);
+                
+                    refreshView(that);
+                }
+            }
+        };
+    };
+    
+    /**
+     * Decorates a target component with the function of "undoability"
+     * 
+     * @param {Object} component a "model-bearing" standard Fluid component to receive the "undo" functionality
+     * @param {Object} options a collection of options settings
+     */
+    fluid.undoDecorator = function (component, userOptions) {
+        var that = fluid.initLittleComponent("undo", userOptions);
+        that.container = that.options.renderer(that, component.container);
+        fluid.initDomBinder(that);
+        fluid.tabindex(that.locate("undoControl"), 0);
+        fluid.tabindex(that.locate("redoControl"), 0);
+        
+        that.component = component;
+        that.initialModel = {};
+        that.extremalModel = {};
+        fluid.model.copyModel(that.initialModel, component.model);
+        fluid.model.copyModel(that.extremalModel, component.model);
+        
+        that.state = STATE_INITIAL;
+        refreshView(that);
+        var listeners = bindHandlers(that);
+        
+        that.returnedOptions = {
+            listeners: listeners
+        };
+        return that;
+    };
+  
+    fluid.defaults("undo", {  
+        selectors: {
+            undoContainer: ".flc-undo-undoControl",
+            undoControl: ".flc-undo-undoControl",
+            redoContainer: ".flc-undo-redoControl",
+            redoControl: ".flc-undo-redoControl"
+        },
+        
+        strings: {
+            undo: "undo edit",
+            redo: "redo edit"
+        },
+                    
+        renderer: defaultRenderer
+    });
+        
+})(jQuery, fluid_1_3);
+/*
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    var createContentFunc = function (content) {
+        return typeof content === "function" ? content : function () {
+            return content;
+        };
+    };
+
+    var setup = function (that) {
+        that.container.tooltip({
+            content: createContentFunc(that.options.content),
+            position: that.options.position,
+            items: that.options.items,
+            open: function (event) {
+                var tt = $(event.target).tooltip("widget");
+                tt.stop(false, true);
+                tt.hide();
+                if (that.options.delay) {
+                    tt.delay(that.options.delay).fadeIn("default", that.events.afterOpen.fire());
+                } else {
+                    tt.show();
+                    that.events.afterOpen.fire();
+                }
+            },
+            close: function (event) {
+                var tt = $(event.target).tooltip("widget");
+                tt.stop(false, true);
+                tt.hide();
+                tt.clearQueue();
+                that.events.afterClose.fire();
+            } 
+        });
+        
+        that.elm = that.container.tooltip("widget");
+        
+        that.elm.addClass(that.options.styles.tooltip);
+    };
+
+    fluid.tooltip = function (container, options) {
+        var that = fluid.initView("fluid.tooltip", container, options);
+        
+        /**
+         * Updates the contents displayed in the tooltip
+         * 
+         * @param {Object} content, the content to be displayed in the tooltip
+         */
+        that.updateContent = function (content) {
+            that.container.tooltip("option", "content", createContentFunc(content));
+        };
+        
+        /**
+         * Destroys the underlying jquery ui tooltip
+         */
+        that.destroy = function () {
+            that.container.tooltip("destroy");
+        };
+        
+        /**
+         * Manually displays the tooltip
+         */
+        that.open = function () {
+            that.container.tooltip("open");
+        };
+        
+        /**
+         * Manually hides the tooltip
+         */
+        that.close = function () {
+            that.container.tooltip("close");
+        };
+        
+        setup(that);
+        
+        return that;
+    };
+    
+    fluid.defaults("fluid.tooltip", {
+        styles: {
+            tooltip: ""
+        },
+        
+        events: {
+            afterOpen: null,
+            afterClose: null  
+        },
+        
+        content: "",
+        
+        position: {
+            my: "left top",
+            at: "left bottom",
+            offset: "0 5"
+        },
+        
+        items: "*",
+        
+        delay: 300
+    });
+
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3, document, setTimeout*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    function sendKey(control, event, virtualCode, charCode) {
+        var kE = document.createEvent("KeyEvents");
+        kE.initKeyEvent(event, 1, 1, null, 0, 0, 0, 0, virtualCode, charCode);
+        control.dispatchEvent(kE);
+    }
+    
+    /** Set the caret position to the end of a text field's value, also taking care
+     * to scroll the field so that this position is visible.
+     * @param {DOM node} control The control to be scrolled (input, or possibly textarea)
+     * @param value The current value of the control
+     */
+    fluid.setCaretToEnd = function (control, value) {
+        var pos = value ? value.length : 0;
+
+        try {
+            control.focus();
+        // see http://www.quirksmode.org/dom/range_intro.html - in Opera, must detect setSelectionRange first, 
+        // since its support for Microsoft TextRange is buggy
+            if (control.setSelectionRange) {
+
+                control.setSelectionRange(pos, pos);
+                if ($.browser.mozilla && pos > 0) {
+                  // ludicrous fix for Firefox failure to scroll to selection position, inspired by
+                  // http://bytes.com/forum/thread496726.html
+                    sendKey(control, "keypress", 92, 92); // type in a junk character
+                    sendKey(control, "keydown", 8, 0); // delete key must be dispatched exactly like this
+                    sendKey(control, "keypress", 8, 0);
+                }
+            }
+
+            else if (control.createTextRange) {
+                var range = control.createTextRange();
+                range.move("character", pos);
+                range.select();
+            }
+        }
+        catch (e) {} 
+    };
+
+    var switchToViewMode = function (that) {
+        that.editContainer.hide();
+        that.displayModeRenderer.show();
+    };
+    
+    var cancel = function (that) {
+        if (that.isEditing()) {
+            // Roll the edit field back to its old value and close it up.
+            // This setTimeout is necessary on Firefox, since any attempt to modify the 
+            // input control value during the stack processing the ESCAPE key will be ignored.
+            setTimeout(function () {
+                that.editView.value(that.model.value);
+            }, 1);
+            switchToViewMode(that);
+            that.events.afterFinishEdit.fire(that.model.value, that.model.value, 
+                that.editField[0], that.viewEl[0]);
+        }
+    };
+    
+    var finish = function (that) {
+        var newValue = that.editView.value();
+        var oldValue = that.model.value;
+
+        var viewNode = that.viewEl[0];
+        var editNode = that.editField[0];
+        var ret = that.events.onFinishEdit.fire(newValue, oldValue, editNode, viewNode);
+        if (ret === false) {
+            return;
+        }
+        
+        that.updateModelValue(newValue);
+        that.events.afterFinishEdit.fire(newValue, oldValue, editNode, viewNode);
+        
+        switchToViewMode(that);
+    };
+    
+    /** 
+     * Do not allow the textEditButton to regain focus upon completion unless
+     * the keypress is enter or esc.
+     */  
+    var bindEditFinish = function (that) {
+        if (that.options.submitOnEnter === undefined) {
+            that.options.submitOnEnter = "textarea" !== fluid.unwrap(that.editField).nodeName.toLowerCase();
+        }
+        function keyCode(evt) {
+            // Fix for handling arrow key presses. See FLUID-760.
+            return evt.keyCode ? evt.keyCode : (evt.which ? evt.which : 0);          
+        }
+        var escHandler = function (evt) {
+            var code = keyCode(evt);
+            if (code === $.ui.keyCode.ESCAPE) {
+                that.textEditButton.focus(0);
+                cancel(that);
+                return false;
+            }
+        };
+        var finishHandler = function (evt) {
+            var code = keyCode(evt);
+            
+            if (code !== $.ui.keyCode.ENTER) {
+                that.textEditButton.blur();
+                return true;
+            }
+            else {
+                finish(that);
+                that.textEditButton.focus(0);
+            }
+            
+            return false;
+        };
+        if (that.options.submitOnEnter) {
+            that.editContainer.keypress(finishHandler);
+        }
+        that.editContainer.keydown(escHandler);
+    };
+
+    var bindBlurHandler = function (that) {
+        if (that.options.blurHandlerBinder) {
+            that.options.blurHandlerBinder(that);
+        }
+        else {
+            var blurHandler = function (evt) {
+                if (that.isEditing()) {
+                    finish(that);
+                }
+                return false;
+            };
+            that.editField.blur(blurHandler);
+        }
+    };
+
+    var initializeEditView = function (that, initial) {
+        if (!that.editInitialized) { 
+            fluid.inlineEdit.renderEditContainer(that, !that.options.lazyEditView || !initial);
+            
+            if (!that.options.lazyEditView || !initial) {
+                that.editView = fluid.initSubcomponent(that, "editView", that.editField);
+                
+                $.extend(true, that.editView, fluid.initSubcomponent(that, "editAccessor", that.editField));
+        
+                bindEditFinish(that);
+                bindBlurHandler(that);
+                that.editView.refreshView(that);
+                that.editInitialized = true;
+            }
+        }
+    };
+    
+    var edit = function (that) {
+        initializeEditView(that, false);
+      
+        var viewEl = that.viewEl;
+        var displayText = that.displayView.value();
+        that.updateModelValue(that.model.value === "" ? "" : displayText);
+        if (that.options.applyEditPadding) {
+            that.editField.width(Math.max(viewEl.width() + that.options.paddings.edit, that.options.paddings.minimumEdit));
+        }
+
+        that.displayModeRenderer.hide();
+        that.editContainer.show();                  
+
+        // Work around for FLUID-726
+        // Without 'setTimeout' the finish handler gets called with the event and the edit field is inactivated.       
+        setTimeout(function () {
+            fluid.setCaretToEnd(that.editField[0], that.editView.value());
+            if (that.options.selectOnEdit) {
+                that.editField[0].select();
+            }
+        }, 0);
+        that.events.afterBeginEdit.fire();
+    };
+
+    var clearEmptyViewStyles = function (textEl, styles, originalViewPadding) {
+        textEl.removeClass(styles.defaultViewStyle);
+        textEl.css('padding-right', originalViewPadding);
+        textEl.removeClass(styles.emptyDefaultViewText);
+    };
+    
+    var showDefaultViewText = function (that) {
+        that.displayView.value(that.options.defaultViewText);
+        that.viewEl.css('padding-right', that.existingPadding);
+        that.viewEl.addClass(that.options.styles.defaultViewStyle);
+    };
+
+    var showNothing = function (that) {
+        that.displayView.value("");
+        
+        // workaround for FLUID-938:
+        // IE can not style an empty inline element, so force element to be display: inline-block
+        if ($.browser.msie) {
+            if (that.viewEl.css('display') === 'inline') {
+                that.viewEl.css('display', "inline-block");
+            }
+        }
+    };
+
+    var showEditedText = function (that) {
+        that.displayView.value(that.model.value);
+        clearEmptyViewStyles(that.viewEl, that.options.styles, that.existingPadding);
+    };
+    
+    var refreshView = function (that, source) {
+        that.displayView.refreshView(that, source);
+        if (that.editView) {
+            that.editView.refreshView(that, source);
+        }
+    };
+    
+    var initModel = function (that, value) {
+        that.model.value = value;
+        that.refreshView();
+    };
+    
+    var updateModelValue = function (that, newValue, source) {
+        var comparator = that.options.modelComparator;
+        var unchanged = comparator ? comparator(that.model.value, newValue) : 
+            that.model.value === newValue;
+        if (!unchanged) {
+            var oldModel = $.extend(true, {}, that.model);
+            that.model.value = newValue;
+            that.events.modelChanged.fire(that.model, oldModel, source);
+            that.refreshView(source);
+        }
+    };
+        
+    var makeIsEditing = function (that) {
+        var isEditing = false;
+
+        that.events.onBeginEdit.addListener(function () {
+            isEditing = true;
+        });
+        that.events.afterFinishEdit.addListener(function () {
+            isEditing = false; 
+        });
+        return function () {
+            return isEditing;
+        };
+    };
+    
+    var makeEditHandler = function (that) {
+        return function () {
+            var prevent = that.events.onBeginEdit.fire();
+            if (prevent === false) {
+                return false;
+            }
+            edit(that);
+            
+            return true;
+        }; 
+    };    
+    
+    // Initialize the tooltip once the document is ready.
+    // For more details, see http://issues.fluidproject.org/browse/FLUID-1030
+    var initTooltips = function (that) {
+        var tooltipOptions = {
+            content: that.options.tooltipText,
+            position: {
+                my: "left top",
+                at: "left bottom",
+                offset: "0 5"
+            },
+            target: "*",
+            delay: that.options.tooltipDelay,
+            styles: {
+                tooltip: that.options.styles.tooltip
+            }     
+        };
+        
+          fluid.tooltip(that.viewEl, tooltipOptions);
+        
+        if (that.textEditButton) {
+            fluid.tooltip(that.textEditButton, tooltipOptions);
+        }
+    };
+    
+    var calculateInitialPadding = function (viewEl) {
+        var padding = viewEl.css("padding-right");
+        return padding ? parseFloat(padding) : 0;
+    };
+    
+    var setupInlineEdit = function (componentContainer, that) {
+        // Hide the edit container to start
+        if (that.editContainer) {
+            that.editContainer.hide();
+        }
+        
+        // Add tooltip handler if required and available
+        if (that.tooltipEnabled()) {
+            initTooltips(that);
+        }
+        
+        // Setup any registered decorators for the component.
+        that.decorators = fluid.initSubcomponents(that, "componentDecorators", 
+            [that, fluid.COMPONENT_OPTIONS]);
+    };
+    
+    /**
+     * Creates a whole list of inline editors.
+     */
+    var setupInlineEdits = function (editables, options) {
+        var editors = [];
+        editables.each(function (idx, editable) {
+            editors.push(fluid.inlineEdit($(editable), options));
+        });
+        
+        return editors;
+    };
+    
+    /**
+     * Instantiates a new Inline Edit component
+     * 
+     * @param {Object} componentContainer a selector, jquery, or a dom element representing the component's container
+     * @param {Object} options a collection of options settings
+     */
+    fluid.inlineEdit = function (componentContainer, userOptions) {   
+        var that = fluid.initView("inlineEdit", componentContainer, userOptions);
+        
+        that.viewEl = fluid.inlineEdit.setupDisplayText(that);
+        
+        that.displayView = fluid.initSubcomponent(that, "displayView", that.viewEl);
+        $.extend(true, that.displayView, fluid.initSubcomponent(that, "displayAccessor", that.viewEl));
+
+        /**
+         * The current value of the inline editable text. The "model" in MVC terms.
+         */
+        that.model = {value: ""};
+       
+        /**
+         * Switches to edit mode.
+         */
+        that.edit = makeEditHandler(that);
+        
+        /**
+         * Determines if the component is currently in edit mode.
+         * 
+         * @return true if edit mode shown, false if view mode is shown
+         */
+        that.isEditing = makeIsEditing(that);
+        
+        /**
+         * Finishes editing, switching back to view mode.
+         */
+        that.finish = function () {
+            finish(that);
+        };
+
+        /**
+         * Cancels the in-progress edit and switches back to view mode.
+         */
+        that.cancel = function () {
+            cancel(that);
+        };
+
+        /**
+         * Determines if the tooltip feature is enabled.
+         * 
+         * @return true if the tooltip feature is turned on, false if not
+         */
+        that.tooltipEnabled = function () {
+            return that.options.useTooltip && $.fn.tooltip;
+        };
+        
+        /**
+         * Updates the state of the inline editor in the DOM, based on changes that may have
+         * happened to the model.
+         * 
+         * @param {Object} source
+         */
+        that.refreshView = function (source) {
+            refreshView(that, source);
+        };
+        
+        /**
+         * Pushes external changes to the model into the inline editor, refreshing its
+         * rendering in the DOM. The modelChanged event will fire.
+         * 
+         * @param {String} newValue The bare value of the model, that is, the string being edited
+         * @param {Object} source An optional "source" (perhaps a DOM element) which triggered this event
+         */
+        that.updateModelValue = function (newValue, source) {
+            updateModelValue(that, newValue, source);
+        };
+        
+        /**
+         * Pushes external changes to the model into the inline editor, refreshing its
+         * rendering in the DOM. The modelChanged event will fire.
+         * 
+         * @param {Object} newValue The full value of the new model, that is, a model object which contains the editable value as the element named "value"
+         * @param {Object} source An optional "source" (perhaps a DOM element) which triggered this event
+         */
+        that.updateModel = function (newModel, source) {
+            updateModelValue(that, newModel.value, source);
+        };
+        
+        that.existingPadding = calculateInitialPadding(that.viewEl);
+        
+        initModel(that, that.displayView.value());
+        
+        that.displayModeRenderer = that.options.displayModeRenderer(that);  
+        initializeEditView(that, true);
+        setupInlineEdit(componentContainer, that);
+        
+        return that;
+    };
+    
+    /**
+     * Set up and style the edit field.  If an edit field is not provided,
+     * default markup is created for the edit field 
+     * 
+     * @param {string} editStyle The default styling for the edit field
+     * @param {Object} editField The edit field markup provided by the integrator
+     * 
+     * @return eField The styled edit field   
+     */
+    fluid.inlineEdit.setupEditField = function (editStyle, editField) {
+        var eField = $(editField);
+        eField = eField.length ? eField : $("<input type='text' class='flc-inlineEdit-edit'/>");
+        eField.addClass(editStyle);
+        return eField;
+    };
+
+    /**
+     * Set up the edit container and append the edit field to the container.  If an edit container
+     * is not provided, default markup is created.
+     * 
+     * @param {Object} displayContainer The display mode container 
+     * @param {Object} editField The edit field that is to be appended to the edit container 
+     * @param {Object} editContainer The edit container markup provided by the integrator   
+     * 
+     * @return eContainer The edit container containing the edit field   
+     */
+    fluid.inlineEdit.setupEditContainer = function (displayContainer, editField, editContainer) {
+        var eContainer = $(editContainer);
+        eContainer = eContainer.length ? eContainer : $("<span></span>");
+        displayContainer.after(eContainer);
+        eContainer.append(editField);
+        
+        return eContainer;
+    };
+    
+    /**
+     * Default renderer for the edit mode view.
+     * 
+     * @return {Object} container The edit container containing the edit field
+     *                  field The styled edit field  
+     */
+    fluid.inlineEdit.defaultEditModeRenderer = function (that) {
+        var editField = fluid.inlineEdit.setupEditField(that.options.styles.edit, that.editField);
+        var editContainer = fluid.inlineEdit.setupEditContainer(that.displayModeRenderer, editField, that.editContainer);
+        var editModeInstruction = fluid.inlineEdit.setupEditModeInstruction(that.options.styles.editModeInstruction, that.options.strings.editModeInstruction);
+        
+        var id = fluid.allocateSimpleId(editModeInstruction);
+        editField.attr("aria-describedby", id);
+
+        fluid.inlineEdit.positionEditModeInstruction(editModeInstruction, editContainer, editField);
+              
+        // Package up the container and field for the component.
+        return {
+            container: editContainer,
+            field: editField 
+        };
+    };
+    
+    /**
+     * Configures the edit container and view, and uses the component's editModeRenderer to render
+     * the edit container.
+     *  
+     * @param {boolean} lazyEditView If true, will delay rendering of the edit container;
+     *                                            Default is false 
+     */
+    fluid.inlineEdit.renderEditContainer = function (that, lazyEditView) {
+        that.editContainer = that.locate("editContainer");
+        that.editField = that.locate("edit");
+        if (that.editContainer.length !== 1) {
+            if (that.editContainer.length > 1) {
+                fluid.fail("InlineEdit did not find a unique container for selector " + that.options.selectors.editContainer +
+                   ": " + fluid.dumpEl(that.editContainer));
+            }
+        }
+        
+        if (!lazyEditView) {
+            return; 
+        } // do not invoke the renderer, unless this is the "final" effective time
+        
+        var editElms = that.options.editModeRenderer(that);
+        if (editElms) {
+            that.editContainer = editElms.container;
+            that.editField = editElms.field;
+        }
+    };
+
+    /**
+     * Set up the edit mode instruction with aria in edit mode
+     * 
+     * @param {String} editModeInstructionStyle The default styling for the instruction
+     * @param {String} editModeInstructionText The default instruction text
+     * 
+     * @return {jQuery} The displayed instruction in edit mode
+     */
+    fluid.inlineEdit.setupEditModeInstruction = function (editModeInstructionStyle, editModeInstructionText) {
+        var editModeInstruction = $("<p></p>");
+        editModeInstruction.addClass(editModeInstructionStyle);
+        editModeInstruction.text(editModeInstructionText);
+
+        return editModeInstruction;
+    };
+
+    /**
+     * Positions the edit mode instruction directly beneath the edit container
+     * 
+     * @param {Object} editModeInstruction The displayed instruction in edit mode
+     * @param {Object} editContainer The edit container in edit mode
+     * @param {Object} editField The edit field in edit mode
+     */    
+    fluid.inlineEdit.positionEditModeInstruction = function (editModeInstruction, editContainer, editField) {
+        editContainer.append(editModeInstruction);
+        
+        editField.focus(function () {
+            editModeInstruction.show();
+
+            var editFieldPosition = editField.offset();
+            editModeInstruction.css({left: editFieldPosition.left});
+            editModeInstruction.css({top: editFieldPosition.top + editField.height() + 5});
+        });
+    };  
+    
+    /**
+     * Set up and style the display mode container for the viewEl and the textEditButton 
+     * 
+     * @param {Object} styles The default styling for the display mode container
+     * @param {Object} displayModeWrapper The markup used to generate the display mode container
+     * 
+     * @return {jQuery} The styled display mode container
+     */
+    fluid.inlineEdit.setupDisplayModeContainer = function (styles, displayModeWrapper) {
+        var displayModeContainer = $(displayModeWrapper);  
+        displayModeContainer = displayModeContainer.length ? displayModeContainer : $("<span></span>");  
+        displayModeContainer.addClass(styles.displayView);
+        
+        return displayModeContainer;
+    };
+    
+    /**
+     * Retrieve the display text from the DOM.  
+     * 
+     * @return {jQuery} The display text
+     */
+    fluid.inlineEdit.setupDisplayText = function (that) {
+        var viewEl = that.locate("text");
+
+        /*
+         *  Remove the display from the tab order to prevent users to think they
+         *  are able to access the inline edit field, but they cannot since the 
+         *  keyboard event binding is only on the button.
+         */
+        viewEl.attr("tabindex", "-1");
+        viewEl.addClass(that.options.styles.text);
+        
+        return viewEl;
+    };
+    
+    /**
+     * Set up the textEditButton.  Append a background image with appropriate
+     * descriptive text to the button.
+     * 
+     * @return {jQuery} The accessible button located after the display text
+     */
+    fluid.inlineEdit.setupTextEditButton = function (that) {
+        var opts = that.options;
+        var textEditButton = that.locate("textEditButton");
+        
+        if  (textEditButton.length === 0) {
+            var markup = $("<a href='#_' class='flc-inlineEdit-textEditButton'></a>");
+            markup.addClass(opts.styles.textEditButton);
+            markup.text(opts.tooltipText);            
+            
+            /**
+             * Set text for the button and listen
+             * for modelChanged to keep it updated
+             */ 
+            fluid.inlineEdit.updateTextEditButton(markup, that.model.value || opts.defaultViewText, opts.strings.textEditButton);
+            that.events.modelChanged.addListener(function () {
+                fluid.inlineEdit.updateTextEditButton(markup, that.model.value || opts.defaultViewText, opts.strings.textEditButton);
+            });        
+            
+            that.locate("text").after(markup);
+            
+            // Refresh the textEditButton with the newly appended options
+            textEditButton = that.locate("textEditButton");
+        } 
+        return textEditButton;
+    };    
+
+    /**
+     * Update the textEditButton text with the current value of the field.
+     * 
+     * @param {Object} textEditButton the textEditButton
+     * @param {String} model The current value of the inline editable text
+     * @param {Object} strings Text option for the textEditButton
+     */
+    fluid.inlineEdit.updateTextEditButton = function (textEditButton, value, stringTemplate) {
+        var buttonText = fluid.stringTemplate(stringTemplate, {
+            text: value
+        });
+        textEditButton.text(buttonText);
+    };
+    
+    /**
+     * Bind mouse hover event handler to the display mode container.  
+     * 
+     * @param {Object} displayModeRenderer The display mode container
+     * @param {String} invitationStyle The default styling for the display mode container on mouse hover
+     */
+    fluid.inlineEdit.bindHoverHandlers = function (displayModeRenderer, invitationStyle) {
+        var over = function (evt) {
+            displayModeRenderer.addClass(invitationStyle);
+        };     
+        var out = function (evt) {
+            displayModeRenderer.removeClass(invitationStyle);
+        };
+        displayModeRenderer.hover(over, out);
+    };    
+    
+    /**
+     * Bind keyboard focus and blur event handlers to an element
+     * 
+     * @param {Object} element The element to which the event handlers are bound
+     * @param {Object} displayModeRenderer The display mode container
+     * @param {Ojbect} styles The default styling for the display mode container on mouse hover
+     */    
+    fluid.inlineEdit.bindHighlightHandler = function (element, displayModeRenderer, styles) {
+        element = $(element);
+        
+        var focusOn = function () {
+            displayModeRenderer.addClass(styles.focus);
+            displayModeRenderer.addClass(styles.invitation);
+        };
+        var focusOff = function () {
+            displayModeRenderer.removeClass(styles.focus);
+            displayModeRenderer.removeClass(styles.invitation);
+        };
+        
+        element.focus(focusOn);
+        element.blur(focusOff);
+    };        
+    
+    /**
+     * Bind mouse click handler to an element
+     * 
+     * @param {Object} element The element to which the event handler is bound
+     * @param {Object} edit Function to invoke the edit mode
+     * 
+     * @return {boolean} Returns false if entering edit mode
+     */
+    fluid.inlineEdit.bindMouseHandlers = function (element, edit) {
+        element = $(element);
+        
+        var triggerGuard = fluid.inlineEdit.makeEditTriggerGuard(element, edit);
+        element.click(function (e) {
+            triggerGuard(e);
+            return false;
+        });
+    };
+
+    /**
+     * Bind keyboard press handler to an element
+     * 
+     * @param {Object} element The element to which the event handler is bound
+     * @param {Object} edit Function to invoke the edit mode
+     * 
+     * @return {boolean} Returns false if entering edit mode
+     */    
+    fluid.inlineEdit.bindKeyboardHandlers = function (element, edit) {
+        element = $(element);
+        element.attr("role", "button");
+        
+        var guard = fluid.inlineEdit.makeEditTriggerGuard(element, edit);
+        fluid.activatable(element, function (event) {
+            return guard(event);
+        });
+    };
+    
+    /**
+     * Creates an event handler that will trigger the edit mode if caused by something other
+     * than standard HTML controls. The event handler will return false if entering edit mode.
+     * 
+     * @param {Object} element The element to trigger the edit mode
+     * @param {Object} edit Function to invoke the edit mode
+     * 
+     * @return {function} The event handler function
+     */    
+    fluid.inlineEdit.makeEditTriggerGuard = function (element, edit) {
+        var selector = fluid.unwrap(element);
+        return function (event) {
+            // FLUID-2017 - avoid triggering edit mode when operating standard HTML controls. Ultimately this
+            // might need to be extensible, in more complex authouring scenarios.
+            var outer = fluid.findAncestor(event.target, function (elem) {
+                if (/input|select|textarea|button|a/i.test(elem.nodeName) || elem === selector) {
+                    return true; 
+                }
+            });
+            if (outer === selector) {
+                edit();
+                return false;
+            }
+        };
+    };
+    
+    /**
+     * Render the display mode view.  
+     * 
+     * @return {jQuery} The display container containing the display text and 
+     *                             textEditbutton for display mode view
+     */
+    fluid.inlineEdit.defaultDisplayModeRenderer = function (that) {
+        var styles = that.options.styles;
+        
+        var displayModeWrapper = fluid.inlineEdit.setupDisplayModeContainer(styles);
+        var displayModeRenderer = that.viewEl.wrap(displayModeWrapper).parent();
+        
+        that.textEditButton = fluid.inlineEdit.setupTextEditButton(that);
+        displayModeRenderer.append(that.textEditButton);
+        
+        // Add event handlers.
+        fluid.inlineEdit.bindHoverHandlers(displayModeRenderer, styles.invitation);
+        fluid.inlineEdit.bindMouseHandlers(that.viewEl, that.edit);
+        fluid.inlineEdit.bindMouseHandlers(that.textEditButton, that.edit);
+        fluid.inlineEdit.bindKeyboardHandlers(that.textEditButton, that.edit);
+        fluid.inlineEdit.bindHighlightHandler(that.viewEl, displayModeRenderer, styles);
+        fluid.inlineEdit.bindHighlightHandler(that.textEditButton, displayModeRenderer, styles);
+        
+        return displayModeRenderer;
+    };    
+    
+    fluid.inlineEdit.standardAccessor = function (element) {
+        var nodeName = element.nodeName.toLowerCase();
+        var func = "input" === nodeName || "textarea" === nodeName ? "val" : "text";
+        return {
+            value: function (newValue) {
+                return $(element)[func](newValue);
+            }
+        };
+    };
+    
+    fluid.inlineEdit.standardDisplayView = function (viewEl) {
+        var that = {
+            refreshView: function (componentThat, source) {
+                if (componentThat.model.value) {
+                    showEditedText(componentThat);
+                } else if (componentThat.options.defaultViewText) {
+                    showDefaultViewText(componentThat);
+                } else {
+                    showNothing(componentThat);
+                }
+                // If necessary, pad the view element enough that it will be evident to the user.
+                if ($.trim(componentThat.viewEl.text()).length === 0) {
+                    componentThat.viewEl.addClass(componentThat.options.styles.emptyDefaultViewText);
+                    
+                    if(componentThat.existingPadding < componentThat.options.paddings.minimumView) {
+                        componentThat.viewEl.css('padding-right', componentThat.options.paddings.minimumView);
+                    }
+                }
+            }
+        };
+        return that;
+    };
+    
+    fluid.inlineEdit.standardEditView = function (editField) {
+        var that = {
+            refreshView: function (componentThat, source) {
+                if (!source || componentThat.editField && componentThat.editField.index(source) === -1) {
+                    componentThat.editView.value(componentThat.model.value);
+                }
+            }
+        };
+        $.extend(true, that, fluid.inlineEdit.standardAccessor(editField));
+        return that;
+    };
+    
+    /**
+     * Instantiates a list of InlineEdit components.
+     * 
+     * @param {Object} componentContainer the element containing the inline editors
+     * @param {Object} options configuration options for the components
+     */
+    fluid.inlineEdits = function (componentContainer, options) {
+        options = options || {};
+        var selectors = $.extend({}, fluid.defaults("inlineEdits").selectors, options.selectors);
+        
+        // Bind to the DOM.
+        var container = fluid.container(componentContainer);
+        var editables = $(selectors.editables, container);
+        
+        return setupInlineEdits(editables, options);
+    };
+    
+    fluid.defaults("inlineEdit", {  
+        selectors: {
+            text: ".flc-inlineEdit-text",
+            editContainer: ".flc-inlineEdit-editContainer",
+            edit: ".flc-inlineEdit-edit",
+            textEditButton: ".flc-inlineEdit-textEditButton"
+        },
+        
+        styles: {
+            text: "fl-inlineEdit-text",
+            edit: "fl-inlineEdit-edit",
+            invitation: "fl-inlineEdit-invitation",
+            defaultViewStyle: "fl-inlineEdit-emptyText-invitation",
+            emptyDefaultViewText: "fl-inlineEdit-emptyDefaultViewText",
+            focus: "fl-inlineEdit-focus",
+            tooltip: "fl-inlineEdit-tooltip",
+            editModeInstruction: "fl-inlineEdit-editModeInstruction",
+            displayView: "fl-inlineEdit-simple-editableText fl-inlineEdit-textContainer",
+            textEditButton: "fl-offScreen-hidden"
+        },
+        
+        events: {
+            modelChanged: null,
+            onBeginEdit: "preventable",
+            afterBeginEdit: null,
+            onFinishEdit: "preventable",
+            afterFinishEdit: null,
+            afterInitEdit: null
+        },
+
+        strings: {
+            textEditButton: "Edit text %text",
+            editModeInstruction: "Escape to cancel, Enter or Tab when finished"
+        },
+        
+        paddings: {
+            edit: 10,
+            minimumEdit: 80,
+            minimumView: 60
+        },
+        
+        applyEditPadding: true,
+        
+        blurHandlerBinder: null,
+        
+        // set this to true or false to cause unconditional submission, otherwise it will
+        // be inferred from the edit element tag type.
+        submitOnEnter: undefined,
+        
+        modelComparator: null,
+        
+        displayAccessor: {
+            type: "fluid.inlineEdit.standardAccessor"
+        },
+        
+        displayView: {
+            type: "fluid.inlineEdit.standardDisplayView"
+        },
+        
+        editAccessor: {
+            type: "fluid.inlineEdit.standardAccessor"
+        },
+        
+        editView: {
+            type: "fluid.inlineEdit.standardEditView"
+        },
+        
+        displayModeRenderer: fluid.inlineEdit.defaultDisplayModeRenderer,
+            
+        editModeRenderer: fluid.inlineEdit.defaultEditModeRenderer,
+        
+        lazyEditView: false,
+        
+        // this is here for backwards API compatibility, but should be in the strings block
+        defaultViewText: "Click here to edit",
+
+        /** View Mode Tooltip Settings **/
+        useTooltip: true,
+        
+        // this is here for backwards API compatibility, but should be in the strings block
+        tooltipText: "Select or press Enter to edit",
+        
+        tooltipDelay: 1000,
+
+        selectOnEdit: false        
+    });
+    
+    fluid.defaults("inlineEdits", {
+        selectors: {
+            editables: ".flc-inlineEditable"
+        }
+    });
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global setTimeout*/
+/*global jQuery, fluid_1_3, fluid*/
+/*global tinyMCE, FCKeditor, FCKeditorAPI, CKEDITOR*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    /*************************************
+     * Shared Rich Text Editor functions *
+     *************************************/
+     
+    fluid.inlineEdit.makeViewAccessor = function (editorGetFn, setValueFn, getValueFn) {
+        return function (editField) {
+            return {
+                value: function (newValue) {
+                    var editor = editorGetFn(editField);
+                    if (!editor) {
+                        if (newValue) {
+                            $(editField).val(newValue);
+                        }
+                        return "";
+                    }
+                    if (newValue) {
+                        setValueFn(editField, editor, newValue);
+                    }
+                    else {
+                        return getValueFn(editor);
+                    }
+                }
+            };
+        };
+    };
+    
+    fluid.inlineEdit.richTextViewAccessor = function (element) {
+        return {
+            value: function (newValue) {
+                return $(element).html(newValue);
+            }
+        };
+    };        
+    
+    var configureInlineEdit = function (configurationName, container, options) {
+        var defaults = fluid.defaults(configurationName); 
+        var assembleOptions = fluid.merge(defaults ? defaults.mergePolicy: null, {}, defaults, options);
+        return fluid.inlineEdit(container, assembleOptions);
+    };
+
+    fluid.inlineEdit.normalizeHTML = function (value) {
+        var togo = $.trim(value.replace(/\s+/g, " "));
+        togo = togo.replace(/\s+<\//g, "</");
+        togo = togo.replace(/\<(\S+)[^\>\s]*\>/g, function (match) {
+            return match.toLowerCase();
+        });
+        return togo;
+    };
+    
+    fluid.inlineEdit.htmlComparator = function (el1, el2) {
+        return fluid.inlineEdit.normalizeHTML(el1) ===
+           fluid.inlineEdit.normalizeHTML(el2);
+    };
+    
+    fluid.inlineEdit.bindRichTextHighlightHandler = function (element, displayModeRenderer, invitationStyle) {
+        element = $(element);
+        
+        var focusOn = function () {
+            displayModeRenderer.addClass(invitationStyle);
+        };
+        var focusOff = function () {
+            displayModeRenderer.removeClass(invitationStyle);
+        };
+        
+        element.focus(focusOn);
+        element.blur(focusOff);
+    };        
+    
+    fluid.inlineEdit.setupRichTextEditButton = function (that) {
+        var opts = that.options;
+        var textEditButton = that.locate("textEditButton");
+        
+        if  (textEditButton.length === 0) {
+            var markup = $("<a href='#_' class='flc-inlineEdit-textEditButton'></a>");
+            markup.text(opts.strings.textEditButton);
+            
+            that.locate("text").after(markup);
+            
+            // Refresh the textEditButton with the newly appended options
+            textEditButton = that.locate("textEditButton");
+        } 
+        return textEditButton;
+    };    
+    
+    /**
+     * Wrap the display text and the textEditButton with the display mode container  
+     * for better style control.
+     */
+    fluid.inlineEdit.richTextDisplayModeRenderer = function (that) {
+        var styles = that.options.styles;
+        
+        var displayModeWrapper = fluid.inlineEdit.setupDisplayModeContainer(styles);
+        var displayModeRenderer = that.viewEl.wrap(displayModeWrapper).parent();
+        
+        that.textEditButton = fluid.inlineEdit.setupRichTextEditButton(that);
+        displayModeRenderer.append(that.textEditButton);
+        displayModeRenderer.addClass(styles.focus);
+        
+        // Add event handlers.
+        fluid.inlineEdit.bindHoverHandlers(displayModeRenderer, styles.invitation);
+        fluid.inlineEdit.bindMouseHandlers(that.textEditButton, that.edit);
+        fluid.inlineEdit.bindKeyboardHandlers(that.textEditButton, that.edit);
+        fluid.inlineEdit.bindRichTextHighlightHandler(that.viewEl, displayModeRenderer, styles.invitation);
+        fluid.inlineEdit.bindRichTextHighlightHandler(that.textEditButton, displayModeRenderer, styles.invitation);
+        
+        return displayModeRenderer;
+    };        
+
+   
+    /************************
+     * Tiny MCE Integration *
+     ************************/
+    
+    /**
+     * Instantiate a rich-text InlineEdit component that uses an instance of TinyMCE.
+     * 
+     * @param {Object} componentContainer the element containing the inline editors
+     * @param {Object} options configuration options for the components
+     */
+    fluid.inlineEdit.tinyMCE = function (container, options) {
+        var inlineEditor = configureInlineEdit("fluid.inlineEdit.tinyMCE", container, options);
+        tinyMCE.init(inlineEditor.options.tinyMCE);
+        return inlineEditor;
+    };
+        
+    fluid.inlineEdit.tinyMCE.getEditor = function (editField) {
+        return tinyMCE.get(editField.id);
+    };
+    
+    fluid.inlineEdit.tinyMCE.setValue = function (editField, editor, value) {
+        // without this, there is an intermittent race condition if the editor has been created on this event.
+        $(editField).val(value); 
+        editor.setContent(value, {format : 'raw'});
+    };
+    
+    fluid.inlineEdit.tinyMCE.getValue = function (editor) {
+        return editor.getContent();
+    };
+    
+    var flTinyMCE = fluid.inlineEdit.tinyMCE; // Shorter alias for awfully long fully-qualified names.
+    flTinyMCE.viewAccessor = fluid.inlineEdit.makeViewAccessor(flTinyMCE.getEditor, 
+                                                               flTinyMCE.setValue,
+                                                               flTinyMCE.getValue);
+   
+    fluid.inlineEdit.tinyMCE.blurHandlerBinder = function (that) {
+        function focusEditor(editor) {
+            setTimeout(function () {
+                tinyMCE.execCommand('mceFocus', false, that.editField[0].id);
+                if ($.browser.mozilla && $.browser.version.substring(0, 3) === "1.8") {
+                    // Have not yet found any way to make this work on FF2.x - best to do nothing,
+                    // for FLUID-2206
+                    //var body = editor.getBody();
+                    //fluid.setCaretToEnd(body.firstChild, "");
+                    return;
+                }
+                editor.selection.select(editor.getBody(), 1);
+                editor.selection.collapse(0);
+            }, 10);
+        }
+        
+        that.events.afterInitEdit.addListener(function (editor) {
+            focusEditor(editor);
+            var editorBody = editor.getBody();
+
+            // NB - this section has no effect - on most browsers no focus events
+            // are delivered to the actual body
+            fluid.deadMansBlur(that.editField, 
+                {exclusions: {body: $(editorBody)}, 
+                 handler: function () {
+                     that.cancel();
+                 }
+                 });
+        });
+            
+        that.events.afterBeginEdit.addListener(function () {
+            var editor = tinyMCE.get(that.editField[0].id);
+            if (editor) {
+                focusEditor(editor);
+            } 
+        });
+    };
+   
+    fluid.inlineEdit.tinyMCE.editModeRenderer = function (that) {
+        var options = that.options.tinyMCE;
+        options.elements = fluid.allocateSimpleId(that.editField);
+        var oldinit = options.init_instance_callback;
+        
+        options.init_instance_callback = function (instance) {
+            that.events.afterInitEdit.fire(instance);
+            if (oldinit) {
+                oldinit();
+            }
+        };
+        
+        tinyMCE.init(options);
+    };
+    
+    fluid.defaults("fluid.inlineEdit.tinyMCE", {
+        tinyMCE : {
+            mode: "exact", 
+            theme: "simple"
+        },
+        useTooltip: true,
+        selectors: {
+            edit: "textarea" 
+        },
+        styles: {
+            invitation: "fl-inlineEdit-richText-invitation",
+            displayView: "fl-inlineEdit-textContainer",
+            text: ""
+                
+        },
+        strings: {
+            textEditButton: "Edit"
+        },
+        displayAccessor: {
+            type: "fluid.inlineEdit.richTextViewAccessor"
+        },
+        editAccessor: {
+            type: "fluid.inlineEdit.tinyMCE.viewAccessor"
+        },
+        lazyEditView: true,
+        defaultViewText: "Click Edit",
+        modelComparator: fluid.inlineEdit.htmlComparator,
+        blurHandlerBinder: fluid.inlineEdit.tinyMCE.blurHandlerBinder,
+        displayModeRenderer: fluid.inlineEdit.richTextDisplayModeRenderer,
+        editModeRenderer: fluid.inlineEdit.tinyMCE.editModeRenderer
+    });
+    
+    
+    /*****************************
+     * FCKEditor 2.x Integration *
+     *****************************/
+         
+    /**
+     * Instantiate a rich-text InlineEdit component that uses an instance of FCKeditor.
+     * Support for FCKEditor 2.x is now deprecated. We recommend the use of the simpler and more
+     * accessible CKEditor 3 instead.
+     * 
+     * @param {Object} componentContainer the element containing the inline editors
+     * @param {Object} options configuration options for the components
+     */
+    fluid.inlineEdit.FCKEditor = function (container, options) {
+        return configureInlineEdit("fluid.inlineEdit.FCKEditor", container, options);
+    };
+    
+    fluid.inlineEdit.FCKEditor.getEditor = function (editField) {
+        var editor = typeof(FCKeditorAPI) === "undefined" ? null: FCKeditorAPI.GetInstance(editField.id);
+        return editor;
+    };
+    
+    fluid.inlineEdit.FCKEditor.complete = fluid.event.getEventFirer();
+    
+    fluid.inlineEdit.FCKEditor.complete.addListener(function (editor) {
+        var editField = editor.LinkedField;
+        var that = $.data(editField, "fluid.inlineEdit.FCKEditor");
+        if (that && that.events) {
+            that.events.afterInitEdit.fire(editor);
+        }
+    });
+    
+    fluid.inlineEdit.FCKEditor.blurHandlerBinder = function (that) {
+        function focusEditor(editor) {
+            editor.Focus(); 
+        }
+        
+        that.events.afterInitEdit.addListener(
+            function (editor) {
+                focusEditor(editor);
+            }
+        );
+        that.events.afterBeginEdit.addListener(function () {
+            var editor = fluid.inlineEdit.FCKEditor.getEditor(that.editField[0]);
+            if (editor) {
+                focusEditor(editor);
+            } 
+        });
+
+    };
+    
+    fluid.inlineEdit.FCKEditor.editModeRenderer = function (that) {
+        var id = fluid.allocateSimpleId(that.editField);
+        $.data(fluid.unwrap(that.editField), "fluid.inlineEdit.FCKEditor", that);
+        var oFCKeditor = new FCKeditor(id);
+        // The Config object and the FCKEditor object itself expose different configuration sets,
+        // which possess a member "BasePath" with different meanings. Solve FLUID-2452, FLUID-2438
+        // by auto-inferring the inner path for Config (method from http://drupal.org/node/344230 )
+        var opcopy = fluid.copy(that.options.FCKEditor);
+        opcopy.BasePath = opcopy.BasePath + "editor/";
+        $.extend(true, oFCKeditor.Config, opcopy);
+        // somehow, some properties like Width and Height are set on the object itself
+
+        $.extend(true, oFCKeditor, that.options.FCKEditor);
+        oFCKeditor.Config.fluidInstance = that;
+        oFCKeditor.ReplaceTextarea();
+    };
+
+    fluid.inlineEdit.FCKEditor.setValue = function (editField, editor, value) {
+        editor.SetHTML(value);
+    };
+    
+    fluid.inlineEdit.FCKEditor.getValue = function (editor) {
+        return editor.GetHTML();
+    };
+    
+    var flFCKEditor = fluid.inlineEdit.FCKEditor;
+    
+    flFCKEditor.viewAccessor = fluid.inlineEdit.makeViewAccessor(flFCKEditor.getEditor,
+                                                                 flFCKEditor.setValue,
+                                                                 flFCKEditor.getValue);
+    
+    fluid.defaults("fluid.inlineEdit.FCKEditor", {
+        selectors: {
+            edit: "textarea" 
+        },
+        styles: {
+            invitation: "fl-inlineEdit-richText-invitation",
+            displayView: "fl-inlineEdit-textContainer",
+            text: ""
+        },
+        strings: {
+            textEditButton: "Edit"
+        },        
+        displayAccessor: {
+            type: "fluid.inlineEdit.richTextViewAccessor"
+        },
+        editAccessor: {
+            type: "fluid.inlineEdit.FCKEditor.viewAccessor"
+        },
+        lazyEditView: true,
+        defaultViewText: "Click Edit",
+        modelComparator: fluid.inlineEdit.htmlComparator,
+        blurHandlerBinder: fluid.inlineEdit.FCKEditor.blurHandlerBinder,
+        displayModeRenderer: fluid.inlineEdit.richTextDisplayModeRenderer,
+        editModeRenderer: fluid.inlineEdit.FCKEditor.editModeRenderer,
+        FCKEditor: {
+            BasePath: "fckeditor/"    
+        }
+    });
+    
+    
+    /****************************
+     * CKEditor 3.x Integration *
+     ****************************/
+    
+    fluid.inlineEdit.CKEditor = function (container, options) {
+        return configureInlineEdit("fluid.inlineEdit.CKEditor", container, options);
+    };
+    
+    fluid.inlineEdit.CKEditor.getEditor = function (editField) {
+        return CKEDITOR.instances[editField.id];
+    };
+    
+    fluid.inlineEdit.CKEditor.setValue = function (editField, editor, value) {
+        editor.setData(value);
+    };
+    
+    fluid.inlineEdit.CKEditor.getValue = function (editor) {
+        return editor.getData();
+    };
+    
+    var flCKEditor = fluid.inlineEdit.CKEditor;
+    flCKEditor.viewAccessor = fluid.inlineEdit.makeViewAccessor(flCKEditor.getEditor,
+                                                                flCKEditor.setValue,
+                                                                flCKEditor.getValue);
+                             
+    fluid.inlineEdit.CKEditor.focus = function (editor) {
+        setTimeout(function () {
+            // CKEditor won't focus itself except in a timeout.
+            editor.focus();
+        }, 0);
+    };
+    
+    // Special hacked HTML normalisation for CKEditor which spuriously inserts whitespace
+    // just after the first opening tag
+    fluid.inlineEdit.CKEditor.normalizeHTML = function (value) {
+        var togo = fluid.inlineEdit.normalizeHTML(value);
+        var angpos = togo.indexOf(">");
+        if (angpos !== -1 && angpos < togo.length - 1) {
+            if (togo.charAt(angpos + 1) !== " ") {
+                togo = togo.substring(0, angpos + 1) + " " + togo.substring(angpos + 1);
+            }
+        }
+        return togo;
+    };
+    
+    fluid.inlineEdit.CKEditor.htmlComparator = function (el1, el2) {
+        return fluid.inlineEdit.CKEditor.normalizeHTML(el1) ===
+           fluid.inlineEdit.CKEditor.normalizeHTML(el2);
+    };
+                                    
+    fluid.inlineEdit.CKEditor.blurHandlerBinder = function (that) {
+        that.events.afterInitEdit.addListener(fluid.inlineEdit.CKEditor.focus);
+        that.events.afterBeginEdit.addListener(function () {
+            var editor = fluid.inlineEdit.CKEditor.getEditor(that.editField[0]);
+            if (editor) {
+                fluid.inlineEdit.CKEditor.focus(editor);
+            }
+        });
+    };
+    
+    fluid.inlineEdit.CKEditor.editModeRenderer = function (that) {
+        var id = fluid.allocateSimpleId(that.editField);
+        $.data(fluid.unwrap(that.editField), "fluid.inlineEdit.CKEditor", that);
+        var editor = CKEDITOR.replace(id, that.options.CKEditor);
+        editor.on("instanceReady", function (e) {
+            fluid.inlineEdit.CKEditor.focus(e.editor);
+            that.events.afterInitEdit.fire(e.editor);
+        });
+    };                                                     
+    
+    fluid.defaults("fluid.inlineEdit.CKEditor", {
+        selectors: {
+            edit: "textarea" 
+        },
+        styles: {
+            invitation: "fl-inlineEdit-richText-invitation",
+            displayView: "fl-inlineEdit-textContainer",
+            text: ""
+        },
+        strings: {
+            textEditButton: "Edit"
+        },        
+        displayAccessor: {
+            type: "fluid.inlineEdit.richTextViewAccessor"
+        },
+        editAccessor: {
+            type: "fluid.inlineEdit.CKEditor.viewAccessor"
+        },
+        lazyEditView: true,
+        defaultViewText: "Click Edit",
+        modelComparator: fluid.inlineEdit.CKEditor.htmlComparator,
+        blurHandlerBinder: fluid.inlineEdit.CKEditor.blurHandlerBinder,
+        displayModeRenderer: fluid.inlineEdit.richTextDisplayModeRenderer,
+        editModeRenderer: fluid.inlineEdit.CKEditor.editModeRenderer,
+        CKEditor: {
+            // CKEditor-specific configuration goes here.
+        }
+    });
+    
+    /************************
+     * Dropdown Integration *
+     ************************/    
+    /**
+     * Instantiate a drop-down InlineEdit component
+     * 
+     * @param {Object} container
+     * @param {Object} options
+     */
+    fluid.inlineEdit.dropdown = function (container, options) {
+        return configureInlineEdit("fluid.inlineEdit.dropdown", container, options);
+    };
+
+    fluid.inlineEdit.dropdown.editModeRenderer = function (that) {
+        var id = fluid.allocateSimpleId(that.editField);
+        that.editField.selectbox({
+            finishHandler: function () {
+                that.finish();
+            }
+        });
+        return {
+            container: that.editContainer,
+            field: $("input.selectbox", that.editContainer) 
+        };
+    };
+   
+    fluid.inlineEdit.dropdown.blurHandlerBinder = function (that) {
+        fluid.deadMansBlur(that.editField, {
+             exclusions: {selectBox: $("div.selectbox-wrapper li", that.editContainer)},
+             handler: function () {
+                that.cancel();
+                }
+            });
+    };
+    
+    fluid.defaults("fluid.inlineEdit.dropdown", {
+        applyEditPadding: false,
+        blurHandlerBinder: fluid.inlineEdit.dropdown.blurHandlerBinder,
+        editModeRenderer: fluid.inlineEdit.dropdown.editModeRenderer
+    });
+})(jQuery, fluid_1_3);
+
+
+// This must be written outside any scope as a result of the FCKEditor event model.
+// Do not overwrite this function, if you wish to add your own listener to FCK completion,
+// register it with the standard fluid event firer at fluid.inlineEdit.FCKEditor.complete
+function FCKeditor_OnComplete(editorInstance) {
+    fluid.inlineEdit.FCKEditor.complete.fire(editorInstance);
+}
+/*
+ * jQuery UI Draggable 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.mouse.js
+ *     jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.draggable", $.ui.mouse, {
+       widgetEventPrefix: "drag",
+       options: {
+               addClasses: true,
+               appendTo: "parent",
+               axis: false,
+               connectToSortable: false,
+               containment: false,
+               cursor: "auto",
+               cursorAt: false,
+               grid: false,
+               handle: false,
+               helper: "original",
+               iframeFix: false,
+               opacity: false,
+               refreshPositions: false,
+               revert: false,
+               revertDuration: 500,
+               scope: "default",
+               scroll: true,
+               scrollSensitivity: 20,
+               scrollSpeed: 20,
+               snap: false,
+               snapMode: "both",
+               snapTolerance: 20,
+               stack: false,
+               zIndex: false
+       },
+       _create: function() {
+
+               if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
+                       this.element[0].style.position = 'relative';
+
+               (this.options.addClasses && this.element.addClass("ui-draggable"));
+               (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
+
+               this._mouseInit();
+
+       },
+
+       destroy: function() {
+               if(!this.element.data('draggable')) return;
+               this.element
+                       .removeData("draggable")
+                       .unbind(".draggable")
+                       .removeClass("ui-draggable"
+                               + " ui-draggable-dragging"
+                               + " ui-draggable-disabled");
+               this._mouseDestroy();
+
+               return this;
+       },
+
+       _mouseCapture: function(event) {
+
+               var o = this.options;
+
+               // among others, prevent a drag on a resizable-handle
+               if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
+                       return false;
+
+               //Quit if we're not on a valid handle
+               this.handle = this._getHandle(event);
+               if (!this.handle)
+                       return false;
+
+               return true;
+
+       },
+
+       _mouseStart: function(event) {
+
+               var o = this.options;
+
+               //Create and append the visible helper
+               this.helper = this._createHelper(event);
+
+               //Cache the helper size
+               this._cacheHelperProportions();
+
+               //If ddmanager is used for droppables, set the global draggable
+               if($.ui.ddmanager)
+                       $.ui.ddmanager.current = this;
+
+               /*
+                * - Position generation -
+                * This block generates everything position related - it's the core of draggables.
+                */
+
+               //Cache the margins of the original element
+               this._cacheMargins();
+
+               //Store the helper's css position
+               this.cssPosition = this.helper.css("position");
+               this.scrollParent = this.helper.scrollParent();
+
+               //The element's absolute position on the page minus margins
+               this.offset = this.positionAbs = this.element.offset();
+               this.offset = {
+                       top: this.offset.top - this.margins.top,
+                       left: this.offset.left - this.margins.left
+               };
+
+               $.extend(this.offset, {
+                       click: { //Where the click happened, relative to the element
+                               left: event.pageX - this.offset.left,
+                               top: event.pageY - this.offset.top
+                       },
+                       parent: this._getParentOffset(),
+                       relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
+               });
+
+               //Generate the original position
+               this.originalPosition = this.position = this._generatePosition(event);
+               this.originalPageX = event.pageX;
+               this.originalPageY = event.pageY;
+
+               //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
+               (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
+
+               //Set a containment if given in the options
+               if(o.containment)
+                       this._setContainment();
+
+               //Trigger event + callbacks
+               if(this._trigger("start", event) === false) {
+                       this._clear();
+                       return false;
+               }
+
+               //Recache the helper size
+               this._cacheHelperProportions();
+
+               //Prepare the droppable offsets
+               if ($.ui.ddmanager && !o.dropBehaviour)
+                       $.ui.ddmanager.prepareOffsets(this, event);
+
+               this.helper.addClass("ui-draggable-dragging");
+               this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
+               return true;
+       },
+
+       _mouseDrag: function(event, noPropagation) {
+
+               //Compute the helpers position
+               this.position = this._generatePosition(event);
+               this.positionAbs = this._convertPositionTo("absolute");
+
+               //Call plugins and callbacks and use the resulting position if something is returned
+               if (!noPropagation) {
+                       var ui = this._uiHash();
+                       if(this._trigger('drag', event, ui) === false) {
+                               this._mouseUp({});
+                               return false;
+                       }
+                       this.position = ui.position;
+               }
+
+               if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
+               if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
+               if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
+
+               return false;
+       },
+
+       _mouseStop: function(event) {
+
+               //If we are using droppables, inform the manager about the drop
+               var dropped = false;
+               if ($.ui.ddmanager && !this.options.dropBehaviour)
+                       dropped = $.ui.ddmanager.drop(this, event);
+
+               //if a drop comes from outside (a sortable)
+               if(this.dropped) {
+                       dropped = this.dropped;
+                       this.dropped = false;
+               }
+               
+               //if the original element is removed, don't bother to continue
+               if(!this.element[0] || !this.element[0].parentNode)
+                       return false;
+
+               if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
+                       var self = this;
+                       $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
+                               if(self._trigger("stop", event) !== false) {
+                                       self._clear();
+                               }
+                       });
+               } else {
+                       if(this._trigger("stop", event) !== false) {
+                               this._clear();
+                       }
+               }
+
+               return false;
+       },
+       
+       cancel: function() {
+               
+               if(this.helper.is(".ui-draggable-dragging")) {
+                       this._mouseUp({});
+               } else {
+                       this._clear();
+               }
+               
+               return this;
+               
+       },
+
+       _getHandle: function(event) {
+
+               var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
+               $(this.options.handle, this.element)
+                       .find("*")
+                       .andSelf()
+                       .each(function() {
+                               if(this == event.target) handle = true;
+                       });
+
+               return handle;
+
+       },
+
+       _createHelper: function(event) {
+
+               var o = this.options;
+               var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
+
+               if(!helper.parents('body').length)
+                       helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
+
+               if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
+                       helper.css("position", "absolute");
+
+               return helper;
+
+       },
+
+       _adjustOffsetFromHelper: function(obj) {
+               if (typeof obj == 'string') {
+                       obj = obj.split(' ');
+               }
+               if ($.isArray(obj)) {
+                       obj = {left: +obj[0], top: +obj[1] || 0};
+               }
+               if ('left' in obj) {
+                       this.offset.click.left = obj.left + this.margins.left;
+               }
+               if ('right' in obj) {
+                       this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
+               }
+               if ('top' in obj) {
+                       this.offset.click.top = obj.top + this.margins.top;
+               }
+               if ('bottom' in obj) {
+                       this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
+               }
+       },
+
+       _getParentOffset: function() {
+
+               //Get the offsetParent and cache its position
+               this.offsetParent = this.helper.offsetParent();
+               var po = this.offsetParent.offset();
+
+               // This is a special case where we need to modify a offset calculated on start, since the following happened:
+               // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
+               // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
+               //    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
+               if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
+                       po.left += this.scrollParent.scrollLeft();
+                       po.top += this.scrollParent.scrollTop();
+               }
+
+               if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
+               || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
+                       po = { top: 0, left: 0 };
+
+               return {
+                       top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
+                       left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
+               };
+
+       },
+
+       _getRelativeOffset: function() {
+
+               if(this.cssPosition == "relative") {
+                       var p = this.element.position();
+                       return {
+                               top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
+                               left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
+                       };
+               } else {
+                       return { top: 0, left: 0 };
+               }
+
+       },
+
+       _cacheMargins: function() {
+               this.margins = {
+                       left: (parseInt(this.element.css("marginLeft"),10) || 0),
+                       top: (parseInt(this.element.css("marginTop"),10) || 0)
+               };
+       },
+
+       _cacheHelperProportions: function() {
+               this.helperProportions = {
+                       width: this.helper.outerWidth(),
+                       height: this.helper.outerHeight()
+               };
+       },
+
+       _setContainment: function() {
+
+               var o = this.options;
+               if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
+               if(o.containment == 'document' || o.containment == 'window') this.containment = [
+                       0 - this.offset.relative.left - this.offset.parent.left,
+                       0 - this.offset.relative.top - this.offset.parent.top,
+                       $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
+                       ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
+               ];
+
+               if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
+                       var ce = $(o.containment)[0]; if(!ce) return;
+                       var co = $(o.containment).offset();
+                       var over = ($(ce).css("overflow") != 'hidden');
+
+                       this.containment = [
+                               co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
+                               co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
+                               co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
+                               co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
+                       ];
+               } else if(o.containment.constructor == Array) {
+                       this.containment = o.containment;
+               }
+
+       },
+
+       _convertPositionTo: function(d, pos) {
+
+               if(!pos) pos = this.position;
+               var mod = d == "absolute" ? 1 : -1;
+               var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+               return {
+                       top: (
+                               pos.top                                                                                                                                 // The absolute mouse position
+                               + this.offset.relative.top * mod                                                                                // Only for relative positioned nodes: Relative offset from element to offset parent
+                               + this.offset.parent.top * mod                                                                                  // The offsetParent's offset without borders (offset + border)
+                               - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
+                       ),
+                       left: (
+                               pos.left                                                                                                                                // The absolute mouse position
+                               + this.offset.relative.left * mod                                                                               // Only for relative positioned nodes: Relative offset from element to offset parent
+                               + this.offset.parent.left * mod                                                                                 // The offsetParent's offset without borders (offset + border)
+                               - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
+                       )
+               };
+
+       },
+
+       _generatePosition: function(event) {
+
+               var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+               var pageX = event.pageX;
+               var pageY = event.pageY;
+
+               /*
+                * - Position constraining -
+                * Constrain the position to a mix of grid, containment.
+                */
+
+               if(this.originalPosition) { //If we are not dragging yet, we won't check for options
+
+                       if(this.containment) {
+                               if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
+                               if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
+                               if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
+                               if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
+                       }
+
+                       if(o.grid) {
+                               var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
+                               pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
+
+                               var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
+                               pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
+                       }
+
+               }
+
+               return {
+                       top: (
+                               pageY                                                                                                                           // The absolute mouse position
+                               - this.offset.click.top                                                                                                 // Click offset (relative to the element)
+                               - this.offset.relative.top                                                                                              // Only for relative positioned nodes: Relative offset from element to offset parent
+                               - this.offset.parent.top                                                                                                // The offsetParent's offset without borders (offset + border)
+                               + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
+                       ),
+                       left: (
+                               pageX                                                                                                                           // The absolute mouse position
+                               - this.offset.click.left                                                                                                // Click offset (relative to the element)
+                               - this.offset.relative.left                                                                                             // Only for relative positioned nodes: Relative offset from element to offset parent
+                               - this.offset.parent.left                                                                                               // The offsetParent's offset without borders (offset + border)
+                               + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
+                       )
+               };
+
+       },
+
+       _clear: function() {
+               this.helper.removeClass("ui-draggable-dragging");
+               if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
+               //if($.ui.ddmanager) $.ui.ddmanager.current = null;
+               this.helper = null;
+               this.cancelHelperRemoval = false;
+       },
+
+       // From now on bulk stuff - mainly helpers
+
+       _trigger: function(type, event, ui) {
+               ui = ui || this._uiHash();
+               $.ui.plugin.call(this, type, [event, ui]);
+               if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
+               return $.Widget.prototype._trigger.call(this, type, event, ui);
+       },
+
+       plugins: {},
+
+       _uiHash: function(event) {
+               return {
+                       helper: this.helper,
+                       position: this.position,
+                       originalPosition: this.originalPosition,
+                       offset: this.positionAbs
+               };
+       }
+
+});
+
+$.extend($.ui.draggable, {
+       version: "1.8"
+});
+
+$.ui.plugin.add("draggable", "connectToSortable", {
+       start: function(event, ui) {
+
+               var inst = $(this).data("draggable"), o = inst.options,
+                       uiSortable = $.extend({}, ui, { item: inst.element });
+               inst.sortables = [];
+               $(o.connectToSortable).each(function() {
+                       var sortable = $.data(this, 'sortable');
+                       if (sortable && !sortable.options.disabled) {
+                               inst.sortables.push({
+                                       instance: sortable,
+                                       shouldRevert: sortable.options.revert
+                               });
+                               sortable._refreshItems();       //Do a one-time refresh at start to refresh the containerCache
+                               sortable._trigger("activate", event, uiSortable);
+                       }
+               });
+
+       },
+       stop: function(event, ui) {
+
+               //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
+               var inst = $(this).data("draggable"),
+                       uiSortable = $.extend({}, ui, { item: inst.element });
+
+               $.each(inst.sortables, function() {
+                       if(this.instance.isOver) {
+
+                               this.instance.isOver = 0;
+
+                               inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
+                               this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
+
+                               //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
+                               if(this.shouldRevert) this.instance.options.revert = true;
+
+                               //Trigger the stop of the sortable
+                               this.instance._mouseStop(event);
+
+                               this.instance.options.helper = this.instance.options._helper;
+
+                               //If the helper has been the original item, restore properties in the sortable
+                               if(inst.options.helper == 'original')
+                                       this.instance.currentItem.css({ top: 'auto', left: 'auto' });
+
+                       } else {
+                               this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
+                               this.instance._trigger("deactivate", event, uiSortable);
+                       }
+
+               });
+
+       },
+       drag: function(event, ui) {
+
+               var inst = $(this).data("draggable"), self = this;
+
+               var checkPos = function(o) {
+                       var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
+                       var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
+                       var itemHeight = o.height, itemWidth = o.width;
+                       var itemTop = o.top, itemLeft = o.left;
+
+                       return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
+               };
+
+               $.each(inst.sortables, function(i) {
+                       
+                       //Copy over some variables to allow calling the sortable's native _intersectsWith
+                       this.instance.positionAbs = inst.positionAbs;
+                       this.instance.helperProportions = inst.helperProportions;
+                       this.instance.offset.click = inst.offset.click;
+                       
+                       if(this.instance._intersectsWith(this.instance.containerCache)) {
+
+                               //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
+                               if(!this.instance.isOver) {
+
+                                       this.instance.isOver = 1;
+                                       //Now we fake the start of dragging for the sortable instance,
+                                       //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
+                                       //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
+                                       this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
+                                       this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
+                                       this.instance.options.helper = function() { return ui.helper[0]; };
+
+                                       event.target = this.instance.currentItem[0];
+                                       this.instance._mouseCapture(event, true);
+                                       this.instance._mouseStart(event, true, true);
+
+                                       //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
+                                       this.instance.offset.click.top = inst.offset.click.top;
+                                       this.instance.offset.click.left = inst.offset.click.left;
+                                       this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
+                                       this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
+
+                                       inst._trigger("toSortable", event);
+                                       inst.dropped = this.instance.element; //draggable revert needs that
+                                       //hack so receive/update callbacks work (mostly)
+                                       inst.currentItem = inst.element;
+                                       this.instance.fromOutside = inst;
+
+                               }
+
+                               //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
+                               if(this.instance.currentItem) this.instance._mouseDrag(event);
+
+                       } else {
+
+                               //If it doesn't intersect with the sortable, and it intersected before,
+                               //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
+                               if(this.instance.isOver) {
+
+                                       this.instance.isOver = 0;
+                                       this.instance.cancelHelperRemoval = true;
+                                       
+                                       //Prevent reverting on this forced stop
+                                       this.instance.options.revert = false;
+                                       
+                                       // The out event needs to be triggered independently
+                                       this.instance._trigger('out', event, this.instance._uiHash(this.instance));
+                                       
+                                       this.instance._mouseStop(event, true);
+                                       this.instance.options.helper = this.instance.options._helper;
+
+                                       //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
+                                       this.instance.currentItem.remove();
+                                       if(this.instance.placeholder) this.instance.placeholder.remove();
+
+                                       inst._trigger("fromSortable", event);
+                                       inst.dropped = false; //draggable revert needs that
+                               }
+
+                       };
+
+               });
+
+       }
+});
+
+$.ui.plugin.add("draggable", "cursor", {
+       start: function(event, ui) {
+               var t = $('body'), o = $(this).data('draggable').options;
+               if (t.css("cursor")) o._cursor = t.css("cursor");
+               t.css("cursor", o.cursor);
+       },
+       stop: function(event, ui) {
+               var o = $(this).data('draggable').options;
+               if (o._cursor) $('body').css("cursor", o._cursor);
+       }
+});
+
+$.ui.plugin.add("draggable", "iframeFix", {
+       start: function(event, ui) {
+               var o = $(this).data('draggable').options;
+               $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
+                       $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
+                       .css({
+                               width: this.offsetWidth+"px", height: this.offsetHeight+"px",
+                               position: "absolute", opacity: "0.001", zIndex: 1000
+                       })
+                       .css($(this).offset())
+                       .appendTo("body");
+               });
+       },
+       stop: function(event, ui) {
+               $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
+       }
+});
+
+$.ui.plugin.add("draggable", "opacity", {
+       start: function(event, ui) {
+               var t = $(ui.helper), o = $(this).data('draggable').options;
+               if(t.css("opacity")) o._opacity = t.css("opacity");
+               t.css('opacity', o.opacity);
+       },
+       stop: function(event, ui) {
+               var o = $(this).data('draggable').options;
+               if(o._opacity) $(ui.helper).css('opacity', o._opacity);
+       }
+});
+
+$.ui.plugin.add("draggable", "scroll", {
+       start: function(event, ui) {
+               var i = $(this).data("draggable");
+               if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
+       },
+       drag: function(event, ui) {
+
+               var i = $(this).data("draggable"), o = i.options, scrolled = false;
+
+               if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
+
+                       if(!o.axis || o.axis != 'x') {
+                               if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
+                                       i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
+                               else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
+                                       i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
+                       }
+
+                       if(!o.axis || o.axis != 'y') {
+                               if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
+                                       i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
+                               else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
+                                       i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
+                       }
+
+               } else {
+
+                       if(!o.axis || o.axis != 'x') {
+                               if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
+                                       scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
+                               else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
+                                       scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
+                       }
+
+                       if(!o.axis || o.axis != 'y') {
+                               if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
+                                       scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
+                               else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
+                                       scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
+                       }
+
+               }
+
+               if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
+                       $.ui.ddmanager.prepareOffsets(i, event);
+
+       }
+});
+
+$.ui.plugin.add("draggable", "snap", {
+       start: function(event, ui) {
+
+               var i = $(this).data("draggable"), o = i.options;
+               i.snapElements = [];
+
+               $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
+                       var $t = $(this); var $o = $t.offset();
+                       if(this != i.element[0]) i.snapElements.push({
+                               item: this,
+                               width: $t.outerWidth(), height: $t.outerHeight(),
+                               top: $o.top, left: $o.left
+                       });
+               });
+
+       },
+       drag: function(event, ui) {
+
+               var inst = $(this).data("draggable"), o = inst.options;
+               var d = o.snapTolerance;
+
+               var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
+                       y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
+
+               for (var i = inst.snapElements.length - 1; i >= 0; i--){
+
+                       var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
+                               t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
+
+                       //Yes, I know, this is insane ;)
+                       if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
+                               if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+                               inst.snapElements[i].snapping = false;
+                               continue;
+                       }
+
+                       if(o.snapMode != 'inner') {
+                               var ts = Math.abs(t - y2) <= d;
+                               var bs = Math.abs(b - y1) <= d;
+                               var ls = Math.abs(l - x2) <= d;
+                               var rs = Math.abs(r - x1) <= d;
+                               if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+                               if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
+                               if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
+                               if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
+                       }
+
+                       var first = (ts || bs || ls || rs);
+
+                       if(o.snapMode != 'outer') {
+                               var ts = Math.abs(t - y1) <= d;
+                               var bs = Math.abs(b - y2) <= d;
+                               var ls = Math.abs(l - x1) <= d;
+                               var rs = Math.abs(r - x2) <= d;
+                               if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
+                               if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+                               if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
+                               if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
+                       }
+
+                       if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
+                               (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+                       inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
+
+               };
+
+       }
+});
+
+$.ui.plugin.add("draggable", "stack", {
+       start: function(event, ui) {
+
+               var o = $(this).data("draggable").options;
+
+               var group = $.makeArray($(o.stack)).sort(function(a,b) {
+                       return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
+               });
+               if (!group.length) { return; }
+               
+               var min = parseInt(group[0].style.zIndex) || 0;
+               $(group).each(function(i) {
+                       this.style.zIndex = min + i;
+               });
+
+               this[0].style.zIndex = min + group.length;
+
+       }
+});
+
+$.ui.plugin.add("draggable", "zIndex", {
+       start: function(event, ui) {
+               var t = $(ui.helper), o = $(this).data("draggable").options;
+               if(t.css("zIndex")) o._zIndex = t.css("zIndex");
+               t.css('zIndex', o.zIndex);
+       },
+       stop: function(event, ui) {
+               var o = $(this).data("draggable").options;
+               if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
+       }
+});
+
+})(jQuery);
+/*
+ * jQuery UI Dialog 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.widget.js
+ *  jquery.ui.button.js
+ *     jquery.ui.draggable.js
+ *     jquery.ui.mouse.js
+ *     jquery.ui.position.js
+ *     jquery.ui.resizable.js
+ */
+(function($) {
+
+var uiDialogClasses =
+       'ui-dialog ' +
+       'ui-widget ' +
+       'ui-widget-content ' +
+       'ui-corner-all ';
+
+$.widget("ui.dialog", {
+       options: {
+               autoOpen: true,
+               buttons: {},
+               closeOnEscape: true,
+               closeText: 'close',
+               dialogClass: '',
+               draggable: true,
+               hide: null,
+               height: 'auto',
+               maxHeight: false,
+               maxWidth: false,
+               minHeight: 150,
+               minWidth: 150,
+               modal: false,
+               position: 'center',
+               resizable: true,
+               show: null,
+               stack: true,
+               title: '',
+               width: 300,
+               zIndex: 1000
+       },
+       _create: function() {
+               this.originalTitle = this.element.attr('title');
+
+               var self = this,
+                       options = self.options,
+
+                       title = options.title || self.originalTitle || '&#160;',
+                       titleId = $.ui.dialog.getTitleId(self.element),
+
+                       uiDialog = (self.uiDialog = $('<div></div>'))
+                               .appendTo(document.body)
+                               .hide()
+                               .addClass(uiDialogClasses + options.dialogClass)
+                               .css({
+                                       zIndex: options.zIndex
+                               })
+                               // setting tabIndex makes the div focusable
+                               // setting outline to 0 prevents a border on focus in Mozilla
+                               .attr('tabIndex', -1).css('outline', 0).keydown(function(event) {
+                                       if (options.closeOnEscape && event.keyCode &&
+                                               event.keyCode === $.ui.keyCode.ESCAPE) {
+                                               
+                                               self.close(event);
+                                               event.preventDefault();
+                                       }
+                               })
+                               .attr({
+                                       role: 'dialog',
+                                       'aria-labelledby': titleId
+                               })
+                               .mousedown(function(event) {
+                                       self.moveToTop(false, event);
+                               }),
+
+                       uiDialogContent = self.element
+                               .show()
+                               .removeAttr('title')
+                               .addClass(
+                                       'ui-dialog-content ' +
+                                       'ui-widget-content')
+                               .appendTo(uiDialog),
+
+                       uiDialogTitlebar = (self.uiDialogTitlebar = $('<div></div>'))
+                               .addClass(
+                                       'ui-dialog-titlebar ' +
+                                       'ui-widget-header ' +
+                                       'ui-corner-all ' +
+                                       'ui-helper-clearfix'
+                               )
+                               .prependTo(uiDialog),
+
+                       uiDialogTitlebarClose = $('<a href="#"></a>')
+                               .addClass(
+                                       'ui-dialog-titlebar-close ' +
+                                       'ui-corner-all'
+                               )
+                               .attr('role', 'button')
+                               .hover(
+                                       function() {
+                                               uiDialogTitlebarClose.addClass('ui-state-hover');
+                                       },
+                                       function() {
+                                               uiDialogTitlebarClose.removeClass('ui-state-hover');
+                                       }
+                               )
+                               .focus(function() {
+                                       uiDialogTitlebarClose.addClass('ui-state-focus');
+                               })
+                               .blur(function() {
+                                       uiDialogTitlebarClose.removeClass('ui-state-focus');
+                               })
+                               .click(function(event) {
+                                       self.close(event);
+                                       return false;
+                               })
+                               .appendTo(uiDialogTitlebar),
+
+                       uiDialogTitlebarCloseText = (self.uiDialogTitlebarCloseText = $('<span></span>'))
+                               .addClass(
+                                       'ui-icon ' +
+                                       'ui-icon-closethick'
+                               )
+                               .text(options.closeText)
+                               .appendTo(uiDialogTitlebarClose),
+
+                       uiDialogTitle = $('<span></span>')
+                               .addClass('ui-dialog-title')
+                               .attr('id', titleId)
+                               .html(title)
+                               .prependTo(uiDialogTitlebar);
+
+               //handling of deprecated beforeclose (vs beforeClose) option
+               //Ticket #4669 http://dev.jqueryui.com/ticket/4669
+               //TODO: remove in 1.9pre
+               if ($.isFunction(options.beforeclose) && !$.isFunction(options.beforeClose)) {
+                       options.beforeClose = options.beforeclose;
+               }
+
+               uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection();
+
+               if (options.draggable && $.fn.draggable) {
+                       self._makeDraggable();
+               }
+               if (options.resizable && $.fn.resizable) {
+                       self._makeResizable();
+               }
+
+               self._createButtons(options.buttons);
+               self._isOpen = false;
+
+               if ($.fn.bgiframe) {
+                       uiDialog.bgiframe();
+               }
+       },
+       _init: function() {
+               if ( this.options.autoOpen ) {
+                       this.open();
+               }
+       },
+
+       destroy: function() {
+               var self = this;
+               
+               if (self.overlay) {
+                       self.overlay.destroy();
+               }
+               self.uiDialog.hide();
+               self.element
+                       .unbind('.dialog')
+                       .removeData('dialog')
+                       .removeClass('ui-dialog-content ui-widget-content')
+                       .hide().appendTo('body');
+               self.uiDialog.remove();
+
+               if (self.originalTitle) {
+                       self.element.attr('title', self.originalTitle);
+               }
+
+               return self;
+       },
+       
+       widget: function() {
+               return this.uiDialog;
+       },
+
+       close: function(event) {
+               var self = this,
+                       maxZ;
+               
+               if (false === self._trigger('beforeClose', event)) {
+                       return;
+               }
+
+               if (self.overlay) {
+                       self.overlay.destroy();
+               }
+               self.uiDialog.unbind('keypress.ui-dialog');
+
+               self._isOpen = false;
+
+               if (self.options.hide) {
+                       self.uiDialog.hide(self.options.hide, function() {
+                               self._trigger('close', event);
+                       });
+               } else {
+                       self.uiDialog.hide();
+                       self._trigger('close', event);
+               }
+
+               $.ui.dialog.overlay.resize();
+
+               // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
+               if (self.options.modal) {
+                       maxZ = 0;
+                       $('.ui-dialog').each(function() {
+                               if (this !== self.uiDialog[0]) {
+                                       maxZ = Math.max(maxZ, $(this).css('z-index'));
+                               }
+                       });
+                       $.ui.dialog.maxZ = maxZ;
+               }
+
+               return self;
+       },
+
+       isOpen: function() {
+               return this._isOpen;
+       },
+
+       // the force parameter allows us to move modal dialogs to their correct
+       // position on open
+       moveToTop: function(force, event) {
+               var self = this,
+                       options = self.options,
+                       saveScroll;
+               
+               if ((options.modal && !force) ||
+                       (!options.stack && !options.modal)) {
+                       return self._trigger('focus', event);
+               }
+               
+               if (options.zIndex > $.ui.dialog.maxZ) {
+                       $.ui.dialog.maxZ = options.zIndex;
+               }
+               if (self.overlay) {
+                       $.ui.dialog.maxZ += 1;
+                       self.overlay.$el.css('z-index', $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ);
+               }
+
+               //Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed.
+               //  http://ui.jquery.com/bugs/ticket/3193
+               saveScroll = { scrollTop: self.element.attr('scrollTop'), scrollLeft: self.element.attr('scrollLeft') };
+               $.ui.dialog.maxZ += 1;
+               self.uiDialog.css('z-index', $.ui.dialog.maxZ);
+               self.element.attr(saveScroll);
+               self._trigger('focus', event);
+
+               return self;
+       },
+
+       open: function() {
+               if (this._isOpen) { return; }
+
+               var self = this,
+                       options = self.options,
+                       uiDialog = self.uiDialog;
+
+               self.overlay = options.modal ? new $.ui.dialog.overlay(self) : null;
+               if (uiDialog.next().length) {
+                       uiDialog.appendTo('body');
+               }
+               self._size();
+               self._position(options.position);
+               uiDialog.show(options.show);
+               self.moveToTop(true);
+
+               // prevent tabbing out of modal dialogs
+               if (options.modal) {
+                       uiDialog.bind('keypress.ui-dialog', function(event) {
+                               if (event.keyCode !== $.ui.keyCode.TAB) {
+                                       return;
+                               }
+       
+                               var tabbables = $(':tabbable', this),
+                                       first = tabbables.filter(':first'),
+                                       last  = tabbables.filter(':last');
+       
+                               if (event.target === last[0] && !event.shiftKey) {
+                                       first.focus(1);
+                                       return false;
+                               } else if (event.target === first[0] && event.shiftKey) {
+                                       last.focus(1);
+                                       return false;
+                               }
+                       });
+               }
+
+               // set focus to the first tabbable element in the content area or the first button
+               // if there are no tabbable elements, set focus on the dialog itself
+               $([])
+                       .add(uiDialog.find('.ui-dialog-content :tabbable:first'))
+                       .add(uiDialog.find('.ui-dialog-buttonpane :tabbable:first'))
+                       .add(uiDialog)
+                       .filter(':first')
+                       .focus();
+
+               self._trigger('open');
+               self._isOpen = true;
+
+               return self;
+       },
+
+       _createButtons: function(buttons) {
+               var self = this,
+                       hasButtons = false,
+                       uiDialogButtonPane = $('<div></div>')
+                               .addClass(
+                                       'ui-dialog-buttonpane ' +
+                                       'ui-widget-content ' +
+                                       'ui-helper-clearfix'
+                               );
+
+               // if we already have a button pane, remove it
+               self.uiDialog.find('.ui-dialog-buttonpane').remove();
+
+               if (typeof buttons === 'object' && buttons !== null) {
+                       $.each(buttons, function() {
+                               return !(hasButtons = true);
+                       });
+               }
+               if (hasButtons) {
+                       $.each(buttons, function(name, fn) {
+                               var button = $('<button type="button"></button>')
+                                       .text(name)
+                                       .click(function() { fn.apply(self.element[0], arguments); })
+                                       .appendTo(uiDialogButtonPane);
+                               if ($.fn.button) {
+                                       button.button();
+                               }
+                       });
+                       uiDialogButtonPane.appendTo(self.uiDialog);
+               }
+       },
+
+       _makeDraggable: function() {
+               var self = this,
+                       options = self.options,
+                       doc = $(document),
+                       heightBeforeDrag;
+
+               function filteredUi(ui) {
+                       return {
+                               position: ui.position,
+                               offset: ui.offset
+                       };
+               }
+
+               self.uiDialog.draggable({
+                       cancel: '.ui-dialog-content, .ui-dialog-titlebar-close',
+                       handle: '.ui-dialog-titlebar',
+                       containment: 'document',
+                       start: function(event, ui) {
+                               heightBeforeDrag = options.height === "auto" ? "auto" : $(this).height();
+                               $(this).height($(this).height()).addClass("ui-dialog-dragging");
+                               self._trigger('dragStart', event, filteredUi(ui));
+                       },
+                       drag: function(event, ui) {
+                               self._trigger('drag', event, filteredUi(ui));
+                       },
+                       stop: function(event, ui) {
+                               options.position = [ui.position.left - doc.scrollLeft(),
+                                       ui.position.top - doc.scrollTop()];
+                               $(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag);
+                               self._trigger('dragStop', event, filteredUi(ui));
+                               $.ui.dialog.overlay.resize();
+                       }
+               });
+       },
+
+       _makeResizable: function(handles) {
+               handles = (handles === undefined ? this.options.resizable : handles);
+               var self = this,
+                       options = self.options,
+                       // .ui-resizable has position: relative defined in the stylesheet
+                       // but dialogs have to use absolute or fixed positioning
+                       position = self.uiDialog.css('position'),
+                       resizeHandles = (typeof handles === 'string' ?
+                               handles :
+                               'n,e,s,w,se,sw,ne,nw'
+                       );
+
+               function filteredUi(ui) {
+                       return {
+                               originalPosition: ui.originalPosition,
+                               originalSize: ui.originalSize,
+                               position: ui.position,
+                               size: ui.size
+                       };
+               }
+
+               self.uiDialog.resizable({
+                       cancel: '.ui-dialog-content',
+                       containment: 'document',
+                       alsoResize: self.element,
+                       maxWidth: options.maxWidth,
+                       maxHeight: options.maxHeight,
+                       minWidth: options.minWidth,
+                       minHeight: self._minHeight(),
+                       handles: resizeHandles,
+                       start: function(event, ui) {
+                               $(this).addClass("ui-dialog-resizing");
+                               self._trigger('resizeStart', event, filteredUi(ui));
+                       },
+                       resize: function(event, ui) {
+                               self._trigger('resize', event, filteredUi(ui));
+                       },
+                       stop: function(event, ui) {
+                               $(this).removeClass("ui-dialog-resizing");
+                               options.height = $(this).height();
+                               options.width = $(this).width();
+                               self._trigger('resizeStop', event, filteredUi(ui));
+                               $.ui.dialog.overlay.resize();
+                       }
+               })
+               .css('position', position)
+               .find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se');
+       },
+
+       _minHeight: function() {
+               var options = this.options;
+
+               if (options.height === 'auto') {
+                       return options.minHeight;
+               } else {
+                       return Math.min(options.minHeight, options.height);
+               }
+       },
+
+       _position: function(position) {
+               var myAt = [],
+                       offset = [0, 0],
+                       isVisible;
+
+               position = position || $.ui.dialog.prototype.options.position;
+
+               // deep extending converts arrays to objects in jQuery <= 1.3.2 :-(
+//             if (typeof position == 'string' || $.isArray(position)) {
+//                     myAt = $.isArray(position) ? position : position.split(' ');
+
+               if (typeof position === 'string' || (typeof position === 'object' && '0' in position)) {
+                       myAt = position.split ? position.split(' ') : [position[0], position[1]];
+                       if (myAt.length === 1) {
+                               myAt[1] = myAt[0];
+                       }
+
+                       $.each(['left', 'top'], function(i, offsetPosition) {
+                               if (+myAt[i] === myAt[i]) {
+                                       offset[i] = myAt[i];
+                                       myAt[i] = offsetPosition;
+                               }
+                       });
+               } else if (typeof position === 'object') {
+                       if ('left' in position) {
+                               myAt[0] = 'left';
+                               offset[0] = position.left;
+                       } else if ('right' in position) {
+                               myAt[0] = 'right';
+                               offset[0] = -position.right;
+                       }
+
+                       if ('top' in position) {
+                               myAt[1] = 'top';
+                               offset[1] = position.top;
+                       } else if ('bottom' in position) {
+                               myAt[1] = 'bottom';
+                               offset[1] = -position.bottom;
+                       }
+               }
+
+               // need to show the dialog to get the actual offset in the position plugin
+               isVisible = this.uiDialog.is(':visible');
+               if (!isVisible) {
+                       this.uiDialog.show();
+               }
+               this.uiDialog
+                       // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
+                       .css({ top: 0, left: 0 })
+                       .position({
+                               my: myAt.join(' '),
+                               at: myAt.join(' '),
+                               offset: offset.join(' '),
+                               of: window,
+                               collision: 'fit',
+                               // ensure that the titlebar is never outside the document
+                               using: function(pos) {
+                                       var topOffset = $(this).css(pos).offset().top;
+                                       if (topOffset < 0) {
+                                               $(this).css('top', pos.top - topOffset);
+                                       }
+                               }
+                       });
+               if (!isVisible) {
+                       this.uiDialog.hide();
+               }
+       },
+
+       _setOption: function(key, value){
+               var self = this,
+                       uiDialog = self.uiDialog,
+                       isResizable = uiDialog.is(':data(resizable)'),
+                       resize = false;
+               
+               switch (key) {
+                       //handling of deprecated beforeclose (vs beforeClose) option
+                       //Ticket #4669 http://dev.jqueryui.com/ticket/4669
+                       //TODO: remove in 1.9pre
+                       case "beforeclose":
+                               key = "beforeClose";
+                               break;
+                       case "buttons":
+                               self._createButtons(value);
+                               break;
+                       case "closeText":
+                               // convert whatever was passed in to a string, for text() to not throw up
+                               self.uiDialogTitlebarCloseText.text("" + value);
+                               break;
+                       case "dialogClass":
+                               uiDialog
+                                       .removeClass(self.options.dialogClass)
+                                       .addClass(uiDialogClasses + value);
+                               break;
+                       case "disabled":
+                               if (value) {
+                                       uiDialog.addClass('ui-dialog-disabled');
+                               } else {
+                                       uiDialog.removeClass('ui-dialog-disabled');
+                               }
+                               break;
+                       case "draggable":
+                               if (value) {
+                                       self._makeDraggable();
+                               } else {
+                                       uiDialog.draggable('destroy');
+                               }
+                               break;
+                       case "height":
+                               resize = true;
+                               break;
+                       case "maxHeight":
+                               if (isResizable) {
+                                       uiDialog.resizable('option', 'maxHeight', value);
+                               }
+                               resize = true;
+                               break;
+                       case "maxWidth":
+                               if (isResizable) {
+                                       uiDialog.resizable('option', 'maxWidth', value);
+                               }
+                               resize = true;
+                               break;
+                       case "minHeight":
+                               if (isResizable) {
+                                       uiDialog.resizable('option', 'minHeight', value);
+                               }
+                               resize = true;
+                               break;
+                       case "minWidth":
+                               if (isResizable) {
+                                       uiDialog.resizable('option', 'minWidth', value);
+                               }
+                               resize = true;
+                               break;
+                       case "position":
+                               self._position(value);
+                               break;
+                       case "resizable":
+                               // currently resizable, becoming non-resizable
+                               if (isResizable && !value) {
+                                       uiDialog.resizable('destroy');
+                               }
+
+                               // currently resizable, changing handles
+                               if (isResizable && typeof value === 'string') {
+                                       uiDialog.resizable('option', 'handles', value);
+                               }
+
+                               // currently non-resizable, becoming resizable
+                               if (!isResizable && value !== false) {
+                                       self._makeResizable(value);
+                               }
+                               break;
+                       case "title":
+                               // convert whatever was passed in o a string, for html() to not throw up
+                               $(".ui-dialog-title", self.uiDialogTitlebar).html("" + (value || '&#160;'));
+                               break;
+                       case "width":
+                               resize = true;
+                               break;
+               }
+
+               $.Widget.prototype._setOption.apply(self, arguments);
+               if (resize) {
+                       self._size();
+               }
+       },
+
+       _size: function() {
+               /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
+                * divs will both have width and height set, so we need to reset them
+                */
+               var options = this.options,
+                       nonContentHeight;
+
+               // reset content sizing
+               // hide for non content measurement because height: 0 doesn't work in IE quirks mode (see #4350)
+               this.element.css('width', 'auto')
+                       .hide();
+
+               // reset wrapper sizing
+               // determine the height of all the non-content elements
+               nonContentHeight = this.uiDialog.css({
+                               height: 'auto',
+                               width: options.width
+                       })
+                       .height();
+
+               this.element
+                       .css(options.height === 'auto' ? {
+                                       minHeight: Math.max(options.minHeight - nonContentHeight, 0),
+                                       height: 'auto'
+                               } : {
+                                       minHeight: 0,
+                                       height: Math.max(options.height - nonContentHeight, 0)                          
+                       })
+                       .show();
+
+               if (this.uiDialog.is(':data(resizable)')) {
+                       this.uiDialog.resizable('option', 'minHeight', this._minHeight());
+               }
+       }
+});
+
+$.extend($.ui.dialog, {
+       version: "1.8",
+
+       uuid: 0,
+       maxZ: 0,
+
+       getTitleId: function($el) {
+               var id = $el.attr('id');
+               if (!id) {
+                       this.uuid += 1;
+                       id = this.uuid;
+               }
+               return 'ui-dialog-title-' + id;
+       },
+
+       overlay: function(dialog) {
+               this.$el = $.ui.dialog.overlay.create(dialog);
+       }
+});
+
+$.extend($.ui.dialog.overlay, {
+       instances: [],
+       // reuse old instances due to IE memory leak with alpha transparency (see #5185)
+       oldInstances: [],
+       maxZ: 0,
+       events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
+               function(event) { return event + '.dialog-overlay'; }).join(' '),
+       create: function(dialog) {
+               if (this.instances.length === 0) {
+                       // prevent use of anchors and inputs
+                       // we use a setTimeout in case the overlay is created from an
+                       // event that we're going to be cancelling (see #2804)
+                       setTimeout(function() {
+                               // handle $(el).dialog().dialog('close') (see #4065)
+                               if ($.ui.dialog.overlay.instances.length) {
+                                       $(document).bind($.ui.dialog.overlay.events, function(event) {
+                                               // stop events if the z-index of the target is < the z-index of the overlay
+                                               return ($(event.target).zIndex() >= $.ui.dialog.overlay.maxZ);
+                                       });
+                               }
+                       }, 1);
+
+                       // allow closing by pressing the escape key
+                       $(document).bind('keydown.dialog-overlay', function(event) {
+                               if (dialog.options.closeOnEscape && event.keyCode &&
+                                       event.keyCode === $.ui.keyCode.ESCAPE) {
+                                       
+                                       dialog.close(event);
+                                       event.preventDefault();
+                               }
+                       });
+
+                       // handle window resize
+                       $(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
+               }
+
+               var $el = (this.oldInstances.pop() || $('<div></div>').addClass('ui-widget-overlay'))
+                       .appendTo(document.body)
+                       .css({
+                               width: this.width(),
+                               height: this.height()
+                       });
+
+               if ($.fn.bgiframe) {
+                       $el.bgiframe();
+               }
+
+               this.instances.push($el);
+               return $el;
+       },
+
+       destroy: function($el) {
+               this.oldInstances.push(this.instances.splice($.inArray($el, this.instances), 1)[0]);
+
+               if (this.instances.length === 0) {
+                       $([document, window]).unbind('.dialog-overlay');
+               }
+
+               $el.remove();
+               
+               // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
+               var maxZ = 0;
+               $.each(this.instances, function() {
+                       maxZ = Math.max(maxZ, this.css('z-index'));
+               });
+               this.maxZ = maxZ;
+       },
+
+       height: function() {
+               var scrollHeight,
+                       offsetHeight;
+               // handle IE 6
+               if ($.browser.msie && $.browser.version < 7) {
+                       scrollHeight = Math.max(
+                               document.documentElement.scrollHeight,
+                               document.body.scrollHeight
+                       );
+                       offsetHeight = Math.max(
+                               document.documentElement.offsetHeight,
+                               document.body.offsetHeight
+                       );
+
+                       if (scrollHeight < offsetHeight) {
+                               return $(window).height() + 'px';
+                       } else {
+                               return scrollHeight + 'px';
+                       }
+               // handle "good" browsers
+               } else {
+                       return $(document).height() + 'px';
+               }
+       },
+
+       width: function() {
+               var scrollWidth,
+                       offsetWidth;
+               // handle IE 6
+               if ($.browser.msie && $.browser.version < 7) {
+                       scrollWidth = Math.max(
+                               document.documentElement.scrollWidth,
+                               document.body.scrollWidth
+                       );
+                       offsetWidth = Math.max(
+                               document.documentElement.offsetWidth,
+                               document.body.offsetWidth
+                       );
+
+                       if (scrollWidth < offsetWidth) {
+                               return $(window).width() + 'px';
+                       } else {
+                               return scrollWidth + 'px';
+                       }
+               // handle "good" browsers
+               } else {
+                       return $(document).width() + 'px';
+               }
+       },
+
+       resize: function() {
+               /* If the dialog is draggable and the user drags it past the
+                * right edge of the window, the document becomes wider so we
+                * need to stretch the overlay. If the user then drags the
+                * dialog back to the left, the document will become narrower,
+                * so we need to shrink the overlay to the appropriate size.
+                * This is handled by shrinking the overlay before setting it
+                * to the full document size.
+                */
+               var $overlays = $([]);
+               $.each($.ui.dialog.overlay.instances, function() {
+                       $overlays = $overlays.add(this);
+               });
+
+               $overlays.css({
+                       width: 0,
+                       height: 0
+               }).css({
+                       width: $.ui.dialog.overlay.width(),
+                       height: $.ui.dialog.overlay.height()
+               });
+       }
+});
+
+$.extend($.ui.dialog.overlay.prototype, {
+       destroy: function() {
+               $.ui.dialog.overlay.destroy(this.$el);
+       }
+});
+
+}(jQuery));
+/*
+ * jQuery UI Accordion 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.accordion", {
+       options: {
+               active: 0,
+               animated: 'slide',
+               autoHeight: true,
+               clearStyle: false,
+               collapsible: false,
+               event: "click",
+               fillSpace: false,
+               header: "> li > :first-child,> :not(li):even",
+               icons: {
+                       header: "ui-icon-triangle-1-e",
+                       headerSelected: "ui-icon-triangle-1-s"
+               },
+               navigation: false,
+               navigationFilter: function() {
+                       return this.href.toLowerCase() == location.href.toLowerCase();
+               }
+       },
+       _create: function() {
+
+               var o = this.options, self = this;
+               this.running = 0;
+
+               this.element.addClass("ui-accordion ui-widget ui-helper-reset");
+               
+               // in lack of child-selectors in CSS we need to mark top-LIs in a UL-accordion for some IE-fix
+               if (this.element[0].nodeName == "UL") {
+                       this.element.children("li").addClass("ui-accordion-li-fix");
+               }
+
+               this.headers = this.element.find(o.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all")
+                       .bind("mouseenter.accordion", function(){ $(this).addClass('ui-state-hover'); })
+                       .bind("mouseleave.accordion", function(){ $(this).removeClass('ui-state-hover'); })
+                       .bind("focus.accordion", function(){ $(this).addClass('ui-state-focus'); })
+                       .bind("blur.accordion", function(){ $(this).removeClass('ui-state-focus'); });
+
+               this.headers
+                       .next()
+                               .addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
+
+               if ( o.navigation ) {
+                       var current = this.element.find("a").filter(o.navigationFilter);
+                       if ( current.length ) {
+                               var header = current.closest(".ui-accordion-header");
+                               if ( header.length ) {
+                                       // anchor within header
+                                       this.active = header;
+                               } else {
+                                       // anchor within content
+                                       this.active = current.closest(".ui-accordion-content").prev();
+                               }
+                       }
+               }
+
+               this.active = this._findActive(this.active || o.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");
+               this.active.next().addClass('ui-accordion-content-active');
+
+               //Append icon elements
+               this._createIcons();
+
+               // IE7-/Win - Extra vertical space in lists fixed
+               if ($.browser.msie) {
+                       this.element.find('a').css('zoom', '1');
+               }
+
+               this.resize();
+
+               //ARIA
+               this.element.attr('role','tablist');
+
+               this.headers
+                       .attr('role','tab')
+                       .bind('keydown', function(event) { return self._keydown(event); })
+                       .next()
+                       .attr('role','tabpanel');
+
+               this.headers
+                       .not(this.active || "")
+                       .attr('aria-expanded','false')
+                       .attr("tabIndex", "-1")
+                       .next()
+                       .hide();
+
+               // make sure at least one header is in the tab order
+               if (!this.active.length) {
+                       this.headers.eq(0).attr('tabIndex','0');
+               } else {
+                       this.active
+                               .attr('aria-expanded','true')
+                               .attr('tabIndex', '0');
+               }
+
+               // only need links in taborder for Safari
+               if (!$.browser.safari)
+                       this.headers.find('a').attr('tabIndex','-1');
+
+               if (o.event) {
+                       this.headers.bind((o.event) + ".accordion", function(event) {
+                               self._clickHandler.call(self, event, this);
+                               event.preventDefault();
+                       });
+               }
+
+       },
+       
+       _createIcons: function() {
+               var o = this.options;
+               if (o.icons) {
+                       $("<span/>").addClass("ui-icon " + o.icons.header).prependTo(this.headers);
+                       this.active.find(".ui-icon").toggleClass(o.icons.header).toggleClass(o.icons.headerSelected);
+                       this.element.addClass("ui-accordion-icons");
+               }
+       },
+       
+       _destroyIcons: function() {
+               this.headers.children(".ui-icon").remove();
+               this.element.removeClass("ui-accordion-icons");
+       },
+
+       destroy: function() {
+               var o = this.options;
+
+               this.element
+                       .removeClass("ui-accordion ui-widget ui-helper-reset")
+                       .removeAttr("role")
+                       .unbind('.accordion')
+                       .removeData('accordion');
+
+               this.headers
+                       .unbind(".accordion")
+                       .removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top")
+                       .removeAttr("role").removeAttr("aria-expanded").removeAttr("tabindex");
+
+               this.headers.find("a").removeAttr("tabindex");
+               this._destroyIcons();
+               var contents = this.headers.next().css("display", "").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active");
+               if (o.autoHeight || o.fillHeight) {
+                       contents.css("height", "");
+               }
+
+               return this;
+       },
+       
+       _setOption: function(key, value) {
+               $.Widget.prototype._setOption.apply(this, arguments);
+                       
+               if (key == "active") {
+                       this.activate(value);
+               }
+               if (key == "icons") {
+                       this._destroyIcons();
+                       if (value) {
+                               this._createIcons();
+                       }
+               }
+               
+       },
+
+       _keydown: function(event) {
+
+               var o = this.options, keyCode = $.ui.keyCode;
+
+               if (o.disabled || event.altKey || event.ctrlKey)
+                       return;
+
+               var length = this.headers.length;
+               var currentIndex = this.headers.index(event.target);
+               var toFocus = false;
+
+               switch(event.keyCode) {
+                       case keyCode.RIGHT:
+                       case keyCode.DOWN:
+                               toFocus = this.headers[(currentIndex + 1) % length];
+                               break;
+                       case keyCode.LEFT:
+                       case keyCode.UP:
+                               toFocus = this.headers[(currentIndex - 1 + length) % length];
+                               break;
+                       case keyCode.SPACE:
+                       case keyCode.ENTER:
+                               this._clickHandler({ target: event.target }, event.target);
+                               event.preventDefault();
+               }
+
+               if (toFocus) {
+                       $(event.target).attr('tabIndex','-1');
+                       $(toFocus).attr('tabIndex','0');
+                       toFocus.focus();
+                       return false;
+               }
+
+               return true;
+
+       },
+
+       resize: function() {
+
+               var o = this.options, maxHeight;
+
+               if (o.fillSpace) {
+                       
+                       if($.browser.msie) { var defOverflow = this.element.parent().css('overflow'); this.element.parent().css('overflow', 'hidden'); }
+                       maxHeight = this.element.parent().height();
+                       if($.browser.msie) { this.element.parent().css('overflow', defOverflow); }
+       
+                       this.headers.each(function() {
+                               maxHeight -= $(this).outerHeight(true);
+                       });
+
+                       this.headers.next().each(function() {
+                  $(this).height(Math.max(0, maxHeight - $(this).innerHeight() + $(this).height()));
+                       }).css('overflow', 'auto');
+
+               } else if ( o.autoHeight ) {
+                       maxHeight = 0;
+                       this.headers.next().each(function() {
+                               maxHeight = Math.max(maxHeight, $(this).height());
+                       }).height(maxHeight);
+               }
+
+               return this;
+       },
+
+       activate: function(index) {
+               // TODO this gets called on init, changing the option without an explicit call for that
+               this.options.active = index;
+               // call clickHandler with custom event
+               var active = this._findActive(index)[0];
+               this._clickHandler({ target: active }, active);
+
+               return this;
+       },
+
+       _findActive: function(selector) {
+               return selector
+                       ? typeof selector == "number"
+                               ? this.headers.filter(":eq(" + selector + ")")
+                               : this.headers.not(this.headers.not(selector))
+                       : selector === false
+                               ? $([])
+                               : this.headers.filter(":eq(0)");
+       },
+
+       // TODO isn't event.target enough? why the seperate target argument?
+       _clickHandler: function(event, target) {
+
+               var o = this.options;
+               if (o.disabled)
+                       return;
+
+               // called only when using activate(false) to close all parts programmatically
+               if (!event.target) {
+                       if (!o.collapsible)
+                               return;
+                       this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
+                               .find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
+                       this.active.next().addClass('ui-accordion-content-active');
+                       var toHide = this.active.next(),
+                               data = {
+                                       options: o,
+                                       newHeader: $([]),
+                                       oldHeader: o.active,
+                                       newContent: $([]),
+                                       oldContent: toHide
+                               },
+                               toShow = (this.active = $([]));
+                       this._toggle(toShow, toHide, data);
+                       return;
+               }
+
+               // get the click target
+               var clicked = $(event.currentTarget || target);
+               var clickedIsActive = clicked[0] == this.active[0];
+               
+               // TODO the option is changed, is that correct?
+               // TODO if it is correct, shouldn't that happen after determining that the click is valid?
+               o.active = o.collapsible && clickedIsActive ? false : $('.ui-accordion-header', this.element).index(clicked);
+
+               // if animations are still active, or the active header is the target, ignore click
+               if (this.running || (!o.collapsible && clickedIsActive)) {
+                       return;
+               }
+
+               // switch classes
+               this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
+                       .find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
+               if (!clickedIsActive) {
+                       clicked.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top")
+                               .find(".ui-icon").removeClass(o.icons.header).addClass(o.icons.headerSelected);
+                       clicked.next().addClass('ui-accordion-content-active');
+               }
+
+               // find elements to show and hide
+               var toShow = clicked.next(),
+                       toHide = this.active.next(),
+                       data = {
+                               options: o,
+                               newHeader: clickedIsActive && o.collapsible ? $([]) : clicked,
+                               oldHeader: this.active,
+                               newContent: clickedIsActive && o.collapsible ? $([]) : toShow,
+                               oldContent: toHide
+                       },
+                       down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
+
+               this.active = clickedIsActive ? $([]) : clicked;
+               this._toggle(toShow, toHide, data, clickedIsActive, down);
+
+               return;
+
+       },
+
+       _toggle: function(toShow, toHide, data, clickedIsActive, down) {
+
+               var o = this.options, self = this;
+
+               this.toShow = toShow;
+               this.toHide = toHide;
+               this.data = data;
+
+               var complete = function() { if(!self) return; return self._completed.apply(self, arguments); };
+
+               // trigger changestart event
+               this._trigger("changestart", null, this.data);
+
+               // count elements to animate
+               this.running = toHide.size() === 0 ? toShow.size() : toHide.size();
+
+               if (o.animated) {
+
+                       var animOptions = {};
+
+                       if ( o.collapsible && clickedIsActive ) {
+                               animOptions = {
+                                       toShow: $([]),
+                                       toHide: toHide,
+                                       complete: complete,
+                                       down: down,
+                                       autoHeight: o.autoHeight || o.fillSpace
+                               };
+                       } else {
+                               animOptions = {
+                                       toShow: toShow,
+                                       toHide: toHide,
+                                       complete: complete,
+                                       down: down,
+                                       autoHeight: o.autoHeight || o.fillSpace
+                               };
+                       }
+
+                       if (!o.proxied) {
+                               o.proxied = o.animated;
+                       }
+
+                       if (!o.proxiedDuration) {
+                               o.proxiedDuration = o.duration;
+                       }
+
+                       o.animated = $.isFunction(o.proxied) ?
+                               o.proxied(animOptions) : o.proxied;
+
+                       o.duration = $.isFunction(o.proxiedDuration) ?
+                               o.proxiedDuration(animOptions) : o.proxiedDuration;
+
+                       var animations = $.ui.accordion.animations,
+                               duration = o.duration,
+                               easing = o.animated;
+
+                       if (easing && !animations[easing] && !$.easing[easing]) {
+                               easing = 'slide';
+                       }
+                       if (!animations[easing]) {
+                               animations[easing] = function(options) {
+                                       this.slide(options, {
+                                               easing: easing,
+                                               duration: duration || 700
+                                       });
+                               };
+                       }
+
+                       animations[easing](animOptions);
+
+               } else {
+
+                       if (o.collapsible && clickedIsActive) {
+                               toShow.toggle();
+                       } else {
+                               toHide.hide();
+                               toShow.show();
+                       }
+
+                       complete(true);
+
+               }
+
+               // TODO assert that the blur and focus triggers are really necessary, remove otherwise
+               toHide.prev().attr('aria-expanded','false').attr("tabIndex", "-1").blur();
+               toShow.prev().attr('aria-expanded','true').attr("tabIndex", "0").focus();
+
+       },
+
+       _completed: function(cancel) {
+
+               var o = this.options;
+
+               this.running = cancel ? 0 : --this.running;
+               if (this.running) return;
+
+               if (o.clearStyle) {
+                       this.toShow.add(this.toHide).css({
+                               height: "",
+                               overflow: ""
+                       });
+               }
+               
+               // other classes are removed before the animation; this one needs to stay until completed
+               this.toHide.removeClass("ui-accordion-content-active");
+
+               this._trigger('change', null, this.data);
+       }
+
+});
+
+
+$.extend($.ui.accordion, {
+       version: "1.8",
+       animations: {
+               slide: function(options, additions) {
+                       options = $.extend({
+                               easing: "swing",
+                               duration: 300
+                       }, options, additions);
+                       if ( !options.toHide.size() ) {
+                               options.toShow.animate({height: "show"}, options);
+                               return;
+                       }
+                       if ( !options.toShow.size() ) {
+                               options.toHide.animate({height: "hide"}, options);
+                               return;
+                       }
+                       var overflow = options.toShow.css('overflow'),
+                               percentDone = 0,
+                               showProps = {},
+                               hideProps = {},
+                               fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
+                               originalWidth;
+                       // fix width before calculating height of hidden element
+                       var s = options.toShow;
+                       originalWidth = s[0].style.width;
+                       s.width( parseInt(s.parent().width(),10) - parseInt(s.css("paddingLeft"),10) - parseInt(s.css("paddingRight"),10) - (parseInt(s.css("borderLeftWidth"),10) || 0) - (parseInt(s.css("borderRightWidth"),10) || 0) );
+                       
+                       $.each(fxAttrs, function(i, prop) {
+                               hideProps[prop] = 'hide';
+                               
+                               var parts = ('' + $.css(options.toShow[0], prop)).match(/^([\d+-.]+)(.*)$/);
+                               showProps[prop] = {
+                                       value: parts[1],
+                                       unit: parts[2] || 'px'
+                               };
+                       });
+                       options.toShow.css({ height: 0, overflow: 'hidden' }).show();
+                       options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate(hideProps,{
+                               step: function(now, settings) {
+                                       // only calculate the percent when animating height
+                                       // IE gets very inconsistent results when animating elements
+                                       // with small values, which is common for padding
+                                       if (settings.prop == 'height') {
+                                               percentDone = ( settings.end - settings.start === 0 ) ? 0 :
+                                                       (settings.now - settings.start) / (settings.end - settings.start);
+                                       }
+                                       
+                                       options.toShow[0].style[settings.prop] =
+                                               (percentDone * showProps[settings.prop].value) + showProps[settings.prop].unit;
+                               },
+                               duration: options.duration,
+                               easing: options.easing,
+                               complete: function() {
+                                       if ( !options.autoHeight ) {
+                                               options.toShow.css("height", "");
+                                       }
+                                       options.toShow.css("width", originalWidth);
+                                       options.toShow.css({overflow: overflow});
+                                       options.complete();
+                               }
+                       });
+               },
+               bounceslide: function(options) {
+                       this.slide(options, {
+                               easing: options.down ? "easeOutBounce" : "swing",
+                               duration: options.down ? 1000 : 200
+                       });
+               }
+       }
+});
+
+})(jQuery);
+/*
+ * jQuery UI Slider 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.mouse.js
+ *     jquery.ui.widget.js
+ */
+
+(function($) {
+
+// number of pages in a slider
+// (how many times can you page up/down to go through the whole range)
+var numPages = 5;
+
+$.widget("ui.slider", $.ui.mouse, {
+       widgetEventPrefix: "slide",
+       options: {
+               animate: false,
+               distance: 0,
+               max: 100,
+               min: 0,
+               orientation: 'horizontal',
+               range: false,
+               step: 1,
+               value: 0,
+               values: null
+       },
+       _create: function() {
+
+               var self = this, o = this.options;
+               this._keySliding = false;
+               this._mouseSliding = false;
+               this._animateOff = true;
+               this._handleIndex = null;
+               this._detectOrientation();
+               this._mouseInit();
+
+               this.element
+                       .addClass("ui-slider"
+                               + " ui-slider-" + this.orientation
+                               + " ui-widget"
+                               + " ui-widget-content"
+                               + " ui-corner-all");
+               
+               if (o.disabled) {
+                       this.element.addClass('ui-slider-disabled ui-disabled');
+               }
+
+               this.range = $([]);
+
+               if (o.range) {
+
+                       if (o.range === true) {
+                               this.range = $('<div></div>');
+                               if (!o.values) o.values = [this._valueMin(), this._valueMin()];
+                               if (o.values.length && o.values.length != 2) {
+                                       o.values = [o.values[0], o.values[0]];
+                               }
+                       } else {
+                               this.range = $('<div></div>');
+                       }
+
+                       this.range
+                               .appendTo(this.element)
+                               .addClass("ui-slider-range");
+
+                       if (o.range == "min" || o.range == "max") {
+                               this.range.addClass("ui-slider-range-" + o.range);
+                       }
+
+                       // note: this isn't the most fittingly semantic framework class for this element,
+                       // but worked best visually with a variety of themes
+                       this.range.addClass("ui-widget-header");
+
+               }
+
+               if ($(".ui-slider-handle", this.element).length == 0)
+                       $('<a href="#"></a>')
+                               .appendTo(this.element)
+                               .addClass("ui-slider-handle");
+
+               if (o.values && o.values.length) {
+                       while ($(".ui-slider-handle", this.element).length < o.values.length)
+                               $('<a href="#"></a>')
+                                       .appendTo(this.element)
+                                       .addClass("ui-slider-handle");
+               }
+
+               this.handles = $(".ui-slider-handle", this.element)
+                       .addClass("ui-state-default"
+                               + " ui-corner-all");
+
+               this.handle = this.handles.eq(0);
+
+               this.handles.add(this.range).filter("a")
+                       .click(function(event) {
+                               event.preventDefault();
+                       })
+                       .hover(function() {
+                               if (!o.disabled) {
+                                       $(this).addClass('ui-state-hover');
+                               }
+                       }, function() {
+                               $(this).removeClass('ui-state-hover');
+                       })
+                       .focus(function() {
+                               if (!o.disabled) {
+                                       $(".ui-slider .ui-state-focus").removeClass('ui-state-focus'); $(this).addClass('ui-state-focus');
+                               } else {
+                                       $(this).blur();
+                               }
+                       })
+                       .blur(function() {
+                               $(this).removeClass('ui-state-focus');
+                       });
+
+               this.handles.each(function(i) {
+                       $(this).data("index.ui-slider-handle", i);
+               });
+
+               this.handles.keydown(function(event) {
+
+                       var ret = true;
+
+                       var index = $(this).data("index.ui-slider-handle");
+
+                       if (self.options.disabled)
+                               return;
+
+                       switch (event.keyCode) {
+                               case $.ui.keyCode.HOME:
+                               case $.ui.keyCode.END:
+                               case $.ui.keyCode.PAGE_UP:
+                               case $.ui.keyCode.PAGE_DOWN:
+                               case $.ui.keyCode.UP:
+                               case $.ui.keyCode.RIGHT:
+                               case $.ui.keyCode.DOWN:
+                               case $.ui.keyCode.LEFT:
+                                       ret = false;
+                                       if (!self._keySliding) {
+                                               self._keySliding = true;
+                                               $(this).addClass("ui-state-active");
+                                               self._start(event, index);
+                                       }
+                                       break;
+                       }
+
+                       var curVal, newVal, step = self._step();
+                       if (self.options.values && self.options.values.length) {
+                               curVal = newVal = self.values(index);
+                       } else {
+                               curVal = newVal = self.value();
+                       }
+
+                       switch (event.keyCode) {
+                               case $.ui.keyCode.HOME:
+                                       newVal = self._valueMin();
+                                       break;
+                               case $.ui.keyCode.END:
+                                       newVal = self._valueMax();
+                                       break;
+                               case $.ui.keyCode.PAGE_UP:
+                                       newVal = curVal + ((self._valueMax() - self._valueMin()) / numPages);
+                                       break;
+                               case $.ui.keyCode.PAGE_DOWN:
+                                       newVal = curVal - ((self._valueMax() - self._valueMin()) / numPages);
+                                       break;
+                               case $.ui.keyCode.UP:
+                               case $.ui.keyCode.RIGHT:
+                                       if(curVal == self._valueMax()) return;
+                                       newVal = curVal + step;
+                                       break;
+                               case $.ui.keyCode.DOWN:
+                               case $.ui.keyCode.LEFT:
+                                       if(curVal == self._valueMin()) return;
+                                       newVal = curVal - step;
+                                       break;
+                       }
+
+                       self._slide(event, index, newVal);
+
+                       return ret;
+
+               }).keyup(function(event) {
+
+                       var index = $(this).data("index.ui-slider-handle");
+
+                       if (self._keySliding) {
+                               self._keySliding = false;
+                               self._stop(event, index);
+                               self._change(event, index);
+                               $(this).removeClass("ui-state-active");
+                       }
+
+               });
+
+               this._refreshValue();
+
+               this._animateOff = false;
+
+       },
+
+       destroy: function() {
+
+               this.handles.remove();
+               this.range.remove();
+
+               this.element
+                       .removeClass("ui-slider"
+                               + " ui-slider-horizontal"
+                               + " ui-slider-vertical"
+                               + " ui-slider-disabled"
+                               + " ui-widget"
+                               + " ui-widget-content"
+                               + " ui-corner-all")
+                       .removeData("slider")
+                       .unbind(".slider");
+
+               this._mouseDestroy();
+
+               return this;
+       },
+
+       _mouseCapture: function(event) {
+
+               var o = this.options;
+
+               if (o.disabled)
+                       return false;
+
+               this.elementSize = {
+                       width: this.element.outerWidth(),
+                       height: this.element.outerHeight()
+               };
+               this.elementOffset = this.element.offset();
+
+               var position = { x: event.pageX, y: event.pageY };
+               var normValue = this._normValueFromMouse(position);
+
+               var distance = this._valueMax() - this._valueMin() + 1, closestHandle;
+               var self = this, index;
+               this.handles.each(function(i) {
+                       var thisDistance = Math.abs(normValue - self.values(i));
+                       if (distance > thisDistance) {
+                               distance = thisDistance;
+                               closestHandle = $(this);
+                               index = i;
+                       }
+               });
+
+               // workaround for bug #3736 (if both handles of a range are at 0,
+               // the first is always used as the one with least distance,
+               // and moving it is obviously prevented by preventing negative ranges)
+               if(o.range == true && this.values(1) == o.min) {
+                       closestHandle = $(this.handles[++index]);
+               }
+
+               this._start(event, index);
+               this._mouseSliding = true;
+
+               self._handleIndex = index;
+
+               closestHandle
+                       .addClass("ui-state-active")
+                       .focus();
+               
+               var offset = closestHandle.offset();
+               var mouseOverHandle = !$(event.target).parents().andSelf().is('.ui-slider-handle');
+               this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
+                       left: event.pageX - offset.left - (closestHandle.width() / 2),
+                       top: event.pageY - offset.top
+                               - (closestHandle.height() / 2)
+                               - (parseInt(closestHandle.css('borderTopWidth'),10) || 0)
+                               - (parseInt(closestHandle.css('borderBottomWidth'),10) || 0)
+                               + (parseInt(closestHandle.css('marginTop'),10) || 0)
+               };
+
+               normValue = this._normValueFromMouse(position);
+               this._slide(event, index, normValue);
+               this._animateOff = true;
+               return true;
+
+       },
+
+       _mouseStart: function(event) {
+               return true;
+       },
+
+       _mouseDrag: function(event) {
+
+               var position = { x: event.pageX, y: event.pageY };
+               var normValue = this._normValueFromMouse(position);
+               
+               this._slide(event, this._handleIndex, normValue);
+
+               return false;
+
+       },
+
+       _mouseStop: function(event) {
+
+               this.handles.removeClass("ui-state-active");
+               this._mouseSliding = false;
+               this._stop(event, this._handleIndex);
+               this._change(event, this._handleIndex);
+               this._handleIndex = null;
+               this._clickOffset = null;
+
+               this._animateOff = false;
+               return false;
+
+       },
+       
+       _detectOrientation: function() {
+               this.orientation = this.options.orientation == 'vertical' ? 'vertical' : 'horizontal';
+       },
+
+       _normValueFromMouse: function(position) {
+
+               var pixelTotal, pixelMouse;
+               if ('horizontal' == this.orientation) {
+                       pixelTotal = this.elementSize.width;
+                       pixelMouse = position.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0);
+               } else {
+                       pixelTotal = this.elementSize.height;
+                       pixelMouse = position.y - this.elementOffset.top - (this._clickOffset ? this._clickOffset.top : 0);
+               }
+
+               var percentMouse = (pixelMouse / pixelTotal);
+               if (percentMouse > 1) percentMouse = 1;
+               if (percentMouse < 0) percentMouse = 0;
+               if ('vertical' == this.orientation)
+                       percentMouse = 1 - percentMouse;
+
+               var valueTotal = this._valueMax() - this._valueMin(),
+                       valueMouse = percentMouse * valueTotal,
+                       valueMouseModStep = valueMouse % this.options.step,
+                       normValue = this._valueMin() + valueMouse - valueMouseModStep;
+
+               if (valueMouseModStep > (this.options.step / 2))
+                       normValue += this.options.step;
+
+               // Since JavaScript has problems with large floats, round
+               // the final value to 5 digits after the decimal point (see #4124)
+               return parseFloat(normValue.toFixed(5));
+
+       },
+
+       _start: function(event, index) {
+               var uiHash = {
+                       handle: this.handles[index],
+                       value: this.value()
+               };
+               if (this.options.values && this.options.values.length) {
+                       uiHash.value = this.values(index);
+                       uiHash.values = this.values();
+               }
+               this._trigger("start", event, uiHash);
+       },
+
+       _slide: function(event, index, newVal) {
+
+               var handle = this.handles[index];
+
+               if (this.options.values && this.options.values.length) {
+
+                       var otherVal = this.values(index ? 0 : 1);
+
+                       if ((this.options.values.length == 2 && this.options.range === true) && 
+                               ((index == 0 && newVal > otherVal) || (index == 1 && newVal < otherVal))){
+                               newVal = otherVal;
+                       }
+
+                       if (newVal != this.values(index)) {
+                               var newValues = this.values();
+                               newValues[index] = newVal;
+                               // A slide can be canceled by returning false from the slide callback
+                               var allowed = this._trigger("slide", event, {
+                                       handle: this.handles[index],
+                                       value: newVal,
+                                       values: newValues
+                               });
+                               var otherVal = this.values(index ? 0 : 1);
+                               if (allowed !== false) {
+                                       this.values(index, newVal, true);
+                               }
+                       }
+
+               } else {
+
+                       if (newVal != this.value()) {
+                               // A slide can be canceled by returning false from the slide callback
+                               var allowed = this._trigger("slide", event, {
+                                       handle: this.handles[index],
+                                       value: newVal
+                               });
+                               if (allowed !== false) {
+                                       this.value(newVal);
+                               }
+                                       
+                       }
+
+               }
+
+       },
+
+       _stop: function(event, index) {
+               var uiHash = {
+                       handle: this.handles[index],
+                       value: this.value()
+               };
+               if (this.options.values && this.options.values.length) {
+                       uiHash.value = this.values(index);
+                       uiHash.values = this.values();
+               }
+               this._trigger("stop", event, uiHash);
+       },
+
+       _change: function(event, index) {
+               if (!this._keySliding && !this._mouseSliding) {
+                       var uiHash = {
+                               handle: this.handles[index],
+                               value: this.value()
+                       };
+                       if (this.options.values && this.options.values.length) {
+                               uiHash.value = this.values(index);
+                               uiHash.values = this.values();
+                       }
+                       this._trigger("change", event, uiHash);
+               }
+       },
+
+       value: function(newValue) {
+
+               if (arguments.length) {
+                       this.options.value = this._trimValue(newValue);
+                       this._refreshValue();
+                       this._change(null, 0);
+               }
+
+               return this._value();
+
+       },
+
+       values: function(index, newValue) {
+
+               if (arguments.length > 1) {
+                       this.options.values[index] = this._trimValue(newValue);
+                       this._refreshValue();
+                       this._change(null, index);
+               }
+
+               if (arguments.length) {
+                       if ($.isArray(arguments[0])) {
+                               var vals = this.options.values, newValues = arguments[0];
+                               for (var i = 0, l = vals.length; i < l; i++) {
+                                       vals[i] = this._trimValue(newValues[i]);
+                                       this._change(null, i);
+                               }
+                               this._refreshValue();
+                       } else {
+                               if (this.options.values && this.options.values.length) {
+                                       return this._values(index);
+                               } else {
+                                       return this.value();
+                               }
+                       }
+               } else {
+                       return this._values();
+               }
+
+       },
+
+       _setOption: function(key, value) {
+               
+               var i,
+                       valsLength = 0;
+               if ( jQuery.isArray(this.options.values) ) {
+                       valsLength = this.options.values.length;
+               };
+
+               $.Widget.prototype._setOption.apply(this, arguments);
+
+               switch (key) {
+                       case 'disabled':
+                               if (value) {
+                                       this.handles.filter(".ui-state-focus").blur();
+                                       this.handles.removeClass("ui-state-hover");
+                                       this.handles.attr("disabled", "disabled");
+                                       this.element.addClass("ui-disabled");
+                               } else {
+                                       this.handles.removeAttr("disabled");
+                                       this.element.removeClass("ui-disabled");
+                               }
+                       case 'orientation':
+
+                               this._detectOrientation();
+                               
+                               this.element
+                                       .removeClass("ui-slider-horizontal ui-slider-vertical")
+                                       .addClass("ui-slider-" + this.orientation);
+                               this._refreshValue();
+                               break;
+                       case 'value':
+                               this._animateOff = true;
+                               this._refreshValue();
+                               this._change(null, 0);
+                               this._animateOff = false;
+                               break;
+                       case 'values':
+                               this._animateOff = true;
+                               this._refreshValue();
+                               for (i = 0; i < valsLength; i++) {
+                                       this._change(null, i);
+                               }
+                               this._animateOff = false;
+                               break;
+               }
+
+       },
+
+       _step: function() {
+               var step = this.options.step;
+               return step;
+       },
+
+       _value: function() {
+               //internal value getter
+               // _value() returns value trimmed by min and max
+               var val = this.options.value;
+               val = this._trimValue(val);
+
+               return val;
+       },
+
+       _values: function(index) {
+               //internal values getter
+               // _values() returns array of values trimmed by min and max
+               // _values(index) returns single value trimmed by min and max
+
+               if (arguments.length) {
+                       var val = this.options.values[index];
+                       val = this._trimValue(val);
+
+                       return val;
+               } else {
+                       // .slice() creates a copy of the array
+                       // this copy gets trimmed by min and max and then returned
+                       var vals = this.options.values.slice();
+                       for (var i = 0, l = vals.length; i < l; i++) {
+                               vals[i] = this._trimValue(vals[i]);
+                       }
+
+                       return vals;
+               }
+
+       },
+       
+       _trimValue: function(val) {
+               if (val < this._valueMin()) val = this._valueMin();
+               if (val > this._valueMax()) val = this._valueMax();
+
+               return val;
+       },
+
+       _valueMin: function() {
+               var valueMin = this.options.min;
+               return valueMin;
+       },
+
+       _valueMax: function() {
+               var valueMax = this.options.max;
+               return valueMax;
+       },
+       
+       _refreshValue: function() {
+
+               var oRange = this.options.range, o = this.options, self = this;
+               var animate = (!this._animateOff) ? o.animate : false;
+
+               if (this.options.values && this.options.values.length) {
+                       var vp0, vp1;
+                       this.handles.each(function(i, j) {
+                               var valPercent = (self.values(i) - self._valueMin()) / (self._valueMax() - self._valueMin()) * 100;
+                               var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
+                               $(this).stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
+                               if (self.options.range === true) {
+                                       if (self.orientation == 'horizontal') {
+                                               (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ left: valPercent + '%' }, o.animate);
+                                               (i == 1) && self.range[animate ? 'animate' : 'css']({ width: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
+                                       } else {
+                                               (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ bottom: (valPercent) + '%' }, o.animate);
+                                               (i == 1) && self.range[animate ? 'animate' : 'css']({ height: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
+                                       }
+                               }
+                               lastValPercent = valPercent;
+                       });
+               } else {
+                       var value = this.value(),
+                               valueMin = this._valueMin(),
+                               valueMax = this._valueMax(),
+                               valPercent = valueMax != valueMin
+                                       ? (value - valueMin) / (valueMax - valueMin) * 100
+                                       : 0;
+                       var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
+                       this.handle.stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
+
+                       (oRange == "min") && (this.orientation == "horizontal") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ width: valPercent + '%' }, o.animate);
+                       (oRange == "max") && (this.orientation == "horizontal") && this.range[animate ? 'animate' : 'css']({ width: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
+                       (oRange == "min") && (this.orientation == "vertical") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ height: valPercent + '%' }, o.animate);
+                       (oRange == "max") && (this.orientation == "vertical") && this.range[animate ? 'animate' : 'css']({ height: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
+               }
+
+       }
+       
+});
+
+$.extend($.ui.slider, {
+       version: "1.8"
+});
+
+})(jQuery);
+/*
+    json2.js
+    2007-11-06
+
+    Public Domain
+
+    No warranty expressed or implied. Use at your own risk.
+
+    See http://www.JSON.org/js.html
+
+    This file creates a global JSON object containing two methods:
+
+        JSON.stringify(value, whitelist)
+            value       any JavaScript value, usually an object or array.
+
+            whitelist   an optional that determines how object values are
+                        stringified.
+
+            This method produces a JSON text from a JavaScript value.
+            There are three possible ways to stringify an object, depending
+            on the optional whitelist parameter.
+
+            If an object has a toJSON method, then the toJSON() method will be
+            called. The value returned from the toJSON method will be
+            stringified.
+
+            Otherwise, if the optional whitelist parameter is an array, then
+            the elements of the array will be used to select members of the
+            object for stringification.
+
+            Otherwise, if there is no whitelist parameter, then all of the
+            members of the object will be stringified.
+
+            Values that do not have JSON representaions, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped, in arrays will be replaced with null. JSON.stringify()
+            returns undefined. Dates will be stringified as quoted ISO dates.
+
+            Example:
+
+            var text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+        JSON.parse(text, filter)
+            This method parses a JSON text to produce an object or
+            array. It can throw a SyntaxError exception.
+
+            The optional filter parameter is a function that can filter and
+            transform the results. It receives each of the keys and values, and
+            its return value is used instead of the original value. If it
+            returns what it received, then structure is not modified. If it
+            returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. If a key contains the string 'date' then
+            // convert the value to a date.
+
+            myData = JSON.parse(text, function (key, value) {
+                return key.indexOf('date') >= 0 ? new Date(value) : value;
+            });
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+
+    Use your own copy. It is extremely unwise to load third party
+    code into your pages.
+*/
+
+/*jslint evil: true */
+/*extern JSON */
+
+if (!this.JSON) {
+
+    JSON = function () {
+
+        function f(n) {    // Format integers to have at least two digits.
+            return n < 10 ? '0' + n : n;
+        }
+
+        Date.prototype.toJSON = function () {
+
+// Eventually, this method will be based on the date.toISOString method.
+
+            return this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z';
+        };
+
+
+        var m = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        };
+
+        function stringify(value, whitelist) {
+            var a,          // The array holding the partial texts.
+                i,          // The loop counter.
+                k,          // The member key.
+                l,          // Length.
+                r = /["\\\x00-\x1f\x7f-\x9f]/g,
+                v;          // The member value.
+
+            switch (typeof value) {
+            case 'string':
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe sequences.
+
+                return r.test(value) ?
+                    '"' + value.replace(r, function (a) {
+                        var c = m[a];
+                        if (c) {
+                            return c;
+                        }
+                        c = a.charCodeAt();
+                        return '\\u00' + Math.floor(c / 16).toString(16) +
+                                                   (c % 16).toString(16);
+                    }) + '"' :
+                    '"' + value + '"';
+
+            case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+                return isFinite(value) ? String(value) : 'null';
+
+            case 'boolean':
+            case 'null':
+                return String(value);
+
+            case 'object':
+
+// Due to a specification blunder in ECMAScript,
+// typeof null is 'object', so watch out for that case.
+
+                if (!value) {
+                    return 'null';
+                }
+
+// If the object has a toJSON method, call it, and stringify the result.
+
+                if (typeof value.toJSON === 'function') {
+                    return stringify(value.toJSON());
+                }
+                a = [];
+                if (typeof value.length === 'number' &&
+                        !(value.propertyIsEnumerable('length'))) {
+
+// The object is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                    l = value.length;
+                    for (i = 0; i < l; i += 1) {
+                        a.push(stringify(value[i], whitelist) || 'null');
+                    }
+
+// Join all of the elements together and wrap them in brackets.
+
+                    return '[' + a.join(',') + ']';
+                }
+                if (whitelist) {
+
+// If a whitelist (array of keys) is provided, use it to select the components
+// of the object.
+
+                    l = whitelist.length;
+                    for (i = 0; i < l; i += 1) {
+                        k = whitelist[i];
+                        if (typeof k === 'string') {
+                            v = stringify(value[k], whitelist);
+                            if (v) {
+                                a.push(stringify(k) + ':' + v);
+                            }
+                        }
+                    }
+                } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                    for (k in value) {
+                        if (typeof k === 'string') {
+                            v = stringify(value[k], whitelist);
+                            if (v) {
+                                a.push(stringify(k) + ':' + v);
+                            }
+                        }
+                    }
+                }
+
+// Join all of the member texts together and wrap them in braces.
+
+                return '{' + a.join(',') + '}';
+            }
+        }
+
+        return {
+            stringify: stringify,
+            parse: function (text, filter) {
+                var j;
+
+                function walk(k, v) {
+                    var i, n;
+                    if (v && typeof v === 'object') {
+                        for (i in v) {
+                            if (Object.prototype.hasOwnProperty.apply(v, [i])) {
+                                n = walk(i, v[i]);
+                                if (n !== undefined) {
+                                    v[i] = n;
+                                }
+                            }
+                        }
+                    }
+                    return filter(k, v);
+                }
+
+
+// Parsing happens in three stages. In the first stage, we run the text against
+// regular expressions that look for non-JSON patterns. We are especially
+// concerned with '()' and 'new' because they can cause invocation, and '='
+// because it can cause mutation. But just to be safe, we want to reject all
+// unexpected forms.
+
+// We split the first stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+                if (/^[\],:{}\s]*$/.test(text.replace(/\\./g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the second stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                    j = eval('(' + text + ')');
+
+// In the optional third stage, we recursively walk the new structure, passing
+// each name/value pair to a filter function for possible transformation.
+
+                    return typeof filter === 'function' ? walk('', j) : j;
+                }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+                throw new SyntaxError('parseJSON');
+            }
+        };
+    }();
+}
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2010 University of Toronto
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+/*global fluid_1_3*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    /** 
+     * Returns the absolute position of a supplied DOM node in pixels.
+     * Implementation taken from quirksmode http://www.quirksmode.org/js/findpos.html
+     */
+    fluid.dom.computeAbsolutePosition = function (element) {
+        var curleft = 0, curtop = 0;
+        if (element.offsetParent) {
+            do {
+                curleft += element.offsetLeft;
+                curtop += element.offsetTop;
+                element = element.offsetParent;
+            } while (element);
+            return [curleft, curtop];
+        }
+    };
+    
+    /** 
+     * Cleanse the children of a DOM node by removing all <script> tags.
+     * This is necessary to prevent the possibility that these blocks are
+     * reevaluated if the node were reattached to the document. 
+     */
+    fluid.dom.cleanseScripts = function (element) {
+        var cleansed = $.data(element, fluid.dom.cleanseScripts.MARKER);
+        if (!cleansed) {
+            fluid.dom.iterateDom(element, function (node) {
+                return node.tagName.toLowerCase() === "script"? "delete" : null;
+            });
+            $.data(element, fluid.dom.cleanseScripts.MARKER, true);
+        }
+    };  
+    fluid.dom.cleanseScripts.MARKER = "fluid-scripts-cleansed";
+
+    /**
+     * Inserts newChild as the next sibling of refChild.
+     * @param {Object} newChild
+     * @param {Object} refChild
+     */
+    fluid.dom.insertAfter = function (newChild, refChild) {
+        var nextSib = refChild.nextSibling;
+        if (!nextSib) {
+            refChild.parentNode.appendChild(newChild);
+        }
+        else {
+            refChild.parentNode.insertBefore(newChild, nextSib);
+        }
+    };
+    
+    // The following two functions taken from http://developer.mozilla.org/En/Whitespace_in_the_DOM
+    /**
+     * Determine whether a node's text content is entirely whitespace.
+     *
+     * @param node  A node implementing the |CharacterData| interface (i.e.,
+     *              a |Text|, |Comment|, or |CDATASection| node
+     * @return     True if all of the text content of |nod| is whitespace,
+     *             otherwise false.
+     */
+    fluid.dom.isWhitespaceNode = function (node) {
+       // Use ECMA-262 Edition 3 String and RegExp features
+        return !(/[^\t\n\r ]/.test(node.data));
+    };
+    
+    /**
+     * Determine if a node should be ignored by the iterator functions.
+     *
+     * @param nod  An object implementing the DOM1 |Node| interface.
+     * @return     true if the node is:
+     *                1) A |Text| node that is all whitespace
+     *                2) A |Comment| node
+     *             and otherwise false.
+     */
+    fluid.dom.isIgnorableNode = function (node) {
+        return (node.nodeType === 8) || // A comment node
+         ((node.nodeType === 3) && fluid.dom.isWhitespaceNode(node)); // a text node, all ws
+    };
+
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+/*global fluid_1_3*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.orientation = {
+        HORIZONTAL: 4,
+        VERTICAL: 1
+    };
+    
+    fluid.rectSides = {
+        // agree with fluid.orientation
+        4: ["left", "right"],
+        1: ["top", "bottom"],
+        // agree with fluid.direction
+        8: "top",
+        12: "bottom",
+        2: "left",
+        3: "right"
+    };
+    
+    /**
+     * This is the position, relative to a given drop target, that a dragged item should be dropped.
+     */
+    fluid.position = {
+        BEFORE: -1,
+        AFTER: 1,
+        INSIDE: 2,
+        REPLACE: 3
+    };
+    
+    /**
+     * For incrementing/decrementing a count or index, or moving in a rectilinear direction.
+     */
+    fluid.direction = {
+        NEXT: 1,
+        PREVIOUS: -1,
+        UP: 8,
+        DOWN: 12,
+        LEFT: 2,
+        RIGHT: 3
+    };
+    
+    fluid.directionSign = function (direction) {
+        return direction === fluid.direction.UP || direction === fluid.direction.LEFT? 
+             fluid.direction.PREVIOUS : fluid.direction.NEXT;
+    };
+    
+    fluid.directionAxis = function (direction) {
+        return direction === fluid.direction.LEFT || direction === fluid.direction.RIGHT?
+            0 : 1; 
+    };
+    
+    fluid.directionOrientation = function (direction) {
+        return fluid.directionAxis(direction)? fluid.orientation.VERTICAL : fluid.orientation.HORIZONTAL;
+    };
+    
+    fluid.keycodeDirection = {
+        up: fluid.direction.UP,
+        down: fluid.direction.DOWN,
+        left: fluid.direction.LEFT,
+        right: fluid.direction.RIGHT
+    };
+    
+    // moves a single node in the DOM to a new position relative to another
+    fluid.moveDom = function (source, target, position) {
+        source = fluid.unwrap(source);
+        target = fluid.unwrap(target);
+        
+        var scan;
+        // fluid.log("moveDom source " + fluid.dumpEl(source) + " target " + fluid.dumpEl(target) + " position " + position);     
+        if (position === fluid.position.INSIDE) {
+            target.appendChild(source);
+        }
+        else if (position === fluid.position.BEFORE) {
+            for (scan = target.previousSibling; ; scan = scan.previousSibling) {
+                if (!scan || !fluid.dom.isIgnorableNode(scan)) {
+                    if (scan !== source) {
+                        fluid.dom.cleanseScripts(source);
+                        target.parentNode.insertBefore(source, target);    
+                    }
+                    break;
+                }
+            }
+        }
+        else if (position === fluid.position.AFTER) {
+            for (scan = target.nextSibling; ; scan = scan.nextSibling) {
+                if (!scan || !fluid.dom.isIgnorableNode(scan)) {
+                    if (scan !== source) {
+                        fluid.dom.cleanseScripts(source);
+                        fluid.dom.insertAfter(source, target);
+                    }
+                    break;
+                }
+            }
+        }
+        else {
+            fluid.fail("Unrecognised position supplied to fluid.moveDom: " + position);
+        }
+    };
+    
+    fluid.normalisePosition = function (position, samespan, targeti, sourcei) {
+        // convert a REPLACE into a primitive BEFORE/AFTER
+        if (position === fluid.position.REPLACE) {
+            position = samespan && targeti >= sourcei? fluid.position.AFTER: fluid.position.BEFORE;
+        }
+        return position;
+    };
+    
+    fluid.permuteDom = function (element, target, position, sourceelements, targetelements) {
+        element = fluid.unwrap(element);
+        target = fluid.unwrap(target);
+        var sourcei = $.inArray(element, sourceelements);
+        if (sourcei === -1) {
+            fluid.fail("Error in permuteDom: source element " + fluid.dumpEl(element) 
+               + " not found in source list " + fluid.dumpEl(sourceelements));
+        }
+        var targeti = $.inArray(target, targetelements);
+        if (targeti === -1) {
+            fluid.fail("Error in permuteDom: target element " + fluid.dumpEl(target) 
+               + " not found in source list " + fluid.dumpEl(targetelements));
+        }
+        var samespan = sourceelements === targetelements;
+        position = fluid.normalisePosition(position, samespan, targeti, sourcei);
+
+        //fluid.log("permuteDom sourcei " + sourcei + " targeti " + targeti);
+        // cache the old neighbourhood of the element for the final move
+        var oldn = {};
+        oldn[fluid.position.AFTER] = element.nextSibling;
+        oldn[fluid.position.BEFORE] = element.previousSibling;
+        fluid.moveDom(sourceelements[sourcei], targetelements[targeti], position);
+        
+        // perform the leftward-moving, AFTER shift
+        var frontlimit = samespan? targeti - 1: sourceelements.length - 2;
+        var i;
+        if (position === fluid.position.BEFORE && samespan) { 
+            // we cannot do skip processing if the element was "fused against the grain" 
+            frontlimit--;
+        }
+        if (!samespan || targeti > sourcei) {
+            for (i = frontlimit; i > sourcei; -- i) {
+                fluid.moveDom(sourceelements[i + 1], sourceelements[i], fluid.position.AFTER);
+            }
+            if (sourcei + 1 < sourceelements.length) {
+                fluid.moveDom(sourceelements[sourcei + 1], oldn[fluid.position.AFTER], fluid.position.BEFORE);
+            }
+        }
+        // perform the rightward-moving, BEFORE shift
+        var backlimit = samespan? sourcei - 1: targetelements.length - 1;
+        if (position === fluid.position.AFTER) { 
+            // we cannot do skip processing if the element was "fused against the grain" 
+            targeti++;
+        }
+        if (!samespan || targeti < sourcei) {
+            for (i = targeti; i < backlimit; ++ i) {
+                fluid.moveDom(targetelements[i], targetelements[i + 1], fluid.position.BEFORE);
+            }
+            if (backlimit >= 0 && backlimit < targetelements.length - 1) {
+                fluid.moveDom(targetelements[backlimit], oldn[fluid.position.BEFORE], fluid.position.AFTER);
+            }                
+        }
+
+    };
+  
+    var curCss = function (a, name) {
+        return window.getComputedStyle? window.getComputedStyle(a, null).getPropertyValue(name) : 
+          a.currentStyle[name];
+    };
+    
+    var isAttached = function (node) {
+        while (node && node.nodeName) {
+            if (node.nodeName === "BODY") {
+                return true;
+            }
+            node = node.parentNode;
+        }
+        return false;
+    };
+    
+    var generalHidden = function (a) {
+        return "hidden" === a.type || curCss(a, "display") === "none" || curCss(a, "visibility") === "hidden" || !isAttached(a);
+    };
+    
+
+    var computeGeometry = function (element, orientation, disposition) {
+        var elem = {};
+        elem.element = element;
+        elem.orientation = orientation;
+        if (disposition === fluid.position.INSIDE) {
+            elem.position = disposition;
+        }
+        if (generalHidden(element)) {
+            elem.clazz = "hidden";
+        }
+        var pos = fluid.dom.computeAbsolutePosition(element) || [0, 0];
+        var width = element.offsetWidth;
+        var height = element.offsetHeight;
+        elem.rect = {left: pos[0], top: pos[1]};
+        elem.rect.right = pos[0] + width;
+        elem.rect.bottom = pos[1] + height;
+        return elem;
+    };
+    
+    // A "suitable large" value for the sentinel blocks at the ends of spans
+    var SENTINEL_DIMENSION = 10000;
+
+    function dumprect(rect) {
+        return "Rect top: " + rect.top +
+                 " left: " + rect.left + 
+               " bottom: " + rect.bottom +
+                " right: " + rect.right;
+    }
+
+    function dumpelem(cacheelem) {
+        if (!cacheelem || !cacheelem.rect) {
+            return "null";
+        } else {
+            return dumprect(cacheelem.rect) + " position: " +
+            cacheelem.position +
+            " for " +
+            fluid.dumpEl(cacheelem.element);
+        }
+    }
+    
+   
+    
+    fluid.dropManager = function () { 
+        var targets = [];
+        var cache = {};
+        var that = {};        
+        
+        var lastClosest;              
+        var lastGeometry;
+        var displacementX, displacementY;
+        
+        that.updateGeometry = function (geometricInfo) {
+            lastGeometry = geometricInfo;
+            targets = [];
+            cache = {};
+            var mapper = geometricInfo.elementMapper;
+            for (var i = 0; i < geometricInfo.extents.length; ++ i) {
+                var thisInfo = geometricInfo.extents[i];
+                var orientation = thisInfo.orientation;
+                var sides = fluid.rectSides[orientation];
+                
+                var processElement = function (element, sentB, sentF, disposition, j) {
+                    var cacheelem = computeGeometry(element, orientation, disposition);
+                    cacheelem.owner = thisInfo;
+                    if (cacheelem.clazz !== "hidden" && mapper) {
+                        cacheelem.clazz = mapper(element);
+                    }
+                    cache[fluid.dropManager.cacheKey(element)] = cacheelem;
+                    var backClass = fluid.dropManager.getRelativeClass(thisInfo.elements, j, fluid.position.BEFORE, cacheelem.clazz, mapper); 
+                    var frontClass = fluid.dropManager.getRelativeClass(thisInfo.elements, j, fluid.position.AFTER, cacheelem.clazz, mapper); 
+                    if (disposition === fluid.position.INSIDE) {
+                        targets[targets.length] = cacheelem;
+                    }
+                    else {
+                        fluid.dropManager.splitElement(targets, sides, cacheelem, disposition, backClass, frontClass);
+                    }
+                    // deal with sentinel blocks by creating near-copies of the end elements
+                    if (sentB && geometricInfo.sentinelize) {
+                        fluid.dropManager.sentinelizeElement(targets, sides, cacheelem, 1, disposition, backClass);
+                    }
+                    if (sentF && geometricInfo.sentinelize) {
+                        fluid.dropManager.sentinelizeElement(targets, sides, cacheelem, 0, disposition, frontClass);
+                    }
+                    //fluid.log(dumpelem(cacheelem));
+                    return cacheelem;
+                };
+                
+                var allHidden = true;
+                for (var j = 0; j < thisInfo.elements.length; ++ j) {
+                    var element = thisInfo.elements[j];
+                    var cacheelem = processElement(element, j === 0, j === thisInfo.elements.length - 1, 
+                            fluid.position.INTERLEAVED, j);
+                    if (cacheelem.clazz !== "hidden") {
+                        allHidden = false;
+                    }
+                }
+                if (allHidden && thisInfo.parentElement) {
+                    processElement(thisInfo.parentElement, true, true, 
+                            fluid.position.INSIDE);
+                }
+            }   
+        };
+        
+        that.startDrag = function (event, handlePos, handleWidth, handleHeight) {
+            var handleMidX = handlePos[0] + handleWidth / 2;
+            var handleMidY = handlePos[1] + handleHeight / 2;
+            var dX = handleMidX - event.pageX;
+            var dY = handleMidY - event.pageY;
+            that.updateGeometry(lastGeometry);
+            lastClosest = null;
+            displacementX = dX;
+            displacementY = dY;
+            $("body").bind("mousemove.fluid-dropManager", that.mouseMove);
+        };
+        
+        that.lastPosition = function () {
+            return lastClosest;
+        };
+        
+        that.endDrag = function () {
+            $("body").unbind("mousemove.fluid-dropManager");
+        };
+        
+        that.mouseMove = function (evt) {
+            var x = evt.pageX + displacementX;
+            var y = evt.pageY + displacementY;
+            //fluid.log("Mouse x " + x + " y " + y );
+            
+            var closestTarget = that.closestTarget(x, y, lastClosest);
+            if (closestTarget && closestTarget !== fluid.dropManager.NO_CHANGE) {
+                lastClosest = closestTarget;
+              
+                that.dropChangeFirer.fire(closestTarget);
+            }
+        };
+        
+        that.dropChangeFirer = fluid.event.getEventFirer();
+        
+        var blankHolder = {
+            element: null
+        };
+        
+        that.closestTarget = function (x, y, lastClosest) {
+            var mindistance = Number.MAX_VALUE;
+            var minelem = blankHolder;
+            var minlockeddistance = Number.MAX_VALUE;
+            var minlockedelem = blankHolder;
+            for (var i = 0; i < targets.length; ++ i) {
+                var cacheelem = targets[i];
+                if (cacheelem.clazz === "hidden") {
+                    continue;
+                }
+                var distance = fluid.geom.minPointRectangle(x, y, cacheelem.rect);
+                if (cacheelem.clazz === "locked") {
+                    if (distance < minlockeddistance) {
+                        minlockeddistance = distance;
+                        minlockedelem = cacheelem;
+                    }
+                } else {
+                    if (distance < mindistance) {
+                        mindistance = distance;
+                        minelem = cacheelem;
+                    }
+                    if (distance === 0) {
+                        break;
+                    }
+                }
+            }
+            if (!minelem) {
+                return minelem;
+            }
+            if (minlockeddistance >= mindistance) {
+                minlockedelem = blankHolder;
+            }
+            //fluid.log("PRE: mindistance " + mindistance + " element " + 
+            //   fluid.dumpEl(minelem.element) + " minlockeddistance " + minlockeddistance
+            //    + " locked elem " + dumpelem(minlockedelem));
+            if (lastClosest && lastClosest.position === minelem.position &&
+                fluid.unwrap(lastClosest.element) === fluid.unwrap(minelem.element) &&
+                fluid.unwrap(lastClosest.lockedelem) === fluid.unwrap(minlockedelem.element)
+                ) {
+                return fluid.dropManager.NO_CHANGE;
+            }
+            //fluid.log("mindistance " + mindistance + " minlockeddistance " + minlockeddistance);
+            return {
+                position: minelem.position,
+                element: minelem.element,
+                lockedelem: minlockedelem.element
+            };
+        };
+        
+        that.shuffleProjectFrom = function (element, direction, includeLocked, disableWrap) {
+            var togo = that.projectFrom(element, direction, includeLocked, disableWrap);
+            if (togo) {
+                togo.position = fluid.position.REPLACE;
+            }
+            return togo;
+        };
+        
+        that.projectFrom = function (element, direction, includeLocked, disableWrap) {
+            that.updateGeometry(lastGeometry);
+            var cacheelem = cache[fluid.dropManager.cacheKey(element)];
+            var projected = fluid.geom.projectFrom(cacheelem.rect, direction, targets, includeLocked, disableWrap);
+            if (!projected.cacheelem) {
+                return null;
+            }
+            var retpos = projected.cacheelem.position;
+            return {element: projected.cacheelem.element, 
+                     position: retpos? retpos : fluid.position.BEFORE 
+                     };
+        };
+        
+        that.logicalFrom = function (element, direction, includeLocked, disableWrap) {
+            var orderables = that.getOwningSpan(element, fluid.position.INTERLEAVED, includeLocked);
+            return {element: fluid.dropManager.getRelativeElement(element, direction, orderables, disableWrap), 
+                position: fluid.position.REPLACE};
+        };
+           
+        that.lockedWrapFrom = function (element, direction, includeLocked, disableWrap) {
+            var base = that.logicalFrom(element, direction, includeLocked, disableWrap);
+            var selectables = that.getOwningSpan(element, fluid.position.INTERLEAVED, includeLocked);
+            var allElements = cache[fluid.dropManager.cacheKey(element)].owner.elements;
+            if (includeLocked || selectables[0] === allElements[0]) {
+                return base;
+            }
+            var directElement = fluid.dropManager.getRelativeElement(element, direction, allElements, disableWrap);
+            if (lastGeometry.elementMapper(directElement) === "locked") {
+                base.element = null;
+                base.clazz = "locked";  
+            }
+            return base;
+        }; 
+        
+        that.getOwningSpan = function (element, position, includeLocked) {
+            var owner = cache[fluid.dropManager.cacheKey(element)].owner; 
+            var elements = position === fluid.position.INSIDE? [owner.parentElement] : owner.elements;
+            if (!includeLocked && lastGeometry.elementMapper) {
+                elements = $.makeArray(elements);
+                fluid.remove_if(elements, function (element) {
+                    return lastGeometry.elementMapper(element) === "locked";
+                });
+            }
+            return elements;
+        };
+        
+        that.geometricMove = function (element, target, position) {
+            var sourceElements = that.getOwningSpan(element, null, true);
+            var targetElements = that.getOwningSpan(target, position, true);
+            fluid.permuteDom(element, target, position, sourceElements, targetElements);
+        };              
+        
+        return that;
+    };    
+   
+    fluid.dropManager.NO_CHANGE = "no change";
+    
+    fluid.dropManager.cacheKey = function (element) {
+        return fluid.allocateSimpleId(element);
+    };
+    
+    fluid.dropManager.sentinelizeElement = function (targets, sides, cacheelem, fc, disposition, clazz) {
+        var elemCopy = $.extend(true, {}, cacheelem);
+        elemCopy.rect[sides[fc]] = elemCopy.rect[sides[1 - fc]] + (fc? 1: -1);
+        elemCopy.rect[sides[1 - fc]] = (fc? -1 : 1) * SENTINEL_DIMENSION;
+        elemCopy.position = disposition === fluid.position.INSIDE?
+           disposition : (fc? fluid.position.BEFORE : fluid.position.AFTER);
+        elemCopy.clazz = clazz;
+        targets[targets.length] = elemCopy;
+    };
+    
+    fluid.dropManager.splitElement = function (targets, sides, cacheelem, disposition, clazz1, clazz2) {
+        var elem1 = $.extend(true, {}, cacheelem);
+        var elem2 = $.extend(true, {}, cacheelem);
+        var midpoint = (elem1.rect[sides[0]] + elem1.rect[sides[1]]) / 2;
+        elem1.rect[sides[1]] = midpoint; 
+        elem1.position = fluid.position.BEFORE;
+        
+        elem2.rect[sides[0]] = midpoint; 
+        elem2.position = fluid.position.AFTER;
+        
+        elem1.clazz = clazz1;
+        elem2.clazz = clazz2;
+        targets[targets.length] = elem1;
+        targets[targets.length] = elem2;
+    };
+    
+    // Expand this configuration point if we ever go back to a full "permissions" model
+    fluid.dropManager.getRelativeClass = function (thisElements, index, relative, thisclazz, mapper) {
+        index += relative;
+        if (index < 0 && thisclazz === "locked") {
+            return "locked";
+        }
+        if (index >= thisElements.length || mapper === null) {
+            return null;
+        } else {
+            relative = thisElements[index];
+            return mapper(relative) === "locked" && thisclazz === "locked" ? "locked" : null;
+        }
+    };
+    
+     fluid.dropManager.getRelativeElement = function (element, direction, elements, disableWrap) {
+        var folded = fluid.directionSign(direction);
+  
+        var index = $(elements).index(element) + folded;
+        if (index < 0) {
+            index += elements.length;
+        }
+        
+        // disable wrap
+        if (disableWrap) {                   
+            if (index === elements.length || index === (elements.length + folded)) {
+                return element;
+            }
+        }
+                      
+        index %= elements.length;
+        return elements[index];              
+    };
+    
+    fluid.geom = fluid.geom || {};
+    
+    // These distance algorithms have been taken from
+    // http://www.cs.mcgill.ca/~cs644/Godfried/2005/Fall/fzamal/concepts.htm
+    
+    /** Returns the minimum squared distance between a point and a rectangle **/
+    fluid.geom.minPointRectangle = function (x, y, rectangle) {
+        var dx = x < rectangle.left? (rectangle.left - x) : 
+                  (x > rectangle.right? (x - rectangle.right) : 0);
+        var dy = y < rectangle.top? (rectangle.top - y) : 
+                  (y > rectangle.bottom? (y - rectangle.bottom) : 0);
+        return dx * dx + dy * dy;
+    };
+    
+    /** Returns the minimum squared distance between two rectangles **/
+    fluid.geom.minRectRect = function (rect1, rect2) {
+        var dx = rect1.right < rect2.left? rect2.left - rect1.right : 
+                 rect2.right < rect1.left? rect1.left - rect2.right :0;
+        var dy = rect1.bottom < rect2.top? rect2.top - rect1.bottom : 
+                 rect2.bottom < rect1.top? rect1.top - rect2.bottom :0;
+        return dx * dx + dy * dy;
+    };
+    
+    var makePenCollect = function () {
+        return {
+            mindist: Number.MAX_VALUE,
+            minrdist: Number.MAX_VALUE
+        };
+    };
+
+    /** Determine the one amongst a set of rectangle targets which is the "best fit"
+     * for an axial motion from a "base rectangle" (commonly arising from the case
+     * of cursor key navigation).
+     * @param {Rectangle} baserect The base rectangl from which the motion is to be referred
+     * @param {fluid.direction} direction  The direction of motion
+     * @param {Array of Rectangle holders} targets An array of objects "cache elements" 
+     * for which the member <code>rect</code> is the holder of the rectangle to be tested.
+     * @param disableWrap which is used to enable or disable wrapping of elements
+     * @return The cache element which is the most appropriate for the requested motion.
+     */
+    fluid.geom.projectFrom = function (baserect, direction, targets, forSelection, disableWrap) {
+        var axis = fluid.directionAxis(direction);
+        var frontSide = fluid.rectSides[direction];
+        var backSide = fluid.rectSides[axis * 15 + 5 - direction];
+        var dirSign = fluid.directionSign(direction);
+        
+        var penrect = {left: (7 * baserect.left + 1 * baserect.right) / 8,
+                       right: (5 * baserect.left + 3 * baserect.right) / 8,
+                       top: (7 * baserect.top + 1 * baserect.bottom) / 8,
+                       bottom: (5 * baserect.top + 3 * baserect.bottom) / 8};
+         
+        penrect[frontSide] = dirSign * SENTINEL_DIMENSION;
+        penrect[backSide] = -penrect[frontSide];
+        
+        function accPen(collect, cacheelem, backSign) {
+            var thisrect = cacheelem.rect;
+            var pdist = fluid.geom.minRectRect(penrect, thisrect);
+            var rdist = -dirSign * backSign * (baserect[backSign === 1 ? frontSide:backSide] 
+                                             - thisrect[backSign === 1 ? backSide:frontSide]);
+            // fluid.log("pdist: " + pdist + " rdist: " + rdist);
+            // the oddity in the rdist comparison is intended to express "half-open"-ness of rectangles
+            // (backSign === 1? 0 : 1) - this is now gone - must be possible to move to perpendicularly abutting regions
+            if (pdist <= collect.mindist && rdist >= 0) {
+                if (pdist === collect.mindist && rdist * backSign > collect.minrdist) {
+                    return;
+                }
+                collect.minrdist = rdist * backSign;
+                collect.mindist = pdist;
+                collect.minelem = cacheelem;
+            }
+        }
+        var collect = makePenCollect();
+        var backcollect = makePenCollect();
+        var lockedcollect = makePenCollect();
+
+        for (var i = 0; i < targets.length; ++ i) {
+            var elem = targets[i];
+            var isPure = elem.owner && elem.element === elem.owner.parentElement;
+            if (elem.clazz === "hidden" || forSelection && isPure) {
+                continue;
+            }
+            else if (!forSelection && elem.clazz === "locked") {
+                accPen(lockedcollect, elem, 1);
+            }
+            else {
+                accPen(collect, elem, 1);
+                accPen(backcollect, elem, -1);
+            }
+            //fluid.log("Element " + i + " " + dumpelem(elem) + " mindist " + collect.mindist);
+        }
+        var wrap = !collect.minelem || backcollect.mindist < collect.mindist ;
+        
+        // disable wrap
+        wrap = wrap && !disableWrap;       
+                
+        var mincollect = wrap? backcollect: collect;        
+        
+        var togo = {
+            wrapped: wrap,
+            cacheelem: mincollect.minelem
+        };
+        if (lockedcollect.mindist < mincollect.mindist) {
+            togo.lockedelem = lockedcollect.minelem;
+        }
+        return togo;
+    };
+})(jQuery, fluid_1_3);
+/*
+Copyright 2007-2009 University of Toronto
+Copyright 2007-2010 University of Cambridge
+Copyright 2010 OCAD University
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery, fluid_1_3, document*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    var defaultAvatarCreator = function (item, cssClass, dropWarning) {
+        fluid.dom.cleanseScripts(fluid.unwrap(item));
+        var avatar = $(item).clone();
+        
+        fluid.dom.iterateDom(avatar.get(0), function (node) {
+            node.removeAttribute("id");
+            if (node.tagName.toLowerCase() === "input") {
+                node.setAttribute("disabled", "disabled");
+            }
+        });
+        
+        avatar.removeAttr("id");
+        avatar.removeClass("ui-droppable");
+        avatar.addClass(cssClass);
+        
+        if (dropWarning) {
+            // Will a 'div' always be valid in this position?
+            var avatarContainer = $(document.createElement("div"));
+            avatarContainer.append(avatar);
+            avatarContainer.append(dropWarning);
+            avatar = avatarContainer;
+        }
+        $("body").append(avatar);
+        if (!$.browser.safari) {
+            // FLUID-1597: Safari appears incapable of correctly determining the dimensions of elements
+            avatar.css("display", "block").width(item.offsetWidth).height(item.offsetHeight);
+        }
+        
+        if ($.browser.opera) { // FLUID-1490. Without this detect, curCSS explodes on the avatar on Firefox.
+            avatar.hide();
+        }
+        return avatar;
+    };
+    
+    function bindHandlersToContainer(container, keyDownHandler, keyUpHandler, mouseMoveHandler) {
+        var actualKeyDown = keyDownHandler;
+        var advancedPrevention = false;
+
+        // FLUID-1598 and others: Opera will refuse to honour a "preventDefault" on a keydown.
+        // http://forums.devshed.com/javascript-development-115/onkeydown-preventdefault-opera-485371.html
+        if ($.browser.opera) {
+            container.keypress(function (evt) {
+                if (advancedPrevention) {
+                    advancedPrevention = false;
+                    evt.preventDefault();
+                    return false;
+                }
+            });
+            actualKeyDown = function (evt) {
+                var oldret = keyDownHandler(evt);
+                if (oldret === false) {
+                    advancedPrevention = true;
+                }
+            };
+        }
+        container.keydown(actualKeyDown);
+        container.keyup(keyUpHandler);
+    }
+    
+    function addRolesToContainer(that) {
+        that.container.attr("role", that.options.containerRole.container);
+        that.container.attr("aria-multiselectable", "false");
+        that.container.attr("aria-readonly", "false");
+        that.container.attr("aria-disabled", "false");
+        // FLUID-3707: We require to have BOTH application role as well as our named role
+        // This however breaks the component completely under NVDA and causes it to perpetually drop back into "browse mode"
+        //that.container.wrap("<div role=\"application\"></div>");
+    }
+    
+    function createAvatarId(parentId) {
+        // Generating the avatar's id to be containerId_avatar
+        // This is safe since there is only a single avatar at a time
+        return parentId + "_avatar";
+    }
+    
+    var adaptKeysets = function (options) {
+        if (options.keysets && !(options.keysets instanceof Array)) {
+            options.keysets = [options.keysets];    
+        }
+    };
+    
+    /**
+     * @param container - A jQueryable designator for the root node of the reorderer (a selector, a DOM node, or a jQuery instance)
+     * @param options - an object containing any of the available options:
+     *                  containerRole - indicates the role, or general use, for this instance of the Reorderer
+     *                  keysets - an object containing sets of keycodes to use for directional navigation. Must contain:
+     *                            modifier - a function that returns a boolean, indicating whether or not the required modifier(s) are activated
+     *                            up
+     *                            down
+     *                            right
+     *                            left
+     *                  styles - an object containing class names for styling the Reorderer
+     *                                  defaultStyle
+     *                                  selected
+     *                                  dragging
+     *                                  hover
+     *                                  dropMarker
+     *                                  mouseDrag
+     *                                  avatar
+     *                  avatarCreator - a function that returns a valid DOM node to be used as the dragging avatar
+     */
+    fluid.reorderer = function (container, options) {
+        if (!container) {
+            fluid.fail("Reorderer initialised with no container");
+        }
+        var thatReorderer = fluid.initView("fluid.reorderer", container, options);
+        options = thatReorderer.options;
+                
+        var dropManager = fluid.dropManager();   
+                
+        thatReorderer.layoutHandler = fluid.initSubcomponent(thatReorderer,
+            "layoutHandler", [thatReorderer.container, options, dropManager, thatReorderer.dom]);
+        
+        thatReorderer.activeItem = undefined;
+
+        adaptKeysets(options);
+        var kbDropWarning = thatReorderer.locate("dropWarning");
+        var mouseDropWarning;
+        if (kbDropWarning) {
+            mouseDropWarning = kbDropWarning.clone();
+        }
+
+        var isMove = function (evt) {
+            var keysets = options.keysets;
+            for (var i = 0; i < keysets.length; i++) {
+                if (keysets[i].modifier(evt)) {
+                    return true;
+                }
+            }
+            return false;
+        };
+        
+        var isActiveItemMovable = function () {
+            return $.inArray(thatReorderer.activeItem, thatReorderer.dom.fastLocate("movables")) >= 0;
+        };
+        
+        var setDropEffects = function (value) {
+            thatReorderer.dom.fastLocate("dropTargets").attr("aria-dropeffect", value);
+        };
+        
+        var styles = options.styles;
+        
+        var noModifier = function (evt) {
+            return (!evt.ctrlKey && !evt.altKey && !evt.shiftKey && !evt.metaKey);
+        };
+        
+        var handleDirectionKeyDown = function (evt) {
+            var item = thatReorderer.activeItem;
+            if (!item) {
+                return true;
+            }
+            var keysets = options.keysets;
+            for (var i = 0; i < keysets.length; i++) {
+                var keyset = keysets[i];
+                var keydir = fluid.keyForValue(keyset, evt.keyCode);
+                if (!keydir) {
+                    continue;
+                }
+                var isMovement = keyset.modifier(evt);
+                
+                var dirnum = fluid.keycodeDirection[keydir];
+                var relativeItem = thatReorderer.layoutHandler.getRelativePosition(item, dirnum, !isMovement);  
+                if (!relativeItem) {
+                    continue;
+                }
+                
+                if (isMovement) {
+                    var prevent = thatReorderer.events.onBeginMove.fire(item);
+                    if (prevent === false) {
+                        return false;
+                    }
+                    if (kbDropWarning.length > 0) {
+                        if (relativeItem.clazz === "locked") {
+                            thatReorderer.events.onShowKeyboardDropWarning.fire(item, kbDropWarning);
+                            kbDropWarning.show();                       
+                        }
+                        else {
+                            kbDropWarning.hide();
+                        }
+                    }
+                    if (relativeItem.element) {
+                        thatReorderer.requestMovement(relativeItem, item);
+                    }
+            
+                } else if (noModifier(evt)) {
+                    item.blur();
+                    $(relativeItem.element).focus();
+                }
+                return false;
+            }
+            return true;
+        };
+
+        thatReorderer.handleKeyDown = function (evt) {
+            if (!thatReorderer.activeItem || thatReorderer.activeItem !== evt.target) {
+                return true;
+            }
+            // If the key pressed is ctrl, and the active item is movable we want to restyle the active item.
+            var jActiveItem = $(thatReorderer.activeItem);
+            if (!jActiveItem.hasClass(styles.dragging) && isMove(evt)) {
+               // Don't treat the active item as dragging unless it is a movable.
+                if (isActiveItemMovable()) {
+                    jActiveItem.removeClass(styles.selected);
+                    jActiveItem.addClass(styles.dragging);
+                    jActiveItem.attr("aria-grabbed", "true");
+                    setDropEffects("move");
+                }
+                return false;
+            }
+            // The only other keys we listen for are the arrows.
+            return handleDirectionKeyDown(evt);
+        };
+
+        thatReorderer.handleKeyUp = function (evt) {
+            if (!thatReorderer.activeItem || thatReorderer.activeItem !== evt.target) {
+                return true;
+            }
+            var jActiveItem = $(thatReorderer.activeItem);
+            
+            // Handle a key up event for the modifier
+            if (jActiveItem.hasClass(styles.dragging) && !isMove(evt)) {
+                if (kbDropWarning) {
+                    kbDropWarning.hide();
+                }
+                jActiveItem.removeClass(styles.dragging);
+                jActiveItem.addClass(styles.selected);
+                jActiveItem.attr("aria-grabbed", "false");
+                setDropEffects("none");
+                return false;
+            }
+            
+            return false;
+        };
+
+        var dropMarker;
+
+        var createDropMarker = function (tagName) {
+            var dropMarker = $(document.createElement(tagName));
+            dropMarker.addClass(options.styles.dropMarker);
+            dropMarker.hide();
+            return dropMarker;
+        };
+
+        thatReorderer.requestMovement = function (requestedPosition, item) {
+            item = fluid.unwrap(item);
+          // Temporary censoring to get around ModuleLayout inability to update relative to self.
+            if (!requestedPosition || fluid.unwrap(requestedPosition.element) === item) {
+                return;
+            }
+            var activeItem = $(thatReorderer.activeItem);
+            
+            // Fixes FLUID-3288.
+            // Need to unbind the blur event as safari will call blur on movements.
+            // This caused the user to have to double tap the arrow keys to move.
+            activeItem.unbind("blur.fluid.reorderer");
+            
+            thatReorderer.events.onMove.fire(item, requestedPosition);
+            dropManager.geometricMove(item, requestedPosition.element, requestedPosition.position);
+            //$(thatReorderer.activeItem).removeClass(options.styles.selected);
+           
+            // refocus on the active item because moving places focus on the body
+            activeItem.focus();
+            
+            thatReorderer.refresh();
+            
+            dropManager.updateGeometry(thatReorderer.layoutHandler.getGeometricInfo());
+
+            thatReorderer.events.afterMove.fire(item, requestedPosition, thatReorderer.dom.fastLocate("movables"));
+        };
+
+        var hoverStyleHandler = function (item, state) {
+            thatReorderer.dom.fastLocate("grabHandle", item)[state?"addClass":"removeClass"](styles.hover);
+        };
+        /**
+         * Takes a $ object and adds 'movable' functionality to it
+         */
+        function initMovable(item) {
+            var styles = options.styles;
+            item.attr("aria-grabbed", "false");
+
+            item.mouseover(
+                function () {
+                    thatReorderer.events.onHover.fire(item, true);
+                }
+            );
+        
+            item.mouseout(
+                function () {
+                    thatReorderer.events.onHover.fire(item, false);
+                }
+            );
+            var avatar;
+        
+            thatReorderer.dom.fastLocate("grabHandle", item).draggable({
+                refreshPositions: false,
+                scroll: true,
+                helper: function () {
+                    var dropWarningEl;
+                    if (mouseDropWarning) {
+                        dropWarningEl = mouseDropWarning[0];
+                    }
+                    avatar = $(options.avatarCreator(item[0], styles.avatar, dropWarningEl));
+                    avatar.attr("id", createAvatarId(thatReorderer.container.id));
+                    return avatar;
+                },
+                start: function (e, ui) {
+                    var prevent = thatReorderer.events.onBeginMove.fire(item);
+                    if (prevent === false) {
+                        return false;
+                    }
+                    var handle = thatReorderer.dom.fastLocate("grabHandle", item)[0];
+                    var handlePos = fluid.dom.computeAbsolutePosition(handle);
+                    var handleWidth = handle.offsetWidth;
+                    var handleHeight = handle.offsetHeight;
+                    item.focus();
+                    item.removeClass(options.styles.selected);
+                    item.addClass(options.styles.mouseDrag);
+                    item.attr("aria-grabbed", "true");
+                    setDropEffects("move");
+                    dropManager.startDrag(e, handlePos, handleWidth, handleHeight);
+                    avatar.show();
+                },
+                stop: function (e, ui) {
+                    item.removeClass(options.styles.mouseDrag);
+                    item.addClass(options.styles.selected);
+                    $(thatReorderer.activeItem).attr("aria-grabbed", "false");
+                    var markerNode = fluid.unwrap(dropMarker);
+                    if (markerNode.parentNode) {
+                        markerNode.parentNode.removeChild(markerNode);
+                    }
+                    avatar.hide();
+                    ui.helper = null;
+                    setDropEffects("none");
+                    dropManager.endDrag();
+                    
+                    thatReorderer.requestMovement(dropManager.lastPosition(), item);
+                    // refocus on the active item because moving places focus on the body
+                    thatReorderer.activeItem.focus();
+                },
+                handle: thatReorderer.dom.fastLocate("grabHandle", item)
+            });
+        }
+           
+        function changeSelectedToDefault(jItem, styles) {
+            jItem.removeClass(styles.selected);
+            jItem.removeClass(styles.dragging);
+            jItem.addClass(styles.defaultStyle);
+            jItem.attr("aria-selected", "false");
+        }
+           
+        var selectItem = function (anItem) {
+            thatReorderer.events.onSelect.fire(anItem);
+            var styles = options.styles;
+            // Set the previous active item back to its default state.
+            if (thatReorderer.activeItem && thatReorderer.activeItem !== anItem) {
+                changeSelectedToDefault($(thatReorderer.activeItem), styles);
+            }
+            // Then select the new item.
+            thatReorderer.activeItem = anItem;
+            var jItem = $(anItem);
+            jItem.removeClass(styles.defaultStyle);
+            jItem.addClass(styles.selected);
+            jItem.attr("aria-selected", "true");
+        };
+   
+        var initSelectables = function () {
+            var handleBlur = function (evt) {
+                changeSelectedToDefault($(this), options.styles);
+                return evt.stopPropagation();
+            };
+        
+            var handleFocus = function (evt) {
+                selectItem(this);
+                return evt.stopPropagation();
+            };
+            
+            var selectables = thatReorderer.dom.fastLocate("selectables");
+            for (var i = 0; i < selectables.length; ++ i) {
+                var selectable = $(selectables[i]);
+                if (!$.data(selectable[0], "fluid.reorderer.selectable-initialised")) { 
+                    selectable.addClass(styles.defaultStyle);
+            
+                    selectable.bind("blur.fluid.reorderer", handleBlur);
+                    selectable.focus(handleFocus);
+                    selectable.click(function (evt) {
+                        var handle = fluid.unwrap(thatReorderer.dom.fastLocate("grabHandle", this));
+                        if (fluid.dom.isContainer(handle, evt.target)) {
+                            $(this).focus();
+                        }
+                    });
+                    
+                    selectable.attr("role", options.containerRole.item);
+                    selectable.attr("aria-selected", "false");
+                    selectable.attr("aria-disabled", "false");
+                    $.data(selectable[0], "fluid.reorderer.selectable-initialised", true);
+                }
+            }
+            if (!thatReorderer.selectableContext) {
+                thatReorderer.selectableContext = fluid.selectable(thatReorderer.container, {
+                    selectableElements: selectables,
+                    selectablesTabindex: thatReorderer.options.selectablesTabindex,
+                    direction: null
+                });
+            }
+        };
+    
+        var dropChangeListener = function (dropTarget) {
+            fluid.moveDom(dropMarker, dropTarget.element, dropTarget.position);
+            dropMarker.css("display", "");
+            if (mouseDropWarning) {
+                if (dropTarget.lockedelem) {
+                    mouseDropWarning.show();
+                }
+                else {
+                    mouseDropWarning.hide();
+                }
+            }
+        };
+    
+        var initItems = function () {
+            var movables = thatReorderer.dom.fastLocate("movables");
+            var dropTargets = thatReorderer.dom.fastLocate("dropTargets");
+            initSelectables();
+        
+            // Setup movables
+            for (var i = 0; i < movables.length; i++) {
+                var item = movables[i];
+                if (!$.data(item, "fluid.reorderer.movable-initialised")) { 
+                    initMovable($(item));
+                    $.data(item, "fluid.reorderer.movable-initialised", true);
+                }
+            }
+
+            // In order to create valid html, the drop marker is the same type as the node being dragged.
+            // This creates a confusing UI in cases such as an ordered list. 
+            // drop marker functionality should be made pluggable. 
+            if (movables.length > 0 && !dropMarker) {
+                dropMarker = createDropMarker(movables[0].tagName);
+            }
+            
+            dropManager.updateGeometry(thatReorderer.layoutHandler.getGeometricInfo());
+            
+            dropManager.dropChangeFirer.addListener(dropChangeListener, "fluid.Reorderer");
+            // Set up dropTargets
+            dropTargets.attr("aria-dropeffect", "none");  
+
+        };
+
+
+        // Final initialization of the Reorderer at the end of the construction process 
+        if (thatReorderer.container) {
+            bindHandlersToContainer(thatReorderer.container, 
+                thatReorderer.handleKeyDown,
+                thatReorderer.handleKeyUp);
+            addRolesToContainer(thatReorderer);
+            fluid.tabbable(thatReorderer.container);
+            initItems();
+        }
+
+        if (options.afterMoveCallbackUrl) {
+            thatReorderer.events.afterMove.addListener(function () {
+                var layoutHandler = thatReorderer.layoutHandler;
+                var model = layoutHandler.getModel? layoutHandler.getModel():
+                     options.acquireModel(thatReorderer);
+                $.post(options.afterMoveCallbackUrl, JSON.stringify(model));
+            }, "postModel");
+        }
+        thatReorderer.events.onHover.addListener(hoverStyleHandler, "style");
+
+        thatReorderer.refresh = function () {
+            thatReorderer.dom.refresh("movables");
+            thatReorderer.dom.refresh("selectables");
+            thatReorderer.dom.refresh("grabHandle", thatReorderer.dom.fastLocate("movables"));
+            thatReorderer.dom.refresh("stylisticOffset", thatReorderer.dom.fastLocate("movables"));
+            thatReorderer.dom.refresh("dropTargets");
+            thatReorderer.events.onRefresh.fire();
+            initItems();
+            thatReorderer.selectableContext.selectables = thatReorderer.dom.fastLocate("selectables");
+            thatReorderer.selectableContext.selectablesUpdated(thatReorderer.activeItem);
+        };
+        
+        fluid.initDependents(thatReorderer);
+
+        thatReorderer.refresh();
+
+        return thatReorderer;
+    };
+    
+    /**
+     * Constants for key codes in events.
+     */    
+    fluid.reorderer.keys = {
+        TAB: 9,
+        ENTER: 13,
+        SHIFT: 16,
+        CTRL: 17,
+        ALT: 18,
+        META: 19,
+        SPACE: 32,
+        LEFT: 37,
+        UP: 38,
+        RIGHT: 39,
+        DOWN: 40,
+        i: 73,
+        j: 74,
+        k: 75,
+        m: 77
+    };
+    
+    /**
+     * The default key sets for the Reorderer. Should be moved into the proper component defaults.
+     */
+    fluid.reorderer.defaultKeysets = [{
+        modifier : function (evt) {
+            return evt.ctrlKey;
+        },
+        up : fluid.reorderer.keys.UP,
+        down : fluid.reorderer.keys.DOWN,
+        right : fluid.reorderer.keys.RIGHT,
+        left : fluid.reorderer.keys.LEFT
+    },
+    {
+        modifier : function (evt) {
+            return evt.ctrlKey;
+        },
+        up : fluid.reorderer.keys.i,
+        down : fluid.reorderer.keys.m,
+        right : fluid.reorderer.keys.k,
+        left : fluid.reorderer.keys.j
+    }];
+    
+    /**
+     * These roles are used to add ARIA roles to orderable items. This list can be extended as needed,
+     * but the values of the container and item roles must match ARIA-specified roles.
+     */  
+    fluid.reorderer.roles = {
+        GRID: { container: "grid", item: "gridcell" },
+        LIST: { container: "list", item: "listitem" },
+        REGIONS: { container: "main", item: "article" }
+    };
+    
+    // Simplified API for reordering lists and grids.
+    var simpleInit = function (container, layoutHandler, options) {
+        options = options || {};
+        options.layoutHandler = layoutHandler;
+        return fluid.reorderer(container, options);
+    };
+    
+    fluid.reorderList = function (container, options) {
+        return simpleInit(container, "fluid.listLayoutHandler", options);
+    };
+    
+    fluid.reorderGrid = function (container, options) {
+        return simpleInit(container, "fluid.gridLayoutHandler", options); 
+    };
+    
+    fluid.reorderer.SHUFFLE_GEOMETRIC_STRATEGY = "shuffleProjectFrom";
+    fluid.reorderer.GEOMETRIC_STRATEGY         = "projectFrom";
+    fluid.reorderer.LOGICAL_STRATEGY           = "logicalFrom";
+    fluid.reorderer.WRAP_LOCKED_STRATEGY       = "lockedWrapFrom";
+    fluid.reorderer.NO_STRATEGY = null;
+    
+    fluid.reorderer.relativeInfoGetter = function (orientation, coStrategy, contraStrategy, dropManager, dom, disableWrap) {
+        return function (item, direction, forSelection) {
+            var dirorient = fluid.directionOrientation(direction);
+            var strategy = dirorient === orientation? coStrategy: contraStrategy;
+            return strategy !== null? dropManager[strategy](item, direction, forSelection, disableWrap) : null;
+        };
+    };
+    
+    fluid.defaults("fluid.reorderer", {
+        styles: {
+            defaultStyle: "fl-reorderer-movable-default",
+            selected: "fl-reorderer-movable-selected",
+            dragging: "fl-reorderer-movable-dragging",
+            mouseDrag: "fl-reorderer-movable-dragging",
+            hover: "fl-reorderer-movable-hover",
+            dropMarker: "fl-reorderer-dropMarker",
+            avatar: "fl-reorderer-avatar"
+        },
+        selectors: {
+            dropWarning: ".flc-reorderer-dropWarning",
+            movables: ".flc-reorderer-movable",
+            grabHandle: "",
+            stylisticOffset: ""
+        },
+        avatarCreator: defaultAvatarCreator,
+        keysets: fluid.reorderer.defaultKeysets,
+        layoutHandler: {
+            type: "fluid.listLayoutHandler"
+        },
+        
+        events: {
+            onShowKeyboardDropWarning: null,
+            onSelect: null,
+            onBeginMove: "preventable",
+            onMove: null,
+            afterMove: null,
+            onHover: null,
+            onRefresh: null
+        },
+        
+        mergePolicy: {
+            keysets: "replace",
+            "selectors.labelSource": "selectors.grabHandle",
+            "selectors.selectables": "selectors.movables",
+            "selectors.dropTargets": "selectors.movables"
+        },
+        components: {
+            labeller: {
+                type: "fluid.reorderer.labeller",
+                options: {
+                    dom: "{reorderer}.dom",
+                    getGeometricInfo: "{reorderer}.layoutHandler.getGeometricInfo",
+                    orientation: "{reorderer}.options.orientation",
+                    layoutType: "{reorderer}.options.layoutHandler", // TODO, get rid of "global defaults"
+                }          
+            }
+        },
+        
+        // The user option to enable or disable wrapping of elements within the container
+        disableWrap: false        
+        
+    });
+
+
+    /*******************
+     * Layout Handlers *
+     *******************/
+
+     fluid.reorderer.makeGeometricInfoGetter = function(orientation, sentinelize, dom) {
+        return function () {
+            var that = {
+                sentinelize: sentinelize,
+                extents: [{
+                    orientation: orientation,
+                    elements: dom.fastLocate("dropTargets")
+                }],
+                elementMapper: function (element) {
+                    return $.inArray(element, dom.fastLocate("movables")) === -1? "locked": null;
+                },
+                elementIndexer: function (element) {
+                    var selectables = dom.fastLocate("selectables");
+                    return {
+                        elementClass: that.elementMapper(element),
+                        index: $.inArray(element, selectables),
+                        length: selectables.length
+                    };
+                }
+            };
+            return that;
+        };
+    };
+    
+    fluid.defaults(true, "fluid.listLayoutHandler", 
+        {orientation:         fluid.orientation.VERTICAL,
+         containerRole:       fluid.reorderer.roles.LIST,
+         selectablesTabindex: -1,
+         sentinelize:         true
+        });
+    
+    // Public layout handlers.
+    fluid.listLayoutHandler = function (container, options, dropManager, dom) {
+        var that = {};
+
+        that.getRelativePosition = 
+          fluid.reorderer.relativeInfoGetter(options.orientation, 
+                fluid.reorderer.LOGICAL_STRATEGY, null, dropManager, dom, options.disableWrap);
+        
+        that.getGeometricInfo = fluid.reorderer.makeGeometricInfoGetter(options.orientation, options.sentinelize, dom);
+        
+        return that;
+    }; // End ListLayoutHandler
+
+    fluid.defaults(true, "fluid.gridLayoutHandler", 
+        {orientation:         fluid.orientation.HORIZONTAL,
+         containerRole:       fluid.reorderer.roles.GRID,
+         selectablesTabindex: -1,
+         sentinelize:         false
+         });
+    /*
+     * Items in the Lightbox are stored in a list, but they are visually presented as a grid that
+     * changes dimensions when the window changes size. As a result, when the user presses the up or
+     * down arrow key, what lies above or below depends on the current window size.
+     * 
+     * The GridLayoutHandler is responsible for handling changes to this virtual 'grid' of items
+     * in the window, and of informing the Lightbox of which items surround a given item.
+     */
+    fluid.gridLayoutHandler = function (container, options, dropManager, dom) {
+        var that = {};
+
+        that.getRelativePosition = 
+           fluid.reorderer.relativeInfoGetter(options.orientation, 
+                 options.disableWrap ? fluid.reorderer.SHUFFLE_GEOMETRIC_STRATEGY : fluid.reorderer.LOGICAL_STRATEGY, fluid.reorderer.SHUFFLE_GEOMETRIC_STRATEGY, 
+                 dropManager, dom, options.disableWrap);
+        
+        that.getGeometricInfo = fluid.reorderer.makeGeometricInfoGetter(options.orientation, options.sentinelize, dom);
+        
+        return that;
+    }; // End of GridLayoutHandler
+
+    fluid.defaults("fluid.reorderer.labeller", {
+        strings: {
+            overallTemplate: "%recentStatus %item %position %movable",
+            position:        "%index of %length",
+            position_moduleLayoutHandler: "%index of %length in %moduleCell %moduleIndex of %moduleLength",
+            moduleCell_0:    "row", // NB, these keys must agree with fluid.a11y.orientation constants
+            moduleCell_1:    "column",
+            movable:         "movable",
+            fixed:           "fixed",
+            recentStatus:    "moved from position %position"
+        },
+        components: {
+            resolver: {
+                type: "fluid.messageResolver",
+                options: {
+                    messageBase: "{labeller}.options.strings"
+                }
+            }
+        },
+        invokers: {
+            renderLabel: {
+                funcName: "fluid.reorderer.labeller.renderLabel",
+                args: ["{labeller}", "@0", "@1"]
+            }  
+        }
+    });
+
+    // Convert from 0-based to 1-based indices for announcement
+    fluid.reorderer.indexRebaser = function(indices) {
+        indices.index++;
+        if (indices.moduleIndex !== undefined) {
+            indices.moduleIndex++;
+        }
+        return indices;
+    };
+
+    /*************
+     * Labelling *
+     *************/
+     
+    fluid.reorderer.labeller = function(options) {
+        var that = fluid.initLittleComponent("fluid.reorderer.labeller", options);
+        fluid.initDependents(that);
+        that.dom = that.options.dom;
+        
+        that.moduleCell = that.resolver.resolve("moduleCell_" + that.options.orientation);
+        var layoutType = fluid.computeNickName(that.options.layoutType);
+        that.positionTemplate = that.resolver.lookup(["position_" + layoutType, "position"]);
+        
+        var movedMap = {};
+        
+        that.returnedOptions = {
+            listeners: {
+                onRefresh: function() {
+                    var selectables = that.dom.locate("selectables");
+                    fluid.each(selectables, function(selectable) {
+                        var labelOptions = {};
+                        var id = fluid.allocateSimpleId(selectable);
+                        var moved = movedMap[id];
+                        var label = plainLabel = that.renderLabel(selectable);
+                        if (moved) {
+                            moved.newRender = plainLabel;
+                            label = that.renderLabel(selectable, moved.oldRender.position);
+                            $(selectable).one("focusout", function() {
+                                if (movedMap[id]) {
+                                    var oldLabel = movedMap[id].newRender.label;
+                                    delete movedMap[id];
+                                    fluid.updateAriaLabel(selectable, oldLabel);
+                                }
+                            });
+                            labelOptions.dynamicLabel = true;
+                        }
+                        fluid.updateAriaLabel(selectable, label.label, labelOptions);
+                    });
+                },
+                onMove: function(item, newPosition) {
+                    fluid.clear(movedMap); // if we somehow were fooled into missing a defocus, at least clear the map on a 2nd move
+                    var movingId = fluid.allocateSimpleId(item);
+                    movedMap[movingId] = {
+                        oldRender: that.renderLabel(item)
+                    };
+                }
+            }
+        };
+        return that;
+    };
+    
+    fluid.reorderer.labeller.renderLabel = function(that, selectable, recentPosition) {
+        var geom = that.options.getGeometricInfo();
+        var indices = fluid.reorderer.indexRebaser(geom.elementIndexer(selectable));
+        indices.moduleCell = that.moduleCell;
+            
+        var elementClass = geom.elementMapper(selectable);
+        var labelSource = that.dom.locate("labelSource", selectable);
+        var recentStatus;
+        if (recentPosition) {
+            recentStatus = that.resolver.resolve("recentStatus", {position: recentPosition});
+        }
+        var topModel = {
+            item: typeof(labelSource) === "string"? labelSource: fluid.dom.getElementText(fluid.unwrap(labelSource)),
+            position: that.positionTemplate.resolveFunc(that.positionTemplate.template, indices),
+            movable: that.resolver.resolve(elementClass === "locked"? "fixed" : "movable"),
+            recentStatus: recentStatus || ""
+        }
+        
+        var template = that.resolver.lookup(["overallTemplate"]);
+        var label = template.resolveFunc(template.template, topModel);
+        return {
+            position: topModel.position,
+            label: label
+        } 
+    };
+
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2009 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    var deriveLightboxCellBase = function (namebase, index) {
+        return namebase + "lightbox-cell:" + index + ":";
+    };
+            
+    var addThumbnailActivateHandler = function (container) {
+        var enterKeyHandler = function (evt) {
+            if (evt.which === fluid.reorderer.keys.ENTER) {
+                var thumbnailAnchors = $("a", evt.target);
+                document.location = thumbnailAnchors.attr("href");
+            }
+        };
+        
+        container.keypress(enterKeyHandler);
+    };
+    
+    // Custom query method seeks all tags descended from a given root with a 
+    // particular tag name, whose id matches a regex.
+    var seekNodesById = function (rootnode, tagname, idmatch) {
+        var inputs = rootnode.getElementsByTagName(tagname);
+        var togo = [];
+        for (var i = 0; i < inputs.length; i += 1) {
+            var input = inputs[i];
+            var id = input.id;
+            if (id && id.match(idmatch)) {
+                togo.push(input);
+            }
+        }
+        return togo;
+    };
+    
+    var createImageCellFinder = function (parentNode, containerId) {
+        parentNode = fluid.unwrap(parentNode);
+        
+        var lightboxCellNamePattern = "^" + deriveLightboxCellBase(containerId, "[0-9]+") + "$";
+        
+        return function () {
+            // This orderable finder assumes that the lightbox thumbnails are 'div' elements
+            return seekNodesById(parentNode, "div", lightboxCellNamePattern);
+        };
+    };
+    
+    var seekForm = function (container) {
+        return fluid.findAncestor(container, function (element) {
+            return $(element).is("form");
+        });
+    };
+    
+    var seekInputs = function (container, reorderform) {
+        return seekNodesById(reorderform, 
+                             "input", 
+                             "^" + deriveLightboxCellBase(container.attr("id"), "[^:]*") + "reorder-index$");
+    };
+    
+    var mapIdsToNames = function (container, reorderform) {
+        var inputs = seekInputs(container, reorderform);
+        for (var i = 0; i < inputs.length; i++) {
+            var input = inputs[i];
+            var name = input.name;
+            input.name = name || input.id;
+        }
+    };
+    
+    /**
+     * Returns a default afterMove listener using the id-based, form-driven scheme for communicating with the server.
+     * It is implemented by nesting hidden form fields inside each thumbnail container. The value of these form elements
+     * represent the order for each image. This default listener submits the form's default 
+     * action via AJAX.
+     * 
+     * @param {jQueryable} container the Image Reorderer's container element 
+     */
+    var createIDAfterMoveListener = function (container) {
+        var reorderform = seekForm(container);
+        mapIdsToNames(container, reorderform);
+        
+        return function () {
+            var inputs, i;
+            inputs = seekInputs(container, reorderform);
+            
+            for (i = 0; i < inputs.length; i += 1) {
+                inputs[i].value = i;
+            }
+        
+            if (reorderform && reorderform.action) {
+                var order = $(reorderform).serialize();
+                $.post(reorderform.action, 
+                       order,
+                       function (type, data, evt) { /* No-op response */ });
+            }
+        };
+    };
+
+    
+    var setDefaultValue = function (target, path, value) {
+        var previousValue = fluid.get(target, path);
+        var valueToSet = previousValue || value;
+        fluid.set(target, path, valueToSet);
+    };
+    
+    // Public Lightbox API
+    /**
+     * Creates a new Lightbox instance from the specified parameters, providing full control over how
+     * the Lightbox is configured.
+     * 
+     * @param {Object} container 
+     * @param {Object} options 
+     */
+    fluid.reorderImages = function (container, options) {
+        // Instantiate a mini-Image Reorderer component, then feed its options to the real Reorderer.
+        var that = fluid.initView("fluid.reorderImages", container, options);
+        
+        // If the user didn't specify their own afterMove or movables options,
+        // set up defaults for them using the old id-based scheme.
+        // Backwards API compatiblity. Remove references to afterMoveCallback by Infusion 1.5.
+        setDefaultValue(that, "options.listeners.afterMove", 
+                        that.options.afterMoveCallback || createIDAfterMoveListener(that.container));
+        setDefaultValue(that, "options.selectors.movables", 
+                        createImageCellFinder(that.container, that.container.attr("id")));
+        
+        var reorderer = fluid.reorderer(that.container, that.options);
+        
+        fluid.tabindex($("a", that.container), -1);
+        addThumbnailActivateHandler(that.container);
+        
+        return reorderer;
+    };
+   
+    // This function now deprecated. Please use fluid.reorderImages() instead.
+    fluid.lightbox = fluid.reorderImages;
+    
+    fluid.defaults("fluid.reorderImages", {
+        layoutHandler: "fluid.gridLayoutHandler",
+
+        selectors: {
+            labelSource: ".flc-reorderer-imageTitle"
+        }
+    });
+
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2009 University of Toronto
+Copyright 2010 OCAD University
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+/*global fluid, fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.registerNamespace("fluid.moduleLayout");
+
+    /**
+     * Calculate the location of the item and the column in which it resides.
+     * @return  An object with column index and item index (within that column) properties.
+     *          These indices are -1 if the item does not exist in the grid.
+     */
+    // unsupported - NON-API function
+    fluid.moduleLayout.findColumnAndItemIndices = function (item, layout) {
+        return fluid.find(layout.columns,
+            function (column, colIndex) {
+                var index = $.inArray(item, column.elements);
+                return index === -1? undefined : {columnIndex: colIndex, itemIndex: index};
+            }, {columnIndex: -1, itemIndex: -1});
+    };
+    // unsupported - NON-API function
+    fluid.moduleLayout.findColIndex = function (item, layout) {
+        return fluid.find(layout.columns,
+            function (column, colIndex) {
+                return item === column.container? colIndex : undefined;
+            }, -1);
+    };
+
+    /**
+     * Move an item within the layout object. 
+     */
+    fluid.moduleLayout.updateLayout = function (item, target, position, layout) {
+        item = fluid.unwrap(item);
+        target = fluid.unwrap(target);
+        var itemIndices = fluid.moduleLayout.findColumnAndItemIndices(item, layout);
+        layout.columns[itemIndices.columnIndex].elements.splice(itemIndices.itemIndex, 1);
+        var targetCol;
+        if (position === fluid.position.INSIDE) {
+            targetCol = layout.columns[fluid.moduleLayout.findColIndex(target, layout)].elements;
+            targetCol.splice(targetCol.length, 0, item);
+
+        } else {
+            var relativeItemIndices = fluid.moduleLayout.findColumnAndItemIndices(target, layout);
+            targetCol = layout.columns[relativeItemIndices.columnIndex].elements;
+            position = fluid.normalisePosition(position, 
+                  itemIndices.columnIndex === relativeItemIndices.columnIndex, 
+                  relativeItemIndices.itemIndex, itemIndices.itemIndex);
+            var relative = position === fluid.position.BEFORE? 0 : 1;
+            targetCol.splice(relativeItemIndices.itemIndex + relative, 0, item);
+        }
+    };
+       
+    /**
+     * Builds a layout object from a set of columns and modules.
+     * @param {jQuery} container
+     * @param {jQuery} columns
+     * @param {jQuery} portlets
+     */
+    fluid.moduleLayout.layoutFromFlat = function (container, columns, portlets) {
+        var layout = {};
+        layout.container = container;
+        layout.columns = fluid.transform(columns, 
+            function (column) {
+                return {
+                    container: column,
+                    elements: $.makeArray(portlets.filter(function () {
+                          // is this a bug in filter? would have expected "this" to be 1st arg
+                        return fluid.dom.isContainer(column, this);
+                    }))
+                };
+            });
+        return layout;
+    };
+      
+    /**
+     * Builds a layout object from a serialisable "layout" object consisting of id lists
+     */
+    fluid.moduleLayout.layoutFromIds = function (idLayout) {
+        return {
+            container: fluid.byId(idLayout.id),
+            columns: fluid.transform(idLayout.columns, 
+                function (column) {
+                    return {
+                        container: fluid.byId(column.id),
+                        elements: fluid.transform(column.children, fluid.byId)
+                    };
+                })
+            };
+    };
+      
+    /**
+     * Serializes the current layout into a structure of ids
+     */
+    fluid.moduleLayout.layoutToIds = function (idLayout) {
+        return {
+            id: fluid.getId(idLayout.container),
+            columns: fluid.transform(idLayout.columns, 
+                function (column) {
+                    return {
+                        id: fluid.getId(column.container),
+                        children: fluid.transform(column.elements, fluid.getId)
+                    };
+                })
+            };
+    };
+    
+    var defaultOnShowKeyboardDropWarning = function (item, dropWarning) {
+        if (dropWarning) {
+            var offset = $(item).offset();
+            dropWarning = $(dropWarning);
+            dropWarning.css("position", "absolute");
+            dropWarning.css("top", offset.top);
+            dropWarning.css("left", offset.left);
+        }
+    };
+    
+    fluid.defaults(true, "fluid.moduleLayoutHandler", 
+        {orientation: fluid.orientation.VERTICAL,
+         containerRole: fluid.reorderer.roles.REGIONS,
+         selectablesTabindex: 0,
+         sentinelize:         true
+         });
+       
+    /**
+     * Module Layout Handler for reordering content modules.
+     * 
+     * General movement guidelines:
+     * 
+     * - Arrowing sideways will always go to the top (moveable) module in the column
+     * - Moving sideways will always move to the top available drop target in the column
+     * - Wrapping is not necessary at this first pass, but is ok
+     */
+    fluid.moduleLayoutHandler = function (container, options, dropManager, dom) {
+        var that = {};
+        
+        function computeLayout() {
+            var togo;
+            if (options.selectors.modules) {
+                togo = fluid.moduleLayout.layoutFromFlat(container, dom.locate("columns"), dom.locate("modules"));
+            }
+            if (!togo) {
+                var idLayout = fluid.get(options, "moduleLayout.layout");
+                fluid.moduleLayout.layoutFromIds(idLayout);
+            }
+            return togo;
+        }
+        var layout = computeLayout();
+        that.layout = layout;
+        
+        function isLocked(item) {
+            var lockedModules = options.selectors.lockedModules? dom.fastLocate("lockedModules") : [];
+            return $.inArray(item, lockedModules) !== -1;
+        }
+
+        that.getRelativePosition  = 
+           fluid.reorderer.relativeInfoGetter(options.orientation, 
+                 fluid.reorderer.WRAP_LOCKED_STRATEGY, fluid.reorderer.GEOMETRIC_STRATEGY, 
+                 dropManager, dom, options.disableWrap);
+                 
+        that.getGeometricInfo = function () {
+            var extents = [];
+            var togo = {extents: extents,
+                        sentinelize: options.sentinelize};
+            togo.elementMapper = function (element) {
+                return isLocked(element)? "locked" : null;
+            };
+            togo.elementIndexer = function (element) {
+                var indices = fluid.moduleLayout.findColumnAndItemIndices(element, that.layout);
+                return {
+                    index:        indices.itemIndex,
+                    length:       layout.columns[indices.columnIndex].elements.length,
+                    moduleIndex:  indices.columnIndex,
+                    moduleLength: layout.columns.length
+                };
+            };
+            for (var col = 0; col < layout.columns.length; col++) {
+                var column = layout.columns[col];
+                var thisEls = {
+                    orientation: options.orientation,
+                    elements: $.makeArray(column.elements),
+                    parentElement: column.container
+                };
+              //  fluid.log("Geometry col " + col + " elements " + fluid.dumpEl(thisEls.elements) + " isLocked [" + 
+              //       fluid.transform(thisEls.elements, togo.elementMapper).join(", ") + "]");
+                extents.push(thisEls);
+            }
+
+            return togo;
+        };
+        
+        function computeModules(all) {
+            return function () {
+                var modules = fluid.accumulate(layout.columns, function (column, list) {
+                    return list.concat(column.elements); // note that concat will not work on a jQuery
+                }, []);
+                if (!all) {
+                    fluid.remove_if(modules, isLocked);
+                }
+                return modules;
+            };
+        }
+        
+        that.returnedOptions = {
+            selectors: {
+                movables: computeModules(false),
+                dropTargets: computeModules(false),
+                selectables: computeModules(true)
+            },
+            listeners: {
+                onMove: {
+                    priority: "last",
+                    listener: function (item, requestedPosition) {
+                        fluid.moduleLayout.updateLayout(item, requestedPosition.element, requestedPosition.position, layout);
+                    }
+                },
+                onRefresh: function () {
+                    layout = computeLayout();
+                    that.layout = layout;
+                },
+                "onShowKeyboardDropWarning.setPosition": defaultOnShowKeyboardDropWarning
+            }
+        };
+        
+        that.getModel = function () {
+            return fluid.moduleLayout.layoutToIds(layout);
+        };
+              
+        return that;
+    };
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2009 University of Toronto
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    /**
+     * Simple way to create a layout reorderer.
+     * @param {selector} a jQueryable (selector, element, jQuery) for the layout container
+     * @param {Object} a map of selectors for columns and modules within the layout
+     * @param {Function} a function to be called when the order changes 
+     * @param {Object} additional configuration options
+     */
+    fluid.reorderLayout = function (container, userOptions) {
+        var assembleOptions = {
+            layoutHandler: "fluid.moduleLayoutHandler",
+            selectors: {
+                columns: ".flc-reorderer-column",
+                modules: ".flc-reorderer-module"
+            }
+        };
+        var options = $.extend(true, assembleOptions, userOptions);
+        return fluid.reorderer(container, options);
+    };    
+})(jQuery, fluid_1_3);
+/*!    SWFObject v2.2 <http://code.google.com/p/swfobject/> 
+       is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
+*/
+
+var swfobject = function() {
+       
+       var UNDEF = "undefined",
+               OBJECT = "object",
+               SHOCKWAVE_FLASH = "Shockwave Flash",
+               SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
+               FLASH_MIME_TYPE = "application/x-shockwave-flash",
+               EXPRESS_INSTALL_ID = "SWFObjectExprInst",
+               ON_READY_STATE_CHANGE = "onreadystatechange",
+               
+               win = window,
+               doc = document,
+               nav = navigator,
+               
+               plugin = false,
+               domLoadFnArr = [main],
+               regObjArr = [],
+               objIdArr = [],
+               listenersArr = [],
+               storedAltContent,
+               storedAltContentId,
+               storedCallbackFn,
+               storedCallbackObj,
+               isDomLoaded = false,
+               isExpressInstallActive = false,
+               dynamicStylesheet,
+               dynamicStylesheetMedia,
+               autoHideShow = true,
+       
+       /* Centralized function for browser feature detection
+               - User agent string detection is only used when no good alternative is possible
+               - Is executed directly for optimal performance
+       */      
+       ua = function() {
+               var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
+                       u = nav.userAgent.toLowerCase(),
+                       p = nav.platform.toLowerCase(),
+                       windows = p ? /win/.test(p) : /win/.test(u),
+                       mac = p ? /mac/.test(p) : /mac/.test(u),
+                       webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
+                       ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
+                       playerVersion = [0,0,0],
+                       d = null;
+               if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
+                       d = nav.plugins[SHOCKWAVE_FLASH].description;
+                       if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
+                               plugin = true;
+                               ie = false; // cascaded feature detection for Internet Explorer
+                               d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
+                               playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
+                               playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
+                               playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
+                       }
+               }
+               else if (typeof win.ActiveXObject != UNDEF) {
+                       try {
+                               var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
+                               if (a) { // a will return null when ActiveX is disabled
+                                       d = a.GetVariable("$version");
+                                       if (d) {
+                                               ie = true; // cascaded feature detection for Internet Explorer
+                                               d = d.split(" ")[1].split(",");
+                                               playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
+                                       }
+                               }
+                       }
+                       catch(e) {}
+               }
+               return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
+       }(),
+       
+       /* Cross-browser onDomLoad
+               - Will fire an event as soon as the DOM of a web page is loaded
+               - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
+               - Regular onload serves as fallback
+       */ 
+       onDomLoad = function() {
+               if (!ua.w3) { return; }
+               if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically 
+                       callDomLoadFunctions();
+               }
+               if (!isDomLoaded) {
+                       if (typeof doc.addEventListener != UNDEF) {
+                               doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
+                       }               
+                       if (ua.ie && ua.win) {
+                               doc.attachEvent(ON_READY_STATE_CHANGE, function() {
+                                       if (doc.readyState == "complete") {
+                                               doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
+                                               callDomLoadFunctions();
+                                       }
+                               });
+                               if (win == top) { // if not inside an iframe
+                                       (function(){
+                                               if (isDomLoaded) { return; }
+                                               try {
+                                                       doc.documentElement.doScroll("left");
+                                               }
+                                               catch(e) {
+                                                       setTimeout(arguments.callee, 0);
+                                                       return;
+                                               }
+                                               callDomLoadFunctions();
+                                       })();
+                               }
+                       }
+                       if (ua.wk) {
+                               (function(){
+                                       if (isDomLoaded) { return; }
+                                       if (!/loaded|complete/.test(doc.readyState)) {
+                                               setTimeout(arguments.callee, 0);
+                                               return;
+                                       }
+                                       callDomLoadFunctions();
+                               })();
+                       }
+                       addLoadEvent(callDomLoadFunctions);
+               }
+       }();
+       
+       function callDomLoadFunctions() {
+               if (isDomLoaded) { return; }
+               try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
+                       var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
+                       t.parentNode.removeChild(t);
+               }
+               catch (e) { return; }
+               isDomLoaded = true;
+               var dl = domLoadFnArr.length;
+               for (var i = 0; i < dl; i++) {
+                       domLoadFnArr[i]();
+               }
+       }
+       
+       function addDomLoadEvent(fn) {
+               if (isDomLoaded) {
+                       fn();
+               }
+               else { 
+                       domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
+               }
+       }
+       
+       /* Cross-browser onload
+               - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
+               - Will fire an event as soon as a web page including all of its assets are loaded 
+        */
+       function addLoadEvent(fn) {
+               if (typeof win.addEventListener != UNDEF) {
+                       win.addEventListener("load", fn, false);
+               }
+               else if (typeof doc.addEventListener != UNDEF) {
+                       doc.addEventListener("load", fn, false);
+               }
+               else if (typeof win.attachEvent != UNDEF) {
+                       addListener(win, "onload", fn);
+               }
+               else if (typeof win.onload == "function") {
+                       var fnOld = win.onload;
+                       win.onload = function() {
+                               fnOld();
+                               fn();
+                       };
+               }
+               else {
+                       win.onload = fn;
+               }
+       }
+       
+       /* Main function
+               - Will preferably execute onDomLoad, otherwise onload (as a fallback)
+       */
+       function main() { 
+               if (plugin) {
+                       testPlayerVersion();
+               }
+               else {
+                       matchVersions();
+               }
+       }
+       
+       /* Detect the Flash Player version for non-Internet Explorer browsers
+               - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
+                 a. Both release and build numbers can be detected
+                 b. Avoid wrong descriptions by corrupt installers provided by Adobe
+                 c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
+               - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
+       */
+       function testPlayerVersion() {
+               var b = doc.getElementsByTagName("body")[0];
+               var o = createElement(OBJECT);
+               o.setAttribute("type", FLASH_MIME_TYPE);
+               var t = b.appendChild(o);
+               if (t) {
+                       var counter = 0;
+                       (function(){
+                               if (typeof t.GetVariable != UNDEF) {
+                                       var d = t.GetVariable("$version");
+                                       if (d) {
+                                               d = d.split(" ")[1].split(",");
+                                               ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
+                                       }
+                               }
+                               else if (counter < 10) {
+                                       counter++;
+                                       setTimeout(arguments.callee, 10);
+                                       return;
+                               }
+                               b.removeChild(o);
+                               t = null;
+                               matchVersions();
+                       })();
+               }
+               else {
+                       matchVersions();
+               }
+       }
+       
+       /* Perform Flash Player and SWF version matching; static publishing only
+       */
+       function matchVersions() {
+               var rl = regObjArr.length;
+               if (rl > 0) {
+                       for (var i = 0; i < rl; i++) { // for each registered object element
+                               var id = regObjArr[i].id;
+                               var cb = regObjArr[i].callbackFn;
+                               var cbObj = {success:false, id:id};
+                               if (ua.pv[0] > 0) {
+                                       var obj = getElementById(id);
+                                       if (obj) {
+                                               if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
+                                                       setVisibility(id, true);
+                                                       if (cb) {
+                                                               cbObj.success = true;
+                                                               cbObj.ref = getObjectById(id);
+                                                               cb(cbObj);
+                                                       }
+                                               }
+                                               else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
+                                                       var att = {};
+                                                       att.data = regObjArr[i].expressInstall;
+                                                       att.width = obj.getAttribute("width") || "0";
+                                                       att.height = obj.getAttribute("height") || "0";
+                                                       if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
+                                                       if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
+                                                       // parse HTML object param element's name-value pairs
+                                                       var par = {};
+                                                       var p = obj.getElementsByTagName("param");
+                                                       var pl = p.length;
+                                                       for (var j = 0; j < pl; j++) {
+                                                               if (p[j].getAttribute("name").toLowerCase() != "movie") {
+                                                                       par[p[j].getAttribute("name")] = p[j].getAttribute("value");
+                                                               }
+                                                       }
+                                                       showExpressInstall(att, par, id, cb);
+                                               }
+                                               else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
+                                                       displayAltContent(obj);
+                                                       if (cb) { cb(cbObj); }
+                                               }
+                                       }
+                               }
+                               else {  // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
+                                       setVisibility(id, true);
+                                       if (cb) {
+                                               var o = getObjectById(id); // test whether there is an HTML object element or not
+                                               if (o && typeof o.SetVariable != UNDEF) { 
+                                                       cbObj.success = true;
+                                                       cbObj.ref = o;
+                                               }
+                                               cb(cbObj);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       function getObjectById(objectIdStr) {
+               var r = null;
+               var o = getElementById(objectIdStr);
+               if (o && o.nodeName == "OBJECT") {
+                       if (typeof o.SetVariable != UNDEF) {
+                               r = o;
+                       }
+                       else {
+                               var n = o.getElementsByTagName(OBJECT)[0];
+                               if (n) {
+                                       r = n;
+                               }
+                       }
+               }
+               return r;
+       }
+       
+       /* Requirements for Adobe Express Install
+               - only one instance can be active at a time
+               - fp 6.0.65 or higher
+               - Win/Mac OS only
+               - no Webkit engines older than version 312
+       */
+       function canExpressInstall() {
+               return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
+       }
+       
+       /* Show the Adobe Express Install dialog
+               - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
+       */
+       function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
+               isExpressInstallActive = true;
+               storedCallbackFn = callbackFn || null;
+               storedCallbackObj = {success:false, id:replaceElemIdStr};
+               var obj = getElementById(replaceElemIdStr);
+               if (obj) {
+                       if (obj.nodeName == "OBJECT") { // static publishing
+                               storedAltContent = abstractAltContent(obj);
+                               storedAltContentId = null;
+                       }
+                       else { // dynamic publishing
+                               storedAltContent = obj;
+                               storedAltContentId = replaceElemIdStr;
+                       }
+                       att.id = EXPRESS_INSTALL_ID;
+                       if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
+                       if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
+                       doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
+                       var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
+                               fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
+                       if (typeof par.flashvars != UNDEF) {
+                               par.flashvars += "&" + fv;
+                       }
+                       else {
+                               par.flashvars = fv;
+                       }
+                       // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
+                       // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
+                       if (ua.ie && ua.win && obj.readyState != 4) {
+                               var newObj = createElement("div");
+                               replaceElemIdStr += "SWFObjectNew";
+                               newObj.setAttribute("id", replaceElemIdStr);
+                               obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
+                               obj.style.display = "none";
+                               (function(){
+                                       if (obj.readyState == 4) {
+                                               obj.parentNode.removeChild(obj);
+                                       }
+                                       else {
+                                               setTimeout(arguments.callee, 10);
+                                       }
+                               })();
+                       }
+                       createSWF(att, par, replaceElemIdStr);
+               }
+       }
+       
+       /* Functions to abstract and display alternative content
+       */
+       function displayAltContent(obj) {
+               if (ua.ie && ua.win && obj.readyState != 4) {
+                       // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
+                       // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
+                       var el = createElement("div");
+                       obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
+                       el.parentNode.replaceChild(abstractAltContent(obj), el);
+                       obj.style.display = "none";
+                       (function(){
+                               if (obj.readyState == 4) {
+                                       obj.parentNode.removeChild(obj);
+                               }
+                               else {
+                                       setTimeout(arguments.callee, 10);
+                               }
+                       })();
+               }
+               else {
+                       obj.parentNode.replaceChild(abstractAltContent(obj), obj);
+               }
+       } 
+
+       function abstractAltContent(obj) {
+               var ac = createElement("div");
+               if (ua.win && ua.ie) {
+                       ac.innerHTML = obj.innerHTML;
+               }
+               else {
+                       var nestedObj = obj.getElementsByTagName(OBJECT)[0];
+                       if (nestedObj) {
+                               var c = nestedObj.childNodes;
+                               if (c) {
+                                       var cl = c.length;
+                                       for (var i = 0; i < cl; i++) {
+                                               if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
+                                                       ac.appendChild(c[i].cloneNode(true));
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return ac;
+       }
+       
+       /* Cross-browser dynamic SWF creation
+       */
+       function createSWF(attObj, parObj, id) {
+               var r, el = getElementById(id);
+               if (ua.wk && ua.wk < 312) { return r; }
+               if (el) {
+                       if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
+                               attObj.id = id;
+                       }
+                       if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
+                               var att = "";
+                               for (var i in attObj) {
+                                       if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
+                                               if (i.toLowerCase() == "data") {
+                                                       parObj.movie = attObj[i];
+                                               }
+                                               else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
+                                                       att += ' class="' + attObj[i] + '"';
+                                               }
+                                               else if (i.toLowerCase() != "classid") {
+                                                       att += ' ' + i + '="' + attObj[i] + '"';
+                                               }
+                                       }
+                               }
+                               var par = "";
+                               for (var j in parObj) {
+                                       if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
+                                               par += '<param name="' + j + '" value="' + parObj[j] + '" />';
+                                       }
+                               }
+                               el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
+                               objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
+                               r = getElementById(attObj.id);  
+                       }
+                       else { // well-behaving browsers
+                               var o = createElement(OBJECT);
+                               o.setAttribute("type", FLASH_MIME_TYPE);
+                               for (var m in attObj) {
+                                       if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
+                                               if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
+                                                       o.setAttribute("class", attObj[m]);
+                                               }
+                                               else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
+                                                       o.setAttribute(m, attObj[m]);
+                                               }
+                                       }
+                               }
+                               for (var n in parObj) {
+                                       if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
+                                               createObjParam(o, n, parObj[n]);
+                                       }
+                               }
+                               el.parentNode.replaceChild(o, el);
+                               r = o;
+                       }
+               }
+               return r;
+       }
+       
+       function createObjParam(el, pName, pValue) {
+               var p = createElement("param");
+               p.setAttribute("name", pName);  
+               p.setAttribute("value", pValue);
+               el.appendChild(p);
+       }
+       
+       /* Cross-browser SWF removal
+               - Especially needed to safely and completely remove a SWF in Internet Explorer
+       */
+       function removeSWF(id) {
+               var obj = getElementById(id);
+               if (obj && obj.nodeName == "OBJECT") {
+                       if (ua.ie && ua.win) {
+                               obj.style.display = "none";
+                               (function(){
+                                       if (obj.readyState == 4) {
+                                               removeObjectInIE(id);
+                                       }
+                                       else {
+                                               setTimeout(arguments.callee, 10);
+                                       }
+                               })();
+                       }
+                       else {
+                               obj.parentNode.removeChild(obj);
+                       }
+               }
+       }
+       
+       function removeObjectInIE(id) {
+               var obj = getElementById(id);
+               if (obj) {
+                       for (var i in obj) {
+                               if (typeof obj[i] == "function") {
+                                       obj[i] = null;
+                               }
+                       }
+                       obj.parentNode.removeChild(obj);
+               }
+       }
+       
+       /* Functions to optimize JavaScript compression
+       */
+       function getElementById(id) {
+               var el = null;
+               try {
+                       el = doc.getElementById(id);
+               }
+               catch (e) {}
+               return el;
+       }
+       
+       function createElement(el) {
+               return doc.createElement(el);
+       }
+       
+       /* Updated attachEvent function for Internet Explorer
+               - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
+       */      
+       function addListener(target, eventType, fn) {
+               target.attachEvent(eventType, fn);
+               listenersArr[listenersArr.length] = [target, eventType, fn];
+       }
+       
+       /* Flash Player and SWF content version matching
+       */
+       function hasPlayerVersion(rv) {
+               var pv = ua.pv, v = rv.split(".");
+               v[0] = parseInt(v[0], 10);
+               v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
+               v[2] = parseInt(v[2], 10) || 0;
+               return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
+       }
+       
+       /* Cross-browser dynamic CSS creation
+               - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
+       */      
+       function createCSS(sel, decl, media, newStyle) {
+               if (ua.ie && ua.mac) { return; }
+               var h = doc.getElementsByTagName("head")[0];
+               if (!h) { return; } // to also support badly authored HTML pages that lack a head element
+               var m = (media && typeof media == "string") ? media : "screen";
+               if (newStyle) {
+                       dynamicStylesheet = null;
+                       dynamicStylesheetMedia = null;
+               }
+               if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 
+                       // create dynamic stylesheet + get a global reference to it
+                       var s = createElement("style");
+                       s.setAttribute("type", "text/css");
+                       s.setAttribute("media", m);
+                       dynamicStylesheet = h.appendChild(s);
+                       if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
+                               dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
+                       }
+                       dynamicStylesheetMedia = m;
+               }
+               // add style rule
+               if (ua.ie && ua.win) {
+                       if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
+                               dynamicStylesheet.addRule(sel, decl);
+                       }
+               }
+               else {
+                       if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
+                               dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
+                       }
+               }
+       }
+       
+       function setVisibility(id, isVisible) {
+               if (!autoHideShow) { return; }
+               var v = isVisible ? "visible" : "hidden";
+               if (isDomLoaded && getElementById(id)) {
+                       getElementById(id).style.visibility = v;
+               }
+               else {
+                       createCSS("#" + id, "visibility:" + v);
+               }
+       }
+
+       /* Filter to avoid XSS attacks
+       */
+       function urlEncodeIfNecessary(s) {
+               var regex = /[\\\"<>\.;]/;
+               var hasBadChars = regex.exec(s) != null;
+               return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
+       }
+       
+       /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
+       */
+       var cleanup = function() {
+               if (ua.ie && ua.win) {
+                       window.attachEvent("onunload", function() {
+                               // remove listeners to avoid memory leaks
+                               var ll = listenersArr.length;
+                               for (var i = 0; i < ll; i++) {
+                                       listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
+                               }
+                               // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
+                               var il = objIdArr.length;
+                               for (var j = 0; j < il; j++) {
+                                       removeSWF(objIdArr[j]);
+                               }
+                               // cleanup library's main closures to avoid memory leaks
+                               for (var k in ua) {
+                                       ua[k] = null;
+                               }
+                               ua = null;
+                               for (var l in swfobject) {
+                                       swfobject[l] = null;
+                               }
+                               swfobject = null;
+                       });
+               }
+       }();
+       
+       return {
+               /* Public API
+                       - Reference: http://code.google.com/p/swfobject/wiki/documentation
+               */ 
+               registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
+                       if (ua.w3 && objectIdStr && swfVersionStr) {
+                               var regObj = {};
+                               regObj.id = objectIdStr;
+                               regObj.swfVersion = swfVersionStr;
+                               regObj.expressInstall = xiSwfUrlStr;
+                               regObj.callbackFn = callbackFn;
+                               regObjArr[regObjArr.length] = regObj;
+                               setVisibility(objectIdStr, false);
+                       }
+                       else if (callbackFn) {
+                               callbackFn({success:false, id:objectIdStr});
+                       }
+               },
+               
+               getObjectById: function(objectIdStr) {
+                       if (ua.w3) {
+                               return getObjectById(objectIdStr);
+                       }
+               },
+               
+               embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
+                       var callbackObj = {success:false, id:replaceElemIdStr};
+                       if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
+                               setVisibility(replaceElemIdStr, false);
+                               addDomLoadEvent(function() {
+                                       widthStr += ""; // auto-convert to string
+                                       heightStr += "";
+                                       var att = {};
+                                       if (attObj && typeof attObj === OBJECT) {
+                                               for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
+                                                       att[i] = attObj[i];
+                                               }
+                                       }
+                                       att.data = swfUrlStr;
+                                       att.width = widthStr;
+                                       att.height = heightStr;
+                                       var par = {}; 
+                                       if (parObj && typeof parObj === OBJECT) {
+                                               for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
+                                                       par[j] = parObj[j];
+                                               }
+                                       }
+                                       if (flashvarsObj && typeof flashvarsObj === OBJECT) {
+                                               for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
+                                                       if (typeof par.flashvars != UNDEF) {
+                                                               par.flashvars += "&" + k + "=" + flashvarsObj[k];
+                                                       }
+                                                       else {
+                                                               par.flashvars = k + "=" + flashvarsObj[k];
+                                                       }
+                                               }
+                                       }
+                                       if (hasPlayerVersion(swfVersionStr)) { // create SWF
+                                               var obj = createSWF(att, par, replaceElemIdStr);
+                                               if (att.id == replaceElemIdStr) {
+                                                       setVisibility(replaceElemIdStr, true);
+                                               }
+                                               callbackObj.success = true;
+                                               callbackObj.ref = obj;
+                                       }
+                                       else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
+                                               att.data = xiSwfUrlStr;
+                                               showExpressInstall(att, par, replaceElemIdStr, callbackFn);
+                                               return;
+                                       }
+                                       else { // show alternative content
+                                               setVisibility(replaceElemIdStr, true);
+                                       }
+                                       if (callbackFn) { callbackFn(callbackObj); }
+                               });
+                       }
+                       else if (callbackFn) { callbackFn(callbackObj); }
+               },
+               
+               switchOffAutoHideShow: function() {
+                       autoHideShow = false;
+               },
+               
+               ua: ua,
+               
+               getFlashPlayerVersion: function() {
+                       return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
+               },
+               
+               hasFlashPlayerVersion: hasPlayerVersion,
+               
+               createSWF: function(attObj, parObj, replaceElemIdStr) {
+                       if (ua.w3) {
+                               return createSWF(attObj, parObj, replaceElemIdStr);
+                       }
+                       else {
+                               return undefined;
+                       }
+               },
+               
+               showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
+                       if (ua.w3 && canExpressInstall()) {
+                               showExpressInstall(att, par, replaceElemIdStr, callbackFn);
+                       }
+               },
+               
+               removeSWF: function(objElemIdStr) {
+                       if (ua.w3) {
+                               removeSWF(objElemIdStr);
+                       }
+               },
+               
+               createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
+                       if (ua.w3) {
+                               createCSS(selStr, declStr, mediaStr, newStyleBoolean);
+                       }
+               },
+               
+               addDomLoadEvent: addDomLoadEvent,
+               
+               addLoadEvent: addLoadEvent,
+               
+               getQueryParamValue: function(param) {
+                       var q = doc.location.search || doc.location.hash;
+                       if (q) {
+                               if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
+                               if (param == null) {
+                                       return urlEncodeIfNecessary(q);
+                               }
+                               var pairs = q.split("&");
+                               for (var i = 0; i < pairs.length; i++) {
+                                       if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
+                                               return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
+                                       }
+                               }
+                       }
+                       return "";
+               },
+               
+               // For internal usage only
+               expressInstallCallback: function() {
+                       if (isExpressInstallActive) {
+                               var obj = getElementById(EXPRESS_INSTALL_ID);
+                               if (obj && storedAltContent) {
+                                       obj.parentNode.replaceChild(storedAltContent, obj);
+                                       if (storedAltContentId) {
+                                               setVisibility(storedAltContentId, true);
+                                               if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
+                                       }
+                                       if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
+                               }
+                               isExpressInstallActive = false;
+                       } 
+               }
+       };
+}();
+/**
+ * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com
+ *
+ * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/,  http://www.vinterwebb.se/
+ *
+ * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilz�n and Mammon Media and is released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ */
+
+
+/* ******************* */
+/* Constructor & Init  */
+/* ******************* */
+var SWFUpload;
+
+if (SWFUpload == undefined) {
+       SWFUpload = function (settings) {
+               this.initSWFUpload(settings);
+       };
+}
+
+SWFUpload.prototype.initSWFUpload = function (settings) {
+       try {
+               this.customSettings = {};       // A container where developers can place their own settings associated with this instance.
+               this.settings = settings;
+               this.eventQueue = [];
+               this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
+               this.movieElement = null;
+
+
+               // Setup global control tracking
+               SWFUpload.instances[this.movieName] = this;
+
+               // Load the settings.  Load the Flash movie.
+               this.initSettings();
+               this.loadFlash();
+               this.displayDebugInfo();
+       } catch (ex) {
+               delete SWFUpload.instances[this.movieName];
+               throw ex;
+       }
+};
+
+/* *************** */
+/* Static Members  */
+/* *************** */
+SWFUpload.instances = {};
+SWFUpload.movieCount = 0;
+SWFUpload.version = "2.2.0 2009-03-25";
+SWFUpload.QUEUE_ERROR = {
+       QUEUE_LIMIT_EXCEEDED                    : -100,
+       FILE_EXCEEDS_SIZE_LIMIT                 : -110,
+       ZERO_BYTE_FILE                                  : -120,
+       INVALID_FILETYPE                                : -130
+};
+SWFUpload.UPLOAD_ERROR = {
+       HTTP_ERROR                                              : -200,
+       MISSING_UPLOAD_URL                      : -210,
+       IO_ERROR                                                : -220,
+       SECURITY_ERROR                                  : -230,
+       UPLOAD_LIMIT_EXCEEDED                   : -240,
+       UPLOAD_FAILED                                   : -250,
+       SPECIFIED_FILE_ID_NOT_FOUND             : -260,
+       FILE_VALIDATION_FAILED                  : -270,
+       FILE_CANCELLED                                  : -280,
+       UPLOAD_STOPPED                                  : -290
+};
+SWFUpload.FILE_STATUS = {
+       QUEUED           : -1,
+       IN_PROGRESS      : -2,
+       ERROR            : -3,
+       COMPLETE         : -4,
+       CANCELLED        : -5
+};
+SWFUpload.BUTTON_ACTION = {
+       SELECT_FILE  : -100,
+       SELECT_FILES : -110,
+       START_UPLOAD : -120
+};
+SWFUpload.CURSOR = {
+       ARROW : -1,
+       HAND : -2
+};
+SWFUpload.WINDOW_MODE = {
+       WINDOW : "window",
+       TRANSPARENT : "transparent",
+       OPAQUE : "opaque"
+};
+
+// Private: takes a URL, determines if it is relative and converts to an absolute URL
+// using the current site. Only processes the URL if it can, otherwise returns the URL untouched
+SWFUpload.completeURL = function(url) {
+       if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//)) {
+               return url;
+       }
+       
+       var currentURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "");
+       
+       var indexSlash = window.location.pathname.lastIndexOf("/");
+       if (indexSlash <= 0) {
+               path = "/";
+       } else {
+               path = window.location.pathname.substr(0, indexSlash) + "/";
+       }
+       
+       return /*currentURL +*/ path + url;
+       
+};
+
+
+/* ******************** */
+/* Instance Members  */
+/* ******************** */
+
+// Private: initSettings ensures that all the
+// settings are set, getting a default value if one was not assigned.
+SWFUpload.prototype.initSettings = function () {
+       this.ensureDefault = function (settingName, defaultValue) {
+               this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];
+       };
+       
+       // Upload backend settings
+       this.ensureDefault("upload_url", "");
+       this.ensureDefault("preserve_relative_urls", false);
+       this.ensureDefault("file_post_name", "Filedata");
+       this.ensureDefault("post_params", {});
+       this.ensureDefault("use_query_string", false);
+       this.ensureDefault("requeue_on_error", false);
+       this.ensureDefault("http_success", []);
+       this.ensureDefault("assume_success_timeout", 0);
+       
+       // File Settings
+       this.ensureDefault("file_types", "*.*");
+       this.ensureDefault("file_types_description", "All Files");
+       this.ensureDefault("file_size_limit", 0);       // Default zero means "unlimited"
+       this.ensureDefault("file_upload_limit", 0);
+       this.ensureDefault("file_queue_limit", 0);
+
+       // Flash Settings
+       this.ensureDefault("flash_url", "swfupload.swf");
+       this.ensureDefault("prevent_swf_caching", true);
+       
+       // Button Settings
+       this.ensureDefault("button_image_url", "");
+       this.ensureDefault("button_width", 1);
+       this.ensureDefault("button_height", 1);
+       this.ensureDefault("button_text", "");
+       this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;");
+       this.ensureDefault("button_text_top_padding", 0);
+       this.ensureDefault("button_text_left_padding", 0);
+       this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES);
+       this.ensureDefault("button_disabled", false);
+       this.ensureDefault("button_placeholder_id", "");
+       this.ensureDefault("button_placeholder", null);
+       this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW);
+       this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW);
+       
+       // Debug Settings
+       this.ensureDefault("debug", false);
+       this.settings.debug_enabled = this.settings.debug;      // Here to maintain v2 API
+       
+       // Event Handlers
+       this.settings.return_upload_start_handler = this.returnUploadStart;
+       this.ensureDefault("swfupload_loaded_handler", null);
+       this.ensureDefault("file_dialog_start_handler", null);
+       this.ensureDefault("file_queued_handler", null);
+       this.ensureDefault("file_queue_error_handler", null);
+       this.ensureDefault("file_dialog_complete_handler", null);
+       
+       this.ensureDefault("upload_start_handler", null);
+       this.ensureDefault("upload_progress_handler", null);
+       this.ensureDefault("upload_error_handler", null);
+       this.ensureDefault("upload_success_handler", null);
+       this.ensureDefault("upload_complete_handler", null);
+       
+       this.ensureDefault("debug_handler", this.debugMessage);
+
+       this.ensureDefault("custom_settings", {});
+
+       // Other settings
+       this.customSettings = this.settings.custom_settings;
+       
+       // Update the flash url if needed
+       if (!!this.settings.prevent_swf_caching) {
+               this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime();
+       }
+       
+       if (!this.settings.preserve_relative_urls) {
+               //this.settings.flash_url = SWFUpload.completeURL(this.settings.flash_url);     // Don't need to do this one since flash doesn't look at it
+               this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url);
+               this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url);
+       }
+       
+       delete this.ensureDefault;
+};
+
+// Private: loadFlash replaces the button_placeholder element with the flash movie.
+SWFUpload.prototype.loadFlash = function () {
+       var targetElement, tempParent;
+
+       // Make sure an element with the ID we are going to use doesn't already exist
+       if (document.getElementById(this.movieName) !== null) {
+               throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
+       }
+
+       // Get the element where we will be placing the flash movie
+       targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder;
+
+       if (targetElement == undefined) {
+               throw "Could not find the placeholder element: " + this.settings.button_placeholder_id;
+       }
+
+       // Append the container and load the flash
+       tempParent = document.createElement("div");
+       tempParent.innerHTML = this.getFlashHTML();     // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
+       targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement);
+
+       // Fix IE Flash/Form bug
+       if (window[this.movieName] == undefined) {
+               window[this.movieName] = this.getMovieElement();
+       }
+       
+};
+
+// Private: getFlashHTML generates the object tag needed to embed the flash in to the document
+SWFUpload.prototype.getFlashHTML = function () {
+       // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
+       return ['<object id="', this.movieName, '" type="application/x-shockwave-flash" data="', this.settings.flash_url, '" width="', this.settings.button_width, '" height="', this.settings.button_height, '" class="swfupload">',
+                               '<param name="wmode" value="', this.settings.button_window_mode, '" />',
+                               '<param name="movie" value="', this.settings.flash_url, '" />',
+                               '<param name="quality" value="high" />',
+                               '<param name="menu" value="false" />',
+                               '<param name="allowScriptAccess" value="always" />',
+                               '<param name="flashvars" value="' + this.getFlashVars() + '" />',
+                               '</object>'].join("");
+};
+
+// Private: getFlashVars builds the parameter string that will be passed
+// to flash in the flashvars param.
+SWFUpload.prototype.getFlashVars = function () {
+       // Build a string from the post param object
+       var paramString = this.buildParamString();
+       var httpSuccessString = this.settings.http_success.join(",");
+       
+       // Build the parameter string
+       return ["movieName=", encodeURIComponent(this.movieName),
+                       "&amp;uploadURL=", encodeURIComponent(this.settings.upload_url),
+                       "&amp;useQueryString=", encodeURIComponent(this.settings.use_query_string),
+                       "&amp;requeueOnError=", encodeURIComponent(this.settings.requeue_on_error),
+                       "&amp;httpSuccess=", encodeURIComponent(httpSuccessString),
+                       "&amp;assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout),
+                       "&amp;params=", encodeURIComponent(paramString),
+                       "&amp;filePostName=", encodeURIComponent(this.settings.file_post_name),
+                       "&amp;fileTypes=", encodeURIComponent(this.settings.file_types),
+                       "&amp;fileTypesDescription=", encodeURIComponent(this.settings.file_types_description),
+                       "&amp;fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit),
+                       "&amp;fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit),
+                       "&amp;fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit),
+                       "&amp;debugEnabled=", encodeURIComponent(this.settings.debug_enabled),
+                       "&amp;buttonImageURL=", encodeURIComponent(this.settings.button_image_url),
+                       "&amp;buttonWidth=", encodeURIComponent(this.settings.button_width),
+                       "&amp;buttonHeight=", encodeURIComponent(this.settings.button_height),
+                       "&amp;buttonText=", encodeURIComponent(this.settings.button_text),
+                       "&amp;buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding),
+                       "&amp;buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding),
+                       "&amp;buttonTextStyle=", encodeURIComponent(this.settings.button_text_style),
+                       "&amp;buttonAction=", encodeURIComponent(this.settings.button_action),
+                       "&amp;buttonDisabled=", encodeURIComponent(this.settings.button_disabled),
+                       "&amp;buttonCursor=", encodeURIComponent(this.settings.button_cursor)
+               ].join("");
+};
+
+// Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload
+// The element is cached after the first lookup
+SWFUpload.prototype.getMovieElement = function () {
+       if (this.movieElement == undefined) {
+               this.movieElement = document.getElementById(this.movieName);
+       }
+
+       if (this.movieElement === null) {
+               throw "Could not find Flash element";
+       }
+       
+       return this.movieElement;
+};
+
+// Private: buildParamString takes the name/value pairs in the post_params setting object
+// and joins them up in to a string formatted "name=value&amp;name=value"
+SWFUpload.prototype.buildParamString = function () {
+       var postParams = this.settings.post_params; 
+       var paramStringPairs = [];
+
+       if (typeof(postParams) === "object") {
+               for (var name in postParams) {
+                       if (postParams.hasOwnProperty(name)) {
+                               paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
+                       }
+               }
+       }
+
+       return paramStringPairs.join("&amp;");
+};
+
+// Public: Used to remove a SWFUpload instance from the page. This method strives to remove
+// all references to the SWF, and other objects so memory is properly freed.
+// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state.
+// Credits: Major improvements provided by steffen
+SWFUpload.prototype.destroy = function () {
+       try {
+               // Make sure Flash is done before we try to remove it
+               this.cancelUpload(null, false);
+               
+
+               // Remove the SWFUpload DOM nodes
+               var movieElement = null;
+               movieElement = this.getMovieElement();
+               
+               if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
+                       // Loop through all the movie's properties and remove all function references (DOM/JS IE 6/7 memory leak workaround)
+                       for (var i in movieElement) {
+                               try {
+                                       if (typeof(movieElement[i]) === "function") {
+                                               movieElement[i] = null;
+                                       }
+                               } catch (ex1) {}
+                       }
+
+                       // Remove the Movie Element from the page
+                       try {
+                               movieElement.parentNode.removeChild(movieElement);
+                       } catch (ex) {}
+               }
+               
+               // Remove IE form fix reference
+               window[this.movieName] = null;
+
+               // Destroy other references
+               SWFUpload.instances[this.movieName] = null;
+               delete SWFUpload.instances[this.movieName];
+
+               this.movieElement = null;
+               this.settings = null;
+               this.customSettings = null;
+               this.eventQueue = null;
+               this.movieName = null;
+               
+               
+               return true;
+       } catch (ex2) {
+               return false;
+       }
+};
+
+
+// Public: displayDebugInfo prints out settings and configuration
+// information about this SWFUpload instance.
+// This function (and any references to it) can be deleted when placing
+// SWFUpload in production.
+SWFUpload.prototype.displayDebugInfo = function () {
+       this.debug(
+               [
+                       "---SWFUpload Instance Info---\n",
+                       "Version: ", SWFUpload.version, "\n",
+                       "Movie Name: ", this.movieName, "\n",
+                       "Settings:\n",
+                       "\t", "upload_url:               ", this.settings.upload_url, "\n",
+                       "\t", "flash_url:                ", this.settings.flash_url, "\n",
+                       "\t", "use_query_string:         ", this.settings.use_query_string.toString(), "\n",
+                       "\t", "requeue_on_error:         ", this.settings.requeue_on_error.toString(), "\n",
+                       "\t", "http_success:             ", this.settings.http_success.join(", "), "\n",
+                       "\t", "assume_success_timeout:   ", this.settings.assume_success_timeout, "\n",
+                       "\t", "file_post_name:           ", this.settings.file_post_name, "\n",
+                       "\t", "post_params:              ", this.settings.post_params.toString(), "\n",
+                       "\t", "file_types:               ", this.settings.file_types, "\n",
+                       "\t", "file_types_description:   ", this.settings.file_types_description, "\n",
+                       "\t", "file_size_limit:          ", this.settings.file_size_limit, "\n",
+                       "\t", "file_upload_limit:        ", this.settings.file_upload_limit, "\n",
+                       "\t", "file_queue_limit:         ", this.settings.file_queue_limit, "\n",
+                       "\t", "debug:                    ", this.settings.debug.toString(), "\n",
+
+                       "\t", "prevent_swf_caching:      ", this.settings.prevent_swf_caching.toString(), "\n",
+
+                       "\t", "button_placeholder_id:    ", this.settings.button_placeholder_id.toString(), "\n",
+                       "\t", "button_placeholder:       ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n",
+                       "\t", "button_image_url:         ", this.settings.button_image_url.toString(), "\n",
+                       "\t", "button_width:             ", this.settings.button_width.toString(), "\n",
+                       "\t", "button_height:            ", this.settings.button_height.toString(), "\n",
+                       "\t", "button_text:              ", this.settings.button_text.toString(), "\n",
+                       "\t", "button_text_style:        ", this.settings.button_text_style.toString(), "\n",
+                       "\t", "button_text_top_padding:  ", this.settings.button_text_top_padding.toString(), "\n",
+                       "\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n",
+                       "\t", "button_action:            ", this.settings.button_action.toString(), "\n",
+                       "\t", "button_disabled:          ", this.settings.button_disabled.toString(), "\n",
+
+                       "\t", "custom_settings:          ", this.settings.custom_settings.toString(), "\n",
+                       "Event Handlers:\n",
+                       "\t", "swfupload_loaded_handler assigned:  ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n",
+                       "\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n",
+                       "\t", "file_queued_handler assigned:       ", (typeof this.settings.file_queued_handler === "function").toString(), "\n",
+                       "\t", "file_queue_error_handler assigned:  ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n",
+                       "\t", "upload_start_handler assigned:      ", (typeof this.settings.upload_start_handler === "function").toString(), "\n",
+                       "\t", "upload_progress_handler assigned:   ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n",
+                       "\t", "upload_error_handler assigned:      ", (typeof this.settings.upload_error_handler === "function").toString(), "\n",
+                       "\t", "upload_success_handler assigned:    ", (typeof this.settings.upload_success_handler === "function").toString(), "\n",
+                       "\t", "upload_complete_handler assigned:   ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n",
+                       "\t", "debug_handler assigned:             ", (typeof this.settings.debug_handler === "function").toString(), "\n"
+               ].join("")
+       );
+};
+
+/* Note: addSetting and getSetting are no longer used by SWFUpload but are included
+       the maintain v2 API compatibility
+*/
+// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used.
+SWFUpload.prototype.addSetting = function (name, value, default_value) {
+    if (value == undefined) {
+        return (this.settings[name] = default_value);
+    } else {
+        return (this.settings[name] = value);
+       }
+};
+
+// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found.
+SWFUpload.prototype.getSetting = function (name) {
+    if (this.settings[name] != undefined) {
+        return this.settings[name];
+       }
+
+    return "";
+};
+
+
+
+// Private: callFlash handles function calls made to the Flash element.
+// Calls are made with a setTimeout for some functions to work around
+// bugs in the ExternalInterface library.
+SWFUpload.prototype.callFlash = function (functionName, argumentArray) {
+       argumentArray = argumentArray || [];
+       
+       var movieElement = this.getMovieElement();
+       var returnValue, returnString;
+
+       // Flash's method if calling ExternalInterface methods (code adapted from MooTools).
+       try {
+               returnString = movieElement.CallFunction('<invoke name="' + functionName + '" returntype="javascript">' + __flash__argumentsToXML(argumentArray, 0) + '</invoke>');
+               returnValue = eval(returnString);
+       } catch (ex) {
+               throw "Call to " + functionName + " failed";
+       }
+       
+       // Unescape file post param values
+       if (returnValue != undefined && typeof returnValue.post === "object") {
+               returnValue = this.unescapeFilePostParams(returnValue);
+       }
+
+       return returnValue;
+};
+
+/* *****************************
+       -- Flash control methods --
+       Your UI should use these
+       to operate SWFUpload
+   ***************************** */
+
+// WARNING: this function does not work in Flash Player 10
+// Public: selectFile causes a File Selection Dialog window to appear.  This
+// dialog only allows 1 file to be selected.
+SWFUpload.prototype.selectFile = function () {
+       this.callFlash("SelectFile");
+};
+
+// WARNING: this function does not work in Flash Player 10
+// Public: selectFiles causes a File Selection Dialog window to appear/ This
+// dialog allows the user to select any number of files
+// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
+// If the selection name length is too long the dialog will fail in an unpredictable manner.  There is no work-around
+// for this bug.
+SWFUpload.prototype.selectFiles = function () {
+       this.callFlash("SelectFiles");
+};
+
+
+// Public: startUpload starts uploading the first file in the queue unless
+// the optional parameter 'fileID' specifies the ID 
+SWFUpload.prototype.startUpload = function (fileID) {
+       this.callFlash("StartUpload", [fileID]);
+};
+
+// Public: cancelUpload cancels any queued file.  The fileID parameter may be the file ID or index.
+// If you do not specify a fileID the current uploading file or first file in the queue is cancelled.
+// If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter.
+SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) {
+       if (triggerErrorEvent !== false) {
+               triggerErrorEvent = true;
+       }
+       this.callFlash("CancelUpload", [fileID, triggerErrorEvent]);
+};
+
+// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue.
+// If nothing is currently uploading then nothing happens.
+SWFUpload.prototype.stopUpload = function () {
+       this.callFlash("StopUpload");
+};
+
+/* ************************
+ * Settings methods
+ *   These methods change the SWFUpload settings.
+ *   SWFUpload settings should not be changed directly on the settings object
+ *   since many of the settings need to be passed to Flash in order to take
+ *   effect.
+ * *********************** */
+
+// Public: getStats gets the file statistics object.
+SWFUpload.prototype.getStats = function () {
+       return this.callFlash("GetStats");
+};
+
+// Public: setStats changes the SWFUpload statistics.  You shouldn't need to 
+// change the statistics but you can.  Changing the statistics does not
+// affect SWFUpload accept for the successful_uploads count which is used
+// by the upload_limit setting to determine how many files the user may upload.
+SWFUpload.prototype.setStats = function (statsObject) {
+       this.callFlash("SetStats", [statsObject]);
+};
+
+// Public: getFile retrieves a File object by ID or Index.  If the file is
+// not found then 'null' is returned.
+SWFUpload.prototype.getFile = function (fileID) {
+       if (typeof(fileID) === "number") {
+               return this.callFlash("GetFileByIndex", [fileID]);
+       } else {
+               return this.callFlash("GetFile", [fileID]);
+       }
+};
+
+// Public: addFileParam sets a name/value pair that will be posted with the
+// file specified by the Files ID.  If the name already exists then the
+// exiting value will be overwritten.
+SWFUpload.prototype.addFileParam = function (fileID, name, value) {
+       return this.callFlash("AddFileParam", [fileID, name, value]);
+};
+
+// Public: removeFileParam removes a previously set (by addFileParam) name/value
+// pair from the specified file.
+SWFUpload.prototype.removeFileParam = function (fileID, name) {
+       this.callFlash("RemoveFileParam", [fileID, name]);
+};
+
+// Public: setUploadUrl changes the upload_url setting.
+SWFUpload.prototype.setUploadURL = function (url) {
+       this.settings.upload_url = url.toString();
+       this.callFlash("SetUploadURL", [url]);
+};
+
+// Public: setPostParams changes the post_params setting
+SWFUpload.prototype.setPostParams = function (paramsObject) {
+       this.settings.post_params = paramsObject;
+       this.callFlash("SetPostParams", [paramsObject]);
+};
+
+// Public: addPostParam adds post name/value pair.  Each name can have only one value.
+SWFUpload.prototype.addPostParam = function (name, value) {
+       this.settings.post_params[name] = value;
+       this.callFlash("SetPostParams", [this.settings.post_params]);
+};
+
+// Public: removePostParam deletes post name/value pair.
+SWFUpload.prototype.removePostParam = function (name) {
+       delete this.settings.post_params[name];
+       this.callFlash("SetPostParams", [this.settings.post_params]);
+};
+
+// Public: setFileTypes changes the file_types setting and the file_types_description setting
+SWFUpload.prototype.setFileTypes = function (types, description) {
+       this.settings.file_types = types;
+       this.settings.file_types_description = description;
+       this.callFlash("SetFileTypes", [types, description]);
+};
+
+// Public: setFileSizeLimit changes the file_size_limit setting
+SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) {
+       this.settings.file_size_limit = fileSizeLimit;
+       this.callFlash("SetFileSizeLimit", [fileSizeLimit]);
+};
+
+// Public: setFileUploadLimit changes the file_upload_limit setting
+SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) {
+       this.settings.file_upload_limit = fileUploadLimit;
+       this.callFlash("SetFileUploadLimit", [fileUploadLimit]);
+};
+
+// Public: setFileQueueLimit changes the file_queue_limit setting
+SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) {
+       this.settings.file_queue_limit = fileQueueLimit;
+       this.callFlash("SetFileQueueLimit", [fileQueueLimit]);
+};
+
+// Public: setFilePostName changes the file_post_name setting
+SWFUpload.prototype.setFilePostName = function (filePostName) {
+       this.settings.file_post_name = filePostName;
+       this.callFlash("SetFilePostName", [filePostName]);
+};
+
+// Public: setUseQueryString changes the use_query_string setting
+SWFUpload.prototype.setUseQueryString = function (useQueryString) {
+       this.settings.use_query_string = useQueryString;
+       this.callFlash("SetUseQueryString", [useQueryString]);
+};
+
+// Public: setRequeueOnError changes the requeue_on_error setting
+SWFUpload.prototype.setRequeueOnError = function (requeueOnError) {
+       this.settings.requeue_on_error = requeueOnError;
+       this.callFlash("SetRequeueOnError", [requeueOnError]);
+};
+
+// Public: setHTTPSuccess changes the http_success setting
+SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) {
+       if (typeof http_status_codes === "string") {
+               http_status_codes = http_status_codes.replace(" ", "").split(",");
+       }
+       
+       this.settings.http_success = http_status_codes;
+       this.callFlash("SetHTTPSuccess", [http_status_codes]);
+};
+
+// Public: setHTTPSuccess changes the http_success setting
+SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) {
+       this.settings.assume_success_timeout = timeout_seconds;
+       this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]);
+};
+
+// Public: setDebugEnabled changes the debug_enabled setting
+SWFUpload.prototype.setDebugEnabled = function (debugEnabled) {
+       this.settings.debug_enabled = debugEnabled;
+       this.callFlash("SetDebugEnabled", [debugEnabled]);
+};
+
+// Public: setButtonImageURL loads a button image sprite
+SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) {
+       if (buttonImageURL == undefined) {
+               buttonImageURL = "";
+       }
+       
+       this.settings.button_image_url = buttonImageURL;
+       this.callFlash("SetButtonImageURL", [buttonImageURL]);
+};
+
+// Public: setButtonDimensions resizes the Flash Movie and button
+SWFUpload.prototype.setButtonDimensions = function (width, height) {
+       this.settings.button_width = width;
+       this.settings.button_height = height;
+       
+       var movie = this.getMovieElement();
+       if (movie != undefined) {
+               movie.style.width = width + "px";
+               movie.style.height = height + "px";
+       }
+       
+       this.callFlash("SetButtonDimensions", [width, height]);
+};
+// Public: setButtonText Changes the text overlaid on the button
+SWFUpload.prototype.setButtonText = function (html) {
+       this.settings.button_text = html;
+       this.callFlash("SetButtonText", [html]);
+};
+// Public: setButtonTextPadding changes the top and left padding of the text overlay
+SWFUpload.prototype.setButtonTextPadding = function (left, top) {
+       this.settings.button_text_top_padding = top;
+       this.settings.button_text_left_padding = left;
+       this.callFlash("SetButtonTextPadding", [left, top]);
+};
+
+// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button
+SWFUpload.prototype.setButtonTextStyle = function (css) {
+       this.settings.button_text_style = css;
+       this.callFlash("SetButtonTextStyle", [css]);
+};
+// Public: setButtonDisabled disables/enables the button
+SWFUpload.prototype.setButtonDisabled = function (isDisabled) {
+       this.settings.button_disabled = isDisabled;
+       this.callFlash("SetButtonDisabled", [isDisabled]);
+};
+// Public: setButtonAction sets the action that occurs when the button is clicked
+SWFUpload.prototype.setButtonAction = function (buttonAction) {
+       this.settings.button_action = buttonAction;
+       this.callFlash("SetButtonAction", [buttonAction]);
+};
+
+// Public: setButtonCursor changes the mouse cursor displayed when hovering over the button
+SWFUpload.prototype.setButtonCursor = function (cursor) {
+       this.settings.button_cursor = cursor;
+       this.callFlash("SetButtonCursor", [cursor]);
+};
+
+/* *******************************
+       Flash Event Interfaces
+       These functions are used by Flash to trigger the various
+       events.
+       
+       All these functions a Private.
+       
+       Because the ExternalInterface library is buggy the event calls
+       are added to a queue and the queue then executed by a setTimeout.
+       This ensures that events are executed in a determinate order and that
+       the ExternalInterface bugs are avoided.
+******************************* */
+
+SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) {
+       // Warning: Don't call this.debug inside here or you'll create an infinite loop
+       
+       if (argumentArray == undefined) {
+               argumentArray = [];
+       } else if (!(argumentArray instanceof Array)) {
+               argumentArray = [argumentArray];
+       }
+       
+       var self = this;
+       if (typeof this.settings[handlerName] === "function") {
+               // Queue the event
+               this.eventQueue.push(function () {
+                       this.settings[handlerName].apply(this, argumentArray);
+               });
+               
+               // Execute the next queued event
+               setTimeout(function () {
+                       self.executeNextEvent();
+               }, 0);
+               
+       } else if (this.settings[handlerName] !== null) {
+               throw "Event handler " + handlerName + " is unknown or is not a function";
+       }
+};
+
+// Private: Causes the next event in the queue to be executed.  Since events are queued using a setTimeout
+// we must queue them in order to garentee that they are executed in order.
+SWFUpload.prototype.executeNextEvent = function () {
+       // Warning: Don't call this.debug inside here or you'll create an infinite loop
+
+       var  f = this.eventQueue ? this.eventQueue.shift() : null;
+       if (typeof(f) === "function") {
+               f.apply(this);
+       }
+};
+
+// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have
+// properties that contain characters that are not valid for JavaScript identifiers. To work around this
+// the Flash Component escapes the parameter names and we must unescape again before passing them along.
+SWFUpload.prototype.unescapeFilePostParams = function (file) {
+       var reg = /[$]([0-9a-f]{4})/i;
+       var unescapedPost = {};
+       var uk;
+
+       if (file != undefined) {
+               for (var k in file.post) {
+                       if (file.post.hasOwnProperty(k)) {
+                               uk = k;
+                               var match;
+                               while ((match = reg.exec(uk)) !== null) {
+                                       uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16)));
+                               }
+                               unescapedPost[uk] = file.post[k];
+                       }
+               }
+
+               file.post = unescapedPost;
+       }
+
+       return file;
+};
+
+// Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working)
+SWFUpload.prototype.testExternalInterface = function () {
+       try {
+               return this.callFlash("TestExternalInterface");
+       } catch (ex) {
+               return false;
+       }
+};
+
+// Private: This event is called by Flash when it has finished loading. Don't modify this.
+// Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded.
+SWFUpload.prototype.flashReady = function () {
+       // Check that the movie element is loaded correctly with its ExternalInterface methods defined
+       var movieElement = this.getMovieElement();
+
+       if (!movieElement) {
+               this.debug("Flash called back ready but the flash movie can't be found.");
+               return;
+       }
+
+       this.cleanUp(movieElement);
+       
+       this.queueEvent("swfupload_loaded_handler");
+};
+
+// Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE.
+// This function is called by Flash each time the ExternalInterface functions are created.
+SWFUpload.prototype.cleanUp = function (movieElement) {
+       // Pro-actively unhook all the Flash functions
+       try {
+               if (this.movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
+                       this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)");
+                       for (var key in movieElement) {
+                               try {
+                                       if (typeof(movieElement[key]) === "function") {
+                                               movieElement[key] = null;
+                                       }
+                               } catch (ex) {
+                               }
+                       }
+               }
+       } catch (ex1) {
+       
+       }
+
+       // Fix Flashes own cleanup code so if the SWFMovie was removed from the page
+       // it doesn't display errors.
+       window["__flash__removeCallback"] = function (instance, name) {
+               try {
+                       if (instance) {
+                               instance[name] = null;
+                       }
+               } catch (flashEx) {
+               
+               }
+       };
+
+};
+
+
+/* This is a chance to do something before the browse window opens */
+SWFUpload.prototype.fileDialogStart = function () {
+       this.queueEvent("file_dialog_start_handler");
+};
+
+
+/* Called when a file is successfully added to the queue. */
+SWFUpload.prototype.fileQueued = function (file) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("file_queued_handler", file);
+};
+
+
+/* Handle errors that occur when an attempt to queue a file fails. */
+SWFUpload.prototype.fileQueueError = function (file, errorCode, message) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("file_queue_error_handler", [file, errorCode, message]);
+};
+
+/* Called after the file dialog has closed and the selected files have been queued.
+       You could call startUpload here if you want the queued files to begin uploading immediately. */
+SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) {
+       this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]);
+};
+
+SWFUpload.prototype.uploadStart = function (file) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("return_upload_start_handler", file);
+};
+
+SWFUpload.prototype.returnUploadStart = function (file) {
+       var returnValue;
+       if (typeof this.settings.upload_start_handler === "function") {
+               file = this.unescapeFilePostParams(file);
+               returnValue = this.settings.upload_start_handler.call(this, file);
+       } else if (this.settings.upload_start_handler != undefined) {
+               throw "upload_start_handler must be a function";
+       }
+
+       // Convert undefined to true so if nothing is returned from the upload_start_handler it is
+       // interpretted as 'true'.
+       if (returnValue === undefined) {
+               returnValue = true;
+       }
+       
+       returnValue = !!returnValue;
+       
+       this.callFlash("ReturnUploadStart", [returnValue]);
+};
+
+
+
+SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]);
+};
+
+SWFUpload.prototype.uploadError = function (file, errorCode, message) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("upload_error_handler", [file, errorCode, message]);
+};
+
+SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("upload_success_handler", [file, serverData, responseReceived]);
+};
+
+SWFUpload.prototype.uploadComplete = function (file) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("upload_complete_handler", file);
+};
+
+/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
+   internal debug console.  You can override this event and have messages written where you want. */
+SWFUpload.prototype.debug = function (message) {
+       this.queueEvent("debug_handler", message);
+};
+
+
+/* **********************************
+       Debug Console
+       The debug console is a self contained, in page location
+       for debug message to be sent.  The Debug Console adds
+       itself to the body if necessary.
+
+       The console is automatically scrolled as messages appear.
+       
+       If you are using your own debug handler or when you deploy to production and
+       have debug disabled you can remove these functions to reduce the file size
+       and complexity.
+********************************** */
+   
+// Private: debugMessage is the default debug_handler.  If you want to print debug messages
+// call the debug() function.  When overriding the function your own function should
+// check to see if the debug setting is true before outputting debug information.
+SWFUpload.prototype.debugMessage = function (message) {
+       if (this.settings.debug) {
+               var exceptionMessage, exceptionValues = [];
+
+               // Check for an exception object and print it nicely
+               if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") {
+                       for (var key in message) {
+                               if (message.hasOwnProperty(key)) {
+                                       exceptionValues.push(key + ": " + message[key]);
+                               }
+                       }
+                       exceptionMessage = exceptionValues.join("\n") || "";
+                       exceptionValues = exceptionMessage.split("\n");
+                       exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: ");
+                       SWFUpload.Console.writeLine(exceptionMessage);
+               } else {
+                       SWFUpload.Console.writeLine(message);
+               }
+       }
+};
+
+SWFUpload.Console = {};
+SWFUpload.Console.writeLine = function (message) {
+       var console, documentForm;
+
+       try {
+               console = document.getElementById("SWFUpload_Console");
+
+               if (!console) {
+                       documentForm = document.createElement("form");
+                       document.getElementsByTagName("body")[0].appendChild(documentForm);
+
+                       console = document.createElement("textarea");
+                       console.id = "SWFUpload_Console";
+                       console.style.fontFamily = "monospace";
+                       console.setAttribute("wrap", "off");
+                       console.wrap = "off";
+                       console.style.overflow = "auto";
+                       console.style.width = "700px";
+                       console.style.height = "350px";
+                       console.style.margin = "5px";
+                       documentForm.appendChild(console);
+               }
+
+               console.value += message + "\n";
+
+               console.scrollTop = console.scrollHeight - console.clientHeight;
+       } catch (ex) {
+               alert("Exception: " + ex.name + " Message: " + ex.message);
+       }
+};
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010-2011 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {    
+    
+    var animateDisplay = function (elm, animation, defaultAnimation) {
+        animation = (animation) ? animation : defaultAnimation;
+        elm.animate(animation.params, animation.duration, animation.callback);
+    };
+    
+    var animateProgress = function (elm, width, speed) {
+        // de-queue any left over animations
+        elm.queue("fx", []); 
+        
+        elm.animate({ 
+            width: width,
+            queue: false
+        }, 
+        speed);
+    };
+    
+    var showProgress = function (that, animation) {
+        if (animation === false) {
+            that.displayElement.show();
+        } else {
+            animateDisplay(that.displayElement, animation, that.options.showAnimation);
+        }
+    };
+    
+    var hideProgress = function (that, delay, animation) {
+        
+        delay = (delay === null || isNaN(delay)) ? that.options.delay : delay;
+        
+        if (delay) {
+            // use a setTimeout to delay the hide for n millies, note use of recursion
+            var timeOut = setTimeout(function () {
+                hideProgress(that, 0, animation);
+            }, delay);
+        } else {
+            if (animation === false) {
+                that.displayElement.hide();
+            } else {
+                animateDisplay(that.displayElement, animation, that.options.hideAnimation);
+            }
+        }   
+    };
+    
+    var updateWidth = function (that, newWidth, dontAnimate) {
+        dontAnimate  = dontAnimate || false;
+        var currWidth = that.indicator.width();
+        var direction = that.options.animate;
+        if ((newWidth > currWidth) && (direction === "both" || direction === "forward") && !dontAnimate) {
+            animateProgress(that.indicator, newWidth, that.options.speed);
+        } else if ((newWidth < currWidth) && (direction === "both" || direction === "backward") && !dontAnimate) {
+            animateProgress(that.indicator, newWidth, that.options.speed);
+        } else {
+            that.indicator.width(newWidth);
+        }
+    };
+         
+    var percentToPixels = function (that, percent) {
+        // progress does not support percents over 100, also all numbers are rounded to integers
+        return Math.round((Math.min(percent, 100) * that.progressBar.width()) / 100);
+    };
+    
+    var refreshRelativeWidth = function (that)  {
+        var pixels = Math.max(percentToPixels(that, parseFloat(that.storedPercent)), that.options.minWidth);
+        updateWidth(that, pixels, true);
+    };
+        
+    var initARIA = function (ariaElement, ariaBusyText) {
+        ariaElement.attr("role", "progressbar");
+        ariaElement.attr("aria-valuemin", "0");
+        ariaElement.attr("aria-valuemax", "100");
+        ariaElement.attr("aria-valuenow", "0");
+        //Empty value for ariaBusyText will default to aria-valuenow.
+        if (ariaBusyText) {
+            ariaElement.attr("aria-valuetext", "");
+        }
+        ariaElement.attr("aria-busy", "false");
+    };
+    
+    var updateARIA = function (that, percent) {
+        var str = that.options.strings;
+        var busy = percent < 100 && percent > 0;
+        that.ariaElement.attr("aria-busy", busy);
+        that.ariaElement.attr("aria-valuenow", percent);   
+        //Empty value for ariaBusyText will default to aria-valuenow.
+        if (str.ariaBusyText) {
+            if (busy) {
+                var busyString = fluid.stringTemplate(str.ariaBusyText, {percentComplete : percent});           
+                that.ariaElement.attr("aria-valuetext", busyString);
+            } else if (percent === 100) {
+                // FLUID-2936: JAWS doesn't currently read the "Progress is complete" message to the user, even though we set it here.
+                that.ariaElement.attr("aria-valuetext", str.ariaDoneText);
+            }
+        }
+    };
+        
+    var updateText = function (label, value) {
+        label.html(value);
+    };
+    
+    var repositionIndicator = function (that) {
+        that.indicator.css("top", that.progressBar.position().top)
+            .css("left", 0)
+            .height(that.progressBar.height());
+        refreshRelativeWidth(that);
+    };
+        
+    var updateProgress = function (that, percent, labelText, animationForShow) {
+        
+        // show progress before updating, jQuery will handle the case if the object is already displayed
+        showProgress(that, animationForShow);
+            
+        // do not update if the value of percent is falsey
+        if (percent !== null) {
+            that.storedPercent = percent;
+        
+            var pixels = Math.max(percentToPixels(that, parseFloat(percent)), that.options.minWidth);   
+            updateWidth(that, pixels);
+        }
+        
+        if (labelText !== null) {
+            updateText(that.label, labelText);
+        }
+        
+        // update ARIA
+        if (that.ariaElement) {
+            updateARIA(that, percent);
+        }
+    };
+        
+    var setupProgress = function (that) {
+        that.displayElement = that.locate("displayElement");
+
+        // hide file progress in case it is showing
+        if (that.options.initiallyHidden) {
+            that.displayElement.hide();
+        }
+
+        that.progressBar = that.locate("progressBar");
+        that.label = that.locate("label");
+        that.indicator = that.locate("indicator");
+        that.ariaElement = that.locate("ariaElement");
+        
+        that.indicator.width(that.options.minWidth);
+
+        that.storedPercent = 0;
+                
+        // initialize ARIA
+        if (that.ariaElement) {
+            initARIA(that.ariaElement, that.options.strings.ariaBusyText);
+        }
+        
+        // afterProgressHidden:  
+        // Registering listener with the callback provided by the user and reinitializing
+        // the event trigger function. 
+        // Note: callback depricated as of 1.5, use afterProgressHidden event
+        if (that.options.hideAnimation.callback) {
+            that.events.afterProgressHidden.addListener(that.options.hideAnimation.callback);           
+        }
+        
+        // triggers the afterProgressHidden event    
+        // Note: callback depricated as of 1.5, use afterProgressHidden event
+        that.options.hideAnimation.callback = that.events.afterProgressHidden.fire;
+
+        
+        // onProgressBegin:
+        // Registering listener with the callback provided by the user and reinitializing
+        // the event trigger function.  
+        // Note: callback depricated as of 1.5, use onProgressBegin event
+        if (that.options.showAnimation.callback) {
+            that.events.onProgressBegin.addListener(that.options.showAnimation.callback);                      
+        } 
+            
+        // triggers the onProgressBegin event
+        // Note: callback depricated as of 1.5, use onProgressBegin event
+        that.options.showAnimation.callback = that.events.onProgressBegin.fire;
+    };
+           
+    /**
+    * Instantiates a new Progress component.
+    * 
+    * @param {jQuery|Selector|Element} container the DOM element in which the Uploader lives
+    * @param {Object} options configuration options for the component.
+    */
+    fluid.progress = function (container, options) {
+        var that = fluid.initView("fluid.progress", container, options);
+        setupProgress(that);
+        
+        /**
+         * Shows the progress bar if is currently hidden.
+         * 
+         * @param {Object} animation a custom animation used when showing the progress bar
+         */
+        that.show = function (animation) {
+            showProgress(that, animation);
+        };
+        
+        /**
+         * Hides the progress bar if it is visible.
+         * 
+         * @param {Number} delay the amount of time to wait before hiding
+         * @param {Object} animation a custom animation used when hiding the progress bar
+         */
+        that.hide = function (delay, animation) {
+            hideProgress(that, delay, animation);
+        };
+        
+        /**
+         * Updates the state of the progress bar.
+         * This will automatically show the progress bar if it is currently hidden.
+         * Percentage is specified as a decimal value, but will be automatically converted if needed.
+         * 
+         * 
+         * @param {Number|String} percentage the current percentage, specified as a "float-ish" value 
+         * @param {String} labelValue the value to set for the label; this can be an HTML string
+         * @param {Object} animationForShow the animation to use when showing the progress bar if it is hidden
+         */
+        that.update = function (percentage, labelValue, animationForShow) {
+            updateProgress(that, percentage, labelValue, animationForShow);
+        };
+        
+        that.refreshView = function () {
+            repositionIndicator(that);
+        };
+                        
+        return that;  
+    };
+      
+    fluid.defaults("fluid.progress", {  
+        selectors: {
+            displayElement: ".flc-progress", // required, the element that gets displayed when progress is displayed, could be the indicator or bar or some larger outer wrapper as in an overlay effect
+            progressBar: ".flc-progress-bar", //required
+            indicator: ".flc-progress-indicator", //required
+            label: ".flc-progress-label", //optional
+            ariaElement: ".flc-progress-bar" // usually required, except in cases where there are more than one progressor for the same data such as a total and a sub-total
+        },
+        
+        strings: {
+            //Empty value for ariaBusyText will default to aria-valuenow.
+            ariaBusyText: "Progress is %percentComplete percent complete",
+            ariaDoneText: "Progress is complete."
+        },
+        
+        // progress display and hide animations, use the jQuery animation primatives, set to false to use no animation
+        // animations must be symetrical (if you hide with width, you'd better show with width) or you get odd effects
+        // see jQuery docs about animations to customize
+        showAnimation: {
+            params: {
+                opacity: "show"
+            }, 
+            duration: "slow",
+            //callback has been deprecated and will be removed as of 1.5, instead use onProgressBegin event 
+            callback: null 
+        }, // equivalent of $().fadeIn("slow")
+        
+        hideAnimation: {
+            params: {
+                opacity: "hide"
+            }, 
+            duration: "slow", 
+            //callback has been deprecated and will be removed as of 1.5, instead use afterProgressHidden event 
+            callback: null
+        }, // equivalent of $().fadeOut("slow")
+        
+        events: {            
+            onProgressBegin: null,
+            afterProgressHidden: null            
+        },
+
+        minWidth: 5, // 0 length indicators can look broken if there is a long pause between updates
+        delay: 0, // the amount to delay the fade out of the progress
+        speed: 200, // default speed for animations, pretty fast
+        animate: "forward", // suppport "forward", "backward", and "both", any other value is no animation either way
+        initiallyHidden: true, // supports progress indicators which may always be present
+        updatePosition: false
+    });
+    
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global window, swfobject, jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.browser = fluid.browser || {};
+    
+    fluid.browser.binaryXHR = function () {
+        var canSendBinary = window.FormData || XMLHttpRequest.prototype.sendAsBinary;
+        return canSendBinary ? fluid.typeTag("fluid.browser.supportsBinaryXHR") : undefined;
+    };
+    
+    fluid.browser.formData  = function () {
+        return window.FormData ? fluid.typeTag("fluid.browser.supportsFormData") : undefined;
+    };
+    
+    fluid.browser.flash = function () {
+        var hasModernFlash = (typeof(swfobject) !== "undefined") && (swfobject.getFlashPlayerVersion().major > 8);
+        return hasModernFlash ? fluid.typeTag("fluid.browser.supportsFlash") : undefined;
+    };
+    
+    fluid.progressiveChecker = function (options) {
+        // TODO: Replace with fluid.makeArray() when merged into trunk.
+        var checks = options.checks ? $.makeArray(options.checks) : [];
+        for (var x = 0; x < checks.length; x++) {
+            var check = checks[x];
+                            
+            if (check.feature) {
+                return fluid.typeTag(check.contextName);
+            }
+
+        }
+        return options.defaultTypeTag;
+    };
+    
+    fluid.defaults("fluid.progressiveChecker", {
+        checks: [], // [{"feature": "{IoC Expression}", "contextName": "context.name"}]
+        defaultTypeTag: undefined
+    });
+    
+    
+    /**********************************************************
+     * This code runs immediately upon inclusion of this file *
+     **********************************************************/
+    
+    // Use JavaScript to hide any markup that is specifically in place for cases when JavaScript is off.
+    // Note: the use of fl-ProgEnhance-basic is deprecated, and replaced by fl-progEnhance-basic.
+    // It is included here for backward compatibility only.
+    $("head").append("<style type='text/css'>.fl-progEnhance-basic, .fl-ProgEnhance-basic { display: none; }</style>");
+    
+    // Browser feature detection--adds corresponding type tags to the static environment,
+    // which can be used to define appropriate demands blocks for components using the IoC system.
+    var features = {
+        supportsBinaryXHR: fluid.browser.binaryXHR(),
+        supportsFormData: fluid.browser.formData(),
+        supportsFlash: fluid.browser.flash()
+    };
+    fluid.merge(null, fluid.staticEnvironment, features);
+    
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true, window, swfobject*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+/************
+ * Uploader *
+ ************/
+
+(function ($, fluid) {
+    
+    var fileOrFiles = function (that, numFiles) {
+        return (numFiles === 1) ? that.options.strings.progress.singleFile : 
+                                  that.options.strings.progress.pluralFiles;
+    };
+    
+    var enableElement = function (that, elm) {
+        elm.removeAttr("disabled");
+        elm.removeClass(that.options.styles.dim);
+    };
+    
+    var disableElement = function (that, elm) {
+        elm.attr("disabled", "disabled");
+        elm.addClass(that.options.styles.dim);
+    };
+    
+    var showElement = function (that, elm) {
+        elm.removeClass(that.options.styles.hidden);
+    };
+     
+    var hideElement = function (that, elm) {
+        elm.addClass(that.options.styles.hidden);
+    };
+    
+    var setTotalProgressStyle = function (that, didError) {
+        didError = didError || false;
+        var indicator = that.totalProgress.indicator;
+        indicator.toggleClass(that.options.styles.totalProgress, !didError);
+        indicator.toggleClass(that.options.styles.totalProgressError, didError);
+    };
+    
+    var setStateEmpty = function (that) {
+        disableElement(that, that.locate("uploadButton"));
+        
+        // If the queue is totally empty, treat it specially.
+        if (that.queue.files.length === 0) { 
+            that.locate("browseButtonText").text(that.options.strings.buttons.browse);
+            that.locate("browseButton").removeClass(that.options.styles.browseButton);
+            showElement(that, that.locate("instructions"));
+        }
+    };
+    
+    var setStateDone = function (that) {
+        disableElement(that, that.locate("uploadButton"));
+        enableElement(that, that.locate("browseButton"));
+        that.strategy.local.enableBrowseButton();
+        hideElement(that, that.locate("pauseButton"));
+        showElement(that, that.locate("uploadButton"));
+    };
+
+    var setStateLoaded = function (that) {
+        that.locate("browseButtonText").text(that.options.strings.buttons.addMore);
+        that.locate("browseButton").addClass(that.options.styles.browseButton);
+        hideElement(that, that.locate("pauseButton"));
+        showElement(that, that.locate("uploadButton"));
+        enableElement(that, that.locate("uploadButton"));
+        enableElement(that, that.locate("browseButton"));
+        that.strategy.local.enableBrowseButton();
+        hideElement(that, that.locate("instructions"));
+        that.totalProgress.hide();
+    };
+    
+    var setStateUploading = function (that) {
+        that.totalProgress.hide(false, false);
+        setTotalProgressStyle(that);
+        hideElement(that, that.locate("uploadButton"));
+        disableElement(that, that.locate("browseButton"));
+        that.strategy.local.disableBrowseButton();
+        enableElement(that, that.locate("pauseButton"));
+        showElement(that, that.locate("pauseButton"));
+        that.locate(that.options.focusWithEvent.afterUploadStart).focus();
+    };    
+    
+    var renderUploadTotalMessage = function (that) {
+        // Render template for the total file status message.
+        var numReadyFiles = that.queue.getReadyFiles().length;
+        var bytesReadyFiles = that.queue.sizeOfReadyFiles();
+        var fileLabelStr = fileOrFiles(that, numReadyFiles);
+                                                   
+        var totalStateStr = fluid.stringTemplate(that.options.strings.progress.toUploadLabel, {
+            fileCount: numReadyFiles, 
+            fileLabel: fileLabelStr, 
+            totalBytes: fluid.uploader.formatFileSize(bytesReadyFiles)
+        });
+        that.locate("totalFileStatusText").html(totalStateStr);
+    };
+        
+    var updateTotalProgress = function (that) {
+        var batch = that.queue.currentBatch;
+        var totalPercent = fluid.uploader.derivePercent(batch.totalBytesUploaded, batch.totalBytes);
+        var numFilesInBatch = batch.files.length;
+        var fileLabelStr = fileOrFiles(that, numFilesInBatch);
+        
+        var totalProgressStr = fluid.stringTemplate(that.options.strings.progress.totalProgressLabel, {
+            curFileN: batch.fileIdx + 1, 
+            totalFilesN: numFilesInBatch, 
+            fileLabel: fileLabelStr,
+            currBytes: fluid.uploader.formatFileSize(batch.totalBytesUploaded), 
+            totalBytes: fluid.uploader.formatFileSize(batch.totalBytes)
+        });  
+        that.totalProgress.update(totalPercent, totalProgressStr);
+    };
+    
+    var updateTotalAtCompletion = function (that) {
+        var numErroredFiles = that.queue.getErroredFiles().length;
+        var numTotalFiles = that.queue.files.length;
+        var fileLabelStr = fileOrFiles(that, numTotalFiles);
+        
+        var errorStr = "";
+        
+        // if there are errors then change the total progress bar
+        // and set up the errorStr so that we can use it in the totalProgressStr
+        if (numErroredFiles > 0) {
+            var errorLabelString = (numErroredFiles === 1) ? that.options.strings.progress.singleError : 
+                                                             that.options.strings.progress.pluralErrors;
+            setTotalProgressStyle(that, true);
+            errorStr = fluid.stringTemplate(that.options.strings.progress.numberOfErrors, {
+                errorsN: numErroredFiles,
+                errorLabel: errorLabelString
+            });
+        }
+        
+        var totalProgressStr = fluid.stringTemplate(that.options.strings.progress.completedLabel, {
+            curFileN: that.queue.getUploadedFiles().length, 
+            totalFilesN: numTotalFiles,
+            errorString: errorStr,
+            fileLabel: fileLabelStr,
+            totalCurrBytes: fluid.uploader.formatFileSize(that.queue.sizeOfUploadedFiles())
+        });
+        
+        that.totalProgress.update(100, totalProgressStr);
+    };
+
+    /*
+     * Summarizes the status of all the files in the file queue.  
+     */
+    var updateQueueSummaryText = function (that) {
+        var fileQueueTable = that.locate("fileQueue");
+        
+        if (that.queue.files.length === 0) {
+            fileQueueTable.attr("summary", that.options.strings.queue.emptyQueue);
+        }
+        else {
+            var queueSummary = fluid.stringTemplate(that.options.strings.queue.queueSummary, {
+                totalUploaded: that.queue.getUploadedFiles().length, 
+                totalInUploadQueue: that.queue.files.length - that.queue.getUploadedFiles().length
+            });        
+            
+            fileQueueTable.attr("summary", queueSummary);
+        }
+    };
+    
+    var bindDOMEvents = function (that) {
+        that.locate("uploadButton").click(function () {
+            that.start();
+        });
+
+        that.locate("pauseButton").click(function () {
+            that.stop();
+        });
+    };
+
+    var updateStateAfterFileDialog = function (that) {
+        if (that.queue.getReadyFiles().length > 0) {
+            setStateLoaded(that);
+            renderUploadTotalMessage(that);
+            that.locate(that.options.focusWithEvent.afterFileDialog).focus();
+            updateQueueSummaryText(that);
+        }
+    };
+    
+    var updateStateAfterFileRemoval = function (that) {
+        if (that.queue.getReadyFiles().length === 0) {
+            setStateEmpty(that);
+        }
+        renderUploadTotalMessage(that);
+        updateQueueSummaryText(that);
+    };
+    
+    var updateStateAfterCompletion = function (that) {
+        if (that.queue.getReadyFiles().length === 0) {
+            setStateDone(that);
+        } else {
+            setStateLoaded(that);
+        }
+        updateTotalAtCompletion(that);
+        updateQueueSummaryText(that);
+    }; 
+    
+    var bindEvents = function (that) {       
+        that.events.afterFileDialog.addListener(function () {
+            updateStateAfterFileDialog(that);
+        });
+        
+        that.events.afterFileQueued.addListener(function (file) {
+            that.queue.addFile(file); 
+        });
+        
+        that.events.onFileRemoved.addListener(function (file) {
+            that.removeFile(file);
+        });
+        
+        that.events.afterFileRemoved.addListener(function () {
+            updateStateAfterFileRemoval(that);
+        });
+        
+        that.events.onUploadStart.addListener(function () {
+            setStateUploading(that);
+        });
+        
+        that.events.onUploadStop.addListener(function () {
+            that.locate(that.options.focusWithEvent.afterUploadStop).focus();
+        });
+        
+        that.events.onFileStart.addListener(function (file) {
+            file.filestatus = fluid.uploader.fileStatusConstants.IN_PROGRESS;
+            that.queue.startFile();
+        });
+        
+        that.events.onFileProgress.addListener(function (file, currentBytes, totalBytes) {
+            that.queue.updateBatchStatus(currentBytes);
+            updateTotalProgress(that); 
+        });
+        
+        that.events.onFileComplete.addListener(function (file) {
+            that.queue.finishFile(file);
+            that.events.afterFileComplete.fire(file); 
+            
+            if (that.queue.shouldUploadNextFile()) {
+                that.strategy.remote.start();
+            } else {
+                if (that.queue.shouldStop) {
+                    that.strategy.remote.stop();
+                }
+
+                that.events.afterUploadComplete.fire(that.queue.currentBatch.files);
+                that.queue.clearCurrentBatch();
+            }
+        });
+        
+        that.events.onFileSuccess.addListener(function (file) {
+            file.filestatus = fluid.uploader.fileStatusConstants.COMPLETE;
+            if (that.queue.currentBatch.bytesUploadedForFile === 0) {
+                that.queue.currentBatch.totalBytesUploaded += file.size;
+            }
+            
+            updateTotalProgress(that); 
+        });
+        
+        that.events.onFileError.addListener(function (file, error) {
+            file.filestatus = fluid.uploader.fileStatusConstants.ERROR;
+            if (error === fluid.uploader.errorConstants.UPLOAD_STOPPED) {
+                that.queue.isUploading = false;
+            } else if (that.queue.isUploading) {
+                that.queue.currentBatch.totalBytesUploaded += file.size;
+                that.queue.currentBatch.numFilesErrored++;
+            }
+        });
+
+        that.events.afterUploadComplete.addListener(function () {
+            that.queue.isUploading = false;
+            updateStateAfterCompletion(that);
+        });
+    };
+    
+    var setupUploader = function (that) {
+        // Setup the environment appropriate if we're in demo mode.
+        if (that.options.demo) {
+            that.demo = fluid.typeTag("fluid.uploader.demo");
+        }
+        
+        fluid.initDependents(that);                 
+
+        // Upload button should not be enabled until there are files to upload
+        disableElement(that, that.locate("uploadButton"));
+        bindDOMEvents(that);
+        bindEvents(that);
+        
+        updateQueueSummaryText(that);
+        that.statusUpdater();
+        
+        // Uploader uses application-style keyboard conventions, so give it a suitable role.
+        that.container.attr("role", "application");
+    };
+    
+    /**
+     * Instantiates a new Uploader component.
+     * 
+     * @param {Object} container the DOM element in which the Uploader lives
+     * @param {Object} options configuration options for the component.
+     */
+    fluid.uploader = function (container, options) {
+        var that = fluid.typeTag("fluid.uploader");
+        // Set up the environment for progressive enhancement.
+        if (fluid.progressiveChecker) {
+            fluid.staticEnvironment.uploaderContext = fluid.invoke("fluid.progressiveChecker", 
+                                                                   null, 
+                                                                   that);
+        }
+        
+        // Invoke an Uploader implementation, which will be specifically resolved using IoC 
+        // based on the static environment configured by the progressiveChecker above.
+        return fluid.invoke("fluid.uploader.impl", [container, options], that);
+    };
+    
+    fluid.demands("fluid.progressiveChecker", "fluid.uploader", {
+        funcName: "fluid.progressiveChecker",
+        args: [{
+            checks: [
+                {
+                    feature: "{fluid.browser.supportsBinaryXHR}",
+                    contextName: "fluid.uploader.html5"
+                },
+                {
+                    feature: "{fluid.browser.supportsFlash}",
+                    contextName: "fluid.uploader.swfUpload"
+                }
+            ],
+
+            defaultTypeTag: fluid.typeTag("fluid.uploader.singleFile")
+        }]
+    });
+    
+    // This method has been deprecated as of Infusion 1.3. Use fluid.uploader() instead, 
+    // which now includes built-in support for progressive enhancement.
+    fluid.progressiveEnhanceableUploader = function (container, enhanceable, options) {
+        return fluid.uploader(container, options);
+    };
+
+    /**
+     * Multiple file Uploader implementation. Use fluid.uploader() for IoC-resolved, progressively
+     * enhanceable Uploader, or call this directly if you don't want support for old-style single uploads
+     *
+     * @param {jQueryable} container the component's container
+     * @param {Object} options configuration options
+     */
+    fluid.uploader.multiFileUploader = function (container, options) {
+        var that = fluid.initView("fluid.uploader.multiFileUploader", container, options);
+        that.queue = fluid.uploader.fileQueue();
+        
+        /**
+         * Opens the native OS browse file dialog.
+         */
+        that.browse = function () {
+            if (!that.queue.isUploading) {
+                that.strategy.local.browse();
+            }
+        };
+        
+        /**
+         * Removes the specified file from the upload queue.
+         * 
+         * @param {File} file the file to remove
+         */
+        that.removeFile = function (file) {
+            that.queue.removeFile(file);
+            that.strategy.local.removeFile(file);
+            that.events.afterFileRemoved.fire(file);
+        };
+        
+        /**
+         * Starts uploading all queued files to the server.
+         */
+        that.start = function () {
+            that.queue.start();
+            that.events.onUploadStart.fire(that.queue.currentBatch.files); 
+            that.strategy.remote.start();
+        };
+        
+        /**
+         * Cancels an in-progress upload.
+         */
+        that.stop = function () {
+            /* FLUID-822: while stopping the upload cycle while a file is in mid-upload should be possible
+             * in practice, it sets up a state where when the upload cycle is restarted SWFUpload will get stuck
+             * therefore we only stop the upload after a file has completed but before the next file begins. 
+             */
+            that.queue.shouldStop = true;
+            that.events.onUploadStop.fire();
+        };
+        
+        setupUploader(that);
+        return that;  
+    };
+    
+    fluid.defaults("fluid.uploader.multiFileUploader", {
+        components: {
+            strategy: {
+                type: "fluid.uploader.progressiveStrategy"
+            },
+            
+            fileQueueView: {
+                type: "fluid.uploader.fileQueueView",
+                options: {
+                    model: "{multiFileUploader}.queue.files",
+                    uploaderContainer: "{multiFileUploader}.container",
+                    events: "{multiFileUploader}.events"
+                }
+            },
+            
+            totalProgress: {
+                type: "fluid.uploader.totalProgressBar",
+                options: {
+                    selectors: {
+                        progressBar: ".flc-uploader-queue-footer",
+                        displayElement: ".flc-uploader-total-progress", 
+                        label: ".flc-uploader-total-progress-text",
+                        indicator: ".flc-uploader-total-progress",
+                        ariaElement: ".flc-uploader-total-progress"
+                    }
+                }
+            }
+        },
+        
+        invokers: {
+            statusUpdater: "fluid.uploader.ariaLiveRegionUpdater"
+        },
+        
+        queueSettings: {
+            uploadURL: "",
+            postParams: {},
+            fileSizeLimit: "20480",
+            fileTypes: "*",
+            fileTypesDescription: null,
+            fileUploadLimit: 0,
+            fileQueueLimit: 0
+        },
+
+        demo: false,
+        
+        selectors: {
+            fileQueue: ".flc-uploader-queue",
+            browseButton: ".flc-uploader-button-browse",
+            browseButtonText: ".flc-uploader-button-browse-text",
+            uploadButton: ".flc-uploader-button-upload",
+            pauseButton: ".flc-uploader-button-pause",
+            totalFileStatusText: ".flc-uploader-total-progress-text",
+            instructions: ".flc-uploader-browse-instructions",
+            statusRegion: ".flc-uploader-status-region"
+        },
+
+        // Specifies a selector name to move keyboard focus to when a particular event fires.
+        // Event listeners must already be implemented to use these options.
+        focusWithEvent: {
+            afterFileDialog: "uploadButton",
+            afterUploadStart: "pauseButton",
+            afterUploadStop: "uploadButton"
+        },
+        
+        styles: {
+            disabled: "fl-uploader-disabled",
+            hidden: "fl-uploader-hidden",
+            dim: "fl-uploader-dim",
+            totalProgress: "fl-uploader-total-progress-okay",
+            totalProgressError: "fl-uploader-total-progress-errored",
+            browseButton: "fl-uploader-browseMore"
+        },
+        
+        events: {
+            afterReady: null,
+            onFileDialog: null,
+            afterFileQueued: null,
+            onFileRemoved: null,
+            afterFileRemoved: null,
+            onQueueError: null,
+            afterFileDialog: null,
+            onUploadStart: null,
+            onUploadStop: null,
+            onFileStart: null,
+            onFileProgress: null,
+            onFileError: null,
+            onFileSuccess: null,
+            onFileComplete: null,
+            afterFileComplete: null,
+            afterUploadComplete: null
+        },
+
+        strings: {
+            progress: {
+                toUploadLabel: "To upload: %fileCount %fileLabel (%totalBytes)", 
+                totalProgressLabel: "Uploading: %curFileN of %totalFilesN %fileLabel (%currBytes of %totalBytes)", 
+                completedLabel: "Uploaded: %curFileN of %totalFilesN %fileLabel (%totalCurrBytes)%errorString",
+                numberOfErrors: ", %errorsN %errorLabel",
+                singleFile: "file",
+                pluralFiles: "files",
+                singleError: "error",
+                pluralErrors: "errors"
+            },
+            buttons: {
+                browse: "Browse Files",
+                addMore: "Add More",
+                stopUpload: "Stop Upload",
+                cancelRemaning: "Cancel remaining Uploads",
+                resumeUpload: "Resume Upload"
+            },
+            queue: {
+                emptyQueue: "File list: No files waiting to be uploaded.",
+                queueSummary: "File list:  %totalUploaded files uploaded, %totalInUploadQueue file waiting to be uploaded." 
+            }
+        },
+        
+        mergePolicy: {
+            model: "preserve"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.impl", "fluid.uploader", {
+        funcName: "fluid.uploader.multiFileUploader"
+    });
+    
+    fluid.demands("fluid.uploader.totalProgressBar", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.progress",
+        args: [
+            "{multiFileUploader}.container",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+        
+   /**
+    * Pretty prints a file's size, converting from bytes to kilobytes or megabytes.
+    * 
+    * @param {Number} bytes the files size, specified as in number bytes.
+    */
+    fluid.uploader.formatFileSize = function (bytes) {
+        if (typeof(bytes) === "number") {
+            if (bytes === 0) {
+                return "0.0 KB";
+            } else if (bytes > 0) {
+                if (bytes < 1048576) {
+                    return (Math.ceil(bytes / 1024 * 10) / 10).toFixed(1) + " KB";
+                }
+                else {
+                    return (Math.ceil(bytes / 1048576 * 10) / 10).toFixed(1) + " MB";
+                }
+            }
+        }
+        return "";
+    };
+
+    fluid.uploader.derivePercent = function (num, total) {
+        return Math.round((num * 100) / total);
+    };
+     
+    // TODO: Refactor this to be a general ARIA utility
+    fluid.uploader.ariaLiveRegionUpdater = function (statusRegion, totalFileStatusText, events) {
+        statusRegion.attr("role", "log");     
+        statusRegion.attr("aria-live", "assertive");
+        statusRegion.attr("aria-relevant", "text");
+        statusRegion.attr("aria-atomic", "true");
+
+        var regionUpdater = function () {
+            statusRegion.text(totalFileStatusText.text());
+        };
+
+        events.afterFileDialog.addListener(regionUpdater);
+        events.afterFileRemoved.addListener(regionUpdater);
+        events.afterUploadComplete.addListener(regionUpdater);
+    };
+    
+    fluid.demands("fluid.uploader.ariaLiveRegionUpdater", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.uploader.ariaLiveRegionUpdater",
+        args: [
+            "{multiFileUploader}.dom.statusRegion",
+            "{multiFileUploader}.dom.totalFileStatusText",
+            "{multiFileUploader}.events"
+        ]
+    });
+
+    
+    /**************************************************
+     * Error constants for the Uploader               *
+     * TODO: These are SWFUpload-specific error codes *
+     **************************************************/
+     
+    fluid.uploader.errorConstants = {
+        HTTP_ERROR: -200,
+        MISSING_UPLOAD_URL: -210,
+        IO_ERROR: -220,
+        SECURITY_ERROR: -230,
+        UPLOAD_LIMIT_EXCEEDED: -240,
+        UPLOAD_FAILED: -250,
+        SPECIFIED_FILE_ID_NOT_FOUND: -260,
+        FILE_VALIDATION_FAILED: -270,
+        FILE_CANCELLED: -280,
+        UPLOAD_STOPPED: -290
+    };
+    
+    fluid.uploader.fileStatusConstants = {
+        QUEUED: -1,
+        IN_PROGRESS: -2,
+        ERROR: -3,
+        COMPLETE: -4,
+        CANCELLED: -5
+    };
+
+
+    var toggleVisibility = function (toShow, toHide) {
+        // For FLUID-2789: hide() doesn't work in Opera
+        if (window.opera) { 
+            toShow.show().removeClass("hideUploaderForOpera");
+            toHide.show().addClass("hideUploaderForOpera");
+        } else {
+            toShow.show();
+            toHide.hide();
+        }
+    };
+
+    // TODO: Need to resolve the issue of the gracefully degraded view being outside of the component's
+    // container. Perhaps we can embed a standard HTML 5 file input element right in the template, 
+    // and hide everything else?
+    var determineContainer = function (options) {
+        var defaults = fluid.defaults("fluid.uploader.singleFileStrategy");
+        return (options && options.container) ? options.container : defaults.container;
+    };
+
+
+    /**
+     * Single file Uploader implementation. Use fluid.uploader() for IoC-resolved, progressively
+     * enhanceable Uploader, or call this directly if you only want a standard single file uploader.
+     * But why would you want that?
+     *
+     * @param {jQueryable} container the component's container
+     * @param {Object} options configuration options
+     */
+    fluid.uploader.singleFileUploader = function (container, options) {
+        var that = fluid.initView("fluid.uploader.singleFileUploader", container, options);
+        // TODO: direct DOM fascism that will fail with multiple uploaders on a single page.
+        toggleVisibility($(that.options.selectors.basicUpload), that.container);
+        return that;
+    };
+
+    fluid.defaults("fluid.uploader.singleFileUploader", {
+        selectors: {
+            basicUpload: ".fl-progEnhance-basic"
+        }
+    });
+
+    fluid.demands("fluid.uploader.impl", ["fluid.uploader", "fluid.uploader.singleFile"], {
+        funcName: "fluid.uploader.singleFileUploader"
+    });
+    
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global SWFUpload, jQuery, fluid_1_3:true*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.uploader = fluid.uploader || {};
+    
+    var filterFiles = function (files, filterFn) {
+        var filteredFiles = [];
+        for (var i = 0; i < files.length; i++) {
+            var file = files[i];
+            if (filterFn(file) === true) {
+                filteredFiles.push(file);
+            }
+        }
+        
+        return filteredFiles;
+    };
+     
+    fluid.uploader.fileQueue = function () {
+        var that = {};
+        that.files = [];
+        that.isUploading = false;
+        
+        /********************
+         * Queue Operations *
+         ********************/
+         
+        that.start = function () {
+            that.setupCurrentBatch();
+            that.isUploading = true;
+            that.shouldStop = false;
+        };
+        
+        that.startFile = function () {
+            that.currentBatch.fileIdx++;
+            that.currentBatch.bytesUploadedForFile = 0;
+            that.currentBatch.previousBytesUploadedForFile = 0; 
+        };
+                
+        that.finishFile = function (file) {
+            that.currentBatch.numFilesCompleted++;
+        };
+        
+        that.shouldUploadNextFile = function () {
+            return !that.shouldStop && 
+                   that.isUploading && 
+                   that.currentBatch.numFilesCompleted < that.currentBatch.files.length;
+        };
+        
+        /*****************************
+         * File manipulation methods *
+         *****************************/
+         
+        that.addFile = function (file) {
+            that.files.push(file);    
+        };
+        
+        that.removeFile = function (file) {
+            var idx = $.inArray(file, that.files);
+            that.files.splice(idx, 1);        
+        };
+        
+        /**********************
+         * Queue Info Methods *
+         **********************/
+         
+        that.totalBytes = function () {
+            return fluid.uploader.fileQueue.sizeOfFiles(that.files);
+        };
+
+        that.getReadyFiles = function () {
+            return filterFiles(that.files, function (file) {
+                return (file.filestatus === fluid.uploader.fileStatusConstants.QUEUED || file.filestatus === fluid.uploader.fileStatusConstants.CANCELLED);
+            });        
+        };
+        
+        that.getErroredFiles = function () {
+            return filterFiles(that.files, function (file) {
+                return (file.filestatus === fluid.uploader.fileStatusConstants.ERROR);
+            });        
+        };
+        
+        that.sizeOfReadyFiles = function () {
+            return fluid.uploader.fileQueue.sizeOfFiles(that.getReadyFiles());
+        };
+        
+        that.getUploadedFiles = function () {
+            return filterFiles(that.files, function (file) {
+                return (file.filestatus === fluid.uploader.fileStatusConstants.COMPLETE);
+            });        
+        };
+
+        that.sizeOfUploadedFiles = function () {
+            return fluid.uploader.fileQueue.sizeOfFiles(that.getUploadedFiles());
+        };
+
+        /*****************
+         * Batch Methods *
+         *****************/
+         
+        that.setupCurrentBatch = function () {
+            that.clearCurrentBatch();
+            that.updateCurrentBatch();
+        };
+        
+        that.clearCurrentBatch = function () {
+            that.currentBatch = {
+                fileIdx: -1,
+                files: [],
+                totalBytes: 0,
+                numFilesCompleted: 0,
+                numFilesErrored: 0,
+                bytesUploadedForFile: 0,
+                previousBytesUploadedForFile: 0,
+                totalBytesUploaded: 0
+            };
+        };
+        
+        that.updateCurrentBatch = function () {
+            var readyFiles = that.getReadyFiles();
+            that.currentBatch.files = readyFiles;
+            that.currentBatch.totalBytes = fluid.uploader.fileQueue.sizeOfFiles(readyFiles);
+        };
+        
+        that.updateBatchStatus = function (currentBytes) {
+            var byteIncrement = currentBytes - that.currentBatch.previousBytesUploadedForFile;
+            that.currentBatch.totalBytesUploaded += byteIncrement;
+            that.currentBatch.bytesUploadedForFile += byteIncrement;
+            that.currentBatch.previousBytesUploadedForFile = currentBytes;
+        };
+                
+        return that;
+    };
+    
+    fluid.uploader.fileQueue.sizeOfFiles = function (files) {
+        var totalBytes = 0;
+        for (var i = 0; i < files.length; i++) {
+            var file = files[i];
+            totalBytes += file.size;
+        }        
+        return totalBytes;
+    };
+          
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    var refreshView = function (that) {
+        var maxHeight = that.options.maxHeight;
+        var isOverMaxHeight = (that.scrollingElm.children().eq(0).height() > maxHeight);
+        var setHeight = (isOverMaxHeight) ? maxHeight : "";
+        that.scrollingElm.height(setHeight);
+    };
+    
+    var scrollBottom = function (that) {
+        that.scrollingElm[0].scrollTop = that.scrollingElm[0].scrollHeight;
+    };
+    
+    var scrollTo = function (that, element) {
+        if (!element || element.length < 1) {
+            return;
+        }
+        
+        var padTop = 0;
+        var padBottom = 0;
+        
+        var elmPosTop = element[0].offsetTop;
+        var elmHeight = element.height();
+        var containerScrollTop = that.scrollingElm[0].scrollTop;
+        var containerHeight = that.scrollingElm.height();
+        
+        if (that.options.padScroll) {
+            // if the combined height of the elements is greater than the 
+            // viewport then then scrollTo element would not be in view
+            var prevElmHeight = element.prev().height();
+            padTop = (prevElmHeight + elmHeight <= containerHeight) ? prevElmHeight : 0;
+            var nextElmHeight = element.next().height();
+            padBottom =  (nextElmHeight + elmHeight <= containerHeight) ? nextElmHeight : 0;
+        }
+        
+        // if the top of the row is ABOVE the view port move the row into position
+        if ((elmPosTop - padTop) < containerScrollTop) {
+            that.scrollingElm[0].scrollTop = elmPosTop - padTop;
+        }
+        
+        // if the bottom of the row is BELOW the viewport then scroll it into position
+        if (((elmPosTop + elmHeight) + padBottom) > (containerScrollTop + containerHeight)) {
+            elmHeight = (elmHeight < containerHeight) ? elmHeight : containerHeight;
+            that.scrollingElm[0].scrollTop = (elmPosTop - containerHeight + elmHeight + padBottom);
+        }
+    };
+    
+    var setupScroller = function (that) {
+        that.scrollingElm = that.container.parents(that.options.selectors.wrapper);
+        
+        // We should render our own sensible default if the scrolling element is missing.
+        if (!that.scrollingElm.length) {
+            fluid.fail({
+                name: "Missing Scroller",
+                message: "The scroller wrapper element was not found."
+            });
+        }
+        
+        // set the height of the scroller unless this is IE6
+        if (!$.browser.msie || $.browser.version > 6) {
+            that.scrollingElm.css("max-height", that.options.maxHeight);
+        }
+    };
+    
+    /**
+     * Creates a new Scroller component.
+     * 
+     * @param {Object} container the element containing the collection of things to make scrollable 
+     * @param {Object} options configuration options for the component
+     */
+    fluid.scroller = function (container, options) {
+        var that = fluid.initView("fluid.scroller", container, options);
+        setupScroller(that);
+
+        /**
+         * Scrolls the specified element into view
+         * 
+         * @param {jQuery} element the element to scroll into view
+         */
+        that.scrollTo = function (element) {
+            scrollTo(that, element);
+        };
+        
+        /**
+         * Scrolls to the bottom of the view.
+         */
+        that.scrollBottom = function () {
+            scrollBottom(that);
+        };
+        
+        /**
+         * Refreshes the scroller's appearance based on any changes to the document.
+         */
+        that.refreshView = function () {
+            if ($.browser.msie && $.browser.version < 7) {
+                refreshView(that);
+            }
+        };
+        
+        that.refreshView();
+        return that;
+    };
+    
+    fluid.defaults("fluid.scroller", {  
+        selectors: {
+            wrapper: ".flc-scroller"
+        },
+        
+        maxHeight: 180,
+        
+        padScroll: true
+    });
+    
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2008-2009 University of Cambridge
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+/*******************
+ * File Queue View *
+ *******************/
+
+(function ($, fluid) {
+    
+    // Real data binding would be nice to replace these two pairs.
+    var rowForFile = function (that, file) {
+        return that.locate("fileQueue").find("#" + file.id);
+    };
+    
+    var errorRowForFile = function (that, file) {
+        return $("#" + file.id + "_error", that.container);
+    };
+    
+    var fileForRow = function (that, row) {
+        var files = that.model;
+        for (var i = 0; i < files.length; i++) {
+            var file = files[i];
+            if (file.id.toString() === row.attr("id")) {
+                return file;
+            }
+        }
+        return null;
+    };
+    
+    var progressorForFile = function (that, file) {
+        var progressId = file.id + "_progress";
+        return that.fileProgressors[progressId];
+    };
+    
+    var startFileProgress = function (that, file) {
+        var fileRowElm = rowForFile(that, file);
+        that.scroller.scrollTo(fileRowElm);
+         
+        // update the progressor and make sure that it's in position
+        var fileProgressor = progressorForFile(that, file);
+        fileProgressor.refreshView();
+        fileProgressor.show();
+    };
+        
+    var updateFileProgress = function (that, file, fileBytesComplete, fileTotalBytes) {
+        var filePercent = fluid.uploader.derivePercent(fileBytesComplete, fileTotalBytes);
+        var filePercentStr = filePercent + "%";    
+        progressorForFile(that, file).update(filePercent, filePercentStr);
+    };
+    
+    var hideFileProgress = function (that, file) {
+        var fileRowElm = rowForFile(that, file);
+        progressorForFile(that, file).hide();
+        if (file.filestatus === fluid.uploader.fileStatusConstants.COMPLETE) {
+            that.locate("fileIconBtn", fileRowElm).removeClass(that.options.styles.dim);
+        } 
+    };
+    
+    var removeFileProgress = function (that, file) {
+        var fileProgressor = progressorForFile(that, file);
+        if (!fileProgressor) {
+            return;
+        }
+        var rowProgressor = fileProgressor.displayElement;
+        rowProgressor.remove();
+    };
+    var animateRowRemoval = function (that, row) {
+        row.fadeOut("fast", function () {
+            row.remove();  
+            that.refreshView();
+        });
+    };
+    
+    var removeFileErrorRow = function (that, file) {
+        if (file.filestatus === fluid.uploader.fileStatusConstants.ERROR) {
+            animateRowRemoval(that, errorRowForFile(that, file));
+        }
+    };
+   
+    var removeFileAndRow = function (that, file, row) {
+        // Clean up the stuff associated with a file row.
+        removeFileProgress(that, file);
+        removeFileErrorRow(that, file);
+        
+        // Remove the file itself.
+        that.events.onFileRemoved.fire(file);
+        animateRowRemoval(that, row);
+    };
+    
+    var removeFileForRow = function (that, row) {
+        var file = fileForRow(that, row);
+        if (!file || file.filestatus === fluid.uploader.fileStatusConstants.COMPLETE) {
+            return;
+        }
+        removeFileAndRow(that, file, row);
+    };
+    
+    var removeRowForFile = function (that, file) {
+        var row = rowForFile(that, file);
+        removeFileAndRow(that, file, row);
+    };
+    
+    var bindHover = function (row, styles) {
+        var over = function () {
+            if (row.hasClass(styles.ready) && !row.hasClass(styles.uploading)) {
+                row.addClass(styles.hover);
+            }
+        };
+        
+        var out = function () {
+            if (row.hasClass(styles.ready) && !row.hasClass(styles.uploading)) {
+                row.removeClass(styles.hover);
+            }   
+        };
+        row.hover(over, out);
+    };
+    
+    var bindDeleteKey = function (that, row) {
+        var deleteHandler = function () {
+            removeFileForRow(that, row);
+        };
+       
+        fluid.activatable(row, null, {
+            additionalBindings: [{
+                key: $.ui.keyCode.DELETE, 
+                activateHandler: deleteHandler
+            }]
+        });
+    };
+    
+    var bindRowHandlers = function (that, row) {
+        if ($.browser.msie && $.browser.version < 7) {
+            bindHover(row, that.options.styles);
+        }
+        
+        that.locate("fileIconBtn", row).click(function () {
+            removeFileForRow(that, row);
+        });
+        
+        bindDeleteKey(that, row);
+    };
+    
+    var renderRowFromTemplate = function (that, file) {
+        var row = that.rowTemplate.clone(),
+            fileName = file.name,
+            fileSize = fluid.uploader.formatFileSize(file.size);
+        
+        row.removeClass(that.options.styles.hiddenTemplate);
+        that.locate("fileName", row).text(fileName);
+        that.locate("fileSize", row).text(fileSize);
+        that.locate("fileIconBtn", row).addClass(that.options.styles.remove);
+        row.attr("id", file.id);
+        row.addClass(that.options.styles.ready);
+        bindRowHandlers(that, row);
+        fluid.updateAriaLabel(row, fileName + " " + fileSize);
+        return row;    
+    };
+    
+    var createProgressorFromTemplate = function (that, row) {
+        // create a new progress bar for the row and position it
+        var rowProgressor = that.rowProgressorTemplate.clone();
+        var rowId = row.attr("id");
+        var progressId = rowId + "_progress";
+        rowProgressor.attr("id", progressId);
+        rowProgressor.css("top", row.position().top);
+        rowProgressor.height(row.height()).width(5);
+        that.container.after(rowProgressor);
+       
+        that.fileProgressors[progressId] = fluid.progress(that.options.uploaderContainer, {
+            selectors: {
+                progressBar: "#" + rowId,
+                displayElement: "#" + progressId,
+                label: "#" + progressId + " .fl-uploader-file-progress-text",
+                indicator: "#" + progressId
+            }
+        });
+    };
+    
+    var addFile = function (that, file) {
+        var row = renderRowFromTemplate(that, file);
+        /* FLUID-2720 - do not hide the row under IE8 */
+        if (!($.browser.msie && ($.browser.version >= 8))) {
+            row.hide();
+        }
+        that.container.append(row);
+        row.attr("title", that.options.strings.status.remove);
+        row.fadeIn("slow");
+        that.scroller.scrollBottom();
+        createProgressorFromTemplate(that, row);
+
+        that.refreshView();
+    };
+    
+    var prepareForUpload = function (that) {
+        var rowButtons = that.locate("fileIconBtn", that.locate("fileRows"));
+        rowButtons.attr("disabled", "disabled");
+        rowButtons.addClass(that.options.styles.dim);    
+    };
+
+    var refreshAfterUpload = function (that) {
+        var rowButtons = that.locate("fileIconBtn", that.locate("fileRows"));
+        rowButtons.removeAttr("disabled");
+        rowButtons.removeClass(that.options.styles.dim);    
+    };
+        
+    var changeRowState = function (that, row, newState) {
+        row.removeClass(that.options.styles.ready).removeClass(that.options.styles.error).addClass(newState);
+    };
+    
+    var markRowAsComplete = function (that, file) {
+        // update styles and keyboard bindings for the file row
+        var row = rowForFile(that, file);
+        changeRowState(that, row, that.options.styles.uploaded);
+        row.attr("title", that.options.strings.status.success);
+        fluid.enabled(row, false);
+        
+        // update the click event and the styling for the file delete button
+        var removeRowBtn = that.locate("fileIconBtn", row);
+        removeRowBtn.unbind("click");
+        removeRowBtn.removeClass(that.options.styles.remove);
+        removeRowBtn.attr("title", that.options.strings.status.success); 
+    };
+    
+    var renderErrorInfoRowFromTemplate = function (that, fileRow, error) {
+        // Render the row by cloning the template and binding its id to the file.
+        var errorRow = that.errorInfoRowTemplate.clone();
+        errorRow.attr("id", fileRow.attr("id") + "_error");
+        
+        // Look up the error message and render it.
+        var errorType = fluid.keyForValue(fluid.uploader.errorConstants, error);
+        var errorMsg = that.options.strings.errors[errorType];
+        that.locate("errorText", errorRow).text(errorMsg);
+        fileRow.after(errorRow);
+        that.scroller.scrollTo(errorRow);
+    };
+    
+    var showErrorForFile = function (that, file, error) {
+        hideFileProgress(that, file);
+        if (file.filestatus === fluid.uploader.fileStatusConstants.ERROR) {
+            var fileRowElm = rowForFile(that, file);
+            changeRowState(that, fileRowElm, that.options.styles.error);
+            renderErrorInfoRowFromTemplate(that, fileRowElm, error);
+        }
+    };
+    
+    var bindModelEvents = function (that) {
+        that.returnedOptions = {
+            listeners: {
+                afterFileQueued: that.addFile,
+                onUploadStart: that.prepareForUpload,
+                onFileStart: that.showFileProgress,
+                onFileProgress: that.updateFileProgress,
+                onFileSuccess: that.markFileComplete,
+                onFileError: that.showErrorForFile,
+                afterFileComplete: that.hideFileProgress,
+                afterUploadComplete: that.refreshAfterUpload
+            }
+        };
+    };
+    
+    var addKeyboardNavigation = function (that) {
+        fluid.tabbable(that.container);
+        that.selectableContext = fluid.selectable(that.container, {
+            selectableSelector: that.options.selectors.fileRows,
+            onSelect: function (itemToSelect) {
+                $(itemToSelect).addClass(that.options.styles.selected);
+            },
+            onUnselect: function (selectedItem) {
+                $(selectedItem).removeClass(that.options.styles.selected);
+            }
+        });
+    };
+    
+    var prepareTemplateElements = function (that) {
+        // Grab our template elements out of the DOM.  
+        that.rowTemplate = that.locate("rowTemplate").remove();
+        that.errorInfoRowTemplate = that.locate("errorInfoRowTemplate").remove();
+        that.errorInfoRowTemplate.removeClass(that.options.styles.hiddenTemplate);
+        that.rowProgressorTemplate = that.locate("rowProgressorTemplate", that.options.uploaderContainer).remove();
+    };
+    
+    var setupFileQueue = function (that) {
+        fluid.initDependents(that);
+        prepareTemplateElements(that);         
+        addKeyboardNavigation(that); 
+        bindModelEvents(that);
+    };
+    
+    /**
+     * Creates a new File Queue view.
+     * 
+     * @param {jQuery|selector} container the file queue's container DOM element
+     * @param {fileQueue} queue a file queue model instance
+     * @param {Object} options configuration options for the view
+     */
+    fluid.uploader.fileQueueView = function (container, events, options) {
+        var that = fluid.initView("fluid.uploader.fileQueueView", container, options);
+        that.fileProgressors = {};
+        that.model = that.options.model;
+        that.events = events;
+        
+        that.addFile = function (file) {
+            addFile(that, file);
+        };
+        
+        that.removeFile = function (file) {
+            removeRowForFile(that, file);
+        };
+        
+        that.prepareForUpload = function () {
+            prepareForUpload(that);
+        };
+        
+        that.refreshAfterUpload = function () {
+            refreshAfterUpload(that);
+        };
+
+        that.showFileProgress = function (file) {
+            startFileProgress(that, file);
+        };
+        
+        that.updateFileProgress = function (file, fileBytesComplete, fileTotalBytes) {
+            updateFileProgress(that, file, fileBytesComplete, fileTotalBytes); 
+        };
+        
+        that.markFileComplete = function (file) {
+            progressorForFile(that, file).update(100, "100%");
+            markRowAsComplete(that, file);
+        };
+        
+        that.showErrorForFile = function (file, error) {
+            showErrorForFile(that, file, error);
+        };
+        
+        that.hideFileProgress = function (file) {
+            hideFileProgress(that, file);
+        };
+        
+        that.refreshView = function () {
+            that.scroller.refreshView();
+            that.selectableContext.refresh();
+        };
+        
+        setupFileQueue(that);     
+        return that;
+    };
+    
+    fluid.demands("fluid.uploader.fileQueueView", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.uploader.fileQueueView",
+        args: [
+            "{multiFileUploader}.dom.fileQueue",
+            {
+                onFileRemoved: "{multiFileUploader}.events.onFileRemoved"
+            },
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    fluid.demands("fluid.scroller", "fluid.uploader.fileQueueView", {
+        funcName: "fluid.scroller",
+        args: [
+            "{fileQueueView}.container"
+        ]
+    });
+    
+    fluid.defaults("fluid.uploader.fileQueueView", {
+        components: {
+            scroller: {
+                type: "fluid.scroller"
+            }
+        },
+        
+        selectors: {
+            fileRows: ".flc-uploader-file",
+            fileName: ".flc-uploader-file-name",
+            fileSize: ".flc-uploader-file-size",
+            fileIconBtn: ".flc-uploader-file-action",      
+            errorText: ".flc-uploader-file-error",
+            
+            rowTemplate: ".flc-uploader-file-tmplt",
+            errorInfoRowTemplate: ".flc-uploader-file-error-tmplt",
+            rowProgressorTemplate: ".flc-uploader-file-progressor-tmplt"
+        },
+        
+        styles: {
+            hover: "fl-uploader-file-hover",
+            selected: "fl-uploader-file-focus",
+            ready: "fl-uploader-file-state-ready",
+            uploading: "fl-uploader-file-state-uploading",
+            uploaded: "fl-uploader-file-state-uploaded",
+            error: "fl-uploader-file-state-error",
+            remove: "fl-uploader-file-action-remove",
+            dim: "fl-uploader-dim",
+            hiddenTemplate: "fl-uploader-hidden-templates"
+        },
+        
+        strings: {
+            progress: {
+                toUploadLabel: "To upload: %fileCount %fileLabel (%totalBytes)", 
+                singleFile: "file",
+                pluralFiles: "files"
+            },
+            status: {
+                success: "File Uploaded",
+                error: "File Upload Error",
+                remove: "Press Delete key to remove file"
+            }, 
+            errors: {
+                HTTP_ERROR: "File upload error: a network error occured or the file was rejected (reason unknown).",
+                IO_ERROR: "File upload error: a network error occured.",
+                UPLOAD_LIMIT_EXCEEDED: "File upload error: you have uploaded as many files as you are allowed during this session",
+                UPLOAD_FAILED: "File upload error: the upload failed for an unknown reason.",
+                QUEUE_LIMIT_EXCEEDED: "You have as many files in the queue as can be added at one time. Removing files from the queue may allow you to add different files.",
+                FILE_EXCEEDS_SIZE_LIMIT: "One or more of the files that you attempted to add to the queue exceeded the limit of %fileSizeLimit.",
+                ZERO_BYTE_FILE: "One or more of the files that you attempted to add contained no data.",
+                INVALID_FILETYPE: "One or more files were not added to the queue because they were of the wrong type."
+            }
+        },
+        
+        mergePolicy: {
+            model: "preserve",
+            events: "preserve"
+        }
+    });
+   
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true, SWFUpload, swfobject */
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    fluid.uploader = fluid.uploader || {};
+    
+    fluid.demands("fluid.uploader.impl", ["fluid.uploader", "fluid.uploader.swfUpload"], {
+        funcName: "fluid.uploader.multiFileUploader"
+    });
+    
+    /**********************
+     * uploader.swfUpload *
+     **********************/
+    
+    fluid.uploader.swfUploadStrategy = function (options) {
+        var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy", options);
+        fluid.initDependents(that);
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.swfUploadStrategy", {
+        components: {
+            engine: {
+                type: "fluid.uploader.swfUploadStrategy.engine",
+                options: {
+                    queueSettings: "{multiFileUploader}.options.queueSettings",
+                    flashMovieSettings: "{swfUploadStrategy}.options.flashMovieSettings"
+                }
+            },
+            
+            local: {
+                type: "fluid.uploader.swfUploadStrategy.local"
+            },
+            
+            remote: {
+                type: "fluid.uploader.remote"
+            }
+        },
+        
+        // TODO: Rename this to "flashSettings" and remove the "flash" prefix from each option
+        flashMovieSettings: {
+            flashURL: "../../../lib/swfupload/flash/swfupload.swf",
+            flashButtonPeerId: "",
+            flashButtonAlwaysVisible: false,
+            flashButtonTransparentEvenInIE: true,
+            flashButtonImageURL: "../images/browse.png", // Used only when the Flash movie is visible.
+            flashButtonCursorEffect: SWFUpload.CURSOR.HAND,
+            debug: false
+        },
+
+        styles: {
+            browseButtonOverlay: "fl-uploader-browse-overlay",
+            flash9Container: "fl-uploader-flash9-container",
+            uploaderWrapperFlash10: "fl-uploader-flash10-wrapper"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.uploader.swfUploadStrategy",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    fluid.demands("fluid.uploader.progressiveStrategy", "fluid.uploader.swfUpload", {
+        funcName: "fluid.uploader.swfUploadStrategy",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    
+    fluid.uploader.swfUploadStrategy.remote = function (swfUpload, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy.remote", options);
+        that.swfUpload = swfUpload;
+        
+        that.start = function () {
+            that.swfUpload.startUpload();
+        };
+        
+        that.stop = function () {
+            that.swfUpload.stopUpload();
+        };
+        return that;
+    };
+    
+    fluid.demands("fluid.uploader.remote", "fluid.uploader.swfUploadStrategy", {
+        funcName: "fluid.uploader.swfUploadStrategy.remote",
+        args: [
+            "{engine}.swfUpload",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+
+    
+    fluid.uploader.swfUploadStrategy.local = function (swfUpload, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy.local", options);
+        that.swfUpload = swfUpload;
+        
+        that.browse = function () {
+            if (that.options.file_queue_limit === 1) {
+                that.swfUpload.selectFile();
+            } else {
+                that.swfUpload.selectFiles();
+            }    
+        };
+        
+        that.removeFile = function (file) {
+            that.swfUpload.cancelUpload(file.id);
+        };
+        
+        that.enableBrowseButton = function () {
+            that.swfUpload.setButtonDisabled(false);
+        };
+        
+        that.disableBrowseButton = function () {
+            that.swfUpload.setButtonDisabled(true);
+        };
+        
+        return that;
+    };
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.local", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.uploader.swfUploadStrategy.local",
+        args: [
+            "{engine}.swfUpload",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    fluid.uploader.swfUploadStrategy.engine = function (options) {
+        var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy.engine", options);
+        
+        // Get the Flash version from swfobject and setup a new context so that the appropriate
+        // Flash 9/10 strategies are selected.
+        var flashVersion = swfobject.getFlashPlayerVersion().major;
+        that.flashVersionContext = fluid.typeTag("fluid.uploader.flash." + flashVersion);
+        
+        // Merge Uploader's generic queue options with our Flash-specific options.
+        that.config = $.extend({}, that.options.queueSettings, that.options.flashMovieSettings);
+        
+        // Configure the SWFUpload subsystem.
+        fluid.initDependents(that);
+        that.flashContainer = that.setupDOM();
+        that.swfUploadConfig = that.setupConfig();
+        that.swfUpload = new SWFUpload(that.swfUploadConfig);
+        that.bindEvents();
+        
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.swfUploadStrategy.engine", {
+        invokers: {
+            setupDOM: "fluid.uploader.swfUploadStrategy.setupDOM",
+            setupConfig: "fluid.uploader.swfUploadStrategy.setupConfig",
+            bindEvents: "fluid.uploader.swfUploadStrategy.eventBinder"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.engine", "fluid.uploader.swfUploadStrategy", {
+        funcName: "fluid.uploader.swfUploadStrategy.engine",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    
+    /**********************
+     * swfUpload.setupDOM *
+     **********************/
+    
+    fluid.uploader.swfUploadStrategy.flash10SetupDOM = function (uploaderContainer, browseButton, styles) {
+        // Wrap the whole uploader first.
+        uploaderContainer.wrap("<div class='" + styles.uploaderWrapperFlash10 + "'></div>");
+
+        // Then create a container and placeholder for the Flash movie as a sibling to the uploader.
+        var flashContainer = $("<div><span></span></div>");
+        flashContainer.addClass(styles.browseButtonOverlay);
+        uploaderContainer.after(flashContainer);
+        
+        browseButton.attr("tabindex", -1);        
+        return flashContainer;   
+    };
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.setupDOM", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.10"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash10SetupDOM",
+        args: [
+            "{multiFileUploader}.container",
+            "{multiFileUploader}.dom.browseButton",
+            "{swfUploadStrategy}.options.styles"
+        ]
+    });
+     
+     
+    /*********************************
+     * swfUpload.setupConfig *
+     *********************************/
+      
+    // Maps SWFUpload's setting names to our component's setting names.
+    var swfUploadOptionsMap = {
+        uploadURL: "upload_url",
+        flashURL: "flash_url",
+        postParams: "post_params",
+        fileSizeLimit: "file_size_limit",
+        fileTypes: "file_types",
+        fileUploadLimit: "file_upload_limit",
+        fileQueueLimit: "file_queue_limit",
+        flashButtonPeerId: "button_placeholder_id",
+        flashButtonImageURL: "button_image_url",
+        flashButtonHeight: "button_height",
+        flashButtonWidth: "button_width",
+        flashButtonWindowMode: "button_window_mode",
+        flashButtonCursorEffect: "button_cursor",
+        debug: "debug"
+    };
+
+    // Maps SWFUpload's callback names to our component's callback names.
+    var swfUploadEventMap = {
+        afterReady: "swfupload_loaded_handler",
+        onFileDialog: "file_dialog_start_handler",
+        afterFileQueued: "file_queued_handler",
+        onQueueError: "file_queue_error_handler",
+        afterFileDialog: "file_dialog_complete_handler",
+        onFileStart: "upload_start_handler",
+        onFileProgress: "upload_progress_handler",
+        onFileComplete: "upload_complete_handler",
+        onFileError: "upload_error_handler",
+        onFileSuccess: "upload_success_handler"
+    };
+    
+    var mapNames = function (nameMap, source, target) {
+        var result = target || {};
+        for (var key in source) {
+            var mappedKey = nameMap[key];
+            if (mappedKey) {
+                result[mappedKey] = source[key];
+            }
+        }
+        
+        return result;
+    };
+    
+    // For each event type, hand the fire function to SWFUpload so it can fire the event at the right time for us.
+    // TODO: Refactor out duplication with mapNames()--should be able to use Engage's mapping tool
+    var mapSWFUploadEvents = function (nameMap, events, target) {
+        var result = target || {};
+        for (var eventType in events) {
+            var fireFn = events[eventType].fire;
+            var mappedName = nameMap[eventType];
+            if (mappedName) {
+                result[mappedName] = fireFn;
+            }   
+        }
+        return result;
+    };
+    
+    fluid.uploader.swfUploadStrategy.convertConfigForSWFUpload = function (config, events) {
+        // Map the event and settings names to SWFUpload's expectations.
+        var convertedConfig = mapNames(swfUploadOptionsMap, config);
+        return mapSWFUploadEvents(swfUploadEventMap, events, convertedConfig);
+    };
+    
+    fluid.uploader.swfUploadStrategy.flash10SetupConfig = function (config, events, flashContainer, browseButton) {
+        config.flashButtonPeerId = fluid.allocateSimpleId(flashContainer.children().eq(0));
+        var isTransparent = config.flashButtonAlwaysVisible ? false : (!$.browser.msie || config.flashButtonTransparentEvenInIE);
+        config.flashButtonImageURL = isTransparent ? undefined : config.flashButtonImageURL;
+        config.flashButtonHeight = config.flashButtonHeight || browseButton.outerHeight();
+        config.flashButtonWidth = config.flashButtonWidth || browseButton.outerWidth();
+        config.flashButtonWindowMode = isTransparent ? SWFUpload.WINDOW_MODE.TRANSPARENT : SWFUpload.WINDOW_MODE.OPAQUE;
+        return fluid.uploader.swfUploadStrategy.convertConfigForSWFUpload(config, events);
+    };
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.setupConfig", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.10"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash10SetupConfig",
+        args: [
+            "{engine}.config",
+            "{multiFileUploader}.events",
+            "{engine}.flashContainer",
+            "{multiFileUploader}.dom.browseButton"
+        ]
+    });
+
+     
+    /*********************************
+     * swfUpload.eventBinder *
+     *********************************/
+     
+    var unbindSWFUploadSelectFiles = function () {
+        // There's a bug in SWFUpload 2.2.0b3 that causes the entire browser to crash 
+        // if selectFile() or selectFiles() is invoked. Remove them so no one will accidently crash their browser.
+        var emptyFunction = function () {};
+        SWFUpload.prototype.selectFile = emptyFunction;
+        SWFUpload.prototype.selectFiles = emptyFunction;
+    };
+    
+    fluid.uploader.swfUploadStrategy.bindFileEventListeners = function (model, events) {
+        // Manually update our public model to keep it in sync with SWFUpload's insane,
+        // always-changing references to its internal model.        
+        var manualModelUpdater = function (file) {
+            fluid.find(model, function (potentialMatch) {
+                if (potentialMatch.id === file.id) {
+                    potentialMatch.filestatus = file.filestatus;
+                    return true;
+                }
+            });
+        };
+        
+        events.onFileStart.addListener(manualModelUpdater);
+        events.onFileProgress.addListener(manualModelUpdater);
+        events.onFileError.addListener(manualModelUpdater);
+        events.onFileSuccess.addListener(manualModelUpdater);
+    };
+    
+    fluid.uploader.swfUploadStrategy.flash10EventBinder = function (model, events, local) {
+        unbindSWFUploadSelectFiles();      
+              
+        events.onUploadStart.addListener(function () {
+            local.disableBrowseButton();
+        });
+        
+        events.afterUploadComplete.addListener(function () {
+            local.enableBrowseButton();            
+        });
+        
+        fluid.uploader.swfUploadStrategy.bindFileEventListeners(model, events);
+    };
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.eventBinder", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.10"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash10EventBinder",
+        args: [
+            "{multiFileUploader}.queue.files",
+            "{multiFileUploader}.events",
+            "{swfUploadStrategy}.local"
+        ]
+    });
+})(jQuery, fluid_1_3);
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    fluid.uploader = fluid.uploader || {};
+    fluid.uploader.swfUploadStrategy = fluid.uploader.swfUploadStrategy || {};
+    
+    /**********************************************************************************
+     * The functions in this file, which provide support for Flash 9 in the Uploader, *
+     * have been deprecated as of Infusion 1.3.                                       * 
+     **********************************************************************************/
+    
+    fluid.uploader.swfUploadStrategy.flash9SetupDOM = function (styles) {
+        var container = $("<div><span></span></div>");
+        container.addClass(styles.flash9Container);
+        $("body").append(container);
+        return container;       
+    };
+
+    fluid.demands("fluid.uploader.swfUploadStrategy.setupDOM", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.9"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash9SetupDOM",
+        args: [
+            "{engine}.options.styles"
+        ]
+    });
+
+    fluid.uploader.swfUploadStrategy.flash9SetupConfig = function (config, events) {
+        return fluid.uploader.swfUploadStrategy.convertConfigForSWFUpload(config, events);
+    };
+
+    fluid.demands("fluid.uploader.swfUploadStrategy.setupConfig", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.9"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash9SetupConfig",
+        args: [
+            "{engine}.config",
+            "{multiFileUploader}.events"
+        ]
+    });
+
+    fluid.uploader.swfUploadStrategy.flash9EventBinder = function (model, events, local, browseButton) {
+        browseButton.click(function (e) {        
+            local.browse();
+            e.preventDefault();
+        });
+        fluid.uploader.swfUploadStrategy.bindFileEventListeners(model, events);
+    };
+
+    fluid.demands("fluid.uploader.swfUploadStrategy.eventBinder", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.9"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash9EventBinder",
+        args: [
+            "{multiFileUploader}.queue.files",
+            "{multiFileUploader}.events",
+            "{local}",
+            "{multiFileUploader}.dom.browseButton"
+        ]
+    });
+
+})(jQuery, fluid_1_3);
+/*
+Copyright 2010 OCAD University 
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true, FormData*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    fluid.uploader = fluid.uploader || {};
+    
+    fluid.demands("fluid.uploader.impl", ["fluid.uploader", "fluid.uploader.html5"], {
+        funcName: "fluid.uploader.multiFileUploader"
+    });
+    
+    fluid.uploader.html5Strategy = function (options) {
+        var that = fluid.initLittleComponent("fluid.uploader.html5Strategy", options);
+        fluid.initDependents(that);
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.html5Strategy", {
+        components: {
+            local: {
+                type: "fluid.uploader.html5Strategy.local",
+                options: {
+                    queueSettings: "{multiFileUploader}.options.queueSettings",
+                    browseButton: "{multiFileUploader}.dom.browseButton",
+                    events: "{multiFileUploader}.events"
+                }
+            },
+            
+            remote: {
+                type: "fluid.uploader.remote",
+                options: {
+                    queueSettings: "{multiFileUploader}.options.queueSettings",
+                    events: "{multiFileUploader}.events"
+                }
+            }
+        },
+        
+        mergePolicy: {
+            events: "preserve",
+            browseButton: "preserve"
+        }
+    });
+
+    fluid.demands("fluid.uploader.html5Strategy", "fluid.multiFileUploader", {
+        funcName: "fluid.uploader.html5Strategy",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    fluid.demands("fluid.uploader.progressiveStrategy", "fluid.uploader.html5", {
+        funcName: "fluid.uploader.html5Strategy",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    
+    // TODO: The following two or three functions probably ultimately belong on a that responsible for
+    // coordinating with the XHR. A fileConnection object or something similar.
+    
+    fluid.uploader.html5Strategy.fileSuccessHandler = function (file, events) {
+        events.onFileSuccess.fire(file);
+        events.onFileComplete.fire(file);
+    };
+    
+    fluid.uploader.html5Strategy.progressTracker = function () {
+        var that = {
+            previousBytesLoaded: 0
+        };
+        
+        that.getChunkSize = function (bytesLoaded) {
+            var chunkSize = bytesLoaded - that.previousBytesLoaded;
+            that.previousBytesLoaded = bytesLoaded;
+            return chunkSize;
+        };
+        
+        return that;
+    };
+    
+    var createFileUploadXHR = function (file, events) {
+        var xhr = new XMLHttpRequest();
+        xhr.onreadystatechange = function () {
+            if (xhr.readyState === 4) {
+                fluid.uploader.html5Strategy.fileSuccessHandler(file, events);
+            }
+        };
+
+        var progressTracker = fluid.uploader.html5Strategy.progressTracker();
+        xhr.upload.onprogress = function (pe) {
+            events.onFileProgress.fire(file, progressTracker.getChunkSize(pe.loaded), pe.total);
+        };
+        
+        return xhr;
+    };
+    
+    // Set additional POST parameters for xhr  
+    var setPostParams =  function (formData, postParams) {
+        $.each(postParams,  function (key, value) {
+            formData.append(key, value);
+        });
+    };
+    
+    fluid.uploader.html5Strategy.remote = function (queue, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.html5Strategy.remote", options);
+        that.queue = queue;
+        that.queueSettings = that.options.queueSettings;
+        that.events = that.options.events;
+        
+        // Upload files in the current batch without exceeding the fileUploadLimit
+        // and the fileSizeLimit.  The fileSizeLimit is scaled to KBs.
+        that.start = function () {
+            var files = that.queue.currentBatch.files;
+            var fileUploadLimit = that.queueSettings.fileUploadLimit;
+            
+            for (var i = 0; i < files.length; i++) {
+                var file = files[i];
+                if (fileUploadLimit === 0 ||
+                        that.queue.currentBatch.numFilesCompleted < fileUploadLimit &&
+                        file.size < (that.queueSettings.fileSizeLimit * 1000)) {
+                    that.uploadFile(file);
+                }
+            }
+            that.events.afterUploadComplete.fire(files);
+        };
+        
+        that.uploadFile = function (file) {
+            that.events.onFileStart.fire(file);
+            that.currentXHR = createFileUploadXHR(file, that.events);
+            that.doUpload(file, that.queueSettings, that.currentXHR);            
+        };
+
+        that.stop = function () {
+            var batch = that.queue.currentBatch,
+                file = that.queue.files[batch.fileIdx];
+            
+            file.filestatus = fluid.uploader.fileStatusConstants.CANCELLED;
+            that.queue.shouldStop = true;
+            that.currentXHR.abort();
+            that.events.onUploadStop.fire();
+        };
+        
+        fluid.initDependents(that);
+        that.events.afterReady.fire();
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.html5Strategy.remote", {
+        invokers: {
+            doUpload: "fluid.uploader.html5Strategy.doUpload"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.remote", "fluid.uploader.html5Strategy", {
+        funcName: "fluid.uploader.html5Strategy.remote",
+        args: [
+            "{multiFileUploader}.queue", 
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    var CRLF = "\r\n";
+    
+    /** 
+     * Firefox 4  implementation.  FF4 has implemented a FormData function which
+     * conveniently provides easy construct of set key/value pairs representing 
+     * form fields and their values.  The FormData is then easily sent using the 
+     * XMLHttpRequest send() method.  
+     */
+    fluid.uploader.html5Strategy.doFormDataUpload = function (file, queueSettings, xhr) {
+        var formData = new FormData();
+        formData.append("file", file);
+        
+        setPostParams(formData, queueSettings.postParams);
+        
+        // set post params here.
+        xhr.open("POST", queueSettings.uploadURL, true);
+        xhr.send(formData);
+    };
+    
+    var generateMultipartBoundary = function () {
+        var boundary = "---------------------------";
+        boundary += Math.floor(Math.random() * 32768);
+        boundary += Math.floor(Math.random() * 32768);
+        boundary += Math.floor(Math.random() * 32768);
+        return boundary;
+    };
+    
+    fluid.uploader.html5Strategy.generateMultiPartContent = function (boundary, file) {
+        var multipart = " ";
+        multipart += "--" + boundary + CRLF;
+        multipart += "Content-Disposition: form-data;" +
+                     " name=\"fileData\";" + 
+                     " filename=\"" + file.name + 
+                     "\"" + CRLF;
+        multipart += "Content-Type: " + file.type + CRLF + CRLF;
+        multipart += file.getAsBinary(); // TODO: Ack, concatting binary data to JS String!
+        multipart += CRLF + "--" + boundary + "--" + CRLF;
+        return multipart;
+    };
+    
+    /*
+     * Create the multipart/form-data content by hand to send the file
+     */
+    fluid.uploader.html5Strategy.doManualMultipartUpload = function (file, queueSettings, xhr) {
+        var boundary = generateMultipartBoundary();
+        var multipart = fluid.uploader.html5Strategy.generateMultiPartContent(boundary, file);
+        
+        xhr.open("POST", queueSettings.uploadURL, true);
+        xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
+        xhr.sendAsBinary(multipart);
+    };
+    
+    // Default configuration for older browsers that don't support FormData
+    fluid.demands("fluid.uploader.html5Strategy.doUpload", "fluid.uploader.html5Strategy.remote", {
+        funcName: "fluid.uploader.html5Strategy.doManualMultipartUpload",
+        args: ["@0", "@1", "@2"]
+    });
+    
+    // Configuration for FF4, Chrome, and Safari 4+, all of which support FormData correctly.
+    fluid.demands("fluid.uploader.html5Strategy.doUpload", [
+        "fluid.uploader.html5Strategy.remote", 
+        "fluid.browser.supportsFormData"
+    ], {
+        funcName: "fluid.uploader.html5Strategy.doFormDataUpload",
+        args: ["@0", "@1", "@2"]
+    });
+    
+    
+    /*
+     * Return the active multi-file input from the input stack
+     */
+    var getActiveMultiFileInput = function (browseButton) {
+        var inputs = browseButton.children();
+        return inputs.eq(inputs.length - 1);
+    };
+    
+    fluid.uploader.html5Strategy.local = function (queue, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.html5Strategy.local", options);
+        that.queue = queue;
+        that.events = that.options.events;
+        that.queueSettings = that.options.queueSettings;
+
+        // Add files to the file queue without exceeding the fileQueueLimit 
+        that.addFiles = function (files) {
+            var filesToUpload = files.length;
+            var fileQueueLimit = that.queueSettings.fileQueueLimit;
+            var filesInQueue = that.queue.files.length - that.queue.getUploadedFiles().length;
+            
+            if (fileQueueLimit !== 0 && (filesToUpload + filesInQueue) > fileQueueLimit) { 
+                filesToUpload = fileQueueLimit - filesInQueue;
+            } 
+            
+            for (var i = 0; i < filesToUpload; i++) {
+                var file = files[i];
+                file.filestatus = fluid.uploader.fileStatusConstants.QUEUED;
+                file.id = "file-" + fluid.allocateGuid();
+                that.events.afterFileQueued.fire(file);
+            }
+            
+            that.events.afterFileDialog.fire(files.length);    
+        };
+        
+        that.removeFile = function (file) {
+        };
+        
+        that.enableBrowseButton = function () {
+            var activeMultiFileInput = getActiveMultiFileInput(that.options.browseButton);
+            activeMultiFileInput.removeAttr("disabled");
+        };
+        
+        that.disableBrowseButton = function () {
+            var activeMultiFileInput = getActiveMultiFileInput(that.options.browseButton);
+            activeMultiFileInput.attr("disabled", "disabled");
+        };
+        
+        fluid.initDependents(that);
+        return that;
+    };
+    
+    
+    fluid.defaults("fluid.uploader.html5Strategy.local", {
+        components: {
+            browseHandler: {
+                type: "fluid.uploader.html5Strategy.browseHandler",
+                options: {
+                    browseButton: "{multiFileUploader}.dom.browseButton",
+                    queueSettings: "{multiFileUploader}.options.queueSettings",
+                    events: "{multiFileUploader}.events",
+                    addFilesFn: "{local}.addFiles"
+                }
+            }
+        },
+        mergePolicy: {
+            browseButton: "preserve",
+            events: "preserve",
+            // TODO: This is awkward--refactor
+            addFilesFn: "preserve"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.html5Strategy.local", "fluid.uploader.html5Strategy", {
+        funcName: "fluid.uploader.html5Strategy.local",
+        args: [
+            "{multiFileUploader}.queue",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    
+    var bindEventsToFileInput = function (that, fileInput) {
+        fileInput.click(function () {
+            that.events.onFileDialog.fire();
+        });
+        
+        fileInput.change(function () {
+            var files = fileInput[0].files;
+            that.options.addFilesFn.apply(null, [files]);
+            that.renderFreshMultiFileInput();
+        });
+        
+        fileInput.focus(function () {
+            that.options.browseButton.addClass("focus");
+        });
+        
+        fileInput.blur(function () {
+            that.options.browseButton.removeClass("focus");
+        });
+    };
+    
+    var renderMultiFileInput = function (that) {
+        var multiFileInput = $(that.options.multiFileInputMarkup);
+        var fileTypes = (that.options.queueSettings.fileTypes).replace(/\;/g, ',');       
+        multiFileInput.attr("accept", fileTypes);
+        that.inputs.push(multiFileInput);
+        bindEventsToFileInput(that, multiFileInput);
+        return multiFileInput;
+    };
+    
+    var setupBrowseHandler = function (that) {
+        var multiFileInput = renderMultiFileInput(that);        
+        that.options.browseButton.append(multiFileInput);
+        that.options.browseButton.attr("tabindex", -1);
+    };
+    
+    fluid.uploader.html5Strategy.browseHandler = function (options) {
+        var that = fluid.initLittleComponent("fluid.uploader.html5Strategy.browseHandler", options);
+        that.inputs = [];
+        that.events = that.options.events;
+        
+        that.renderFreshMultiFileInput = function () {
+            // Update the stack of multi file input elements we have in the DOM.
+            var previousInput = that.inputs[that.inputs.length - 1];
+            previousInput.hide();
+            previousInput.attr("tabindex", -1);
+            var newInput = renderMultiFileInput(that);
+            previousInput.after(newInput);
+        };
+        
+        setupBrowseHandler(that);
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.html5Strategy.browseHandler", {
+        multiFileInputMarkup: "<input type='file' multiple='' class='fl-hidden'/>"
+    });
+    
+    fluid.demands("fluid.uploader.html5Strategy.browseHandler", "fluid.uploader.html5Strategy.local", {
+        funcName: "fluid.uploader.html5Strategy.browseHandler",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+
+})(jQuery, fluid_1_3);    
+/*
+Copyright 2009 University of Toronto
+Copyright 2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.uploader = fluid.uploader || {};
+    fluid.uploader.swfUploadStrategy = fluid.uploader.swfUploadStrategy || {};
+    
+    var startUploading; // Define early due to subtle circular dependency.
+    
+    var updateProgress = function (file, events, demoState, isUploading) {
+        if (!isUploading) {
+            return;
+        }
+        
+        var chunk = Math.min(demoState.chunkSize, file.size);
+        demoState.bytesUploaded = Math.min(demoState.bytesUploaded + chunk, file.size);
+        events.onFileProgress.fire(file, demoState.bytesUploaded, file.size);
+    };
+    
+    var finishAndContinueOrCleanup = function (that, file) {
+        that.queue.finishFile(file);
+        that.events.afterFileComplete.fire(file);
+        
+        if (that.queue.shouldUploadNextFile()) {
+            startUploading(that);
+        } else {
+            that.events.afterUploadComplete.fire(that.queue.currentBatch.files);
+            that.queue.clearCurrentBatch();
+        }
+    };
+    
+    var finishUploading = function (that) {
+        if (!that.queue.isUploading) {
+            return;
+        }
+        
+        var file = that.demoState.currentFile;
+        that.events.onFileSuccess.fire(file);
+        that.demoState.fileIdx++;
+        finishAndContinueOrCleanup(that, file);
+    };
+    
+    var simulateUpload = function (that) {
+        if (!that.queue.isUploading) {
+            return;
+        }
+        
+        var file = that.demoState.currentFile;
+        if (that.demoState.bytesUploaded < file.size) {
+            fluid.invokeAfterRandomDelay(function () {
+                updateProgress(file, that.events, that.demoState, that.queue.isUploading);
+                simulateUpload(that);
+            });
+        } else {
+            finishUploading(that);
+        } 
+    };
+    
+    startUploading = function (that) {
+        // Reset our upload stats for each new file.
+        that.demoState.currentFile = that.queue.files[that.demoState.fileIdx];
+        that.demoState.chunksForCurrentFile = Math.ceil(that.demoState.currentFile / that.demoState.chunkSize);
+        that.demoState.bytesUploaded = 0;
+        that.queue.isUploading = true;
+        
+        that.events.onFileStart.fire(that.demoState.currentFile);
+        simulateUpload(that);
+    };
+
+    var stopDemo = function (that) {
+        var file = that.demoState.currentFile;
+        file.filestatus = fluid.uploader.fileStatusConstants.CANCELLED;
+        that.queue.shouldStop = true;
+        
+        // In SWFUpload's world, pausing is a combinination of an UPLOAD_STOPPED error and a complete.
+        that.events.onFileError.fire(file, 
+                                     fluid.uploader.errorConstants.UPLOAD_STOPPED, 
+                                     "The demo upload was paused by the user.");
+        finishAndContinueOrCleanup(that, file);
+        that.events.onUploadStop.fire();
+    };
+    
+    var setupDemo = function (that) {
+        if (that.simulateDelay === undefined || that.simulateDelay === null) {
+            that.simulateDelay = true;
+        }
+          
+        // Initialize state for our upload simulation.
+        that.demoState = {
+            fileIdx: 0,
+            chunkSize: 200000
+        };
+        
+        return that;
+    };
+       
+    /**
+     * The Demo Engine wraps a SWFUpload engine and simulates the upload process.
+     * 
+     * @param {FileQueue} queue the Uploader's file queue instance
+     * @param {Object} the Uploader's bundle of event firers
+     * @param {Object} configuration options for SWFUpload (in its native dialect)
+     */
+     // TODO: boil down events to only those we actually need.
+     // TODO: remove swfupload references and move into general namespace. Are there any real SWFUpload references here?
+    fluid.uploader.demoRemote = function (queue, events, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.demoRemote", options);
+        that.queue = queue;
+        that.events = events;
+        
+        that.start = function () {
+            startUploading(that);   
+        };
+        
+        /**
+         * Cancels a simulated upload.
+         * This method overrides the default behaviour in SWFUploadManager.
+         */
+        that.stop = function () {
+            stopDemo(that);
+        };
+        
+        setupDemo(that);
+        return that;
+    };
+    
+    /**
+     * Invokes a function after a random delay by using setTimeout.
+     * If the simulateDelay option is false, the function is invoked immediately.
+     * This is an odd function, but a potential candidate for central inclusion.
+     * 
+     * @param {Function} fn the function to invoke
+     */
+    fluid.invokeAfterRandomDelay = function (fn) {
+        var delay = Math.floor(Math.random() * 1000 + 100);
+        setTimeout(fn, delay);
+    };
+    
+    fluid.demands("fluid.uploader.remote", ["fluid.uploader.multiFileUploader", "fluid.uploader.demo"], {
+        funcName: "fluid.uploader.demoRemote",
+        args: [
+            "{multiFileUploader}.queue",
+            "{multiFileUploader}.events",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/README.txt b/docs/jscripts/infusion/README.txt
new file mode 100644 (file)
index 0000000..3c2ecfd
--- /dev/null
@@ -0,0 +1,273 @@
+Fluid Infusion 1.3
+====================
+Main Project Site:  http://fluidproject.org
+Documentation:      http://wiki.fluidproject.org/display/fluid/Infusion+Documentation
+
+
+What's New in 1.3
+===================
+
+    * Bug fixes
+    * Renderer improvements
+    * Transactional change applier
+    * Sneak Peek at the IoC system
+    * Accessibility Improvements
+          o HTML5 Uploader
+          o Better feedback for the Progress component
+          o Enhanced screen reader semantics for the Inline Edit component
+          o Location and movement announcements for the Reorderer component
+          o No Wrap option for the Reorderer component
+    * Many improved demos
+
+
+What's in this Release
+======================
+
+This release is available in two forms:
+    Deployment Bundle - infusion-1.3.zip 
+    Source Code Bundle - infusion-1.3-src.zip
+
+In addition to source code, samples and tests, both bundles include at the top level a single JavaScript file
+
+    InfusionAll.js
+
+that is a combination of all other source files. Developers can include this single file in their
+pages to provide all the necessary support for the Infusion component Library. In the Deployment Bundle,
+this script is compressed and suitable for production use.
+
+The Deployment Bundle also includes a WAR file suitable for deployment in Java-based containers: 
+        fluid-components-1.3.war
+
+Source Code
+-----------
+The organization of the full source code for the Infusion library is as follows:
+
+        components/
+             inlineEdit/
+             pager/
+             progress/
+             reorderer/
+             tableOfContents/
+             uiOptions/
+             undo/
+             uploader/
+        framework/
+             core/
+             fss/
+             renderer/
+        lib/
+             fastXmlPull/
+             jquery/
+             json/
+             swfobject/
+             swfupload/
+
+In the Deployment Bundle, the JavaScript source has been minified: comments and whitespace have
+been removed. 
+
+Developers wishing to learn about the Fluid Infusion code, or debug their applications, should use
+the Source Code Bundle.
+
+Demo Portal
+-----------
+The bundle now comes with a convenient one-stop-shop for seeing all components in action. It is organized as follows:
+
+        demos/
+            fss/
+                layout/
+                mobile/
+                reset/
+                text/
+                themes/
+            inlineEdit/
+                rich/
+                simple/
+            keyboard-a11y/            
+            pager/
+            portal/                
+            progress/
+            renderer/
+            reorderer/
+                gridReorderer/
+                imageReorderer/
+                layoutReorderer/                
+                listReorderer/
+            uiOptions/
+            uploader/
+
+            
+Other Examples and Sample Code
+------------------------------
+Sample code illustrating how Infusion components can be used:
+
+        integration-demos/
+             sakai/     (showcases: Inline Edit, Pager, UI Options, FSS)
+             uportal/   (showcases: Reorderer, UI Options, FSS)
+        standalone-demos/
+             pager/
+             renderer/
+             reorderer/
+             table-of-contents/
+
+Tests
+-----
+        tests/
+            component-tests/
+            escalated-tests/
+            framework-tests/
+            lib/
+            manual-tests/
+            test-core/
+
+License
+-------
+Fluid Infusion code is licensed under a dual ECL 2.0 / BSD license. The specific licenses can be
+found in the license file:
+        licenses/Infusion-LICENSE.txt
+
+Infusion also depends upon some third party open source modules. These are contained in their own
+folders, and their licenses are also present in
+        licenses/
+
+Third Party Software in Infusion
+--------------------------------
+This is a list of publicly available software that is included in the Fluid Infusion bundle, along
+with their licensing terms.
+
+    * jQuery javascript library v1.4.2: http://jquery.com/ (MIT and GPL licensed http://docs.jquery.com/Licensing)
+    * jQuery UI javascript widget library v1.8: http://ui.jquery.com/ (MIT and GPL licensed http://docs.jquery.com/Licensing)
+    * jQuery UI tooltip plugin from the tooltip branch on github for jQuery UI v1.9: (MIT and GPL licensed http://docs.jquery.com/Licensing)
+            https://github.com/jquery/jquery-ui/tree/tooltip
+            commit  48a5977d3325869abd7b
+            tree    43fd0cda4af2cdcd33f5
+            parent  bdd815e8dcdeace8be6d 
+    * jQuery QUnit revision 2dbf603: http://docs.jquery.com/QUnit (MIT and GPL licensed http://docs.jquery.com/Licensing)
+    * jQuery Chili code highlighter http://code.google.com/p/jquery-chili-js/ (MIT licensed)
+    * Douglas Crockford's JSON parsing and stringifying methods (from 2007-11-06): http://www.json.org/ (Public Domain)
+    * SWFUpload v2.2.0.1: http://swfupload.org/ (MIT licensed http://www.opensource.org/licenses/mit-license.php)
+    * SWFObject v2.2: http://code.google.com/p/swfobject/ (MIT licensed http://www.opensource.org/licenses/mit-license.php)
+    * Sample markup and stylesheets from Sakai v2.5 (http://sakaiproject.org) and uPortal v2.6 (http://www.uportal.org/)
+    
+Other third party software
+
+    * fastXmlPull is based on XML for Script's Fast Pull Parser v3.1
+      (see: http://wiki.fluidproject.org/display/fluid/Licensing+for+fastXmlPull.js )
+    * fluid.reset.css is based on YUI's CSS reset styling v2.5.2
+      see: http://developer.yahoo.com/yui/reset/ (BSD licensed http://developer.yahoo.com/yui/license.html)
+    
+Readme
+------
+This file.
+        README.txt
+
+
+Documentation
+=============
+
+The Fluid Project uses a wiki for documentation and project collaboration: http://wiki.fluidproject.org.
+The main Infusion documentation can be found at:
+
+    http://wiki.fluidproject.org/display/fluid/Infusion+Documentation
+
+The documentation for Infusion consists of a number of information pages stored in the Fluid Wiki.
+The pages include tutorials, API descriptions, testing procedures, and data-gathering approaches. To make the 
+manual pages easy to navigate we have added the following guides:
+
+    * The above-mentioned landing page, which links to all of our documentation.
+    * A link to the documentation appears at the top of the left-side wiki navigation
+      bar with the name "Infusion Documentation".
+
+NOTE: Starting with Infusion 1.3, we are beginning to migrate our documentation to a new home.
+Some of our Sneak Peek functionality is now documented at
+    http://wiki.fluidproject.org/display/docs/Infusion+Documentation+Home
+
+
+Supported Browsers
+==================
+Chrome 4: full support in Win XP
+Firefox 4: full support in Mac OS 10.6 and Win 7
+Firefox 3.6: full support in Mac OS 10.6, Win XP and Win 7
+Internet Explorer 9.x: full support in Win 7
+Internet Explorer 8.x: full support in Win XP and Win 7
+Internet Explorer 7.x: full support in Win XP
+Internet Explorer 6.x: full support in Win XP
+Safari 4: full support in Mac OS 10.6
+
+For more information on Fluid Infusion browser support, please see:
+    http://wiki.fluidproject.org/display/fluid/Browser+Support
+
+
+Status of Components and Framework Features
+===========================================
+
+Production: supports A-Grade browsers, stable for production usage across a wide range of
+applications and use cases
+    * Fluid Skinning System 
+    * Infusion Framework Core
+    * Inline Edit: Simple Text
+    * Renderer
+    * Reorderer: List, Grid, Layout, Image
+    * Undo
+
+Preview: still growing, but with broad browser support. Expect new features in upcoming releases
+    * Pager
+    * Progress
+    * UI Options
+    * Uploader
+
+Sneak Peek: in development; APIs will change. Share your feedback, ideas, and code
+    * IoC
+    * Transactional ChangeApplier
+    * Inline Edit: Dropdown
+    * Inline Edit: Rich Text
+    * Mobile Fluid Skinning System
+    * Table of Contents
+
+
+Known Issues
+============
+
+The Fluid Project uses a JIRA website to track bugs: http://issues.fluidproject.org.
+Some of the known issues in this release are described here:
+
+FSS:
+    FLUID-2504: Flexible columns don't maintain proper alignment under certain conditions
+    FLUID-2434: In IE, major font size changes break text positioning within form controls
+
+Framework:
+    FLUID-2577: Renderer performance can be slow on IE 6 and 7 in some contexts.
+
+Inline Edit: 
+    FLUID-3632: Chrome 4 in WIN XP does not allow tabbing out of tinyMCE editor's edit field
+    FLUID-3811: Previous edits are persisting in Edit Mode despite demo being reloaded in IE8
+    FLUID-1600: Pressing the "Tab" key to exit edit mode places focus on the wrong item
+
+Layout Reorderer: 
+    FLUID-3864: Layout Reorderer failed to move portlets back to the first column in three-columns view with keyboard
+    FLUID-3089: If columns become stacked, can't drag item into lower column
+    FLUID-858:  Portlet Columns load with no padding between them in IE7
+
+Pager:
+    FLUID-2880: The Pager will be refactored. Note that as a result of this, there will be significant changes to the Pager API
+    FLUID-2329: The self-rendering mode of the Pager is not the default mode
+    FLUID-3584: Clicking page numbers throws an error: using IE 6
+
+Renderer: 
+    FLUID-3493: Renderer appears to corrupt templates containing empty tags on Opera (maybe others)
+    FLUID-3277: Attempt to add children to leaf component in tree results in "targetlump is undefined" error
+    FLUID-3276: Enclosing branch nodes within markup which has "headers" attribute causes them to become invisible to the renderer
+
+Reorderer: 
+    FLUID-3288: Moving an item with the keyboard "loses" the "ctrl-key is down" status
+    FLUID-118:  Dragging an image offscreen or out of the frame has some unexpected results.
+
+UI Options: 
+    FLUID-3621: The text in buttons does not change size.
+    FLUID-2481: "Links" selection does not work correctly in UIOptions
+    FLUID-2398: Minimum font size control changes the text size even when the base size is larger then the minimum.
+    
+Uploader: 
+    FLUID-3241: Can only tab to the "Browse Files" button once: using IE
+    FLUID-2052: Cannot tab away from the "Browse Files" button with Flash 10*
+    * For information related to known issues with Flash 10 compatibility, 
+      see http://wiki.fluidproject.org/x/kwZo
diff --git a/docs/jscripts/infusion/components/inlineEdit/css/InlineEdit.css b/docs/jscripts/infusion/components/inlineEdit/css/InlineEdit.css
new file mode 100644 (file)
index 0000000..67c55a2
--- /dev/null
@@ -0,0 +1,79 @@
+/* Themed Appearance + Themed States */
+
+.fl-inlineEdit-edit {
+       margin-left : -1px;
+       border      : 2px solid #0085FF;
+}
+
+.fl-inlineEdit-simple-editableText {
+    padding       : 2px 2px 1px 2px;
+    border-bottom : 1px dotted #0086D8;
+}
+
+.fl-inlineEdit-textContainer {
+    display: inline-block;     
+}
+
+.fl-inlineEdit-text {
+    display             : inline;
+    padding-right       : 20px;
+    background-image    : url('../images/inline_edit_edit_button_16x16.png');
+    background-repeat   : no-repeat;
+    background-position : right center;  
+}
+
+.fl-inlineEdit-focus {
+       border        : 1px solid #0085FF;
+       padding       : 1px 0px 0px 1px;
+       margin-bottom : 1px;
+}
+
+/* Base Appearance & States Appearance */
+.fl-inlineEdit-tooltip {
+    position    : absolute;
+    background  : #FFFABB;
+    border      : 1px solid #C9B857;
+    font        : normal sans-serif;
+    padding     : 3px 5px;
+}
+
+/* Put keyboard focus instruction on a foreground layer */
+.fl-inlineEdit-editModeInstruction {
+    position    : absolute;
+    background  : #0081C1;
+    font        : normal sans-serif;
+    color       : #FFFFFF;
+    padding     : 3px 5px;
+    max-width   : 310px;
+    z-index     : 1;    
+}
+
+/* :hover */
+.fl-inlineEdit-invitation {
+    background  : #E1EDF5;
+    cursor      : pointer;
+}
+
+.fl-inlineEdit-richText-invitation {
+    background  : lightyellow;
+    cursor      : pointer;
+}
+
+.fl-inlineEdit-emptyText-invitation  {
+    font-style : italic;
+    color      : #9f9f9f;
+}
+
+.fl-inlineEdit-emptyDefaultViewText {
+    padding-top : 16px;        
+}
+
+/* :focus */
+:focus {
+    outline: none;
+}
+
+/* overrides fss-reset FLUID-3860 */
+p {
+       margin : 0px;
+}
\ No newline at end of file
diff --git a/docs/jscripts/infusion/components/inlineEdit/images/inline_edit_edit_button_16x16.png b/docs/jscripts/infusion/components/inlineEdit/images/inline_edit_edit_button_16x16.png
new file mode 100644 (file)
index 0000000..61d31a5
Binary files /dev/null and b/docs/jscripts/infusion/components/inlineEdit/images/inline_edit_edit_button_16x16.png differ
diff --git a/docs/jscripts/infusion/components/inlineEdit/js/InlineEdit.js b/docs/jscripts/infusion/components/inlineEdit/js/InlineEdit.js
new file mode 100644 (file)
index 0000000..6581d4c
--- /dev/null
@@ -0,0 +1,908 @@
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3, document, setTimeout*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    function sendKey(control, event, virtualCode, charCode) {
+        var kE = document.createEvent("KeyEvents");
+        kE.initKeyEvent(event, 1, 1, null, 0, 0, 0, 0, virtualCode, charCode);
+        control.dispatchEvent(kE);
+    }
+    
+    /** Set the caret position to the end of a text field's value, also taking care
+     * to scroll the field so that this position is visible.
+     * @param {DOM node} control The control to be scrolled (input, or possibly textarea)
+     * @param value The current value of the control
+     */
+    fluid.setCaretToEnd = function (control, value) {
+        var pos = value ? value.length : 0;
+
+        try {
+            control.focus();
+        // see http://www.quirksmode.org/dom/range_intro.html - in Opera, must detect setSelectionRange first, 
+        // since its support for Microsoft TextRange is buggy
+            if (control.setSelectionRange) {
+
+                control.setSelectionRange(pos, pos);
+                if ($.browser.mozilla && pos > 0) {
+                  // ludicrous fix for Firefox failure to scroll to selection position, inspired by
+                  // http://bytes.com/forum/thread496726.html
+                    sendKey(control, "keypress", 92, 92); // type in a junk character
+                    sendKey(control, "keydown", 8, 0); // delete key must be dispatched exactly like this
+                    sendKey(control, "keypress", 8, 0);
+                }
+            }
+
+            else if (control.createTextRange) {
+                var range = control.createTextRange();
+                range.move("character", pos);
+                range.select();
+            }
+        }
+        catch (e) {} 
+    };
+
+    var switchToViewMode = function (that) {
+        that.editContainer.hide();
+        that.displayModeRenderer.show();
+    };
+    
+    var cancel = function (that) {
+        if (that.isEditing()) {
+            // Roll the edit field back to its old value and close it up.
+            // This setTimeout is necessary on Firefox, since any attempt to modify the 
+            // input control value during the stack processing the ESCAPE key will be ignored.
+            setTimeout(function () {
+                that.editView.value(that.model.value);
+            }, 1);
+            switchToViewMode(that);
+            that.events.afterFinishEdit.fire(that.model.value, that.model.value, 
+                that.editField[0], that.viewEl[0]);
+        }
+    };
+    
+    var finish = function (that) {
+        var newValue = that.editView.value();
+        var oldValue = that.model.value;
+
+        var viewNode = that.viewEl[0];
+        var editNode = that.editField[0];
+        var ret = that.events.onFinishEdit.fire(newValue, oldValue, editNode, viewNode);
+        if (ret === false) {
+            return;
+        }
+        
+        that.updateModelValue(newValue);
+        that.events.afterFinishEdit.fire(newValue, oldValue, editNode, viewNode);
+        
+        switchToViewMode(that);
+    };
+    
+    /** 
+     * Do not allow the textEditButton to regain focus upon completion unless
+     * the keypress is enter or esc.
+     */  
+    var bindEditFinish = function (that) {
+        if (that.options.submitOnEnter === undefined) {
+            that.options.submitOnEnter = "textarea" !== fluid.unwrap(that.editField).nodeName.toLowerCase();
+        }
+        function keyCode(evt) {
+            // Fix for handling arrow key presses. See FLUID-760.
+            return evt.keyCode ? evt.keyCode : (evt.which ? evt.which : 0);          
+        }
+        var escHandler = function (evt) {
+            var code = keyCode(evt);
+            if (code === $.ui.keyCode.ESCAPE) {
+                that.textEditButton.focus(0);
+                cancel(that);
+                return false;
+            }
+        };
+        var finishHandler = function (evt) {
+            var code = keyCode(evt);
+            
+            if (code !== $.ui.keyCode.ENTER) {
+                that.textEditButton.blur();
+                return true;
+            }
+            else {
+                finish(that);
+                that.textEditButton.focus(0);
+            }
+            
+            return false;
+        };
+        if (that.options.submitOnEnter) {
+            that.editContainer.keypress(finishHandler);
+        }
+        that.editContainer.keydown(escHandler);
+    };
+
+    var bindBlurHandler = function (that) {
+        if (that.options.blurHandlerBinder) {
+            that.options.blurHandlerBinder(that);
+        }
+        else {
+            var blurHandler = function (evt) {
+                if (that.isEditing()) {
+                    finish(that);
+                }
+                return false;
+            };
+            that.editField.blur(blurHandler);
+        }
+    };
+
+    var initializeEditView = function (that, initial) {
+        if (!that.editInitialized) { 
+            fluid.inlineEdit.renderEditContainer(that, !that.options.lazyEditView || !initial);
+            
+            if (!that.options.lazyEditView || !initial) {
+                that.editView = fluid.initSubcomponent(that, "editView", that.editField);
+                
+                $.extend(true, that.editView, fluid.initSubcomponent(that, "editAccessor", that.editField));
+        
+                bindEditFinish(that);
+                bindBlurHandler(that);
+                that.editView.refreshView(that);
+                that.editInitialized = true;
+            }
+        }
+    };
+    
+    var edit = function (that) {
+        initializeEditView(that, false);
+      
+        var viewEl = that.viewEl;
+        var displayText = that.displayView.value();
+        that.updateModelValue(that.model.value === "" ? "" : displayText);
+        if (that.options.applyEditPadding) {
+            that.editField.width(Math.max(viewEl.width() + that.options.paddings.edit, that.options.paddings.minimumEdit));
+        }
+
+        that.displayModeRenderer.hide();
+        that.editContainer.show();                  
+
+        // Work around for FLUID-726
+        // Without 'setTimeout' the finish handler gets called with the event and the edit field is inactivated.       
+        setTimeout(function () {
+            fluid.setCaretToEnd(that.editField[0], that.editView.value());
+            if (that.options.selectOnEdit) {
+                that.editField[0].select();
+            }
+        }, 0);
+        that.events.afterBeginEdit.fire();
+    };
+
+    var clearEmptyViewStyles = function (textEl, styles, originalViewPadding) {
+        textEl.removeClass(styles.defaultViewStyle);
+        textEl.css('padding-right', originalViewPadding);
+        textEl.removeClass(styles.emptyDefaultViewText);
+    };
+    
+    var showDefaultViewText = function (that) {
+        that.displayView.value(that.options.defaultViewText);
+        that.viewEl.css('padding-right', that.existingPadding);
+        that.viewEl.addClass(that.options.styles.defaultViewStyle);
+    };
+
+    var showNothing = function (that) {
+        that.displayView.value("");
+        
+        // workaround for FLUID-938:
+        // IE can not style an empty inline element, so force element to be display: inline-block
+        if ($.browser.msie) {
+            if (that.viewEl.css('display') === 'inline') {
+                that.viewEl.css('display', "inline-block");
+            }
+        }
+    };
+
+    var showEditedText = function (that) {
+        that.displayView.value(that.model.value);
+        clearEmptyViewStyles(that.viewEl, that.options.styles, that.existingPadding);
+    };
+    
+    var refreshView = function (that, source) {
+        that.displayView.refreshView(that, source);
+        if (that.editView) {
+            that.editView.refreshView(that, source);
+        }
+    };
+    
+    var initModel = function (that, value) {
+        that.model.value = value;
+        that.refreshView();
+    };
+    
+    var updateModelValue = function (that, newValue, source) {
+        var comparator = that.options.modelComparator;
+        var unchanged = comparator ? comparator(that.model.value, newValue) : 
+            that.model.value === newValue;
+        if (!unchanged) {
+            var oldModel = $.extend(true, {}, that.model);
+            that.model.value = newValue;
+            that.events.modelChanged.fire(that.model, oldModel, source);
+            that.refreshView(source);
+        }
+    };
+        
+    var makeIsEditing = function (that) {
+        var isEditing = false;
+
+        that.events.onBeginEdit.addListener(function () {
+            isEditing = true;
+        });
+        that.events.afterFinishEdit.addListener(function () {
+            isEditing = false; 
+        });
+        return function () {
+            return isEditing;
+        };
+    };
+    
+    var makeEditHandler = function (that) {
+        return function () {
+            var prevent = that.events.onBeginEdit.fire();
+            if (prevent === false) {
+                return false;
+            }
+            edit(that);
+            
+            return true;
+        }; 
+    };    
+    
+    // Initialize the tooltip once the document is ready.
+    // For more details, see http://issues.fluidproject.org/browse/FLUID-1030
+    var initTooltips = function (that) {
+        var tooltipOptions = {
+            content: that.options.tooltipText,
+            position: {
+                my: "left top",
+                at: "left bottom",
+                offset: "0 5"
+            },
+            target: "*",
+            delay: that.options.tooltipDelay,
+            styles: {
+                tooltip: that.options.styles.tooltip
+            }     
+        };
+        
+          fluid.tooltip(that.viewEl, tooltipOptions);
+        
+        if (that.textEditButton) {
+            fluid.tooltip(that.textEditButton, tooltipOptions);
+        }
+    };
+    
+    var calculateInitialPadding = function (viewEl) {
+        var padding = viewEl.css("padding-right");
+        return padding ? parseFloat(padding) : 0;
+    };
+    
+    var setupInlineEdit = function (componentContainer, that) {
+        // Hide the edit container to start
+        if (that.editContainer) {
+            that.editContainer.hide();
+        }
+        
+        // Add tooltip handler if required and available
+        if (that.tooltipEnabled()) {
+            initTooltips(that);
+        }
+        
+        // Setup any registered decorators for the component.
+        that.decorators = fluid.initSubcomponents(that, "componentDecorators", 
+            [that, fluid.COMPONENT_OPTIONS]);
+    };
+    
+    /**
+     * Creates a whole list of inline editors.
+     */
+    var setupInlineEdits = function (editables, options) {
+        var editors = [];
+        editables.each(function (idx, editable) {
+            editors.push(fluid.inlineEdit($(editable), options));
+        });
+        
+        return editors;
+    };
+    
+    /**
+     * Instantiates a new Inline Edit component
+     * 
+     * @param {Object} componentContainer a selector, jquery, or a dom element representing the component's container
+     * @param {Object} options a collection of options settings
+     */
+    fluid.inlineEdit = function (componentContainer, userOptions) {   
+        var that = fluid.initView("inlineEdit", componentContainer, userOptions);
+        
+        that.viewEl = fluid.inlineEdit.setupDisplayText(that);
+        
+        that.displayView = fluid.initSubcomponent(that, "displayView", that.viewEl);
+        $.extend(true, that.displayView, fluid.initSubcomponent(that, "displayAccessor", that.viewEl));
+
+        /**
+         * The current value of the inline editable text. The "model" in MVC terms.
+         */
+        that.model = {value: ""};
+       
+        /**
+         * Switches to edit mode.
+         */
+        that.edit = makeEditHandler(that);
+        
+        /**
+         * Determines if the component is currently in edit mode.
+         * 
+         * @return true if edit mode shown, false if view mode is shown
+         */
+        that.isEditing = makeIsEditing(that);
+        
+        /**
+         * Finishes editing, switching back to view mode.
+         */
+        that.finish = function () {
+            finish(that);
+        };
+
+        /**
+         * Cancels the in-progress edit and switches back to view mode.
+         */
+        that.cancel = function () {
+            cancel(that);
+        };
+
+        /**
+         * Determines if the tooltip feature is enabled.
+         * 
+         * @return true if the tooltip feature is turned on, false if not
+         */
+        that.tooltipEnabled = function () {
+            return that.options.useTooltip && $.fn.tooltip;
+        };
+        
+        /**
+         * Updates the state of the inline editor in the DOM, based on changes that may have
+         * happened to the model.
+         * 
+         * @param {Object} source
+         */
+        that.refreshView = function (source) {
+            refreshView(that, source);
+        };
+        
+        /**
+         * Pushes external changes to the model into the inline editor, refreshing its
+         * rendering in the DOM. The modelChanged event will fire.
+         * 
+         * @param {String} newValue The bare value of the model, that is, the string being edited
+         * @param {Object} source An optional "source" (perhaps a DOM element) which triggered this event
+         */
+        that.updateModelValue = function (newValue, source) {
+            updateModelValue(that, newValue, source);
+        };
+        
+        /**
+         * Pushes external changes to the model into the inline editor, refreshing its
+         * rendering in the DOM. The modelChanged event will fire.
+         * 
+         * @param {Object} newValue The full value of the new model, that is, a model object which contains the editable value as the element named "value"
+         * @param {Object} source An optional "source" (perhaps a DOM element) which triggered this event
+         */
+        that.updateModel = function (newModel, source) {
+            updateModelValue(that, newModel.value, source);
+        };
+        
+        that.existingPadding = calculateInitialPadding(that.viewEl);
+        
+        initModel(that, that.displayView.value());
+        
+        that.displayModeRenderer = that.options.displayModeRenderer(that);  
+        initializeEditView(that, true);
+        setupInlineEdit(componentContainer, that);
+        
+        return that;
+    };
+    
+    /**
+     * Set up and style the edit field.  If an edit field is not provided,
+     * default markup is created for the edit field 
+     * 
+     * @param {string} editStyle The default styling for the edit field
+     * @param {Object} editField The edit field markup provided by the integrator
+     * 
+     * @return eField The styled edit field   
+     */
+    fluid.inlineEdit.setupEditField = function (editStyle, editField) {
+        var eField = $(editField);
+        eField = eField.length ? eField : $("<input type='text' class='flc-inlineEdit-edit'/>");
+        eField.addClass(editStyle);
+        return eField;
+    };
+
+    /**
+     * Set up the edit container and append the edit field to the container.  If an edit container
+     * is not provided, default markup is created.
+     * 
+     * @param {Object} displayContainer The display mode container 
+     * @param {Object} editField The edit field that is to be appended to the edit container 
+     * @param {Object} editContainer The edit container markup provided by the integrator   
+     * 
+     * @return eContainer The edit container containing the edit field   
+     */
+    fluid.inlineEdit.setupEditContainer = function (displayContainer, editField, editContainer) {
+        var eContainer = $(editContainer);
+        eContainer = eContainer.length ? eContainer : $("<span></span>");
+        displayContainer.after(eContainer);
+        eContainer.append(editField);
+        
+        return eContainer;
+    };
+    
+    /**
+     * Default renderer for the edit mode view.
+     * 
+     * @return {Object} container The edit container containing the edit field
+     *                  field The styled edit field  
+     */
+    fluid.inlineEdit.defaultEditModeRenderer = function (that) {
+        var editField = fluid.inlineEdit.setupEditField(that.options.styles.edit, that.editField);
+        var editContainer = fluid.inlineEdit.setupEditContainer(that.displayModeRenderer, editField, that.editContainer);
+        var editModeInstruction = fluid.inlineEdit.setupEditModeInstruction(that.options.styles.editModeInstruction, that.options.strings.editModeInstruction);
+        
+        var id = fluid.allocateSimpleId(editModeInstruction);
+        editField.attr("aria-describedby", id);
+
+        fluid.inlineEdit.positionEditModeInstruction(editModeInstruction, editContainer, editField);
+              
+        // Package up the container and field for the component.
+        return {
+            container: editContainer,
+            field: editField 
+        };
+    };
+    
+    /**
+     * Configures the edit container and view, and uses the component's editModeRenderer to render
+     * the edit container.
+     *  
+     * @param {boolean} lazyEditView If true, will delay rendering of the edit container;
+     *                                            Default is false 
+     */
+    fluid.inlineEdit.renderEditContainer = function (that, lazyEditView) {
+        that.editContainer = that.locate("editContainer");
+        that.editField = that.locate("edit");
+        if (that.editContainer.length !== 1) {
+            if (that.editContainer.length > 1) {
+                fluid.fail("InlineEdit did not find a unique container for selector " + that.options.selectors.editContainer +
+                   ": " + fluid.dumpEl(that.editContainer));
+            }
+        }
+        
+        if (!lazyEditView) {
+            return; 
+        } // do not invoke the renderer, unless this is the "final" effective time
+        
+        var editElms = that.options.editModeRenderer(that);
+        if (editElms) {
+            that.editContainer = editElms.container;
+            that.editField = editElms.field;
+        }
+    };
+
+    /**
+     * Set up the edit mode instruction with aria in edit mode
+     * 
+     * @param {String} editModeInstructionStyle The default styling for the instruction
+     * @param {String} editModeInstructionText The default instruction text
+     * 
+     * @return {jQuery} The displayed instruction in edit mode
+     */
+    fluid.inlineEdit.setupEditModeInstruction = function (editModeInstructionStyle, editModeInstructionText) {
+        var editModeInstruction = $("<p></p>");
+        editModeInstruction.addClass(editModeInstructionStyle);
+        editModeInstruction.text(editModeInstructionText);
+
+        return editModeInstruction;
+    };
+
+    /**
+     * Positions the edit mode instruction directly beneath the edit container
+     * 
+     * @param {Object} editModeInstruction The displayed instruction in edit mode
+     * @param {Object} editContainer The edit container in edit mode
+     * @param {Object} editField The edit field in edit mode
+     */    
+    fluid.inlineEdit.positionEditModeInstruction = function (editModeInstruction, editContainer, editField) {
+        editContainer.append(editModeInstruction);
+        
+        editField.focus(function () {
+            editModeInstruction.show();
+
+            var editFieldPosition = editField.offset();
+            editModeInstruction.css({left: editFieldPosition.left});
+            editModeInstruction.css({top: editFieldPosition.top + editField.height() + 5});
+        });
+    };  
+    
+    /**
+     * Set up and style the display mode container for the viewEl and the textEditButton 
+     * 
+     * @param {Object} styles The default styling for the display mode container
+     * @param {Object} displayModeWrapper The markup used to generate the display mode container
+     * 
+     * @return {jQuery} The styled display mode container
+     */
+    fluid.inlineEdit.setupDisplayModeContainer = function (styles, displayModeWrapper) {
+        var displayModeContainer = $(displayModeWrapper);  
+        displayModeContainer = displayModeContainer.length ? displayModeContainer : $("<span></span>");  
+        displayModeContainer.addClass(styles.displayView);
+        
+        return displayModeContainer;
+    };
+    
+    /**
+     * Retrieve the display text from the DOM.  
+     * 
+     * @return {jQuery} The display text
+     */
+    fluid.inlineEdit.setupDisplayText = function (that) {
+        var viewEl = that.locate("text");
+
+        /*
+         *  Remove the display from the tab order to prevent users to think they
+         *  are able to access the inline edit field, but they cannot since the 
+         *  keyboard event binding is only on the button.
+         */
+        viewEl.attr("tabindex", "-1");
+        viewEl.addClass(that.options.styles.text);
+        
+        return viewEl;
+    };
+    
+    /**
+     * Set up the textEditButton.  Append a background image with appropriate
+     * descriptive text to the button.
+     * 
+     * @return {jQuery} The accessible button located after the display text
+     */
+    fluid.inlineEdit.setupTextEditButton = function (that) {
+        var opts = that.options;
+        var textEditButton = that.locate("textEditButton");
+        
+        if  (textEditButton.length === 0) {
+            var markup = $("<a href='#_' class='flc-inlineEdit-textEditButton'></a>");
+            markup.addClass(opts.styles.textEditButton);
+            markup.text(opts.tooltipText);            
+            
+            /**
+             * Set text for the button and listen
+             * for modelChanged to keep it updated
+             */ 
+            fluid.inlineEdit.updateTextEditButton(markup, that.model.value || opts.defaultViewText, opts.strings.textEditButton);
+            that.events.modelChanged.addListener(function () {
+                fluid.inlineEdit.updateTextEditButton(markup, that.model.value || opts.defaultViewText, opts.strings.textEditButton);
+            });        
+            
+            that.locate("text").after(markup);
+            
+            // Refresh the textEditButton with the newly appended options
+            textEditButton = that.locate("textEditButton");
+        } 
+        return textEditButton;
+    };    
+
+    /**
+     * Update the textEditButton text with the current value of the field.
+     * 
+     * @param {Object} textEditButton the textEditButton
+     * @param {String} model The current value of the inline editable text
+     * @param {Object} strings Text option for the textEditButton
+     */
+    fluid.inlineEdit.updateTextEditButton = function (textEditButton, value, stringTemplate) {
+        var buttonText = fluid.stringTemplate(stringTemplate, {
+            text: value
+        });
+        textEditButton.text(buttonText);
+    };
+    
+    /**
+     * Bind mouse hover event handler to the display mode container.  
+     * 
+     * @param {Object} displayModeRenderer The display mode container
+     * @param {String} invitationStyle The default styling for the display mode container on mouse hover
+     */
+    fluid.inlineEdit.bindHoverHandlers = function (displayModeRenderer, invitationStyle) {
+        var over = function (evt) {
+            displayModeRenderer.addClass(invitationStyle);
+        };     
+        var out = function (evt) {
+            displayModeRenderer.removeClass(invitationStyle);
+        };
+        displayModeRenderer.hover(over, out);
+    };    
+    
+    /**
+     * Bind keyboard focus and blur event handlers to an element
+     * 
+     * @param {Object} element The element to which the event handlers are bound
+     * @param {Object} displayModeRenderer The display mode container
+     * @param {Ojbect} styles The default styling for the display mode container on mouse hover
+     */    
+    fluid.inlineEdit.bindHighlightHandler = function (element, displayModeRenderer, styles) {
+        element = $(element);
+        
+        var focusOn = function () {
+            displayModeRenderer.addClass(styles.focus);
+            displayModeRenderer.addClass(styles.invitation);
+        };
+        var focusOff = function () {
+            displayModeRenderer.removeClass(styles.focus);
+            displayModeRenderer.removeClass(styles.invitation);
+        };
+        
+        element.focus(focusOn);
+        element.blur(focusOff);
+    };        
+    
+    /**
+     * Bind mouse click handler to an element
+     * 
+     * @param {Object} element The element to which the event handler is bound
+     * @param {Object} edit Function to invoke the edit mode
+     * 
+     * @return {boolean} Returns false if entering edit mode
+     */
+    fluid.inlineEdit.bindMouseHandlers = function (element, edit) {
+        element = $(element);
+        
+        var triggerGuard = fluid.inlineEdit.makeEditTriggerGuard(element, edit);
+        element.click(function (e) {
+            triggerGuard(e);
+            return false;
+        });
+    };
+
+    /**
+     * Bind keyboard press handler to an element
+     * 
+     * @param {Object} element The element to which the event handler is bound
+     * @param {Object} edit Function to invoke the edit mode
+     * 
+     * @return {boolean} Returns false if entering edit mode
+     */    
+    fluid.inlineEdit.bindKeyboardHandlers = function (element, edit) {
+        element = $(element);
+        element.attr("role", "button");
+        
+        var guard = fluid.inlineEdit.makeEditTriggerGuard(element, edit);
+        fluid.activatable(element, function (event) {
+            return guard(event);
+        });
+    };
+    
+    /**
+     * Creates an event handler that will trigger the edit mode if caused by something other
+     * than standard HTML controls. The event handler will return false if entering edit mode.
+     * 
+     * @param {Object} element The element to trigger the edit mode
+     * @param {Object} edit Function to invoke the edit mode
+     * 
+     * @return {function} The event handler function
+     */    
+    fluid.inlineEdit.makeEditTriggerGuard = function (element, edit) {
+        var selector = fluid.unwrap(element);
+        return function (event) {
+            // FLUID-2017 - avoid triggering edit mode when operating standard HTML controls. Ultimately this
+            // might need to be extensible, in more complex authouring scenarios.
+            var outer = fluid.findAncestor(event.target, function (elem) {
+                if (/input|select|textarea|button|a/i.test(elem.nodeName) || elem === selector) {
+                    return true; 
+                }
+            });
+            if (outer === selector) {
+                edit();
+                return false;
+            }
+        };
+    };
+    
+    /**
+     * Render the display mode view.  
+     * 
+     * @return {jQuery} The display container containing the display text and 
+     *                             textEditbutton for display mode view
+     */
+    fluid.inlineEdit.defaultDisplayModeRenderer = function (that) {
+        var styles = that.options.styles;
+        
+        var displayModeWrapper = fluid.inlineEdit.setupDisplayModeContainer(styles);
+        var displayModeRenderer = that.viewEl.wrap(displayModeWrapper).parent();
+        
+        that.textEditButton = fluid.inlineEdit.setupTextEditButton(that);
+        displayModeRenderer.append(that.textEditButton);
+        
+        // Add event handlers.
+        fluid.inlineEdit.bindHoverHandlers(displayModeRenderer, styles.invitation);
+        fluid.inlineEdit.bindMouseHandlers(that.viewEl, that.edit);
+        fluid.inlineEdit.bindMouseHandlers(that.textEditButton, that.edit);
+        fluid.inlineEdit.bindKeyboardHandlers(that.textEditButton, that.edit);
+        fluid.inlineEdit.bindHighlightHandler(that.viewEl, displayModeRenderer, styles);
+        fluid.inlineEdit.bindHighlightHandler(that.textEditButton, displayModeRenderer, styles);
+        
+        return displayModeRenderer;
+    };    
+    
+    fluid.inlineEdit.standardAccessor = function (element) {
+        var nodeName = element.nodeName.toLowerCase();
+        var func = "input" === nodeName || "textarea" === nodeName ? "val" : "text";
+        return {
+            value: function (newValue) {
+                return $(element)[func](newValue);
+            }
+        };
+    };
+    
+    fluid.inlineEdit.standardDisplayView = function (viewEl) {
+        var that = {
+            refreshView: function (componentThat, source) {
+                if (componentThat.model.value) {
+                    showEditedText(componentThat);
+                } else if (componentThat.options.defaultViewText) {
+                    showDefaultViewText(componentThat);
+                } else {
+                    showNothing(componentThat);
+                }
+                // If necessary, pad the view element enough that it will be evident to the user.
+                if ($.trim(componentThat.viewEl.text()).length === 0) {
+                    componentThat.viewEl.addClass(componentThat.options.styles.emptyDefaultViewText);
+                    
+                    if(componentThat.existingPadding < componentThat.options.paddings.minimumView) {
+                        componentThat.viewEl.css('padding-right', componentThat.options.paddings.minimumView);
+                    }
+                }
+            }
+        };
+        return that;
+    };
+    
+    fluid.inlineEdit.standardEditView = function (editField) {
+        var that = {
+            refreshView: function (componentThat, source) {
+                if (!source || componentThat.editField && componentThat.editField.index(source) === -1) {
+                    componentThat.editView.value(componentThat.model.value);
+                }
+            }
+        };
+        $.extend(true, that, fluid.inlineEdit.standardAccessor(editField));
+        return that;
+    };
+    
+    /**
+     * Instantiates a list of InlineEdit components.
+     * 
+     * @param {Object} componentContainer the element containing the inline editors
+     * @param {Object} options configuration options for the components
+     */
+    fluid.inlineEdits = function (componentContainer, options) {
+        options = options || {};
+        var selectors = $.extend({}, fluid.defaults("inlineEdits").selectors, options.selectors);
+        
+        // Bind to the DOM.
+        var container = fluid.container(componentContainer);
+        var editables = $(selectors.editables, container);
+        
+        return setupInlineEdits(editables, options);
+    };
+    
+    fluid.defaults("inlineEdit", {  
+        selectors: {
+            text: ".flc-inlineEdit-text",
+            editContainer: ".flc-inlineEdit-editContainer",
+            edit: ".flc-inlineEdit-edit",
+            textEditButton: ".flc-inlineEdit-textEditButton"
+        },
+        
+        styles: {
+            text: "fl-inlineEdit-text",
+            edit: "fl-inlineEdit-edit",
+            invitation: "fl-inlineEdit-invitation",
+            defaultViewStyle: "fl-inlineEdit-emptyText-invitation",
+            emptyDefaultViewText: "fl-inlineEdit-emptyDefaultViewText",
+            focus: "fl-inlineEdit-focus",
+            tooltip: "fl-inlineEdit-tooltip",
+            editModeInstruction: "fl-inlineEdit-editModeInstruction",
+            displayView: "fl-inlineEdit-simple-editableText fl-inlineEdit-textContainer",
+            textEditButton: "fl-offScreen-hidden"
+        },
+        
+        events: {
+            modelChanged: null,
+            onBeginEdit: "preventable",
+            afterBeginEdit: null,
+            onFinishEdit: "preventable",
+            afterFinishEdit: null,
+            afterInitEdit: null
+        },
+
+        strings: {
+            textEditButton: "Edit text %text",
+            editModeInstruction: "Escape to cancel, Enter or Tab when finished"
+        },
+        
+        paddings: {
+            edit: 10,
+            minimumEdit: 80,
+            minimumView: 60
+        },
+        
+        applyEditPadding: true,
+        
+        blurHandlerBinder: null,
+        
+        // set this to true or false to cause unconditional submission, otherwise it will
+        // be inferred from the edit element tag type.
+        submitOnEnter: undefined,
+        
+        modelComparator: null,
+        
+        displayAccessor: {
+            type: "fluid.inlineEdit.standardAccessor"
+        },
+        
+        displayView: {
+            type: "fluid.inlineEdit.standardDisplayView"
+        },
+        
+        editAccessor: {
+            type: "fluid.inlineEdit.standardAccessor"
+        },
+        
+        editView: {
+            type: "fluid.inlineEdit.standardEditView"
+        },
+        
+        displayModeRenderer: fluid.inlineEdit.defaultDisplayModeRenderer,
+            
+        editModeRenderer: fluid.inlineEdit.defaultEditModeRenderer,
+        
+        lazyEditView: false,
+        
+        // this is here for backwards API compatibility, but should be in the strings block
+        defaultViewText: "Click here to edit",
+
+        /** View Mode Tooltip Settings **/
+        useTooltip: true,
+        
+        // this is here for backwards API compatibility, but should be in the strings block
+        tooltipText: "Select or press Enter to edit",
+        
+        tooltipDelay: 1000,
+
+        selectOnEdit: false        
+    });
+    
+    fluid.defaults("inlineEdits", {
+        selectors: {
+            editables: ".flc-inlineEditable"
+        }
+    });
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/inlineEdit/js/InlineEditIntegrations.js b/docs/jscripts/infusion/components/inlineEdit/js/InlineEditIntegrations.js
new file mode 100644 (file)
index 0000000..1c30a2b
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global setTimeout*/
+/*global jQuery, fluid_1_3, fluid*/
+/*global tinyMCE, FCKeditor, FCKeditorAPI, CKEDITOR*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    /*************************************
+     * Shared Rich Text Editor functions *
+     *************************************/
+     
+    fluid.inlineEdit.makeViewAccessor = function (editorGetFn, setValueFn, getValueFn) {
+        return function (editField) {
+            return {
+                value: function (newValue) {
+                    var editor = editorGetFn(editField);
+                    if (!editor) {
+                        if (newValue) {
+                            $(editField).val(newValue);
+                        }
+                        return "";
+                    }
+                    if (newValue) {
+                        setValueFn(editField, editor, newValue);
+                    }
+                    else {
+                        return getValueFn(editor);
+                    }
+                }
+            };
+        };
+    };
+    
+    fluid.inlineEdit.richTextViewAccessor = function (element) {
+        return {
+            value: function (newValue) {
+                return $(element).html(newValue);
+            }
+        };
+    };        
+    
+    var configureInlineEdit = function (configurationName, container, options) {
+        var defaults = fluid.defaults(configurationName); 
+        var assembleOptions = fluid.merge(defaults ? defaults.mergePolicy: null, {}, defaults, options);
+        return fluid.inlineEdit(container, assembleOptions);
+    };
+
+    fluid.inlineEdit.normalizeHTML = function (value) {
+        var togo = $.trim(value.replace(/\s+/g, " "));
+        togo = togo.replace(/\s+<\//g, "</");
+        togo = togo.replace(/\<(\S+)[^\>\s]*\>/g, function (match) {
+            return match.toLowerCase();
+        });
+        return togo;
+    };
+    
+    fluid.inlineEdit.htmlComparator = function (el1, el2) {
+        return fluid.inlineEdit.normalizeHTML(el1) ===
+           fluid.inlineEdit.normalizeHTML(el2);
+    };
+    
+    fluid.inlineEdit.bindRichTextHighlightHandler = function (element, displayModeRenderer, invitationStyle) {
+        element = $(element);
+        
+        var focusOn = function () {
+            displayModeRenderer.addClass(invitationStyle);
+        };
+        var focusOff = function () {
+            displayModeRenderer.removeClass(invitationStyle);
+        };
+        
+        element.focus(focusOn);
+        element.blur(focusOff);
+    };        
+    
+    fluid.inlineEdit.setupRichTextEditButton = function (that) {
+        var opts = that.options;
+        var textEditButton = that.locate("textEditButton");
+        
+        if  (textEditButton.length === 0) {
+            var markup = $("<a href='#_' class='flc-inlineEdit-textEditButton'></a>");
+            markup.text(opts.strings.textEditButton);
+            
+            that.locate("text").after(markup);
+            
+            // Refresh the textEditButton with the newly appended options
+            textEditButton = that.locate("textEditButton");
+        } 
+        return textEditButton;
+    };    
+    
+    /**
+     * Wrap the display text and the textEditButton with the display mode container  
+     * for better style control.
+     */
+    fluid.inlineEdit.richTextDisplayModeRenderer = function (that) {
+        var styles = that.options.styles;
+        
+        var displayModeWrapper = fluid.inlineEdit.setupDisplayModeContainer(styles);
+        var displayModeRenderer = that.viewEl.wrap(displayModeWrapper).parent();
+        
+        that.textEditButton = fluid.inlineEdit.setupRichTextEditButton(that);
+        displayModeRenderer.append(that.textEditButton);
+        displayModeRenderer.addClass(styles.focus);
+        
+        // Add event handlers.
+        fluid.inlineEdit.bindHoverHandlers(displayModeRenderer, styles.invitation);
+        fluid.inlineEdit.bindMouseHandlers(that.textEditButton, that.edit);
+        fluid.inlineEdit.bindKeyboardHandlers(that.textEditButton, that.edit);
+        fluid.inlineEdit.bindRichTextHighlightHandler(that.viewEl, displayModeRenderer, styles.invitation);
+        fluid.inlineEdit.bindRichTextHighlightHandler(that.textEditButton, displayModeRenderer, styles.invitation);
+        
+        return displayModeRenderer;
+    };        
+
+   
+    /************************
+     * Tiny MCE Integration *
+     ************************/
+    
+    /**
+     * Instantiate a rich-text InlineEdit component that uses an instance of TinyMCE.
+     * 
+     * @param {Object} componentContainer the element containing the inline editors
+     * @param {Object} options configuration options for the components
+     */
+    fluid.inlineEdit.tinyMCE = function (container, options) {
+        var inlineEditor = configureInlineEdit("fluid.inlineEdit.tinyMCE", container, options);
+        tinyMCE.init(inlineEditor.options.tinyMCE);
+        return inlineEditor;
+    };
+        
+    fluid.inlineEdit.tinyMCE.getEditor = function (editField) {
+        return tinyMCE.get(editField.id);
+    };
+    
+    fluid.inlineEdit.tinyMCE.setValue = function (editField, editor, value) {
+        // without this, there is an intermittent race condition if the editor has been created on this event.
+        $(editField).val(value); 
+        editor.setContent(value, {format : 'raw'});
+    };
+    
+    fluid.inlineEdit.tinyMCE.getValue = function (editor) {
+        return editor.getContent();
+    };
+    
+    var flTinyMCE = fluid.inlineEdit.tinyMCE; // Shorter alias for awfully long fully-qualified names.
+    flTinyMCE.viewAccessor = fluid.inlineEdit.makeViewAccessor(flTinyMCE.getEditor, 
+                                                               flTinyMCE.setValue,
+                                                               flTinyMCE.getValue);
+   
+    fluid.inlineEdit.tinyMCE.blurHandlerBinder = function (that) {
+        function focusEditor(editor) {
+            setTimeout(function () {
+                tinyMCE.execCommand('mceFocus', false, that.editField[0].id);
+                if ($.browser.mozilla && $.browser.version.substring(0, 3) === "1.8") {
+                    // Have not yet found any way to make this work on FF2.x - best to do nothing,
+                    // for FLUID-2206
+                    //var body = editor.getBody();
+                    //fluid.setCaretToEnd(body.firstChild, "");
+                    return;
+                }
+                editor.selection.select(editor.getBody(), 1);
+                editor.selection.collapse(0);
+            }, 10);
+        }
+        
+        that.events.afterInitEdit.addListener(function (editor) {
+            focusEditor(editor);
+            var editorBody = editor.getBody();
+
+            // NB - this section has no effect - on most browsers no focus events
+            // are delivered to the actual body
+            fluid.deadMansBlur(that.editField, 
+                {exclusions: {body: $(editorBody)}, 
+                 handler: function () {
+                     that.cancel();
+                 }
+                 });
+        });
+            
+        that.events.afterBeginEdit.addListener(function () {
+            var editor = tinyMCE.get(that.editField[0].id);
+            if (editor) {
+                focusEditor(editor);
+            } 
+        });
+    };
+   
+    fluid.inlineEdit.tinyMCE.editModeRenderer = function (that) {
+        var options = that.options.tinyMCE;
+        options.elements = fluid.allocateSimpleId(that.editField);
+        var oldinit = options.init_instance_callback;
+        
+        options.init_instance_callback = function (instance) {
+            that.events.afterInitEdit.fire(instance);
+            if (oldinit) {
+                oldinit();
+            }
+        };
+        
+        tinyMCE.init(options);
+    };
+    
+    fluid.defaults("fluid.inlineEdit.tinyMCE", {
+        tinyMCE : {
+            mode: "exact", 
+            theme: "simple"
+        },
+        useTooltip: true,
+        selectors: {
+            edit: "textarea" 
+        },
+        styles: {
+            invitation: "fl-inlineEdit-richText-invitation",
+            displayView: "fl-inlineEdit-textContainer",
+            text: ""
+                
+        },
+        strings: {
+            textEditButton: "Edit"
+        },
+        displayAccessor: {
+            type: "fluid.inlineEdit.richTextViewAccessor"
+        },
+        editAccessor: {
+            type: "fluid.inlineEdit.tinyMCE.viewAccessor"
+        },
+        lazyEditView: true,
+        defaultViewText: "Click Edit",
+        modelComparator: fluid.inlineEdit.htmlComparator,
+        blurHandlerBinder: fluid.inlineEdit.tinyMCE.blurHandlerBinder,
+        displayModeRenderer: fluid.inlineEdit.richTextDisplayModeRenderer,
+        editModeRenderer: fluid.inlineEdit.tinyMCE.editModeRenderer
+    });
+    
+    
+    /*****************************
+     * FCKEditor 2.x Integration *
+     *****************************/
+         
+    /**
+     * Instantiate a rich-text InlineEdit component that uses an instance of FCKeditor.
+     * Support for FCKEditor 2.x is now deprecated. We recommend the use of the simpler and more
+     * accessible CKEditor 3 instead.
+     * 
+     * @param {Object} componentContainer the element containing the inline editors
+     * @param {Object} options configuration options for the components
+     */
+    fluid.inlineEdit.FCKEditor = function (container, options) {
+        return configureInlineEdit("fluid.inlineEdit.FCKEditor", container, options);
+    };
+    
+    fluid.inlineEdit.FCKEditor.getEditor = function (editField) {
+        var editor = typeof(FCKeditorAPI) === "undefined" ? null: FCKeditorAPI.GetInstance(editField.id);
+        return editor;
+    };
+    
+    fluid.inlineEdit.FCKEditor.complete = fluid.event.getEventFirer();
+    
+    fluid.inlineEdit.FCKEditor.complete.addListener(function (editor) {
+        var editField = editor.LinkedField;
+        var that = $.data(editField, "fluid.inlineEdit.FCKEditor");
+        if (that && that.events) {
+            that.events.afterInitEdit.fire(editor);
+        }
+    });
+    
+    fluid.inlineEdit.FCKEditor.blurHandlerBinder = function (that) {
+        function focusEditor(editor) {
+            editor.Focus(); 
+        }
+        
+        that.events.afterInitEdit.addListener(
+            function (editor) {
+                focusEditor(editor);
+            }
+        );
+        that.events.afterBeginEdit.addListener(function () {
+            var editor = fluid.inlineEdit.FCKEditor.getEditor(that.editField[0]);
+            if (editor) {
+                focusEditor(editor);
+            } 
+        });
+
+    };
+    
+    fluid.inlineEdit.FCKEditor.editModeRenderer = function (that) {
+        var id = fluid.allocateSimpleId(that.editField);
+        $.data(fluid.unwrap(that.editField), "fluid.inlineEdit.FCKEditor", that);
+        var oFCKeditor = new FCKeditor(id);
+        // The Config object and the FCKEditor object itself expose different configuration sets,
+        // which possess a member "BasePath" with different meanings. Solve FLUID-2452, FLUID-2438
+        // by auto-inferring the inner path for Config (method from http://drupal.org/node/344230 )
+        var opcopy = fluid.copy(that.options.FCKEditor);
+        opcopy.BasePath = opcopy.BasePath + "editor/";
+        $.extend(true, oFCKeditor.Config, opcopy);
+        // somehow, some properties like Width and Height are set on the object itself
+
+        $.extend(true, oFCKeditor, that.options.FCKEditor);
+        oFCKeditor.Config.fluidInstance = that;
+        oFCKeditor.ReplaceTextarea();
+    };
+
+    fluid.inlineEdit.FCKEditor.setValue = function (editField, editor, value) {
+        editor.SetHTML(value);
+    };
+    
+    fluid.inlineEdit.FCKEditor.getValue = function (editor) {
+        return editor.GetHTML();
+    };
+    
+    var flFCKEditor = fluid.inlineEdit.FCKEditor;
+    
+    flFCKEditor.viewAccessor = fluid.inlineEdit.makeViewAccessor(flFCKEditor.getEditor,
+                                                                 flFCKEditor.setValue,
+                                                                 flFCKEditor.getValue);
+    
+    fluid.defaults("fluid.inlineEdit.FCKEditor", {
+        selectors: {
+            edit: "textarea" 
+        },
+        styles: {
+            invitation: "fl-inlineEdit-richText-invitation",
+            displayView: "fl-inlineEdit-textContainer",
+            text: ""
+        },
+        strings: {
+            textEditButton: "Edit"
+        },        
+        displayAccessor: {
+            type: "fluid.inlineEdit.richTextViewAccessor"
+        },
+        editAccessor: {
+            type: "fluid.inlineEdit.FCKEditor.viewAccessor"
+        },
+        lazyEditView: true,
+        defaultViewText: "Click Edit",
+        modelComparator: fluid.inlineEdit.htmlComparator,
+        blurHandlerBinder: fluid.inlineEdit.FCKEditor.blurHandlerBinder,
+        displayModeRenderer: fluid.inlineEdit.richTextDisplayModeRenderer,
+        editModeRenderer: fluid.inlineEdit.FCKEditor.editModeRenderer,
+        FCKEditor: {
+            BasePath: "fckeditor/"    
+        }
+    });
+    
+    
+    /****************************
+     * CKEditor 3.x Integration *
+     ****************************/
+    
+    fluid.inlineEdit.CKEditor = function (container, options) {
+        return configureInlineEdit("fluid.inlineEdit.CKEditor", container, options);
+    };
+    
+    fluid.inlineEdit.CKEditor.getEditor = function (editField) {
+        return CKEDITOR.instances[editField.id];
+    };
+    
+    fluid.inlineEdit.CKEditor.setValue = function (editField, editor, value) {
+        editor.setData(value);
+    };
+    
+    fluid.inlineEdit.CKEditor.getValue = function (editor) {
+        return editor.getData();
+    };
+    
+    var flCKEditor = fluid.inlineEdit.CKEditor;
+    flCKEditor.viewAccessor = fluid.inlineEdit.makeViewAccessor(flCKEditor.getEditor,
+                                                                flCKEditor.setValue,
+                                                                flCKEditor.getValue);
+                             
+    fluid.inlineEdit.CKEditor.focus = function (editor) {
+        setTimeout(function () {
+            // CKEditor won't focus itself except in a timeout.
+            editor.focus();
+        }, 0);
+    };
+    
+    // Special hacked HTML normalisation for CKEditor which spuriously inserts whitespace
+    // just after the first opening tag
+    fluid.inlineEdit.CKEditor.normalizeHTML = function (value) {
+        var togo = fluid.inlineEdit.normalizeHTML(value);
+        var angpos = togo.indexOf(">");
+        if (angpos !== -1 && angpos < togo.length - 1) {
+            if (togo.charAt(angpos + 1) !== " ") {
+                togo = togo.substring(0, angpos + 1) + " " + togo.substring(angpos + 1);
+            }
+        }
+        return togo;
+    };
+    
+    fluid.inlineEdit.CKEditor.htmlComparator = function (el1, el2) {
+        return fluid.inlineEdit.CKEditor.normalizeHTML(el1) ===
+           fluid.inlineEdit.CKEditor.normalizeHTML(el2);
+    };
+                                    
+    fluid.inlineEdit.CKEditor.blurHandlerBinder = function (that) {
+        that.events.afterInitEdit.addListener(fluid.inlineEdit.CKEditor.focus);
+        that.events.afterBeginEdit.addListener(function () {
+            var editor = fluid.inlineEdit.CKEditor.getEditor(that.editField[0]);
+            if (editor) {
+                fluid.inlineEdit.CKEditor.focus(editor);
+            }
+        });
+    };
+    
+    fluid.inlineEdit.CKEditor.editModeRenderer = function (that) {
+        var id = fluid.allocateSimpleId(that.editField);
+        $.data(fluid.unwrap(that.editField), "fluid.inlineEdit.CKEditor", that);
+        var editor = CKEDITOR.replace(id, that.options.CKEditor);
+        editor.on("instanceReady", function (e) {
+            fluid.inlineEdit.CKEditor.focus(e.editor);
+            that.events.afterInitEdit.fire(e.editor);
+        });
+    };                                                     
+    
+    fluid.defaults("fluid.inlineEdit.CKEditor", {
+        selectors: {
+            edit: "textarea" 
+        },
+        styles: {
+            invitation: "fl-inlineEdit-richText-invitation",
+            displayView: "fl-inlineEdit-textContainer",
+            text: ""
+        },
+        strings: {
+            textEditButton: "Edit"
+        },        
+        displayAccessor: {
+            type: "fluid.inlineEdit.richTextViewAccessor"
+        },
+        editAccessor: {
+            type: "fluid.inlineEdit.CKEditor.viewAccessor"
+        },
+        lazyEditView: true,
+        defaultViewText: "Click Edit",
+        modelComparator: fluid.inlineEdit.CKEditor.htmlComparator,
+        blurHandlerBinder: fluid.inlineEdit.CKEditor.blurHandlerBinder,
+        displayModeRenderer: fluid.inlineEdit.richTextDisplayModeRenderer,
+        editModeRenderer: fluid.inlineEdit.CKEditor.editModeRenderer,
+        CKEditor: {
+            // CKEditor-specific configuration goes here.
+        }
+    });
+    
+    /************************
+     * Dropdown Integration *
+     ************************/    
+    /**
+     * Instantiate a drop-down InlineEdit component
+     * 
+     * @param {Object} container
+     * @param {Object} options
+     */
+    fluid.inlineEdit.dropdown = function (container, options) {
+        return configureInlineEdit("fluid.inlineEdit.dropdown", container, options);
+    };
+
+    fluid.inlineEdit.dropdown.editModeRenderer = function (that) {
+        var id = fluid.allocateSimpleId(that.editField);
+        that.editField.selectbox({
+            finishHandler: function () {
+                that.finish();
+            }
+        });
+        return {
+            container: that.editContainer,
+            field: $("input.selectbox", that.editContainer) 
+        };
+    };
+   
+    fluid.inlineEdit.dropdown.blurHandlerBinder = function (that) {
+        fluid.deadMansBlur(that.editField, {
+             exclusions: {selectBox: $("div.selectbox-wrapper li", that.editContainer)},
+             handler: function () {
+                that.cancel();
+                }
+            });
+    };
+    
+    fluid.defaults("fluid.inlineEdit.dropdown", {
+        applyEditPadding: false,
+        blurHandlerBinder: fluid.inlineEdit.dropdown.blurHandlerBinder,
+        editModeRenderer: fluid.inlineEdit.dropdown.editModeRenderer
+    });
+})(jQuery, fluid_1_3);
+
+
+// This must be written outside any scope as a result of the FCKEditor event model.
+// Do not overwrite this function, if you wish to add your own listener to FCK completion,
+// register it with the standard fluid event firer at fluid.inlineEdit.FCKEditor.complete
+function FCKeditor_OnComplete(editorInstance) {
+    fluid.inlineEdit.FCKEditor.complete.fire(editorInstance);
+}
diff --git a/docs/jscripts/infusion/components/progress/js/Progress.js b/docs/jscripts/infusion/components/progress/js/Progress.js
new file mode 100644 (file)
index 0000000..d14b62d
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010-2011 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {    
+    
+    var animateDisplay = function (elm, animation, defaultAnimation) {
+        animation = (animation) ? animation : defaultAnimation;
+        elm.animate(animation.params, animation.duration, animation.callback);
+    };
+    
+    var animateProgress = function (elm, width, speed) {
+        // de-queue any left over animations
+        elm.queue("fx", []); 
+        
+        elm.animate({ 
+            width: width,
+            queue: false
+        }, 
+        speed);
+    };
+    
+    var showProgress = function (that, animation) {
+        if (animation === false) {
+            that.displayElement.show();
+        } else {
+            animateDisplay(that.displayElement, animation, that.options.showAnimation);
+        }
+    };
+    
+    var hideProgress = function (that, delay, animation) {
+        
+        delay = (delay === null || isNaN(delay)) ? that.options.delay : delay;
+        
+        if (delay) {
+            // use a setTimeout to delay the hide for n millies, note use of recursion
+            var timeOut = setTimeout(function () {
+                hideProgress(that, 0, animation);
+            }, delay);
+        } else {
+            if (animation === false) {
+                that.displayElement.hide();
+            } else {
+                animateDisplay(that.displayElement, animation, that.options.hideAnimation);
+            }
+        }   
+    };
+    
+    var updateWidth = function (that, newWidth, dontAnimate) {
+        dontAnimate  = dontAnimate || false;
+        var currWidth = that.indicator.width();
+        var direction = that.options.animate;
+        if ((newWidth > currWidth) && (direction === "both" || direction === "forward") && !dontAnimate) {
+            animateProgress(that.indicator, newWidth, that.options.speed);
+        } else if ((newWidth < currWidth) && (direction === "both" || direction === "backward") && !dontAnimate) {
+            animateProgress(that.indicator, newWidth, that.options.speed);
+        } else {
+            that.indicator.width(newWidth);
+        }
+    };
+         
+    var percentToPixels = function (that, percent) {
+        // progress does not support percents over 100, also all numbers are rounded to integers
+        return Math.round((Math.min(percent, 100) * that.progressBar.width()) / 100);
+    };
+    
+    var refreshRelativeWidth = function (that)  {
+        var pixels = Math.max(percentToPixels(that, parseFloat(that.storedPercent)), that.options.minWidth);
+        updateWidth(that, pixels, true);
+    };
+        
+    var initARIA = function (ariaElement, ariaBusyText) {
+        ariaElement.attr("role", "progressbar");
+        ariaElement.attr("aria-valuemin", "0");
+        ariaElement.attr("aria-valuemax", "100");
+        ariaElement.attr("aria-valuenow", "0");
+        //Empty value for ariaBusyText will default to aria-valuenow.
+        if (ariaBusyText) {
+            ariaElement.attr("aria-valuetext", "");
+        }
+        ariaElement.attr("aria-busy", "false");
+    };
+    
+    var updateARIA = function (that, percent) {
+        var str = that.options.strings;
+        var busy = percent < 100 && percent > 0;
+        that.ariaElement.attr("aria-busy", busy);
+        that.ariaElement.attr("aria-valuenow", percent);   
+        //Empty value for ariaBusyText will default to aria-valuenow.
+        if (str.ariaBusyText) {
+            if (busy) {
+                var busyString = fluid.stringTemplate(str.ariaBusyText, {percentComplete : percent});           
+                that.ariaElement.attr("aria-valuetext", busyString);
+            } else if (percent === 100) {
+                // FLUID-2936: JAWS doesn't currently read the "Progress is complete" message to the user, even though we set it here.
+                that.ariaElement.attr("aria-valuetext", str.ariaDoneText);
+            }
+        }
+    };
+        
+    var updateText = function (label, value) {
+        label.html(value);
+    };
+    
+    var repositionIndicator = function (that) {
+        that.indicator.css("top", that.progressBar.position().top)
+            .css("left", 0)
+            .height(that.progressBar.height());
+        refreshRelativeWidth(that);
+    };
+        
+    var updateProgress = function (that, percent, labelText, animationForShow) {
+        
+        // show progress before updating, jQuery will handle the case if the object is already displayed
+        showProgress(that, animationForShow);
+            
+        // do not update if the value of percent is falsey
+        if (percent !== null) {
+            that.storedPercent = percent;
+        
+            var pixels = Math.max(percentToPixels(that, parseFloat(percent)), that.options.minWidth);   
+            updateWidth(that, pixels);
+        }
+        
+        if (labelText !== null) {
+            updateText(that.label, labelText);
+        }
+        
+        // update ARIA
+        if (that.ariaElement) {
+            updateARIA(that, percent);
+        }
+    };
+        
+    var setupProgress = function (that) {
+        that.displayElement = that.locate("displayElement");
+
+        // hide file progress in case it is showing
+        if (that.options.initiallyHidden) {
+            that.displayElement.hide();
+        }
+
+        that.progressBar = that.locate("progressBar");
+        that.label = that.locate("label");
+        that.indicator = that.locate("indicator");
+        that.ariaElement = that.locate("ariaElement");
+        
+        that.indicator.width(that.options.minWidth);
+
+        that.storedPercent = 0;
+                
+        // initialize ARIA
+        if (that.ariaElement) {
+            initARIA(that.ariaElement, that.options.strings.ariaBusyText);
+        }
+        
+        // afterProgressHidden:  
+        // Registering listener with the callback provided by the user and reinitializing
+        // the event trigger function. 
+        // Note: callback depricated as of 1.5, use afterProgressHidden event
+        if (that.options.hideAnimation.callback) {
+            that.events.afterProgressHidden.addListener(that.options.hideAnimation.callback);           
+        }
+        
+        // triggers the afterProgressHidden event    
+        // Note: callback depricated as of 1.5, use afterProgressHidden event
+        that.options.hideAnimation.callback = that.events.afterProgressHidden.fire;
+
+        
+        // onProgressBegin:
+        // Registering listener with the callback provided by the user and reinitializing
+        // the event trigger function.  
+        // Note: callback depricated as of 1.5, use onProgressBegin event
+        if (that.options.showAnimation.callback) {
+            that.events.onProgressBegin.addListener(that.options.showAnimation.callback);                      
+        } 
+            
+        // triggers the onProgressBegin event
+        // Note: callback depricated as of 1.5, use onProgressBegin event
+        that.options.showAnimation.callback = that.events.onProgressBegin.fire;
+    };
+           
+    /**
+    * Instantiates a new Progress component.
+    * 
+    * @param {jQuery|Selector|Element} container the DOM element in which the Uploader lives
+    * @param {Object} options configuration options for the component.
+    */
+    fluid.progress = function (container, options) {
+        var that = fluid.initView("fluid.progress", container, options);
+        setupProgress(that);
+        
+        /**
+         * Shows the progress bar if is currently hidden.
+         * 
+         * @param {Object} animation a custom animation used when showing the progress bar
+         */
+        that.show = function (animation) {
+            showProgress(that, animation);
+        };
+        
+        /**
+         * Hides the progress bar if it is visible.
+         * 
+         * @param {Number} delay the amount of time to wait before hiding
+         * @param {Object} animation a custom animation used when hiding the progress bar
+         */
+        that.hide = function (delay, animation) {
+            hideProgress(that, delay, animation);
+        };
+        
+        /**
+         * Updates the state of the progress bar.
+         * This will automatically show the progress bar if it is currently hidden.
+         * Percentage is specified as a decimal value, but will be automatically converted if needed.
+         * 
+         * 
+         * @param {Number|String} percentage the current percentage, specified as a "float-ish" value 
+         * @param {String} labelValue the value to set for the label; this can be an HTML string
+         * @param {Object} animationForShow the animation to use when showing the progress bar if it is hidden
+         */
+        that.update = function (percentage, labelValue, animationForShow) {
+            updateProgress(that, percentage, labelValue, animationForShow);
+        };
+        
+        that.refreshView = function () {
+            repositionIndicator(that);
+        };
+                        
+        return that;  
+    };
+      
+    fluid.defaults("fluid.progress", {  
+        selectors: {
+            displayElement: ".flc-progress", // required, the element that gets displayed when progress is displayed, could be the indicator or bar or some larger outer wrapper as in an overlay effect
+            progressBar: ".flc-progress-bar", //required
+            indicator: ".flc-progress-indicator", //required
+            label: ".flc-progress-label", //optional
+            ariaElement: ".flc-progress-bar" // usually required, except in cases where there are more than one progressor for the same data such as a total and a sub-total
+        },
+        
+        strings: {
+            //Empty value for ariaBusyText will default to aria-valuenow.
+            ariaBusyText: "Progress is %percentComplete percent complete",
+            ariaDoneText: "Progress is complete."
+        },
+        
+        // progress display and hide animations, use the jQuery animation primatives, set to false to use no animation
+        // animations must be symetrical (if you hide with width, you'd better show with width) or you get odd effects
+        // see jQuery docs about animations to customize
+        showAnimation: {
+            params: {
+                opacity: "show"
+            }, 
+            duration: "slow",
+            //callback has been deprecated and will be removed as of 1.5, instead use onProgressBegin event 
+            callback: null 
+        }, // equivalent of $().fadeIn("slow")
+        
+        hideAnimation: {
+            params: {
+                opacity: "hide"
+            }, 
+            duration: "slow", 
+            //callback has been deprecated and will be removed as of 1.5, instead use afterProgressHidden event 
+            callback: null
+        }, // equivalent of $().fadeOut("slow")
+        
+        events: {            
+            onProgressBegin: null,
+            afterProgressHidden: null            
+        },
+
+        minWidth: 5, // 0 length indicators can look broken if there is a long pause between updates
+        delay: 0, // the amount to delay the fade out of the progress
+        speed: 200, // default speed for animations, pretty fast
+        animate: "forward", // suppport "forward", "backward", and "both", any other value is no animation either way
+        initiallyHidden: true, // supports progress indicators which may always be present
+        updatePosition: false
+    });
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/reorderer/css/ImageReorderer.css b/docs/jscripts/infusion/components/reorderer/css/ImageReorderer.css
new file mode 100644 (file)
index 0000000..a9118ce
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This version of ImageReorderer.css has been deprecated as of Infusion 1.1.2, 
+ * and will be replaced in a future version of Infusion with the newer, fresher, simpler stylesheet 
+ * showcased in the Image Reorderer demo. 
+ * 
+ * If you're just starting to use the Image Reorderer, we recommend you use the demo styles instead. 
+ * See the CSS file located at: src/webapp/demos/reorderer/imageReorderer/css/imageReorderer.css
+ */
+
+.fl-reorderer-image-container{
+  overflow: hidden; width: 100%;
+  margin-top: 50px;
+}
+.fl-reorderer-image-container img {
+    border: 0px;
+}
+.fl-reorderer-image-inner-container{
+  height: 100px;
+  width: 140px;
+  background-color: #eee;
+  overflow: hidden;
+  margin: 0 auto 3px auto;
+}
+
+.fl-reorderer-image-inner-container img{
+  width: 150px;
+}
+
+.fl-reorderer-movable-default, .fl-reorderer-movable-selected{
+    height: 150px !important;
+    width: 150px;
+    border: 2px solid #ddd;
+    float: left;
+    margin: 5px !important;
+    text-align: center;
+    padding-top: 10px;
+}
+
+.fl-reorderer-movable-hover {
+       border-color:#666;
+}
+
+.fl-reorderer-movable-selected {
+    border-color:#666;
+    background-color: #ddd;
+    cursor: move;
+}
+
+.fl-reorderer-movable-selected img {
+  border: 1px solid #eee;
+}
+
+.fl-reorderer-movable-dragging{
+       height: 150px !important;
+       width: 150px;
+       border: 2px solid #666;
+       background-color: #eee;
+       float: left;
+       margin: 5px !important;
+       text-align: center;
+       opacity: .2;
+       filter: alpha(opacity=20);
+       padding-top: 10px;
+}
+
+.fl-reorderer-imageTitle{
+       margin-top:5px;
+       height: 40px;
+       overflow: hidden;
+}
+
+.fl-reorderer-imageTitle a{
+  text-decoration: none;
+}
+
+.fl-reorderer-imageTitle a:hover{
+  text-decoration: underline;
+}
+
+div .fl-reorderer-dropMarker {
+  height: 160px !important;
+  margin:0 -2px;
+  padding:0 2px;  
+  background-color: red;
+  float: left;
+}
+
+.fl-reorderer-instructions{
+       color: #666;
+       background-color: lightyellow;
+       padding: 3px;
+       border: 2px solid #ddd;
+       font-size: .9em;
+       position: absolute;
+       top: 0;
+       right: 0;
+       width: 340px;
+}
+
+.fl-reorderer-downarrow{
+       font-size: 0px; line-height: 0%; width: 0px;
+       margin: 5px 8px 30px 0;
+       float: left;
+       border-top: 10px solid red;
+       border-left: 10px solid lightyellow;
+       border-right: 10px solid lightyellow;
+}
+
+.fl-reorderer-screen-reader-instructions {
+    position:absolute;
+    left:0px;
+    top:-500px;
+    width:1px;
+    height:1px;
+    overflow:hidden;
+}
\ No newline at end of file
diff --git a/docs/jscripts/infusion/components/reorderer/css/Reorderer.css b/docs/jscripts/infusion/components/reorderer/css/Reorderer.css
new file mode 100644 (file)
index 0000000..01c16ed
--- /dev/null
@@ -0,0 +1,73 @@
+.fl-reorderer-instructions {
+    background-color:#F5FFB0;
+    border:1px solid #FFE390;
+    margin:5px;
+    padding:10px;
+}
+.fl-reorderer-instructions strong {
+    display:block;
+}
+/* Base Appearance + States */
+.fl-reorderer-movable-default {
+    cursor:move;
+} 
+
+/* automatically applied the keyboard selected re-orderable item */
+.fl-reorderer-movable-selected {
+       background-color: lightyellow;
+}
+
+/* automatically applied as the mouse hovers over re-orderable item */
+.fl-reorderer-movable-hover {
+    background-color: lightyellow; /* !important;;*/
+}
+
+/* automatically applied the keyboard selected re-orderable item */
+.fl-reorderer-movable-dragging {
+    background     :#ccc;    
+}
+
+.fl-reorderer-avatar {
+    cursor          : move;
+    list-style-type : none;
+    filter          : alpha(opacity=75);
+    opacity         : 0.75;    
+}
+
+
+.fl-reorderer-dropWarning {
+    display : none;
+    padding: 5px 25px; 
+    color : #fff;
+    opacity:1;
+    filter:alpha(opacity=100);
+    background: #900 url('../images/themes/_common/exclamation.png') no-repeat 5px center;
+}
+
+/* DROP MARKER: different layouts might require different drop marker appearances */
+/* Horizontal drop marker: designed for vertical layouts (default) */
+.fl-reorderer-verticalLayout .fl-reorderer-dropMarker, 
+.fluid-vertical-order .fl-reorderer-dropMarker{
+    background-color: #F00;
+    height          : 0;
+    padding         : 2px 0 0 0;
+    border-width    : 0;
+    margin          : -2px 0 0 0;
+    list-style-type : none;
+    font-size       : 0;
+    line-height     : 0;
+    overflow        : hidden;
+}
+/* Vertical drop marker: designed for grid or horizontal layouts */
+.fl-reorderer-horizontalLayout .fl-reorderer-dropMarker,
+.fluid-horizontal-order .fl-reorderer-dropMarker {
+    background-color: #F00;
+    padding         : 0 3px;
+    border-width    : 0;
+    margin          : 0 -3px !important;
+    list-style-type : none;
+    overflow        : hidden;
+    width           : 0 !important;
+}
+
+/* Themed Appearance + States */
diff --git a/docs/jscripts/infusion/components/reorderer/images/Banana.jpg b/docs/jscripts/infusion/components/reorderer/images/Banana.jpg
new file mode 100644 (file)
index 0000000..7967d44
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Banana.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Blackberry.jpg b/docs/jscripts/infusion/components/reorderer/images/Blackberry.jpg
new file mode 100644 (file)
index 0000000..159da7d
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Blackberry.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Cherry.jpg b/docs/jscripts/infusion/components/reorderer/images/Cherry.jpg
new file mode 100644 (file)
index 0000000..d89e4dd
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Cherry.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Dragonfruit.jpg b/docs/jscripts/infusion/components/reorderer/images/Dragonfruit.jpg
new file mode 100644 (file)
index 0000000..0845cf6
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Dragonfruit.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Fig.jpg b/docs/jscripts/infusion/components/reorderer/images/Fig.jpg
new file mode 100644 (file)
index 0000000..a6f6366
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Fig.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Grapes.jpg b/docs/jscripts/infusion/components/reorderer/images/Grapes.jpg
new file mode 100644 (file)
index 0000000..eb940e0
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Grapes.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Kiwano.jpg b/docs/jscripts/infusion/components/reorderer/images/Kiwano.jpg
new file mode 100644 (file)
index 0000000..a45b187
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Kiwano.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Kiwi.jpg b/docs/jscripts/infusion/components/reorderer/images/Kiwi.jpg
new file mode 100644 (file)
index 0000000..3df27b8
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Kiwi.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Kumquat.jpg b/docs/jscripts/infusion/components/reorderer/images/Kumquat.jpg
new file mode 100644 (file)
index 0000000..96db6fa
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Kumquat.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Lemon.jpg b/docs/jscripts/infusion/components/reorderer/images/Lemon.jpg
new file mode 100644 (file)
index 0000000..d0bd7c7
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Lemon.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Mangosteen.jpg b/docs/jscripts/infusion/components/reorderer/images/Mangosteen.jpg
new file mode 100644 (file)
index 0000000..e416ac9
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Mangosteen.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Orange.jpg b/docs/jscripts/infusion/components/reorderer/images/Orange.jpg
new file mode 100644 (file)
index 0000000..35ce31a
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Orange.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/RedApple.jpg b/docs/jscripts/infusion/components/reorderer/images/RedApple.jpg
new file mode 100644 (file)
index 0000000..17368bf
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/RedApple.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/images/Tamarillo.jpg b/docs/jscripts/infusion/components/reorderer/images/Tamarillo.jpg
new file mode 100644 (file)
index 0000000..59ce319
Binary files /dev/null and b/docs/jscripts/infusion/components/reorderer/images/Tamarillo.jpg differ
diff --git a/docs/jscripts/infusion/components/reorderer/js/GeometricManager.js b/docs/jscripts/infusion/components/reorderer/js/GeometricManager.js
new file mode 100644 (file)
index 0000000..175a855
--- /dev/null
@@ -0,0 +1,627 @@
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+/*global fluid_1_3*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.orientation = {
+        HORIZONTAL: 4,
+        VERTICAL: 1
+    };
+    
+    fluid.rectSides = {
+        // agree with fluid.orientation
+        4: ["left", "right"],
+        1: ["top", "bottom"],
+        // agree with fluid.direction
+        8: "top",
+        12: "bottom",
+        2: "left",
+        3: "right"
+    };
+    
+    /**
+     * This is the position, relative to a given drop target, that a dragged item should be dropped.
+     */
+    fluid.position = {
+        BEFORE: -1,
+        AFTER: 1,
+        INSIDE: 2,
+        REPLACE: 3
+    };
+    
+    /**
+     * For incrementing/decrementing a count or index, or moving in a rectilinear direction.
+     */
+    fluid.direction = {
+        NEXT: 1,
+        PREVIOUS: -1,
+        UP: 8,
+        DOWN: 12,
+        LEFT: 2,
+        RIGHT: 3
+    };
+    
+    fluid.directionSign = function (direction) {
+        return direction === fluid.direction.UP || direction === fluid.direction.LEFT? 
+             fluid.direction.PREVIOUS : fluid.direction.NEXT;
+    };
+    
+    fluid.directionAxis = function (direction) {
+        return direction === fluid.direction.LEFT || direction === fluid.direction.RIGHT?
+            0 : 1; 
+    };
+    
+    fluid.directionOrientation = function (direction) {
+        return fluid.directionAxis(direction)? fluid.orientation.VERTICAL : fluid.orientation.HORIZONTAL;
+    };
+    
+    fluid.keycodeDirection = {
+        up: fluid.direction.UP,
+        down: fluid.direction.DOWN,
+        left: fluid.direction.LEFT,
+        right: fluid.direction.RIGHT
+    };
+    
+    // moves a single node in the DOM to a new position relative to another
+    fluid.moveDom = function (source, target, position) {
+        source = fluid.unwrap(source);
+        target = fluid.unwrap(target);
+        
+        var scan;
+        // fluid.log("moveDom source " + fluid.dumpEl(source) + " target " + fluid.dumpEl(target) + " position " + position);     
+        if (position === fluid.position.INSIDE) {
+            target.appendChild(source);
+        }
+        else if (position === fluid.position.BEFORE) {
+            for (scan = target.previousSibling; ; scan = scan.previousSibling) {
+                if (!scan || !fluid.dom.isIgnorableNode(scan)) {
+                    if (scan !== source) {
+                        fluid.dom.cleanseScripts(source);
+                        target.parentNode.insertBefore(source, target);    
+                    }
+                    break;
+                }
+            }
+        }
+        else if (position === fluid.position.AFTER) {
+            for (scan = target.nextSibling; ; scan = scan.nextSibling) {
+                if (!scan || !fluid.dom.isIgnorableNode(scan)) {
+                    if (scan !== source) {
+                        fluid.dom.cleanseScripts(source);
+                        fluid.dom.insertAfter(source, target);
+                    }
+                    break;
+                }
+            }
+        }
+        else {
+            fluid.fail("Unrecognised position supplied to fluid.moveDom: " + position);
+        }
+    };
+    
+    fluid.normalisePosition = function (position, samespan, targeti, sourcei) {
+        // convert a REPLACE into a primitive BEFORE/AFTER
+        if (position === fluid.position.REPLACE) {
+            position = samespan && targeti >= sourcei? fluid.position.AFTER: fluid.position.BEFORE;
+        }
+        return position;
+    };
+    
+    fluid.permuteDom = function (element, target, position, sourceelements, targetelements) {
+        element = fluid.unwrap(element);
+        target = fluid.unwrap(target);
+        var sourcei = $.inArray(element, sourceelements);
+        if (sourcei === -1) {
+            fluid.fail("Error in permuteDom: source element " + fluid.dumpEl(element) 
+               + " not found in source list " + fluid.dumpEl(sourceelements));
+        }
+        var targeti = $.inArray(target, targetelements);
+        if (targeti === -1) {
+            fluid.fail("Error in permuteDom: target element " + fluid.dumpEl(target) 
+               + " not found in source list " + fluid.dumpEl(targetelements));
+        }
+        var samespan = sourceelements === targetelements;
+        position = fluid.normalisePosition(position, samespan, targeti, sourcei);
+
+        //fluid.log("permuteDom sourcei " + sourcei + " targeti " + targeti);
+        // cache the old neighbourhood of the element for the final move
+        var oldn = {};
+        oldn[fluid.position.AFTER] = element.nextSibling;
+        oldn[fluid.position.BEFORE] = element.previousSibling;
+        fluid.moveDom(sourceelements[sourcei], targetelements[targeti], position);
+        
+        // perform the leftward-moving, AFTER shift
+        var frontlimit = samespan? targeti - 1: sourceelements.length - 2;
+        var i;
+        if (position === fluid.position.BEFORE && samespan) { 
+            // we cannot do skip processing if the element was "fused against the grain" 
+            frontlimit--;
+        }
+        if (!samespan || targeti > sourcei) {
+            for (i = frontlimit; i > sourcei; -- i) {
+                fluid.moveDom(sourceelements[i + 1], sourceelements[i], fluid.position.AFTER);
+            }
+            if (sourcei + 1 < sourceelements.length) {
+                fluid.moveDom(sourceelements[sourcei + 1], oldn[fluid.position.AFTER], fluid.position.BEFORE);
+            }
+        }
+        // perform the rightward-moving, BEFORE shift
+        var backlimit = samespan? sourcei - 1: targetelements.length - 1;
+        if (position === fluid.position.AFTER) { 
+            // we cannot do skip processing if the element was "fused against the grain" 
+            targeti++;
+        }
+        if (!samespan || targeti < sourcei) {
+            for (i = targeti; i < backlimit; ++ i) {
+                fluid.moveDom(targetelements[i], targetelements[i + 1], fluid.position.BEFORE);
+            }
+            if (backlimit >= 0 && backlimit < targetelements.length - 1) {
+                fluid.moveDom(targetelements[backlimit], oldn[fluid.position.BEFORE], fluid.position.AFTER);
+            }                
+        }
+
+    };
+  
+    var curCss = function (a, name) {
+        return window.getComputedStyle? window.getComputedStyle(a, null).getPropertyValue(name) : 
+          a.currentStyle[name];
+    };
+    
+    var isAttached = function (node) {
+        while (node && node.nodeName) {
+            if (node.nodeName === "BODY") {
+                return true;
+            }
+            node = node.parentNode;
+        }
+        return false;
+    };
+    
+    var generalHidden = function (a) {
+        return "hidden" === a.type || curCss(a, "display") === "none" || curCss(a, "visibility") === "hidden" || !isAttached(a);
+    };
+    
+
+    var computeGeometry = function (element, orientation, disposition) {
+        var elem = {};
+        elem.element = element;
+        elem.orientation = orientation;
+        if (disposition === fluid.position.INSIDE) {
+            elem.position = disposition;
+        }
+        if (generalHidden(element)) {
+            elem.clazz = "hidden";
+        }
+        var pos = fluid.dom.computeAbsolutePosition(element) || [0, 0];
+        var width = element.offsetWidth;
+        var height = element.offsetHeight;
+        elem.rect = {left: pos[0], top: pos[1]};
+        elem.rect.right = pos[0] + width;
+        elem.rect.bottom = pos[1] + height;
+        return elem;
+    };
+    
+    // A "suitable large" value for the sentinel blocks at the ends of spans
+    var SENTINEL_DIMENSION = 10000;
+
+    function dumprect(rect) {
+        return "Rect top: " + rect.top +
+                 " left: " + rect.left + 
+               " bottom: " + rect.bottom +
+                " right: " + rect.right;
+    }
+
+    function dumpelem(cacheelem) {
+        if (!cacheelem || !cacheelem.rect) {
+            return "null";
+        } else {
+            return dumprect(cacheelem.rect) + " position: " +
+            cacheelem.position +
+            " for " +
+            fluid.dumpEl(cacheelem.element);
+        }
+    }
+    
+   
+    
+    fluid.dropManager = function () { 
+        var targets = [];
+        var cache = {};
+        var that = {};        
+        
+        var lastClosest;              
+        var lastGeometry;
+        var displacementX, displacementY;
+        
+        that.updateGeometry = function (geometricInfo) {
+            lastGeometry = geometricInfo;
+            targets = [];
+            cache = {};
+            var mapper = geometricInfo.elementMapper;
+            for (var i = 0; i < geometricInfo.extents.length; ++ i) {
+                var thisInfo = geometricInfo.extents[i];
+                var orientation = thisInfo.orientation;
+                var sides = fluid.rectSides[orientation];
+                
+                var processElement = function (element, sentB, sentF, disposition, j) {
+                    var cacheelem = computeGeometry(element, orientation, disposition);
+                    cacheelem.owner = thisInfo;
+                    if (cacheelem.clazz !== "hidden" && mapper) {
+                        cacheelem.clazz = mapper(element);
+                    }
+                    cache[fluid.dropManager.cacheKey(element)] = cacheelem;
+                    var backClass = fluid.dropManager.getRelativeClass(thisInfo.elements, j, fluid.position.BEFORE, cacheelem.clazz, mapper); 
+                    var frontClass = fluid.dropManager.getRelativeClass(thisInfo.elements, j, fluid.position.AFTER, cacheelem.clazz, mapper); 
+                    if (disposition === fluid.position.INSIDE) {
+                        targets[targets.length] = cacheelem;
+                    }
+                    else {
+                        fluid.dropManager.splitElement(targets, sides, cacheelem, disposition, backClass, frontClass);
+                    }
+                    // deal with sentinel blocks by creating near-copies of the end elements
+                    if (sentB && geometricInfo.sentinelize) {
+                        fluid.dropManager.sentinelizeElement(targets, sides, cacheelem, 1, disposition, backClass);
+                    }
+                    if (sentF && geometricInfo.sentinelize) {
+                        fluid.dropManager.sentinelizeElement(targets, sides, cacheelem, 0, disposition, frontClass);
+                    }
+                    //fluid.log(dumpelem(cacheelem));
+                    return cacheelem;
+                };
+                
+                var allHidden = true;
+                for (var j = 0; j < thisInfo.elements.length; ++ j) {
+                    var element = thisInfo.elements[j];
+                    var cacheelem = processElement(element, j === 0, j === thisInfo.elements.length - 1, 
+                            fluid.position.INTERLEAVED, j);
+                    if (cacheelem.clazz !== "hidden") {
+                        allHidden = false;
+                    }
+                }
+                if (allHidden && thisInfo.parentElement) {
+                    processElement(thisInfo.parentElement, true, true, 
+                            fluid.position.INSIDE);
+                }
+            }   
+        };
+        
+        that.startDrag = function (event, handlePos, handleWidth, handleHeight) {
+            var handleMidX = handlePos[0] + handleWidth / 2;
+            var handleMidY = handlePos[1] + handleHeight / 2;
+            var dX = handleMidX - event.pageX;
+            var dY = handleMidY - event.pageY;
+            that.updateGeometry(lastGeometry);
+            lastClosest = null;
+            displacementX = dX;
+            displacementY = dY;
+            $("body").bind("mousemove.fluid-dropManager", that.mouseMove);
+        };
+        
+        that.lastPosition = function () {
+            return lastClosest;
+        };
+        
+        that.endDrag = function () {
+            $("body").unbind("mousemove.fluid-dropManager");
+        };
+        
+        that.mouseMove = function (evt) {
+            var x = evt.pageX + displacementX;
+            var y = evt.pageY + displacementY;
+            //fluid.log("Mouse x " + x + " y " + y );
+            
+            var closestTarget = that.closestTarget(x, y, lastClosest);
+            if (closestTarget && closestTarget !== fluid.dropManager.NO_CHANGE) {
+                lastClosest = closestTarget;
+              
+                that.dropChangeFirer.fire(closestTarget);
+            }
+        };
+        
+        that.dropChangeFirer = fluid.event.getEventFirer();
+        
+        var blankHolder = {
+            element: null
+        };
+        
+        that.closestTarget = function (x, y, lastClosest) {
+            var mindistance = Number.MAX_VALUE;
+            var minelem = blankHolder;
+            var minlockeddistance = Number.MAX_VALUE;
+            var minlockedelem = blankHolder;
+            for (var i = 0; i < targets.length; ++ i) {
+                var cacheelem = targets[i];
+                if (cacheelem.clazz === "hidden") {
+                    continue;
+                }
+                var distance = fluid.geom.minPointRectangle(x, y, cacheelem.rect);
+                if (cacheelem.clazz === "locked") {
+                    if (distance < minlockeddistance) {
+                        minlockeddistance = distance;
+                        minlockedelem = cacheelem;
+                    }
+                } else {
+                    if (distance < mindistance) {
+                        mindistance = distance;
+                        minelem = cacheelem;
+                    }
+                    if (distance === 0) {
+                        break;
+                    }
+                }
+            }
+            if (!minelem) {
+                return minelem;
+            }
+            if (minlockeddistance >= mindistance) {
+                minlockedelem = blankHolder;
+            }
+            //fluid.log("PRE: mindistance " + mindistance + " element " + 
+            //   fluid.dumpEl(minelem.element) + " minlockeddistance " + minlockeddistance
+            //    + " locked elem " + dumpelem(minlockedelem));
+            if (lastClosest && lastClosest.position === minelem.position &&
+                fluid.unwrap(lastClosest.element) === fluid.unwrap(minelem.element) &&
+                fluid.unwrap(lastClosest.lockedelem) === fluid.unwrap(minlockedelem.element)
+                ) {
+                return fluid.dropManager.NO_CHANGE;
+            }
+            //fluid.log("mindistance " + mindistance + " minlockeddistance " + minlockeddistance);
+            return {
+                position: minelem.position,
+                element: minelem.element,
+                lockedelem: minlockedelem.element
+            };
+        };
+        
+        that.shuffleProjectFrom = function (element, direction, includeLocked, disableWrap) {
+            var togo = that.projectFrom(element, direction, includeLocked, disableWrap);
+            if (togo) {
+                togo.position = fluid.position.REPLACE;
+            }
+            return togo;
+        };
+        
+        that.projectFrom = function (element, direction, includeLocked, disableWrap) {
+            that.updateGeometry(lastGeometry);
+            var cacheelem = cache[fluid.dropManager.cacheKey(element)];
+            var projected = fluid.geom.projectFrom(cacheelem.rect, direction, targets, includeLocked, disableWrap);
+            if (!projected.cacheelem) {
+                return null;
+            }
+            var retpos = projected.cacheelem.position;
+            return {element: projected.cacheelem.element, 
+                     position: retpos? retpos : fluid.position.BEFORE 
+                     };
+        };
+        
+        that.logicalFrom = function (element, direction, includeLocked, disableWrap) {
+            var orderables = that.getOwningSpan(element, fluid.position.INTERLEAVED, includeLocked);
+            return {element: fluid.dropManager.getRelativeElement(element, direction, orderables, disableWrap), 
+                position: fluid.position.REPLACE};
+        };
+           
+        that.lockedWrapFrom = function (element, direction, includeLocked, disableWrap) {
+            var base = that.logicalFrom(element, direction, includeLocked, disableWrap);
+            var selectables = that.getOwningSpan(element, fluid.position.INTERLEAVED, includeLocked);
+            var allElements = cache[fluid.dropManager.cacheKey(element)].owner.elements;
+            if (includeLocked || selectables[0] === allElements[0]) {
+                return base;
+            }
+            var directElement = fluid.dropManager.getRelativeElement(element, direction, allElements, disableWrap);
+            if (lastGeometry.elementMapper(directElement) === "locked") {
+                base.element = null;
+                base.clazz = "locked";  
+            }
+            return base;
+        }; 
+        
+        that.getOwningSpan = function (element, position, includeLocked) {
+            var owner = cache[fluid.dropManager.cacheKey(element)].owner; 
+            var elements = position === fluid.position.INSIDE? [owner.parentElement] : owner.elements;
+            if (!includeLocked && lastGeometry.elementMapper) {
+                elements = $.makeArray(elements);
+                fluid.remove_if(elements, function (element) {
+                    return lastGeometry.elementMapper(element) === "locked";
+                });
+            }
+            return elements;
+        };
+        
+        that.geometricMove = function (element, target, position) {
+            var sourceElements = that.getOwningSpan(element, null, true);
+            var targetElements = that.getOwningSpan(target, position, true);
+            fluid.permuteDom(element, target, position, sourceElements, targetElements);
+        };              
+        
+        return that;
+    };    
+   
+    fluid.dropManager.NO_CHANGE = "no change";
+    
+    fluid.dropManager.cacheKey = function (element) {
+        return fluid.allocateSimpleId(element);
+    };
+    
+    fluid.dropManager.sentinelizeElement = function (targets, sides, cacheelem, fc, disposition, clazz) {
+        var elemCopy = $.extend(true, {}, cacheelem);
+        elemCopy.rect[sides[fc]] = elemCopy.rect[sides[1 - fc]] + (fc? 1: -1);
+        elemCopy.rect[sides[1 - fc]] = (fc? -1 : 1) * SENTINEL_DIMENSION;
+        elemCopy.position = disposition === fluid.position.INSIDE?
+           disposition : (fc? fluid.position.BEFORE : fluid.position.AFTER);
+        elemCopy.clazz = clazz;
+        targets[targets.length] = elemCopy;
+    };
+    
+    fluid.dropManager.splitElement = function (targets, sides, cacheelem, disposition, clazz1, clazz2) {
+        var elem1 = $.extend(true, {}, cacheelem);
+        var elem2 = $.extend(true, {}, cacheelem);
+        var midpoint = (elem1.rect[sides[0]] + elem1.rect[sides[1]]) / 2;
+        elem1.rect[sides[1]] = midpoint; 
+        elem1.position = fluid.position.BEFORE;
+        
+        elem2.rect[sides[0]] = midpoint; 
+        elem2.position = fluid.position.AFTER;
+        
+        elem1.clazz = clazz1;
+        elem2.clazz = clazz2;
+        targets[targets.length] = elem1;
+        targets[targets.length] = elem2;
+    };
+    
+    // Expand this configuration point if we ever go back to a full "permissions" model
+    fluid.dropManager.getRelativeClass = function (thisElements, index, relative, thisclazz, mapper) {
+        index += relative;
+        if (index < 0 && thisclazz === "locked") {
+            return "locked";
+        }
+        if (index >= thisElements.length || mapper === null) {
+            return null;
+        } else {
+            relative = thisElements[index];
+            return mapper(relative) === "locked" && thisclazz === "locked" ? "locked" : null;
+        }
+    };
+    
+     fluid.dropManager.getRelativeElement = function (element, direction, elements, disableWrap) {
+        var folded = fluid.directionSign(direction);
+  
+        var index = $(elements).index(element) + folded;
+        if (index < 0) {
+            index += elements.length;
+        }
+        
+        // disable wrap
+        if (disableWrap) {                   
+            if (index === elements.length || index === (elements.length + folded)) {
+                return element;
+            }
+        }
+                      
+        index %= elements.length;
+        return elements[index];              
+    };
+    
+    fluid.geom = fluid.geom || {};
+    
+    // These distance algorithms have been taken from
+    // http://www.cs.mcgill.ca/~cs644/Godfried/2005/Fall/fzamal/concepts.htm
+    
+    /** Returns the minimum squared distance between a point and a rectangle **/
+    fluid.geom.minPointRectangle = function (x, y, rectangle) {
+        var dx = x < rectangle.left? (rectangle.left - x) : 
+                  (x > rectangle.right? (x - rectangle.right) : 0);
+        var dy = y < rectangle.top? (rectangle.top - y) : 
+                  (y > rectangle.bottom? (y - rectangle.bottom) : 0);
+        return dx * dx + dy * dy;
+    };
+    
+    /** Returns the minimum squared distance between two rectangles **/
+    fluid.geom.minRectRect = function (rect1, rect2) {
+        var dx = rect1.right < rect2.left? rect2.left - rect1.right : 
+                 rect2.right < rect1.left? rect1.left - rect2.right :0;
+        var dy = rect1.bottom < rect2.top? rect2.top - rect1.bottom : 
+                 rect2.bottom < rect1.top? rect1.top - rect2.bottom :0;
+        return dx * dx + dy * dy;
+    };
+    
+    var makePenCollect = function () {
+        return {
+            mindist: Number.MAX_VALUE,
+            minrdist: Number.MAX_VALUE
+        };
+    };
+
+    /** Determine the one amongst a set of rectangle targets which is the "best fit"
+     * for an axial motion from a "base rectangle" (commonly arising from the case
+     * of cursor key navigation).
+     * @param {Rectangle} baserect The base rectangl from which the motion is to be referred
+     * @param {fluid.direction} direction  The direction of motion
+     * @param {Array of Rectangle holders} targets An array of objects "cache elements" 
+     * for which the member <code>rect</code> is the holder of the rectangle to be tested.
+     * @param disableWrap which is used to enable or disable wrapping of elements
+     * @return The cache element which is the most appropriate for the requested motion.
+     */
+    fluid.geom.projectFrom = function (baserect, direction, targets, forSelection, disableWrap) {
+        var axis = fluid.directionAxis(direction);
+        var frontSide = fluid.rectSides[direction];
+        var backSide = fluid.rectSides[axis * 15 + 5 - direction];
+        var dirSign = fluid.directionSign(direction);
+        
+        var penrect = {left: (7 * baserect.left + 1 * baserect.right) / 8,
+                       right: (5 * baserect.left + 3 * baserect.right) / 8,
+                       top: (7 * baserect.top + 1 * baserect.bottom) / 8,
+                       bottom: (5 * baserect.top + 3 * baserect.bottom) / 8};
+         
+        penrect[frontSide] = dirSign * SENTINEL_DIMENSION;
+        penrect[backSide] = -penrect[frontSide];
+        
+        function accPen(collect, cacheelem, backSign) {
+            var thisrect = cacheelem.rect;
+            var pdist = fluid.geom.minRectRect(penrect, thisrect);
+            var rdist = -dirSign * backSign * (baserect[backSign === 1 ? frontSide:backSide] 
+                                             - thisrect[backSign === 1 ? backSide:frontSide]);
+            // fluid.log("pdist: " + pdist + " rdist: " + rdist);
+            // the oddity in the rdist comparison is intended to express "half-open"-ness of rectangles
+            // (backSign === 1? 0 : 1) - this is now gone - must be possible to move to perpendicularly abutting regions
+            if (pdist <= collect.mindist && rdist >= 0) {
+                if (pdist === collect.mindist && rdist * backSign > collect.minrdist) {
+                    return;
+                }
+                collect.minrdist = rdist * backSign;
+                collect.mindist = pdist;
+                collect.minelem = cacheelem;
+            }
+        }
+        var collect = makePenCollect();
+        var backcollect = makePenCollect();
+        var lockedcollect = makePenCollect();
+
+        for (var i = 0; i < targets.length; ++ i) {
+            var elem = targets[i];
+            var isPure = elem.owner && elem.element === elem.owner.parentElement;
+            if (elem.clazz === "hidden" || forSelection && isPure) {
+                continue;
+            }
+            else if (!forSelection && elem.clazz === "locked") {
+                accPen(lockedcollect, elem, 1);
+            }
+            else {
+                accPen(collect, elem, 1);
+                accPen(backcollect, elem, -1);
+            }
+            //fluid.log("Element " + i + " " + dumpelem(elem) + " mindist " + collect.mindist);
+        }
+        var wrap = !collect.minelem || backcollect.mindist < collect.mindist ;
+        
+        // disable wrap
+        wrap = wrap && !disableWrap;       
+                
+        var mincollect = wrap? backcollect: collect;        
+        
+        var togo = {
+            wrapped: wrap,
+            cacheelem: mincollect.minelem
+        };
+        if (lockedcollect.mindist < mincollect.mindist) {
+            togo.lockedelem = lockedcollect.minelem;
+        }
+        return togo;
+    };
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/reorderer/js/ImageReorderer.js b/docs/jscripts/infusion/components/reorderer/js/ImageReorderer.js
new file mode 100644 (file)
index 0000000..5e39bd2
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2009 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    var deriveLightboxCellBase = function (namebase, index) {
+        return namebase + "lightbox-cell:" + index + ":";
+    };
+            
+    var addThumbnailActivateHandler = function (container) {
+        var enterKeyHandler = function (evt) {
+            if (evt.which === fluid.reorderer.keys.ENTER) {
+                var thumbnailAnchors = $("a", evt.target);
+                document.location = thumbnailAnchors.attr("href");
+            }
+        };
+        
+        container.keypress(enterKeyHandler);
+    };
+    
+    // Custom query method seeks all tags descended from a given root with a 
+    // particular tag name, whose id matches a regex.
+    var seekNodesById = function (rootnode, tagname, idmatch) {
+        var inputs = rootnode.getElementsByTagName(tagname);
+        var togo = [];
+        for (var i = 0; i < inputs.length; i += 1) {
+            var input = inputs[i];
+            var id = input.id;
+            if (id && id.match(idmatch)) {
+                togo.push(input);
+            }
+        }
+        return togo;
+    };
+    
+    var createImageCellFinder = function (parentNode, containerId) {
+        parentNode = fluid.unwrap(parentNode);
+        
+        var lightboxCellNamePattern = "^" + deriveLightboxCellBase(containerId, "[0-9]+") + "$";
+        
+        return function () {
+            // This orderable finder assumes that the lightbox thumbnails are 'div' elements
+            return seekNodesById(parentNode, "div", lightboxCellNamePattern);
+        };
+    };
+    
+    var seekForm = function (container) {
+        return fluid.findAncestor(container, function (element) {
+            return $(element).is("form");
+        });
+    };
+    
+    var seekInputs = function (container, reorderform) {
+        return seekNodesById(reorderform, 
+                             "input", 
+                             "^" + deriveLightboxCellBase(container.attr("id"), "[^:]*") + "reorder-index$");
+    };
+    
+    var mapIdsToNames = function (container, reorderform) {
+        var inputs = seekInputs(container, reorderform);
+        for (var i = 0; i < inputs.length; i++) {
+            var input = inputs[i];
+            var name = input.name;
+            input.name = name || input.id;
+        }
+    };
+    
+    /**
+     * Returns a default afterMove listener using the id-based, form-driven scheme for communicating with the server.
+     * It is implemented by nesting hidden form fields inside each thumbnail container. The value of these form elements
+     * represent the order for each image. This default listener submits the form's default 
+     * action via AJAX.
+     * 
+     * @param {jQueryable} container the Image Reorderer's container element 
+     */
+    var createIDAfterMoveListener = function (container) {
+        var reorderform = seekForm(container);
+        mapIdsToNames(container, reorderform);
+        
+        return function () {
+            var inputs, i;
+            inputs = seekInputs(container, reorderform);
+            
+            for (i = 0; i < inputs.length; i += 1) {
+                inputs[i].value = i;
+            }
+        
+            if (reorderform && reorderform.action) {
+                var order = $(reorderform).serialize();
+                $.post(reorderform.action, 
+                       order,
+                       function (type, data, evt) { /* No-op response */ });
+            }
+        };
+    };
+
+    
+    var setDefaultValue = function (target, path, value) {
+        var previousValue = fluid.get(target, path);
+        var valueToSet = previousValue || value;
+        fluid.set(target, path, valueToSet);
+    };
+    
+    // Public Lightbox API
+    /**
+     * Creates a new Lightbox instance from the specified parameters, providing full control over how
+     * the Lightbox is configured.
+     * 
+     * @param {Object} container 
+     * @param {Object} options 
+     */
+    fluid.reorderImages = function (container, options) {
+        // Instantiate a mini-Image Reorderer component, then feed its options to the real Reorderer.
+        var that = fluid.initView("fluid.reorderImages", container, options);
+        
+        // If the user didn't specify their own afterMove or movables options,
+        // set up defaults for them using the old id-based scheme.
+        // Backwards API compatiblity. Remove references to afterMoveCallback by Infusion 1.5.
+        setDefaultValue(that, "options.listeners.afterMove", 
+                        that.options.afterMoveCallback || createIDAfterMoveListener(that.container));
+        setDefaultValue(that, "options.selectors.movables", 
+                        createImageCellFinder(that.container, that.container.attr("id")));
+        
+        var reorderer = fluid.reorderer(that.container, that.options);
+        
+        fluid.tabindex($("a", that.container), -1);
+        addThumbnailActivateHandler(that.container);
+        
+        return reorderer;
+    };
+   
+    // This function now deprecated. Please use fluid.reorderImages() instead.
+    fluid.lightbox = fluid.reorderImages;
+    
+    fluid.defaults("fluid.reorderImages", {
+        layoutHandler: "fluid.gridLayoutHandler",
+
+        selectors: {
+            labelSource: ".flc-reorderer-imageTitle"
+        }
+    });
+
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/reorderer/js/LayoutReorderer.js b/docs/jscripts/infusion/components/reorderer/js/LayoutReorderer.js
new file mode 100644 (file)
index 0000000..ef44c2a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2009 University of Toronto
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    /**
+     * Simple way to create a layout reorderer.
+     * @param {selector} a jQueryable (selector, element, jQuery) for the layout container
+     * @param {Object} a map of selectors for columns and modules within the layout
+     * @param {Function} a function to be called when the order changes 
+     * @param {Object} additional configuration options
+     */
+    fluid.reorderLayout = function (container, userOptions) {
+        var assembleOptions = {
+            layoutHandler: "fluid.moduleLayoutHandler",
+            selectors: {
+                columns: ".flc-reorderer-column",
+                modules: ".flc-reorderer-module"
+            }
+        };
+        var options = $.extend(true, assembleOptions, userOptions);
+        return fluid.reorderer(container, options);
+    };    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/reorderer/js/ModuleLayout.js b/docs/jscripts/infusion/components/reorderer/js/ModuleLayout.js
new file mode 100644 (file)
index 0000000..5a9cca1
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2009 University of Toronto
+Copyright 2010 OCAD University
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+/*global fluid, fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.registerNamespace("fluid.moduleLayout");
+
+    /**
+     * Calculate the location of the item and the column in which it resides.
+     * @return  An object with column index and item index (within that column) properties.
+     *          These indices are -1 if the item does not exist in the grid.
+     */
+    // unsupported - NON-API function
+    fluid.moduleLayout.findColumnAndItemIndices = function (item, layout) {
+        return fluid.find(layout.columns,
+            function (column, colIndex) {
+                var index = $.inArray(item, column.elements);
+                return index === -1? undefined : {columnIndex: colIndex, itemIndex: index};
+            }, {columnIndex: -1, itemIndex: -1});
+    };
+    // unsupported - NON-API function
+    fluid.moduleLayout.findColIndex = function (item, layout) {
+        return fluid.find(layout.columns,
+            function (column, colIndex) {
+                return item === column.container? colIndex : undefined;
+            }, -1);
+    };
+
+    /**
+     * Move an item within the layout object. 
+     */
+    fluid.moduleLayout.updateLayout = function (item, target, position, layout) {
+        item = fluid.unwrap(item);
+        target = fluid.unwrap(target);
+        var itemIndices = fluid.moduleLayout.findColumnAndItemIndices(item, layout);
+        layout.columns[itemIndices.columnIndex].elements.splice(itemIndices.itemIndex, 1);
+        var targetCol;
+        if (position === fluid.position.INSIDE) {
+            targetCol = layout.columns[fluid.moduleLayout.findColIndex(target, layout)].elements;
+            targetCol.splice(targetCol.length, 0, item);
+
+        } else {
+            var relativeItemIndices = fluid.moduleLayout.findColumnAndItemIndices(target, layout);
+            targetCol = layout.columns[relativeItemIndices.columnIndex].elements;
+            position = fluid.normalisePosition(position, 
+                  itemIndices.columnIndex === relativeItemIndices.columnIndex, 
+                  relativeItemIndices.itemIndex, itemIndices.itemIndex);
+            var relative = position === fluid.position.BEFORE? 0 : 1;
+            targetCol.splice(relativeItemIndices.itemIndex + relative, 0, item);
+        }
+    };
+       
+    /**
+     * Builds a layout object from a set of columns and modules.
+     * @param {jQuery} container
+     * @param {jQuery} columns
+     * @param {jQuery} portlets
+     */
+    fluid.moduleLayout.layoutFromFlat = function (container, columns, portlets) {
+        var layout = {};
+        layout.container = container;
+        layout.columns = fluid.transform(columns, 
+            function (column) {
+                return {
+                    container: column,
+                    elements: $.makeArray(portlets.filter(function () {
+                          // is this a bug in filter? would have expected "this" to be 1st arg
+                        return fluid.dom.isContainer(column, this);
+                    }))
+                };
+            });
+        return layout;
+    };
+      
+    /**
+     * Builds a layout object from a serialisable "layout" object consisting of id lists
+     */
+    fluid.moduleLayout.layoutFromIds = function (idLayout) {
+        return {
+            container: fluid.byId(idLayout.id),
+            columns: fluid.transform(idLayout.columns, 
+                function (column) {
+                    return {
+                        container: fluid.byId(column.id),
+                        elements: fluid.transform(column.children, fluid.byId)
+                    };
+                })
+            };
+    };
+      
+    /**
+     * Serializes the current layout into a structure of ids
+     */
+    fluid.moduleLayout.layoutToIds = function (idLayout) {
+        return {
+            id: fluid.getId(idLayout.container),
+            columns: fluid.transform(idLayout.columns, 
+                function (column) {
+                    return {
+                        id: fluid.getId(column.container),
+                        children: fluid.transform(column.elements, fluid.getId)
+                    };
+                })
+            };
+    };
+    
+    var defaultOnShowKeyboardDropWarning = function (item, dropWarning) {
+        if (dropWarning) {
+            var offset = $(item).offset();
+            dropWarning = $(dropWarning);
+            dropWarning.css("position", "absolute");
+            dropWarning.css("top", offset.top);
+            dropWarning.css("left", offset.left);
+        }
+    };
+    
+    fluid.defaults(true, "fluid.moduleLayoutHandler", 
+        {orientation: fluid.orientation.VERTICAL,
+         containerRole: fluid.reorderer.roles.REGIONS,
+         selectablesTabindex: 0,
+         sentinelize:         true
+         });
+       
+    /**
+     * Module Layout Handler for reordering content modules.
+     * 
+     * General movement guidelines:
+     * 
+     * - Arrowing sideways will always go to the top (moveable) module in the column
+     * - Moving sideways will always move to the top available drop target in the column
+     * - Wrapping is not necessary at this first pass, but is ok
+     */
+    fluid.moduleLayoutHandler = function (container, options, dropManager, dom) {
+        var that = {};
+        
+        function computeLayout() {
+            var togo;
+            if (options.selectors.modules) {
+                togo = fluid.moduleLayout.layoutFromFlat(container, dom.locate("columns"), dom.locate("modules"));
+            }
+            if (!togo) {
+                var idLayout = fluid.get(options, "moduleLayout.layout");
+                fluid.moduleLayout.layoutFromIds(idLayout);
+            }
+            return togo;
+        }
+        var layout = computeLayout();
+        that.layout = layout;
+        
+        function isLocked(item) {
+            var lockedModules = options.selectors.lockedModules? dom.fastLocate("lockedModules") : [];
+            return $.inArray(item, lockedModules) !== -1;
+        }
+
+        that.getRelativePosition  = 
+           fluid.reorderer.relativeInfoGetter(options.orientation, 
+                 fluid.reorderer.WRAP_LOCKED_STRATEGY, fluid.reorderer.GEOMETRIC_STRATEGY, 
+                 dropManager, dom, options.disableWrap);
+                 
+        that.getGeometricInfo = function () {
+            var extents = [];
+            var togo = {extents: extents,
+                        sentinelize: options.sentinelize};
+            togo.elementMapper = function (element) {
+                return isLocked(element)? "locked" : null;
+            };
+            togo.elementIndexer = function (element) {
+                var indices = fluid.moduleLayout.findColumnAndItemIndices(element, that.layout);
+                return {
+                    index:        indices.itemIndex,
+                    length:       layout.columns[indices.columnIndex].elements.length,
+                    moduleIndex:  indices.columnIndex,
+                    moduleLength: layout.columns.length
+                };
+            };
+            for (var col = 0; col < layout.columns.length; col++) {
+                var column = layout.columns[col];
+                var thisEls = {
+                    orientation: options.orientation,
+                    elements: $.makeArray(column.elements),
+                    parentElement: column.container
+                };
+              //  fluid.log("Geometry col " + col + " elements " + fluid.dumpEl(thisEls.elements) + " isLocked [" + 
+              //       fluid.transform(thisEls.elements, togo.elementMapper).join(", ") + "]");
+                extents.push(thisEls);
+            }
+
+            return togo;
+        };
+        
+        function computeModules(all) {
+            return function () {
+                var modules = fluid.accumulate(layout.columns, function (column, list) {
+                    return list.concat(column.elements); // note that concat will not work on a jQuery
+                }, []);
+                if (!all) {
+                    fluid.remove_if(modules, isLocked);
+                }
+                return modules;
+            };
+        }
+        
+        that.returnedOptions = {
+            selectors: {
+                movables: computeModules(false),
+                dropTargets: computeModules(false),
+                selectables: computeModules(true)
+            },
+            listeners: {
+                onMove: {
+                    priority: "last",
+                    listener: function (item, requestedPosition) {
+                        fluid.moduleLayout.updateLayout(item, requestedPosition.element, requestedPosition.position, layout);
+                    }
+                },
+                onRefresh: function () {
+                    layout = computeLayout();
+                    that.layout = layout;
+                },
+                "onShowKeyboardDropWarning.setPosition": defaultOnShowKeyboardDropWarning
+            }
+        };
+        
+        that.getModel = function () {
+            return fluid.moduleLayout.layoutToIds(layout);
+        };
+              
+        return that;
+    };
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/reorderer/js/Reorderer.js b/docs/jscripts/infusion/components/reorderer/js/Reorderer.js
new file mode 100644 (file)
index 0000000..711f1f7
--- /dev/null
@@ -0,0 +1,837 @@
+/*
+Copyright 2007-2009 University of Toronto
+Copyright 2007-2010 University of Cambridge
+Copyright 2010 OCAD University
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery, fluid_1_3, document*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    var defaultAvatarCreator = function (item, cssClass, dropWarning) {
+        fluid.dom.cleanseScripts(fluid.unwrap(item));
+        var avatar = $(item).clone();
+        
+        fluid.dom.iterateDom(avatar.get(0), function (node) {
+            node.removeAttribute("id");
+            if (node.tagName.toLowerCase() === "input") {
+                node.setAttribute("disabled", "disabled");
+            }
+        });
+        
+        avatar.removeAttr("id");
+        avatar.removeClass("ui-droppable");
+        avatar.addClass(cssClass);
+        
+        if (dropWarning) {
+            // Will a 'div' always be valid in this position?
+            var avatarContainer = $(document.createElement("div"));
+            avatarContainer.append(avatar);
+            avatarContainer.append(dropWarning);
+            avatar = avatarContainer;
+        }
+        $("body").append(avatar);
+        if (!$.browser.safari) {
+            // FLUID-1597: Safari appears incapable of correctly determining the dimensions of elements
+            avatar.css("display", "block").width(item.offsetWidth).height(item.offsetHeight);
+        }
+        
+        if ($.browser.opera) { // FLUID-1490. Without this detect, curCSS explodes on the avatar on Firefox.
+            avatar.hide();
+        }
+        return avatar;
+    };
+    
+    function bindHandlersToContainer(container, keyDownHandler, keyUpHandler, mouseMoveHandler) {
+        var actualKeyDown = keyDownHandler;
+        var advancedPrevention = false;
+
+        // FLUID-1598 and others: Opera will refuse to honour a "preventDefault" on a keydown.
+        // http://forums.devshed.com/javascript-development-115/onkeydown-preventdefault-opera-485371.html
+        if ($.browser.opera) {
+            container.keypress(function (evt) {
+                if (advancedPrevention) {
+                    advancedPrevention = false;
+                    evt.preventDefault();
+                    return false;
+                }
+            });
+            actualKeyDown = function (evt) {
+                var oldret = keyDownHandler(evt);
+                if (oldret === false) {
+                    advancedPrevention = true;
+                }
+            };
+        }
+        container.keydown(actualKeyDown);
+        container.keyup(keyUpHandler);
+    }
+    
+    function addRolesToContainer(that) {
+        that.container.attr("role", that.options.containerRole.container);
+        that.container.attr("aria-multiselectable", "false");
+        that.container.attr("aria-readonly", "false");
+        that.container.attr("aria-disabled", "false");
+        // FLUID-3707: We require to have BOTH application role as well as our named role
+        // This however breaks the component completely under NVDA and causes it to perpetually drop back into "browse mode"
+        //that.container.wrap("<div role=\"application\"></div>");
+    }
+    
+    function createAvatarId(parentId) {
+        // Generating the avatar's id to be containerId_avatar
+        // This is safe since there is only a single avatar at a time
+        return parentId + "_avatar";
+    }
+    
+    var adaptKeysets = function (options) {
+        if (options.keysets && !(options.keysets instanceof Array)) {
+            options.keysets = [options.keysets];    
+        }
+    };
+    
+    /**
+     * @param container - A jQueryable designator for the root node of the reorderer (a selector, a DOM node, or a jQuery instance)
+     * @param options - an object containing any of the available options:
+     *                  containerRole - indicates the role, or general use, for this instance of the Reorderer
+     *                  keysets - an object containing sets of keycodes to use for directional navigation. Must contain:
+     *                            modifier - a function that returns a boolean, indicating whether or not the required modifier(s) are activated
+     *                            up
+     *                            down
+     *                            right
+     *                            left
+     *                  styles - an object containing class names for styling the Reorderer
+     *                                  defaultStyle
+     *                                  selected
+     *                                  dragging
+     *                                  hover
+     *                                  dropMarker
+     *                                  mouseDrag
+     *                                  avatar
+     *                  avatarCreator - a function that returns a valid DOM node to be used as the dragging avatar
+     */
+    fluid.reorderer = function (container, options) {
+        if (!container) {
+            fluid.fail("Reorderer initialised with no container");
+        }
+        var thatReorderer = fluid.initView("fluid.reorderer", container, options);
+        options = thatReorderer.options;
+                
+        var dropManager = fluid.dropManager();   
+                
+        thatReorderer.layoutHandler = fluid.initSubcomponent(thatReorderer,
+            "layoutHandler", [thatReorderer.container, options, dropManager, thatReorderer.dom]);
+        
+        thatReorderer.activeItem = undefined;
+
+        adaptKeysets(options);
+        var kbDropWarning = thatReorderer.locate("dropWarning");
+        var mouseDropWarning;
+        if (kbDropWarning) {
+            mouseDropWarning = kbDropWarning.clone();
+        }
+
+        var isMove = function (evt) {
+            var keysets = options.keysets;
+            for (var i = 0; i < keysets.length; i++) {
+                if (keysets[i].modifier(evt)) {
+                    return true;
+                }
+            }
+            return false;
+        };
+        
+        var isActiveItemMovable = function () {
+            return $.inArray(thatReorderer.activeItem, thatReorderer.dom.fastLocate("movables")) >= 0;
+        };
+        
+        var setDropEffects = function (value) {
+            thatReorderer.dom.fastLocate("dropTargets").attr("aria-dropeffect", value);
+        };
+        
+        var styles = options.styles;
+        
+        var noModifier = function (evt) {
+            return (!evt.ctrlKey && !evt.altKey && !evt.shiftKey && !evt.metaKey);
+        };
+        
+        var handleDirectionKeyDown = function (evt) {
+            var item = thatReorderer.activeItem;
+            if (!item) {
+                return true;
+            }
+            var keysets = options.keysets;
+            for (var i = 0; i < keysets.length; i++) {
+                var keyset = keysets[i];
+                var keydir = fluid.keyForValue(keyset, evt.keyCode);
+                if (!keydir) {
+                    continue;
+                }
+                var isMovement = keyset.modifier(evt);
+                
+                var dirnum = fluid.keycodeDirection[keydir];
+                var relativeItem = thatReorderer.layoutHandler.getRelativePosition(item, dirnum, !isMovement);  
+                if (!relativeItem) {
+                    continue;
+                }
+                
+                if (isMovement) {
+                    var prevent = thatReorderer.events.onBeginMove.fire(item);
+                    if (prevent === false) {
+                        return false;
+                    }
+                    if (kbDropWarning.length > 0) {
+                        if (relativeItem.clazz === "locked") {
+                            thatReorderer.events.onShowKeyboardDropWarning.fire(item, kbDropWarning);
+                            kbDropWarning.show();                       
+                        }
+                        else {
+                            kbDropWarning.hide();
+                        }
+                    }
+                    if (relativeItem.element) {
+                        thatReorderer.requestMovement(relativeItem, item);
+                    }
+            
+                } else if (noModifier(evt)) {
+                    item.blur();
+                    $(relativeItem.element).focus();
+                }
+                return false;
+            }
+            return true;
+        };
+
+        thatReorderer.handleKeyDown = function (evt) {
+            if (!thatReorderer.activeItem || thatReorderer.activeItem !== evt.target) {
+                return true;
+            }
+            // If the key pressed is ctrl, and the active item is movable we want to restyle the active item.
+            var jActiveItem = $(thatReorderer.activeItem);
+            if (!jActiveItem.hasClass(styles.dragging) && isMove(evt)) {
+               // Don't treat the active item as dragging unless it is a movable.
+                if (isActiveItemMovable()) {
+                    jActiveItem.removeClass(styles.selected);
+                    jActiveItem.addClass(styles.dragging);
+                    jActiveItem.attr("aria-grabbed", "true");
+                    setDropEffects("move");
+                }
+                return false;
+            }
+            // The only other keys we listen for are the arrows.
+            return handleDirectionKeyDown(evt);
+        };
+
+        thatReorderer.handleKeyUp = function (evt) {
+            if (!thatReorderer.activeItem || thatReorderer.activeItem !== evt.target) {
+                return true;
+            }
+            var jActiveItem = $(thatReorderer.activeItem);
+            
+            // Handle a key up event for the modifier
+            if (jActiveItem.hasClass(styles.dragging) && !isMove(evt)) {
+                if (kbDropWarning) {
+                    kbDropWarning.hide();
+                }
+                jActiveItem.removeClass(styles.dragging);
+                jActiveItem.addClass(styles.selected);
+                jActiveItem.attr("aria-grabbed", "false");
+                setDropEffects("none");
+                return false;
+            }
+            
+            return false;
+        };
+
+        var dropMarker;
+
+        var createDropMarker = function (tagName) {
+            var dropMarker = $(document.createElement(tagName));
+            dropMarker.addClass(options.styles.dropMarker);
+            dropMarker.hide();
+            return dropMarker;
+        };
+
+        thatReorderer.requestMovement = function (requestedPosition, item) {
+            item = fluid.unwrap(item);
+          // Temporary censoring to get around ModuleLayout inability to update relative to self.
+            if (!requestedPosition || fluid.unwrap(requestedPosition.element) === item) {
+                return;
+            }
+            var activeItem = $(thatReorderer.activeItem);
+            
+            // Fixes FLUID-3288.
+            // Need to unbind the blur event as safari will call blur on movements.
+            // This caused the user to have to double tap the arrow keys to move.
+            activeItem.unbind("blur.fluid.reorderer");
+            
+            thatReorderer.events.onMove.fire(item, requestedPosition);
+            dropManager.geometricMove(item, requestedPosition.element, requestedPosition.position);
+            //$(thatReorderer.activeItem).removeClass(options.styles.selected);
+           
+            // refocus on the active item because moving places focus on the body
+            activeItem.focus();
+            
+            thatReorderer.refresh();
+            
+            dropManager.updateGeometry(thatReorderer.layoutHandler.getGeometricInfo());
+
+            thatReorderer.events.afterMove.fire(item, requestedPosition, thatReorderer.dom.fastLocate("movables"));
+        };
+
+        var hoverStyleHandler = function (item, state) {
+            thatReorderer.dom.fastLocate("grabHandle", item)[state?"addClass":"removeClass"](styles.hover);
+        };
+        /**
+         * Takes a $ object and adds 'movable' functionality to it
+         */
+        function initMovable(item) {
+            var styles = options.styles;
+            item.attr("aria-grabbed", "false");
+
+            item.mouseover(
+                function () {
+                    thatReorderer.events.onHover.fire(item, true);
+                }
+            );
+        
+            item.mouseout(
+                function () {
+                    thatReorderer.events.onHover.fire(item, false);
+                }
+            );
+            var avatar;
+        
+            thatReorderer.dom.fastLocate("grabHandle", item).draggable({
+                refreshPositions: false,
+                scroll: true,
+                helper: function () {
+                    var dropWarningEl;
+                    if (mouseDropWarning) {
+                        dropWarningEl = mouseDropWarning[0];
+                    }
+                    avatar = $(options.avatarCreator(item[0], styles.avatar, dropWarningEl));
+                    avatar.attr("id", createAvatarId(thatReorderer.container.id));
+                    return avatar;
+                },
+                start: function (e, ui) {
+                    var prevent = thatReorderer.events.onBeginMove.fire(item);
+                    if (prevent === false) {
+                        return false;
+                    }
+                    var handle = thatReorderer.dom.fastLocate("grabHandle", item)[0];
+                    var handlePos = fluid.dom.computeAbsolutePosition(handle);
+                    var handleWidth = handle.offsetWidth;
+                    var handleHeight = handle.offsetHeight;
+                    item.focus();
+                    item.removeClass(options.styles.selected);
+                    item.addClass(options.styles.mouseDrag);
+                    item.attr("aria-grabbed", "true");
+                    setDropEffects("move");
+                    dropManager.startDrag(e, handlePos, handleWidth, handleHeight);
+                    avatar.show();
+                },
+                stop: function (e, ui) {
+                    item.removeClass(options.styles.mouseDrag);
+                    item.addClass(options.styles.selected);
+                    $(thatReorderer.activeItem).attr("aria-grabbed", "false");
+                    var markerNode = fluid.unwrap(dropMarker);
+                    if (markerNode.parentNode) {
+                        markerNode.parentNode.removeChild(markerNode);
+                    }
+                    avatar.hide();
+                    ui.helper = null;
+                    setDropEffects("none");
+                    dropManager.endDrag();
+                    
+                    thatReorderer.requestMovement(dropManager.lastPosition(), item);
+                    // refocus on the active item because moving places focus on the body
+                    thatReorderer.activeItem.focus();
+                },
+                handle: thatReorderer.dom.fastLocate("grabHandle", item)
+            });
+        }
+           
+        function changeSelectedToDefault(jItem, styles) {
+            jItem.removeClass(styles.selected);
+            jItem.removeClass(styles.dragging);
+            jItem.addClass(styles.defaultStyle);
+            jItem.attr("aria-selected", "false");
+        }
+           
+        var selectItem = function (anItem) {
+            thatReorderer.events.onSelect.fire(anItem);
+            var styles = options.styles;
+            // Set the previous active item back to its default state.
+            if (thatReorderer.activeItem && thatReorderer.activeItem !== anItem) {
+                changeSelectedToDefault($(thatReorderer.activeItem), styles);
+            }
+            // Then select the new item.
+            thatReorderer.activeItem = anItem;
+            var jItem = $(anItem);
+            jItem.removeClass(styles.defaultStyle);
+            jItem.addClass(styles.selected);
+            jItem.attr("aria-selected", "true");
+        };
+   
+        var initSelectables = function () {
+            var handleBlur = function (evt) {
+                changeSelectedToDefault($(this), options.styles);
+                return evt.stopPropagation();
+            };
+        
+            var handleFocus = function (evt) {
+                selectItem(this);
+                return evt.stopPropagation();
+            };
+            
+            var selectables = thatReorderer.dom.fastLocate("selectables");
+            for (var i = 0; i < selectables.length; ++ i) {
+                var selectable = $(selectables[i]);
+                if (!$.data(selectable[0], "fluid.reorderer.selectable-initialised")) { 
+                    selectable.addClass(styles.defaultStyle);
+            
+                    selectable.bind("blur.fluid.reorderer", handleBlur);
+                    selectable.focus(handleFocus);
+                    selectable.click(function (evt) {
+                        var handle = fluid.unwrap(thatReorderer.dom.fastLocate("grabHandle", this));
+                        if (fluid.dom.isContainer(handle, evt.target)) {
+                            $(this).focus();
+                        }
+                    });
+                    
+                    selectable.attr("role", options.containerRole.item);
+                    selectable.attr("aria-selected", "false");
+                    selectable.attr("aria-disabled", "false");
+                    $.data(selectable[0], "fluid.reorderer.selectable-initialised", true);
+                }
+            }
+            if (!thatReorderer.selectableContext) {
+                thatReorderer.selectableContext = fluid.selectable(thatReorderer.container, {
+                    selectableElements: selectables,
+                    selectablesTabindex: thatReorderer.options.selectablesTabindex,
+                    direction: null
+                });
+            }
+        };
+    
+        var dropChangeListener = function (dropTarget) {
+            fluid.moveDom(dropMarker, dropTarget.element, dropTarget.position);
+            dropMarker.css("display", "");
+            if (mouseDropWarning) {
+                if (dropTarget.lockedelem) {
+                    mouseDropWarning.show();
+                }
+                else {
+                    mouseDropWarning.hide();
+                }
+            }
+        };
+    
+        var initItems = function () {
+            var movables = thatReorderer.dom.fastLocate("movables");
+            var dropTargets = thatReorderer.dom.fastLocate("dropTargets");
+            initSelectables();
+        
+            // Setup movables
+            for (var i = 0; i < movables.length; i++) {
+                var item = movables[i];
+                if (!$.data(item, "fluid.reorderer.movable-initialised")) { 
+                    initMovable($(item));
+                    $.data(item, "fluid.reorderer.movable-initialised", true);
+                }
+            }
+
+            // In order to create valid html, the drop marker is the same type as the node being dragged.
+            // This creates a confusing UI in cases such as an ordered list. 
+            // drop marker functionality should be made pluggable. 
+            if (movables.length > 0 && !dropMarker) {
+                dropMarker = createDropMarker(movables[0].tagName);
+            }
+            
+            dropManager.updateGeometry(thatReorderer.layoutHandler.getGeometricInfo());
+            
+            dropManager.dropChangeFirer.addListener(dropChangeListener, "fluid.Reorderer");
+            // Set up dropTargets
+            dropTargets.attr("aria-dropeffect", "none");  
+
+        };
+
+
+        // Final initialization of the Reorderer at the end of the construction process 
+        if (thatReorderer.container) {
+            bindHandlersToContainer(thatReorderer.container, 
+                thatReorderer.handleKeyDown,
+                thatReorderer.handleKeyUp);
+            addRolesToContainer(thatReorderer);
+            fluid.tabbable(thatReorderer.container);
+            initItems();
+        }
+
+        if (options.afterMoveCallbackUrl) {
+            thatReorderer.events.afterMove.addListener(function () {
+                var layoutHandler = thatReorderer.layoutHandler;
+                var model = layoutHandler.getModel? layoutHandler.getModel():
+                     options.acquireModel(thatReorderer);
+                $.post(options.afterMoveCallbackUrl, JSON.stringify(model));
+            }, "postModel");
+        }
+        thatReorderer.events.onHover.addListener(hoverStyleHandler, "style");
+
+        thatReorderer.refresh = function () {
+            thatReorderer.dom.refresh("movables");
+            thatReorderer.dom.refresh("selectables");
+            thatReorderer.dom.refresh("grabHandle", thatReorderer.dom.fastLocate("movables"));
+            thatReorderer.dom.refresh("stylisticOffset", thatReorderer.dom.fastLocate("movables"));
+            thatReorderer.dom.refresh("dropTargets");
+            thatReorderer.events.onRefresh.fire();
+            initItems();
+            thatReorderer.selectableContext.selectables = thatReorderer.dom.fastLocate("selectables");
+            thatReorderer.selectableContext.selectablesUpdated(thatReorderer.activeItem);
+        };
+        
+        fluid.initDependents(thatReorderer);
+
+        thatReorderer.refresh();
+
+        return thatReorderer;
+    };
+    
+    /**
+     * Constants for key codes in events.
+     */    
+    fluid.reorderer.keys = {
+        TAB: 9,
+        ENTER: 13,
+        SHIFT: 16,
+        CTRL: 17,
+        ALT: 18,
+        META: 19,
+        SPACE: 32,
+        LEFT: 37,
+        UP: 38,
+        RIGHT: 39,
+        DOWN: 40,
+        i: 73,
+        j: 74,
+        k: 75,
+        m: 77
+    };
+    
+    /**
+     * The default key sets for the Reorderer. Should be moved into the proper component defaults.
+     */
+    fluid.reorderer.defaultKeysets = [{
+        modifier : function (evt) {
+            return evt.ctrlKey;
+        },
+        up : fluid.reorderer.keys.UP,
+        down : fluid.reorderer.keys.DOWN,
+        right : fluid.reorderer.keys.RIGHT,
+        left : fluid.reorderer.keys.LEFT
+    },
+    {
+        modifier : function (evt) {
+            return evt.ctrlKey;
+        },
+        up : fluid.reorderer.keys.i,
+        down : fluid.reorderer.keys.m,
+        right : fluid.reorderer.keys.k,
+        left : fluid.reorderer.keys.j
+    }];
+    
+    /**
+     * These roles are used to add ARIA roles to orderable items. This list can be extended as needed,
+     * but the values of the container and item roles must match ARIA-specified roles.
+     */  
+    fluid.reorderer.roles = {
+        GRID: { container: "grid", item: "gridcell" },
+        LIST: { container: "list", item: "listitem" },
+        REGIONS: { container: "main", item: "article" }
+    };
+    
+    // Simplified API for reordering lists and grids.
+    var simpleInit = function (container, layoutHandler, options) {
+        options = options || {};
+        options.layoutHandler = layoutHandler;
+        return fluid.reorderer(container, options);
+    };
+    
+    fluid.reorderList = function (container, options) {
+        return simpleInit(container, "fluid.listLayoutHandler", options);
+    };
+    
+    fluid.reorderGrid = function (container, options) {
+        return simpleInit(container, "fluid.gridLayoutHandler", options); 
+    };
+    
+    fluid.reorderer.SHUFFLE_GEOMETRIC_STRATEGY = "shuffleProjectFrom";
+    fluid.reorderer.GEOMETRIC_STRATEGY         = "projectFrom";
+    fluid.reorderer.LOGICAL_STRATEGY           = "logicalFrom";
+    fluid.reorderer.WRAP_LOCKED_STRATEGY       = "lockedWrapFrom";
+    fluid.reorderer.NO_STRATEGY = null;
+    
+    fluid.reorderer.relativeInfoGetter = function (orientation, coStrategy, contraStrategy, dropManager, dom, disableWrap) {
+        return function (item, direction, forSelection) {
+            var dirorient = fluid.directionOrientation(direction);
+            var strategy = dirorient === orientation? coStrategy: contraStrategy;
+            return strategy !== null? dropManager[strategy](item, direction, forSelection, disableWrap) : null;
+        };
+    };
+    
+    fluid.defaults("fluid.reorderer", {
+        styles: {
+            defaultStyle: "fl-reorderer-movable-default",
+            selected: "fl-reorderer-movable-selected",
+            dragging: "fl-reorderer-movable-dragging",
+            mouseDrag: "fl-reorderer-movable-dragging",
+            hover: "fl-reorderer-movable-hover",
+            dropMarker: "fl-reorderer-dropMarker",
+            avatar: "fl-reorderer-avatar"
+        },
+        selectors: {
+            dropWarning: ".flc-reorderer-dropWarning",
+            movables: ".flc-reorderer-movable",
+            grabHandle: "",
+            stylisticOffset: ""
+        },
+        avatarCreator: defaultAvatarCreator,
+        keysets: fluid.reorderer.defaultKeysets,
+        layoutHandler: {
+            type: "fluid.listLayoutHandler"
+        },
+        
+        events: {
+            onShowKeyboardDropWarning: null,
+            onSelect: null,
+            onBeginMove: "preventable",
+            onMove: null,
+            afterMove: null,
+            onHover: null,
+            onRefresh: null
+        },
+        
+        mergePolicy: {
+            keysets: "replace",
+            "selectors.labelSource": "selectors.grabHandle",
+            "selectors.selectables": "selectors.movables",
+            "selectors.dropTargets": "selectors.movables"
+        },
+        components: {
+            labeller: {
+                type: "fluid.reorderer.labeller",
+                options: {
+                    dom: "{reorderer}.dom",
+                    getGeometricInfo: "{reorderer}.layoutHandler.getGeometricInfo",
+                    orientation: "{reorderer}.options.orientation",
+                    layoutType: "{reorderer}.options.layoutHandler", // TODO, get rid of "global defaults"
+                }          
+            }
+        },
+        
+        // The user option to enable or disable wrapping of elements within the container
+        disableWrap: false        
+        
+    });
+
+
+    /*******************
+     * Layout Handlers *
+     *******************/
+
+     fluid.reorderer.makeGeometricInfoGetter = function(orientation, sentinelize, dom) {
+        return function () {
+            var that = {
+                sentinelize: sentinelize,
+                extents: [{
+                    orientation: orientation,
+                    elements: dom.fastLocate("dropTargets")
+                }],
+                elementMapper: function (element) {
+                    return $.inArray(element, dom.fastLocate("movables")) === -1? "locked": null;
+                },
+                elementIndexer: function (element) {
+                    var selectables = dom.fastLocate("selectables");
+                    return {
+                        elementClass: that.elementMapper(element),
+                        index: $.inArray(element, selectables),
+                        length: selectables.length
+                    };
+                }
+            };
+            return that;
+        };
+    };
+    
+    fluid.defaults(true, "fluid.listLayoutHandler", 
+        {orientation:         fluid.orientation.VERTICAL,
+         containerRole:       fluid.reorderer.roles.LIST,
+         selectablesTabindex: -1,
+         sentinelize:         true
+        });
+    
+    // Public layout handlers.
+    fluid.listLayoutHandler = function (container, options, dropManager, dom) {
+        var that = {};
+
+        that.getRelativePosition = 
+          fluid.reorderer.relativeInfoGetter(options.orientation, 
+                fluid.reorderer.LOGICAL_STRATEGY, null, dropManager, dom, options.disableWrap);
+        
+        that.getGeometricInfo = fluid.reorderer.makeGeometricInfoGetter(options.orientation, options.sentinelize, dom);
+        
+        return that;
+    }; // End ListLayoutHandler
+
+    fluid.defaults(true, "fluid.gridLayoutHandler", 
+        {orientation:         fluid.orientation.HORIZONTAL,
+         containerRole:       fluid.reorderer.roles.GRID,
+         selectablesTabindex: -1,
+         sentinelize:         false
+         });
+    /*
+     * Items in the Lightbox are stored in a list, but they are visually presented as a grid that
+     * changes dimensions when the window changes size. As a result, when the user presses the up or
+     * down arrow key, what lies above or below depends on the current window size.
+     * 
+     * The GridLayoutHandler is responsible for handling changes to this virtual 'grid' of items
+     * in the window, and of informing the Lightbox of which items surround a given item.
+     */
+    fluid.gridLayoutHandler = function (container, options, dropManager, dom) {
+        var that = {};
+
+        that.getRelativePosition = 
+           fluid.reorderer.relativeInfoGetter(options.orientation, 
+                 options.disableWrap ? fluid.reorderer.SHUFFLE_GEOMETRIC_STRATEGY : fluid.reorderer.LOGICAL_STRATEGY, fluid.reorderer.SHUFFLE_GEOMETRIC_STRATEGY, 
+                 dropManager, dom, options.disableWrap);
+        
+        that.getGeometricInfo = fluid.reorderer.makeGeometricInfoGetter(options.orientation, options.sentinelize, dom);
+        
+        return that;
+    }; // End of GridLayoutHandler
+
+    fluid.defaults("fluid.reorderer.labeller", {
+        strings: {
+            overallTemplate: "%recentStatus %item %position %movable",
+            position:        "%index of %length",
+            position_moduleLayoutHandler: "%index of %length in %moduleCell %moduleIndex of %moduleLength",
+            moduleCell_0:    "row", // NB, these keys must agree with fluid.a11y.orientation constants
+            moduleCell_1:    "column",
+            movable:         "movable",
+            fixed:           "fixed",
+            recentStatus:    "moved from position %position"
+        },
+        components: {
+            resolver: {
+                type: "fluid.messageResolver",
+                options: {
+                    messageBase: "{labeller}.options.strings"
+                }
+            }
+        },
+        invokers: {
+            renderLabel: {
+                funcName: "fluid.reorderer.labeller.renderLabel",
+                args: ["{labeller}", "@0", "@1"]
+            }  
+        }
+    });
+
+    // Convert from 0-based to 1-based indices for announcement
+    fluid.reorderer.indexRebaser = function(indices) {
+        indices.index++;
+        if (indices.moduleIndex !== undefined) {
+            indices.moduleIndex++;
+        }
+        return indices;
+    };
+
+    /*************
+     * Labelling *
+     *************/
+     
+    fluid.reorderer.labeller = function(options) {
+        var that = fluid.initLittleComponent("fluid.reorderer.labeller", options);
+        fluid.initDependents(that);
+        that.dom = that.options.dom;
+        
+        that.moduleCell = that.resolver.resolve("moduleCell_" + that.options.orientation);
+        var layoutType = fluid.computeNickName(that.options.layoutType);
+        that.positionTemplate = that.resolver.lookup(["position_" + layoutType, "position"]);
+        
+        var movedMap = {};
+        
+        that.returnedOptions = {
+            listeners: {
+                onRefresh: function() {
+                    var selectables = that.dom.locate("selectables");
+                    fluid.each(selectables, function(selectable) {
+                        var labelOptions = {};
+                        var id = fluid.allocateSimpleId(selectable);
+                        var moved = movedMap[id];
+                        var label = plainLabel = that.renderLabel(selectable);
+                        if (moved) {
+                            moved.newRender = plainLabel;
+                            label = that.renderLabel(selectable, moved.oldRender.position);
+                            $(selectable).one("focusout", function() {
+                                if (movedMap[id]) {
+                                    var oldLabel = movedMap[id].newRender.label;
+                                    delete movedMap[id];
+                                    fluid.updateAriaLabel(selectable, oldLabel);
+                                }
+                            });
+                            labelOptions.dynamicLabel = true;
+                        }
+                        fluid.updateAriaLabel(selectable, label.label, labelOptions);
+                    });
+                },
+                onMove: function(item, newPosition) {
+                    fluid.clear(movedMap); // if we somehow were fooled into missing a defocus, at least clear the map on a 2nd move
+                    var movingId = fluid.allocateSimpleId(item);
+                    movedMap[movingId] = {
+                        oldRender: that.renderLabel(item)
+                    };
+                }
+            }
+        };
+        return that;
+    };
+    
+    fluid.reorderer.labeller.renderLabel = function(that, selectable, recentPosition) {
+        var geom = that.options.getGeometricInfo();
+        var indices = fluid.reorderer.indexRebaser(geom.elementIndexer(selectable));
+        indices.moduleCell = that.moduleCell;
+            
+        var elementClass = geom.elementMapper(selectable);
+        var labelSource = that.dom.locate("labelSource", selectable);
+        var recentStatus;
+        if (recentPosition) {
+            recentStatus = that.resolver.resolve("recentStatus", {position: recentPosition});
+        }
+        var topModel = {
+            item: typeof(labelSource) === "string"? labelSource: fluid.dom.getElementText(fluid.unwrap(labelSource)),
+            position: that.positionTemplate.resolveFunc(that.positionTemplate.template, indices),
+            movable: that.resolver.resolve(elementClass === "locked"? "fixed" : "movable"),
+            recentStatus: recentStatus || ""
+        }
+        
+        var template = that.resolver.lookup(["overallTemplate"]);
+        var label = template.resolveFunc(template.template, topModel);
+        return {
+            position: topModel.position,
+            label: label
+        } 
+    };
+
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/reorderer/js/ReordererDOMUtilities.js b/docs/jscripts/infusion/components/reorderer/js/ReordererDOMUtilities.js
new file mode 100644 (file)
index 0000000..3612e05
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2010 University of Toronto
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+/*global fluid_1_3*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    /** 
+     * Returns the absolute position of a supplied DOM node in pixels.
+     * Implementation taken from quirksmode http://www.quirksmode.org/js/findpos.html
+     */
+    fluid.dom.computeAbsolutePosition = function (element) {
+        var curleft = 0, curtop = 0;
+        if (element.offsetParent) {
+            do {
+                curleft += element.offsetLeft;
+                curtop += element.offsetTop;
+                element = element.offsetParent;
+            } while (element);
+            return [curleft, curtop];
+        }
+    };
+    
+    /** 
+     * Cleanse the children of a DOM node by removing all <script> tags.
+     * This is necessary to prevent the possibility that these blocks are
+     * reevaluated if the node were reattached to the document. 
+     */
+    fluid.dom.cleanseScripts = function (element) {
+        var cleansed = $.data(element, fluid.dom.cleanseScripts.MARKER);
+        if (!cleansed) {
+            fluid.dom.iterateDom(element, function (node) {
+                return node.tagName.toLowerCase() === "script"? "delete" : null;
+            });
+            $.data(element, fluid.dom.cleanseScripts.MARKER, true);
+        }
+    };  
+    fluid.dom.cleanseScripts.MARKER = "fluid-scripts-cleansed";
+
+    /**
+     * Inserts newChild as the next sibling of refChild.
+     * @param {Object} newChild
+     * @param {Object} refChild
+     */
+    fluid.dom.insertAfter = function (newChild, refChild) {
+        var nextSib = refChild.nextSibling;
+        if (!nextSib) {
+            refChild.parentNode.appendChild(newChild);
+        }
+        else {
+            refChild.parentNode.insertBefore(newChild, nextSib);
+        }
+    };
+    
+    // The following two functions taken from http://developer.mozilla.org/En/Whitespace_in_the_DOM
+    /**
+     * Determine whether a node's text content is entirely whitespace.
+     *
+     * @param node  A node implementing the |CharacterData| interface (i.e.,
+     *              a |Text|, |Comment|, or |CDATASection| node
+     * @return     True if all of the text content of |nod| is whitespace,
+     *             otherwise false.
+     */
+    fluid.dom.isWhitespaceNode = function (node) {
+       // Use ECMA-262 Edition 3 String and RegExp features
+        return !(/[^\t\n\r ]/.test(node.data));
+    };
+    
+    /**
+     * Determine if a node should be ignored by the iterator functions.
+     *
+     * @param nod  An object implementing the DOM1 |Node| interface.
+     * @return     true if the node is:
+     *                1) A |Text| node that is all whitespace
+     *                2) A |Comment| node
+     *             and otherwise false.
+     */
+    fluid.dom.isIgnorableNode = function (node) {
+        return (node.nodeType === 8) || // A comment node
+         ((node.nodeType === 3) && fluid.dom.isWhitespaceNode(node)); // a text node, all ws
+    };
+
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/tooltip/js/Tooltip.js b/docs/jscripts/infusion/components/tooltip/js/Tooltip.js
new file mode 100644 (file)
index 0000000..1b629eb
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    var createContentFunc = function (content) {
+        return typeof content === "function" ? content : function () {
+            return content;
+        };
+    };
+
+    var setup = function (that) {
+        that.container.tooltip({
+            content: createContentFunc(that.options.content),
+            position: that.options.position,
+            items: that.options.items,
+            open: function (event) {
+                var tt = $(event.target).tooltip("widget");
+                tt.stop(false, true);
+                tt.hide();
+                if (that.options.delay) {
+                    tt.delay(that.options.delay).fadeIn("default", that.events.afterOpen.fire());
+                } else {
+                    tt.show();
+                    that.events.afterOpen.fire();
+                }
+            },
+            close: function (event) {
+                var tt = $(event.target).tooltip("widget");
+                tt.stop(false, true);
+                tt.hide();
+                tt.clearQueue();
+                that.events.afterClose.fire();
+            } 
+        });
+        
+        that.elm = that.container.tooltip("widget");
+        
+        that.elm.addClass(that.options.styles.tooltip);
+    };
+
+    fluid.tooltip = function (container, options) {
+        var that = fluid.initView("fluid.tooltip", container, options);
+        
+        /**
+         * Updates the contents displayed in the tooltip
+         * 
+         * @param {Object} content, the content to be displayed in the tooltip
+         */
+        that.updateContent = function (content) {
+            that.container.tooltip("option", "content", createContentFunc(content));
+        };
+        
+        /**
+         * Destroys the underlying jquery ui tooltip
+         */
+        that.destroy = function () {
+            that.container.tooltip("destroy");
+        };
+        
+        /**
+         * Manually displays the tooltip
+         */
+        that.open = function () {
+            that.container.tooltip("open");
+        };
+        
+        /**
+         * Manually hides the tooltip
+         */
+        that.close = function () {
+            that.container.tooltip("close");
+        };
+        
+        setup(that);
+        
+        return that;
+    };
+    
+    fluid.defaults("fluid.tooltip", {
+        styles: {
+            tooltip: ""
+        },
+        
+        events: {
+            afterOpen: null,
+            afterClose: null  
+        },
+        
+        content: "",
+        
+        position: {
+            my: "left top",
+            at: "left bottom",
+            offset: "0 5"
+        },
+        
+        items: "*",
+        
+        delay: 300
+    });
+
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/undo/js/Undo.js b/docs/jscripts/infusion/components/undo/js/Undo.js
new file mode 100644 (file)
index 0000000..56eae69
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+Copyright 2008-2009 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+  // The three states of the undo component
+    var STATE_INITIAL = "state_initial", 
+        STATE_CHANGED = "state_changed",
+        STATE_REVERTED = "state_reverted";
+  
+    function defaultRenderer(that, targetContainer) {
+        var str = that.options.strings;
+        var markup = "<span class='flc-undo'>" + 
+            "<a href='#' class='flc-undo-undoControl'>" + str.undo + "</a>" + 
+            "<a href='#' class='flc-undo-redoControl'>" + str.redo + "</a>" + 
+            "</span>";
+        var markupNode = $(markup).attr({
+            "role": "region",  
+            "aria-live": "polite", 
+            "aria-relevant": "all"
+        });
+        targetContainer.append(markupNode);
+        return markupNode;
+    }
+    
+    function refreshView(that) {
+        if (that.state === STATE_INITIAL) {
+            that.locate("undoContainer").hide();
+            that.locate("redoContainer").hide();
+        }
+        else if (that.state === STATE_CHANGED) {
+            that.locate("undoContainer").show();
+            that.locate("redoContainer").hide();
+        }
+        else if (that.state === STATE_REVERTED) {
+            that.locate("undoContainer").hide();
+            that.locate("redoContainer").show();          
+        }
+    }
+   
+    
+    var bindHandlers = function (that) { 
+        that.locate("undoControl").click( 
+            function () {
+                if (that.state !== STATE_REVERTED) {
+                    fluid.model.copyModel(that.extremalModel, that.component.model);
+                    that.component.updateModel(that.initialModel, that);
+                    that.state = STATE_REVERTED;
+                    refreshView(that);
+                    that.locate("redoControl").focus();
+                }
+                return false;
+            }
+        );
+        that.locate("redoControl").click( 
+            function () {
+                if (that.state !== STATE_CHANGED) {
+                    that.component.updateModel(that.extremalModel, that);
+                    that.state = STATE_CHANGED;
+                    refreshView(that);
+                    that.locate("undoControl").focus();
+                }
+                return false;
+            }
+        );
+        return {
+            modelChanged: function (newModel, oldModel, source) {
+                if (source !== that) {
+                    that.state = STATE_CHANGED;
+                
+                    fluid.model.copyModel(that.initialModel, oldModel);
+                
+                    refreshView(that);
+                }
+            }
+        };
+    };
+    
+    /**
+     * Decorates a target component with the function of "undoability"
+     * 
+     * @param {Object} component a "model-bearing" standard Fluid component to receive the "undo" functionality
+     * @param {Object} options a collection of options settings
+     */
+    fluid.undoDecorator = function (component, userOptions) {
+        var that = fluid.initLittleComponent("undo", userOptions);
+        that.container = that.options.renderer(that, component.container);
+        fluid.initDomBinder(that);
+        fluid.tabindex(that.locate("undoControl"), 0);
+        fluid.tabindex(that.locate("redoControl"), 0);
+        
+        that.component = component;
+        that.initialModel = {};
+        that.extremalModel = {};
+        fluid.model.copyModel(that.initialModel, component.model);
+        fluid.model.copyModel(that.extremalModel, component.model);
+        
+        that.state = STATE_INITIAL;
+        refreshView(that);
+        var listeners = bindHandlers(that);
+        
+        that.returnedOptions = {
+            listeners: listeners
+        };
+        return that;
+    };
+  
+    fluid.defaults("undo", {  
+        selectors: {
+            undoContainer: ".flc-undo-undoControl",
+            undoControl: ".flc-undo-undoControl",
+            redoContainer: ".flc-undo-redoControl",
+            redoControl: ".flc-undo-redoControl"
+        },
+        
+        strings: {
+            undo: "undo edit",
+            redo: "redo edit"
+        },
+                    
+        renderer: defaultRenderer
+    });
+        
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/uploader/ReadMe.txt b/docs/jscripts/infusion/components/uploader/ReadMe.txt
new file mode 100644 (file)
index 0000000..eec74c1
--- /dev/null
@@ -0,0 +1,103 @@
+Infusion Uploader Read Me
+
+1) Upgrading
+2) Known Issues
+3) Troubleshooting
+4) Running the Uploader with out a Server
+
+--------------------------------------
+
+UPGRADING from previous versions:
+
+Before upgrading from Infusion 0.5 or earlier, please refer to the Uploader API documentation and the 
+latest example code. The Fluid Uploader was extensively refactored in the 0.6 release, and a fresh new 
+API has been introduced.
+
+Please refer to the Uploader API documentation on the Fluid Wiki and the 
+Infusion 1.0 example code before using the Uploader with an existing integration. 
+http://wiki.fluidproject.org/display/fluid/Uploader+API
+
+--------------------------------------
+
+KNOWN ISSUES: 
+
+* To support Flash 10 (released on 9/26/2008), the Uploader required a new version of the SWFUpload 
+  Flash component (2.2.0 beta 3). This new version, still in beta, still has numerous bugs. We have 
+  worked around many of the bugs and inconsistencies in the SWFUpload code, but there are still 
+  significant compromises and issues in this release. For this reason we do not consider this version 
+  of the Uploader to be production-ready. 
+
+  In the previous version of the Uploader, the Flash component worked completely "behind the scenes". 
+  To support Flash 10, the Uploader displays a Flash-based "Browse files..." button in place of a 
+  HTML button. The Flash-based button presents the following quirks:
+  
+      In Firefox and IE, the Flash-based Browse button does not size correctly when the text/page 
+      is resized or zoomed.
+
+      In Firefox (FF):
+      - The AIRA role for the Browse button is read correctly as "button" but the text of the button, 
+        "Browse Files", is ignored.
+      - The Flash-based Browse button traps keyboard navigation, refusing to give up focus without a 
+        mouse click. 
+      
+      In Internet Explorer (IE):
+      - AIRA is not supported by Internet Explorer.
+
+    We are exploring work-arounds for most of these issues, and will have a patch out as soon 
+    as possible to fix them.
+
+* In previous versions of the Uploader the upload process would stop immediately at the moment that 
+  the Stop Upload button was clicked.
+   
+  With Infusion 0.8, we wait for the current file to complete or to error before we stop the upload 
+  process. This avoids a serious bug in the SWFUploader where the Upload process could get stuck when 
+  the Upload process as resumed.
+
+
+--------------------------------------
+
+TROUBLE SHOOTING:
+
+* When running the Uploader sample code on a local system without a server, check to make 
+  sure that you have followed the instructions below under "RUNNING THE UPLOADER ON A 
+  LOCAL SYSTEM WITHOUT A SERVER". 
+
+* If you see this error in your console: 
+  [Exception... "'Invalid function name' when calling method: [nsIDOMEventListener::handleEvent]" 
+  nsresult: "0x8057001e (NS_ERROR_XPC_JS_THREW_STRING)" location: "<unknown>" data: no]
+
+  The flashUrl option is probably wrong. Check that first. 
+
+
+--------------------------------------
+
+RUNNING THE UPLOADER ON A LOCAL SYSTEM WITHOUT A SERVER
+
+Running the Uploader locally without a server is intended for basic testing purposes only. The 
+DemoUploadManager provides a simulated conversation with the server, but it doesn't represent a
+fully accurate picture of the component's behaviour when used in a real deployment environment.
+
+So see the Uploader in action with a real server, have a look at Fluid's Image Gallery demo:
+
+http://build.fluidproject.org:8080/sakai-imagegallery2-web/site/AddImages/
+
+
+Additionally, you may need to modify some of your Flash settings to allow the local SWFUpload 
+object to access your file system. To do so, follow these directions:
+
+1. Open your browser
+2. Browse to:
+   http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html
+3. In the Flash Settings panel, click "Edit locations..."
+4. Select "Add location..."
+5. Click "Browse for folder..."
+6. Select the local /src/webapp/lib/swfupload/flash/ directory that contains the swfupload.swf file
+7. Restart your browser
+
+You should be good to go! 
+
+However, if you move your installation, you'll need to do this all over again. There are settings 
+that will allow the file to be run from any location on your local machine but these instructions 
+are the minimum settings and therefor pose the least security risk.
+
+These settings are global and do not need to repeated for every browser on a given system. 
diff --git a/docs/jscripts/infusion/components/uploader/css/Uploader.css b/docs/jscripts/infusion/components/uploader/css/Uploader.css
new file mode 100644 (file)
index 0000000..01ff5f5
--- /dev/null
@@ -0,0 +1,508 @@
+.fl-uploader { 
+    width: 434px;
+    position: relative;
+    display: block;
+    clear: both;
+}
+
+.fl-uploader-manually-degrade, .fl-uploader-manually-enhance {
+    display: block;
+    width: 434px;
+    clear: both;
+    float: none;
+    text-align: center;
+    padding: 1em;
+}
+
+/* Deprecated: fl-ProgEnhanced-*  */
+.fl-ProgEnhance-basic div input {
+    margin-top: 0.6em;
+}
+.fl-ProgEnhance-enhanced {
+    display: none;
+}
+/* New Syntax: fl-progEnhanced-*  */
+.fl-progEnhance-basic div input {
+    margin-top: 0.6em;
+}
+.fl-progEnhance-enhanced {
+    display: none;
+}
+
+.fl-uploader td, .fl-uploader th {
+    border: none;
+}
+
+.fl-uploader table {
+    border-collapse: separate;
+    margin: 0;
+}
+
+.fl-uploader form {
+    height: auto !important;
+}
+
+.fl-uploader button {
+    background-color: #4F99D3;
+    border-color: #2A3990;
+    border-width: 1px;
+    border-style: solid;
+    font-weight: bolder;
+    font-size: 1.06em;
+    color: #FFF;
+    height: 32px;
+    padding-left: 2em;
+    padding-right: 2em;
+    margin-right: 1em;
+    outline: none;
+    cursor: pointer;
+}
+
+.fl-uploader button:focus, .fl-uploader button.focus {
+    outline: 2px solid #142B8C;
+}
+
+.fl-uploader button:hover {
+    background-color: #115F8F;
+}
+
+.fl-uploader button.fl-uploader-button-default {
+    background-color: #74B74A;
+    border-color: #006838;
+}
+
+.fl-uploader button.fl-uploader-button-default:hover {
+    background-color: #519325;
+    border-color: #142B8C;
+}
+
+.fl-uploader button.fl-uploader-button-cancel {
+    background-color: #D03E4F;
+    border-color: #BF1E2D;
+}
+
+.fl-uploader button.fl-uploader-button-cancel:hover {
+    background-color: #9E182B;
+    border-color: #142B8C;
+}
+
+.fl-uploader button.fl-uploader-dim {
+    background-color: #999;
+    border-color: #666666;
+}
+
+.fl-uploader button.fl-uploader-dim:hover {
+    background-color: #999;
+    border-color: #666666;
+}
+
+.fl-uploader button span.text-description {
+    display: none;
+}
+
+.fl-uploader span.fl-uploader-browse {
+    background-image: url(../images/add.png);
+    background-attachment: scroll;
+    background-repeat: no-repeat;
+    background-position: 1px center;
+    padding: 5px 5px 0px 22px;
+    border: none;
+    white-space: nowrap;
+    color: #427ABE;
+    font-weight: normal;
+}
+
+.fl-uploader-footer-buttons {
+    vertical-align: center;
+}
+
+/* Special note about .fl-uploader-browse, .fl-uploader-browse.fl-uploader-browseMore, .fl-uploader-browse input
+ *
+ * Because of the inconsistencies in affecting <input type="file"> styling across browsers, we've used the following hack:
+ * 1. Create a fake "Browse files"/"Add more" button
+ * 2. Make the real button invisible
+ * 3. Put the real, invisible button on top of the fake button
+ */
+
+/* Fake add files button wrapper ("Browse files") */
+.fl-uploader-browse {
+    display: block;
+    overflow: hidden;
+    width: 82px;
+    height: 24px;
+    cursor: default;
+}
+
+/* Fake add files button wrapper ("Add more")
+ * Same as above, but width-adjusted for shorter text */
+.fl-uploader-browse.fl-uploader-browseMore {
+    width: 65px;
+}
+
+/* Actual, real add files button (invisible) */
+.fl-uploader-browse input {
+    position: relative;
+    overflow: hidden;
+    float: right;
+
+    /* Make the real button big enough to cover the fake button */
+    height: 40px;
+    letter-spacing: 5px;
+    bottom: 28px;
+    left: 10px;
+
+    /* Make the button invisible */
+    opacity: 0;
+    -moz-opacity: 0;
+    filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);
+}
+
+html>body .fl-uploader span.fl-uploader-browse {
+    border: 1px solid transparent;
+}
+
+.fl-uploader span.fl-uploader-browse:hover {
+    border: 1px solid #CCC;
+    background-color: #FFF;
+}
+
+.fl-uploader span.fl-uploader-browse:focus, .fl-uploader span.fl-uploader-browse.focus {
+    outline: 2px solid #142B8C;
+    background-color: #FFF;
+}
+
+.fl-uploader-browse-overlay {
+    position: absolute;
+    bottom: 50px;
+    left: 325px;
+    z-index: 7;
+}
+
+.fl-uploader-flash10-wrapper {
+    position: relative;
+    float: left;
+}
+
+/* FILE QUEUE TABLE STYLES */
+
+/* caption */
+
+.fl-uploader-queue-wrapper caption {
+    display: none;
+}
+
+/* header rows */
+
+.fl-uploader-queue-header {
+    background-color: transparent;
+    color: #666;
+}
+
+.fl-uploader-queue-header th { 
+    text-align: left;
+    font-size: 0.94em;
+    font-weight: normal;
+    padding: 6px;
+}
+
+/* footer rows */
+
+.fl-uploader-queue-footer {
+    position: relative;
+    background-color: #E6E6E6;
+    border: 1px solid #999;
+}
+
+.fl-uploader-queue-footer table { 
+    width: 100%;
+    position:relative;
+    z-index:6;
+}
+
+.fl-uploader-queue-footer td {
+    padding: 8px 6px;
+    vertical-align: middle;
+    font-size: 1.07em;
+    font-weight: bolder;
+    color: #666;
+}
+
+.fl-uploader-footer-buttons {
+    text-align: right;
+}
+
+/* tbody */
+
+.fl-scroller {
+    display: block;
+    position: relative;
+    background-color: white;
+    overflow: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
+    border-width: 1px 1px 0;
+    border-style: solid;
+    border-color: #999;
+}
+
+.fl-scroller .fl-scroller-inner {
+    position: relative;
+    overflow: hidden;
+}
+        
+.fl-uploader-queue {
+    position: relative;
+    font-size: 0.9em;
+    color: #333;
+    z-index: 6;
+}
+
+/* hidden from IE6 */
+html>body .fl-uploader-queue {
+    width: 100%;    
+}
+
+/* this style is here for debugging, if everything is working right the list items are focusable but the list not. */
+.fl-uploader-queue:focus {
+    border: 1px solid #142B8C;
+}
+
+.fl-uploader-queue tr:focus, .fl-uploader-queue tr.fl-uploader-file-focus {
+    background-color: #FFF9DC;
+}
+
+.fl-uploader-queue tr {
+    background-color: transparent;
+}
+
+/* second CSS selector is added only for IE6 */
+
+.fl-uploader-queue tr.fl-uploader-file-state-ready:hover, .fl-uploader-queue tr.fl-uploader-file-hover  {
+    background-color: #FFF9DC;
+}
+
+.fl-uploader-queue tr.fl-uploader-dim {
+    color: #666;
+}
+
+.fl-uploader-queue td, .fl-uploader-queue th { 
+    text-align: left;
+    padding: 3px 6px;
+    border-bottom: 1px solid #BBCFDC;
+}
+
+.fl-uploader-queue-wrapper .fl-uploader-file-name {
+    width: 322px;
+    overflow: hidden;
+}
+
+.fl-uploader-queue-wrapper .fl-uploader-file-size {
+    text-align: right;
+    width: 4em;
+    white-space: nowrap;
+}
+
+.fl-uploader-queue .fl-uploader-file-action {
+    border: 1px solid transparent;
+    height: 20px;
+    width: 20px;
+    overflow: hidden;
+    background-color: transparent;
+    background-attachment: scroll;
+    background-repeat: no-repeat;
+    background-position: 1px 1px;
+    outline: 0;
+    padding: 0;
+    margin: 0;
+    outline: none;
+}
+
+.fl-uploader-queue .fl-uploader-file-action:hover {
+    border: 1px solid transparent;
+    background-color: transparent;
+    cursor: auto;
+}
+
+.fl-uploader-queue .fl-uploader-file-actions .fl-uploader-dim {
+    border: 1px solid transparent;
+    background-color: transparent;
+    cursor: auto;
+}
+
+.fl-uploader-queue .fl-uploader-file-action-remove {
+    background-image: url(../images/remove.png);
+    cursor: pointer !important;
+}
+
+.fl-uploader-queue .fl-uploader-file-action-remove:hover, .fl-uploader-queue tr.fl-uploader-file-state-ready:focus .fl-uploader-file-action-remove, .fl-uploader-queue tr.focus .fl-uploader-file-action-remove {
+    border: 1px solid #CCC;
+    background-color: #FFF;
+}
+
+.fl-uploader-queue .fl-uploader-file-action-remove:focus, .fl-uploader-queue .fl-uploader-file-action-remove.focus {
+    background-color: #4F99D3;
+    outline: none;
+}
+
+.fl-uploader-queue .fl-uploader-file-state-uploaded th, .fl-uploader-queue tr.fl-uploader-file-state-uploaded th, .fl-uploader-queue tr.focus th   {
+    border-left: 4px solid #74B74A;
+    color: #5E7A5E;
+}
+
+.fl-uploader-queue .fl-uploader-file-state-uploaded td {
+    color: #5E7A5E;
+}
+
+.fl-uploader-queue .fl-uploader-file-state-uploaded .fl-uploader-file-action {
+    background-image: url(../images/tick.png);
+}
+
+/* ERRORS
+ * 
+ */
+
+.fl-uploader-queue .fl-uploader-file-state-error th {
+    border-left: 4px solid #F5E392;
+    background-color: #FCFBE6;
+    border-bottom-width: 0;
+}
+
+.fl-uploader-queue .fl-uploader-file-state-error td {
+    background-color: #FCFBE6;
+    border-bottom-width: 0;
+}
+
+.fl-uploader-queue .fl-uploader-file-error td {
+    background-color: #FCFBE6;
+    border-left: 4px solid #F5E392;
+    background-image: url(../images/error.png);
+    background-position: 8px 2px;
+    background-repeat: no-repeat;
+    padding-bottom: 8px;
+    padding-left: 32px;
+}
+
+.fl-uploader-queue-wrapper td.fl-uploader-file-actions, .fl-uploader-queue-wrapper th.fl-uploader-file-actions {
+    white-space: nowrap;
+    text-align: center;
+}
+
+.fl-uploader-queue td.fileStatus { 
+    white-space: nowrap;
+}
+
+.fl-uploader-queue-wrapper .fl-uploader-browse-instructions {
+    background-color: #F0F9FF;
+    color: #666;
+    text-align: center;
+    border-left: 1px solid #999;
+    border-right: 1px solid #999;
+    padding: 6px;
+}
+
+.fl-uploader-btns {
+    margin-top: 1em;
+    text-align:right;
+}
+
+.fl-uploader-btns .fl-uploader-upload, .fl-uploader-btns .fl-uploader-resume,  .fl-uploader-btns .fl-uploader-pause {
+    width: 8.4em;
+}
+
+.fl-uploader-btns .fl-uploader-done, .fl-uploader-btns .fl-uploader-cancel {
+    width: 7.6em;
+}
+
+.fl-uploader-btns button {
+    padding: 0;
+}
+
+button span.fl-uploader-button-text-hidden {
+    display: none;
+}
+
+/* element states */
+
+.fl-uploader-disabled, .fl-uploader-dim {
+    cursor: auto; 
+}
+
+.fl-uploader-disabled {
+    background-color: #CCC !important;
+    border-color: #999 !important;
+}
+
+.fl-uploader-dim {
+    opacity: 0.4;
+    filter:alpha(opacity=40);
+}
+
+.fl-uploader-hidden {
+    display: none;
+}
+
+.fl-uploader-hidden-templates {
+    display: none;
+}
+
+
+/* PROGRESS */
+
+.fl-uploader-file-progress, .fl-uploader-total-progress-okay, .fl-uploader-total-progress-errored {
+    position: absolute;
+    display: none;
+    background-attachment: scroll;
+    background-position: bottom left;
+    background-repeat: repeat-x;
+    overflow: hidden;
+    left: 0;
+    z-index:4;
+}
+
+.fl-uploader-file-progress {
+    background-image: url('../images/gradient-file-green.png');
+    background-color: #D1D6DD;
+    border-right: 1px solid #9FCE7F;
+    width:76%;
+}
+
+.fl-uploader-file-progress .fl-uploader-file-progress-text {
+    display: none;
+}
+
+.fl-uploader-total-progress-okay, .fl-uploader-total-progress-errored {
+    top: 0;
+    height: 100%;
+    width:76%;
+} 
+
+.fl-uploader-total-progress-okay {
+    background-image: url('../images/gradient-total-green.png');
+    border-right: 1px solid #9FCE7F;
+    background-color: #BDBFC1;
+}
+
+.fl-uploader-total-progress-errored {
+    background-image: url('../images/gradient-total-yellow.png');
+    border-right: 1px solid #E8C81C;
+    background-color: #F6ECA0;
+}
+
+/* Hidden SWF for Flash 9*/
+
+.fl-uploader-flash9-container {
+    overflow: hidden; 
+    width: 1px; 
+    height: 1px;
+}
+
+/* an alternative way to hide the Uploader because of a bug in Opera,
+ * described in FLUID-2789 */
+.hideUploaderForOpera {
+    visibility: hidden;
+    overflow: hidden;
+    height: 0;
+    width: 0;
+    padding: 0;
+}
diff --git a/docs/jscripts/infusion/components/uploader/html/Uploader.html b/docs/jscripts/infusion/components/uploader/html/Uploader.html
new file mode 100644 (file)
index 0000000..393804a
--- /dev/null
@@ -0,0 +1,135 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+        <title>Uploader Template</title>
+  
+        <link rel="stylesheet" type="text/css" href="../../../framework/fss/css/fss-reset.css" />
+        <link rel="stylesheet" type="text/css" href="../../../framework/fss/css/fss-layout.css" />
+        <link rel="stylesheet" type="text/css" href="../css/Uploader.css" />
+        
+        <!-- Fluid and jQuery Dependencies -->
+        <script type="text/javascript" src="../../../lib/jquery/core/js/jquery.js"></script>
+        <script type="text/javascript" src="../../../lib/jquery/ui/js/jquery.ui.core.js"></script>
+        <script type="text/javascript" src="../../../framework/core/js/jquery.keyboard-a11y.js"></script>
+        <script type="text/javascript" src="../../../lib/swfobject/js/swfobject.js"></script>
+        <script type="text/javascript" src="../../../lib/swfupload/js/swfupload.js"></script>
+        <script type="text/javascript" src="../../../framework/core/js/Fluid.js"></script>
+        <script type="text/javascript" src="../../../framework/core/js/FluidDocument.js"></script>
+        <script type="text/javascript" src="../../../framework/core/js/FluidView.js"></script>
+        <script type="text/javascript" src="../../../framework/core/js/DataBinding.js"></script>
+        <script type="text/javascript" src="../../../framework/core/js/FluidIoC.js"></script>
+        <script type="text/javascript" src="../../../framework/enhancement/js/ProgressiveEnhancement.js"></script>
+        
+        <!-- Uploader dependencies -->
+        <script type="text/javascript" src="../js/Uploader.js"></script>
+        <script type="text/javascript" src="../js/FileQueue.js"></script>
+        <script type="text/javascript" src="../js/Scroller.js"></script>
+        <script type="text/javascript" src="../../progress/js/Progress.js"></script>
+        <script type="text/javascript" src="../js/FileQueueView.js"></script>
+        <script type="text/javascript" src="../js/FlashUploaderSupport.js"></script>
+        <script type="text/javascript" src="../js/Flash9UploaderSupport.js"></script>
+        <script type="text/javascript" src="../js/HTML5UploaderSupport.js"></script>
+        <script type="text/javascript" src="../js/DemoUploadManager.js"></script>
+        
+    </head>
+    
+    <body>
+        <div id="uploader-contents">
+            
+            <!-- This form will be progressively enhanced by the Fluid Uploader component. -->
+            <form method="post" enctype="multipart/form-data" class="fl-uploader fl-progEnhance-basic">
+                <p>Browse to upload a file.</p>
+                <input name="fileData" type="file" />
+                <div>
+                    <input type="submit" value="Save"/>
+                </div>
+            </form>
+            
+            <!-- This is the markup for the Fluid Uploader component itself. -->
+            <form class="flc-uploader fl-uploader" 
+                  method="get" 
+                  enctype="multipart/form-data">
+                      
+                <!-- The file queue -->
+                <div class="fl-uploader-queue-wrapper">
+                    <!-- Top of the queue -->
+                    <div class="fl-uploader-queue-header">
+                        <table cellspacing="0" cellpadding="0" summary="Headers for the file queue." role="presentation">
+                            <caption>File Upload Queue:</caption>
+                            <tr>
+                                <th scope="col" class="fl-uploader-file-name">File Name</th>
+                                <th scope="col" class="fl-uploader-file-size">Size&nbsp;&nbsp;</th>
+                                <th scope="col" class="fl-uploader-file-actions">&nbsp;</th>
+                            </tr>
+                        </table>
+                    </div>
+                    
+                    <!-- Scrollable view -->
+                    <div class="flc-scroller fl-scroller">
+                        <div class="fl-scroller-inner">
+                            <table cellspacing="0" class="flc-uploader-queue fl-uploader-queue" summary="Queue of files to upload." role="presentation">
+                                <tbody>
+                                    <!-- Rows will be rendered in here. -->
+                                    
+                                    <!-- Template markup for the file queue rows -->
+                                    <tr class="flc-uploader-file-tmplt flc-uploader-file fl-uploader-hidden-templates">
+                                        <th class="flc-uploader-file-name fl-uploader-file-name" scope="row">File Name Placeholder</th>
+                                        <td class="flc-uploader-file-size fl-uploader-file-size">0 KB</td>
+                                        <td class="fl-uploader-file-actions">
+                                            <button type="button" class="flc-uploader-file-action fl-uploader-file-action" tabindex="-1">
+                                                <span class="fl-uploader-button-text fl-uploader-hidden">Remove file from queue</span>
+                                            </button>
+                                        </td>
+                                    </tr>
+                                    
+                                    <!-- Template for the file error info rows -->
+                                    <tr class="flc-uploader-file-error-tmplt fl-uploader-file-error fl-uploader-hidden-templates">
+                                        <td colspan="3" class="flc-uploader-file-error"></td>
+                                    </tr>
+                                </tbody>
+                            </table>
+                            <div class="flc-uploader-file-progressor-tmplt fl-uploader-file-progress">
+                                <span class="fl-uploader-file-progress-text fl-uploader-hidden">76%</span>
+                            </div>
+                        </div>
+                    </div>
+                    
+                    <div class="flc-uploader-browse-instructions fl-uploader-browse-instructions">
+                        Choose <em>Browse files</em> to add files to the queue 
+                    </div>
+        
+                    <!-- Foot of the queue -->
+                    <div class="flc-uploader-queue-footer fl-uploader-queue-footer">
+                        <table cellspacing="0" cellpadding="0" summary="Status of file queue." role="presentation">
+                               <tr>
+                                   <td class="flc-uploader-total-progress-text">
+                                       Total: 0 files (0 KB)
+                                   </td>
+                                   <td class=".fl-uploader-footer-buttons" align="right" >
+                                       <span class="flc-uploader-button-browse fl-uploader-browse">
+                                           <span class="flc-uploader-button-browse-text">Browse files</span>
+                                       </span>
+                                   </td>
+                               </tr>
+                        </table>
+                        <div class="flc-uploader-total-progress fl-uploader-total-progress-okay">&nbsp;</div>
+                    </div>
+                </div>
+                
+                <!-- Action buttons -->
+                <div class="fl-uploader-btns">
+                    <button type="button" class="flc-uploader-button-pause fl-uploader-pause fl-uploader-hidden">Stop Upload</button><button type="button" class="flc-uploader-button-upload fl-uploader-upload fl-uploader-button-default fl-uploader-dim" disabled="disabled">Upload</button>
+                </div>
+                
+                <div class="flc-uploader-status-region fl-offScreen-hidden"></div>
+            </form>        
+        </div>
+            
+        <script type="text/javascript">
+            var myUploader = fluid.uploader(".flc-uploader", {
+                demo: true     
+               });
+        </script> 
+    </body>
+</html>
diff --git a/docs/jscripts/infusion/components/uploader/images/add.png b/docs/jscripts/infusion/components/uploader/images/add.png
new file mode 100644 (file)
index 0000000..6332fef
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/add.png differ
diff --git a/docs/jscripts/infusion/components/uploader/images/browse.png b/docs/jscripts/infusion/components/uploader/images/browse.png
new file mode 100644 (file)
index 0000000..dd3927c
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/browse.png differ
diff --git a/docs/jscripts/infusion/components/uploader/images/error.png b/docs/jscripts/infusion/components/uploader/images/error.png
new file mode 100644 (file)
index 0000000..628cf2d
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/error.png differ
diff --git a/docs/jscripts/infusion/components/uploader/images/gradient-file-green.png b/docs/jscripts/infusion/components/uploader/images/gradient-file-green.png
new file mode 100644 (file)
index 0000000..95374c8
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/gradient-file-green.png differ
diff --git a/docs/jscripts/infusion/components/uploader/images/gradient-file-grey.png b/docs/jscripts/infusion/components/uploader/images/gradient-file-grey.png
new file mode 100644 (file)
index 0000000..7da71fe
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/gradient-file-grey.png differ
diff --git a/docs/jscripts/infusion/components/uploader/images/gradient-total-green.png b/docs/jscripts/infusion/components/uploader/images/gradient-total-green.png
new file mode 100644 (file)
index 0000000..cc999e5
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/gradient-total-green.png differ
diff --git a/docs/jscripts/infusion/components/uploader/images/gradient-total-grey.png b/docs/jscripts/infusion/components/uploader/images/gradient-total-grey.png
new file mode 100644 (file)
index 0000000..0246eca
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/gradient-total-grey.png differ
diff --git a/docs/jscripts/infusion/components/uploader/images/gradient-total-yellow.png b/docs/jscripts/infusion/components/uploader/images/gradient-total-yellow.png
new file mode 100644 (file)
index 0000000..98a4db6
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/gradient-total-yellow.png differ
diff --git a/docs/jscripts/infusion/components/uploader/images/remove.png b/docs/jscripts/infusion/components/uploader/images/remove.png
new file mode 100644 (file)
index 0000000..8409ecf
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/remove.png differ
diff --git a/docs/jscripts/infusion/components/uploader/images/tick.png b/docs/jscripts/infusion/components/uploader/images/tick.png
new file mode 100644 (file)
index 0000000..a9925a0
Binary files /dev/null and b/docs/jscripts/infusion/components/uploader/images/tick.png differ
diff --git a/docs/jscripts/infusion/components/uploader/js/DemoUploadManager.js b/docs/jscripts/infusion/components/uploader/js/DemoUploadManager.js
new file mode 100644 (file)
index 0000000..2f6ea37
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+Copyright 2009 University of Toronto
+Copyright 2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.uploader = fluid.uploader || {};
+    fluid.uploader.swfUploadStrategy = fluid.uploader.swfUploadStrategy || {};
+    
+    var startUploading; // Define early due to subtle circular dependency.
+    
+    var updateProgress = function (file, events, demoState, isUploading) {
+        if (!isUploading) {
+            return;
+        }
+        
+        var chunk = Math.min(demoState.chunkSize, file.size);
+        demoState.bytesUploaded = Math.min(demoState.bytesUploaded + chunk, file.size);
+        events.onFileProgress.fire(file, demoState.bytesUploaded, file.size);
+    };
+    
+    var finishAndContinueOrCleanup = function (that, file) {
+        that.queue.finishFile(file);
+        that.events.afterFileComplete.fire(file);
+        
+        if (that.queue.shouldUploadNextFile()) {
+            startUploading(that);
+        } else {
+            that.events.afterUploadComplete.fire(that.queue.currentBatch.files);
+            that.queue.clearCurrentBatch();
+        }
+    };
+    
+    var finishUploading = function (that) {
+        if (!that.queue.isUploading) {
+            return;
+        }
+        
+        var file = that.demoState.currentFile;
+        that.events.onFileSuccess.fire(file);
+        that.demoState.fileIdx++;
+        finishAndContinueOrCleanup(that, file);
+    };
+    
+    var simulateUpload = function (that) {
+        if (!that.queue.isUploading) {
+            return;
+        }
+        
+        var file = that.demoState.currentFile;
+        if (that.demoState.bytesUploaded < file.size) {
+            fluid.invokeAfterRandomDelay(function () {
+                updateProgress(file, that.events, that.demoState, that.queue.isUploading);
+                simulateUpload(that);
+            });
+        } else {
+            finishUploading(that);
+        } 
+    };
+    
+    startUploading = function (that) {
+        // Reset our upload stats for each new file.
+        that.demoState.currentFile = that.queue.files[that.demoState.fileIdx];
+        that.demoState.chunksForCurrentFile = Math.ceil(that.demoState.currentFile / that.demoState.chunkSize);
+        that.demoState.bytesUploaded = 0;
+        that.queue.isUploading = true;
+        
+        that.events.onFileStart.fire(that.demoState.currentFile);
+        simulateUpload(that);
+    };
+
+    var stopDemo = function (that) {
+        var file = that.demoState.currentFile;
+        file.filestatus = fluid.uploader.fileStatusConstants.CANCELLED;
+        that.queue.shouldStop = true;
+        
+        // In SWFUpload's world, pausing is a combinination of an UPLOAD_STOPPED error and a complete.
+        that.events.onFileError.fire(file, 
+                                     fluid.uploader.errorConstants.UPLOAD_STOPPED, 
+                                     "The demo upload was paused by the user.");
+        finishAndContinueOrCleanup(that, file);
+        that.events.onUploadStop.fire();
+    };
+    
+    var setupDemo = function (that) {
+        if (that.simulateDelay === undefined || that.simulateDelay === null) {
+            that.simulateDelay = true;
+        }
+          
+        // Initialize state for our upload simulation.
+        that.demoState = {
+            fileIdx: 0,
+            chunkSize: 200000
+        };
+        
+        return that;
+    };
+       
+    /**
+     * The Demo Engine wraps a SWFUpload engine and simulates the upload process.
+     * 
+     * @param {FileQueue} queue the Uploader's file queue instance
+     * @param {Object} the Uploader's bundle of event firers
+     * @param {Object} configuration options for SWFUpload (in its native dialect)
+     */
+     // TODO: boil down events to only those we actually need.
+     // TODO: remove swfupload references and move into general namespace. Are there any real SWFUpload references here?
+    fluid.uploader.demoRemote = function (queue, events, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.demoRemote", options);
+        that.queue = queue;
+        that.events = events;
+        
+        that.start = function () {
+            startUploading(that);   
+        };
+        
+        /**
+         * Cancels a simulated upload.
+         * This method overrides the default behaviour in SWFUploadManager.
+         */
+        that.stop = function () {
+            stopDemo(that);
+        };
+        
+        setupDemo(that);
+        return that;
+    };
+    
+    /**
+     * Invokes a function after a random delay by using setTimeout.
+     * If the simulateDelay option is false, the function is invoked immediately.
+     * This is an odd function, but a potential candidate for central inclusion.
+     * 
+     * @param {Function} fn the function to invoke
+     */
+    fluid.invokeAfterRandomDelay = function (fn) {
+        var delay = Math.floor(Math.random() * 1000 + 100);
+        setTimeout(fn, delay);
+    };
+    
+    fluid.demands("fluid.uploader.remote", ["fluid.uploader.multiFileUploader", "fluid.uploader.demo"], {
+        funcName: "fluid.uploader.demoRemote",
+        args: [
+            "{multiFileUploader}.queue",
+            "{multiFileUploader}.events",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/uploader/js/FileQueue.js b/docs/jscripts/infusion/components/uploader/js/FileQueue.js
new file mode 100644 (file)
index 0000000..15b8e97
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global SWFUpload, jQuery, fluid_1_3:true*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.uploader = fluid.uploader || {};
+    
+    var filterFiles = function (files, filterFn) {
+        var filteredFiles = [];
+        for (var i = 0; i < files.length; i++) {
+            var file = files[i];
+            if (filterFn(file) === true) {
+                filteredFiles.push(file);
+            }
+        }
+        
+        return filteredFiles;
+    };
+     
+    fluid.uploader.fileQueue = function () {
+        var that = {};
+        that.files = [];
+        that.isUploading = false;
+        
+        /********************
+         * Queue Operations *
+         ********************/
+         
+        that.start = function () {
+            that.setupCurrentBatch();
+            that.isUploading = true;
+            that.shouldStop = false;
+        };
+        
+        that.startFile = function () {
+            that.currentBatch.fileIdx++;
+            that.currentBatch.bytesUploadedForFile = 0;
+            that.currentBatch.previousBytesUploadedForFile = 0; 
+        };
+                
+        that.finishFile = function (file) {
+            that.currentBatch.numFilesCompleted++;
+        };
+        
+        that.shouldUploadNextFile = function () {
+            return !that.shouldStop && 
+                   that.isUploading && 
+                   that.currentBatch.numFilesCompleted < that.currentBatch.files.length;
+        };
+        
+        /*****************************
+         * File manipulation methods *
+         *****************************/
+         
+        that.addFile = function (file) {
+            that.files.push(file);    
+        };
+        
+        that.removeFile = function (file) {
+            var idx = $.inArray(file, that.files);
+            that.files.splice(idx, 1);        
+        };
+        
+        /**********************
+         * Queue Info Methods *
+         **********************/
+         
+        that.totalBytes = function () {
+            return fluid.uploader.fileQueue.sizeOfFiles(that.files);
+        };
+
+        that.getReadyFiles = function () {
+            return filterFiles(that.files, function (file) {
+                return (file.filestatus === fluid.uploader.fileStatusConstants.QUEUED || file.filestatus === fluid.uploader.fileStatusConstants.CANCELLED);
+            });        
+        };
+        
+        that.getErroredFiles = function () {
+            return filterFiles(that.files, function (file) {
+                return (file.filestatus === fluid.uploader.fileStatusConstants.ERROR);
+            });        
+        };
+        
+        that.sizeOfReadyFiles = function () {
+            return fluid.uploader.fileQueue.sizeOfFiles(that.getReadyFiles());
+        };
+        
+        that.getUploadedFiles = function () {
+            return filterFiles(that.files, function (file) {
+                return (file.filestatus === fluid.uploader.fileStatusConstants.COMPLETE);
+            });        
+        };
+
+        that.sizeOfUploadedFiles = function () {
+            return fluid.uploader.fileQueue.sizeOfFiles(that.getUploadedFiles());
+        };
+
+        /*****************
+         * Batch Methods *
+         *****************/
+         
+        that.setupCurrentBatch = function () {
+            that.clearCurrentBatch();
+            that.updateCurrentBatch();
+        };
+        
+        that.clearCurrentBatch = function () {
+            that.currentBatch = {
+                fileIdx: -1,
+                files: [],
+                totalBytes: 0,
+                numFilesCompleted: 0,
+                numFilesErrored: 0,
+                bytesUploadedForFile: 0,
+                previousBytesUploadedForFile: 0,
+                totalBytesUploaded: 0
+            };
+        };
+        
+        that.updateCurrentBatch = function () {
+            var readyFiles = that.getReadyFiles();
+            that.currentBatch.files = readyFiles;
+            that.currentBatch.totalBytes = fluid.uploader.fileQueue.sizeOfFiles(readyFiles);
+        };
+        
+        that.updateBatchStatus = function (currentBytes) {
+            var byteIncrement = currentBytes - that.currentBatch.previousBytesUploadedForFile;
+            that.currentBatch.totalBytesUploaded += byteIncrement;
+            that.currentBatch.bytesUploadedForFile += byteIncrement;
+            that.currentBatch.previousBytesUploadedForFile = currentBytes;
+        };
+                
+        return that;
+    };
+    
+    fluid.uploader.fileQueue.sizeOfFiles = function (files) {
+        var totalBytes = 0;
+        for (var i = 0; i < files.length; i++) {
+            var file = files[i];
+            totalBytes += file.size;
+        }        
+        return totalBytes;
+    };
+          
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/uploader/js/FileQueueView.js b/docs/jscripts/infusion/components/uploader/js/FileQueueView.js
new file mode 100644 (file)
index 0000000..6ed4020
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2008-2009 University of Cambridge
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+/*******************
+ * File Queue View *
+ *******************/
+
+(function ($, fluid) {
+    
+    // Real data binding would be nice to replace these two pairs.
+    var rowForFile = function (that, file) {
+        return that.locate("fileQueue").find("#" + file.id);
+    };
+    
+    var errorRowForFile = function (that, file) {
+        return $("#" + file.id + "_error", that.container);
+    };
+    
+    var fileForRow = function (that, row) {
+        var files = that.model;
+        for (var i = 0; i < files.length; i++) {
+            var file = files[i];
+            if (file.id.toString() === row.attr("id")) {
+                return file;
+            }
+        }
+        return null;
+    };
+    
+    var progressorForFile = function (that, file) {
+        var progressId = file.id + "_progress";
+        return that.fileProgressors[progressId];
+    };
+    
+    var startFileProgress = function (that, file) {
+        var fileRowElm = rowForFile(that, file);
+        that.scroller.scrollTo(fileRowElm);
+         
+        // update the progressor and make sure that it's in position
+        var fileProgressor = progressorForFile(that, file);
+        fileProgressor.refreshView();
+        fileProgressor.show();
+    };
+        
+    var updateFileProgress = function (that, file, fileBytesComplete, fileTotalBytes) {
+        var filePercent = fluid.uploader.derivePercent(fileBytesComplete, fileTotalBytes);
+        var filePercentStr = filePercent + "%";    
+        progressorForFile(that, file).update(filePercent, filePercentStr);
+    };
+    
+    var hideFileProgress = function (that, file) {
+        var fileRowElm = rowForFile(that, file);
+        progressorForFile(that, file).hide();
+        if (file.filestatus === fluid.uploader.fileStatusConstants.COMPLETE) {
+            that.locate("fileIconBtn", fileRowElm).removeClass(that.options.styles.dim);
+        } 
+    };
+    
+    var removeFileProgress = function (that, file) {
+        var fileProgressor = progressorForFile(that, file);
+        if (!fileProgressor) {
+            return;
+        }
+        var rowProgressor = fileProgressor.displayElement;
+        rowProgressor.remove();
+    };
+    var animateRowRemoval = function (that, row) {
+        row.fadeOut("fast", function () {
+            row.remove();  
+            that.refreshView();
+        });
+    };
+    
+    var removeFileErrorRow = function (that, file) {
+        if (file.filestatus === fluid.uploader.fileStatusConstants.ERROR) {
+            animateRowRemoval(that, errorRowForFile(that, file));
+        }
+    };
+   
+    var removeFileAndRow = function (that, file, row) {
+        // Clean up the stuff associated with a file row.
+        removeFileProgress(that, file);
+        removeFileErrorRow(that, file);
+        
+        // Remove the file itself.
+        that.events.onFileRemoved.fire(file);
+        animateRowRemoval(that, row);
+    };
+    
+    var removeFileForRow = function (that, row) {
+        var file = fileForRow(that, row);
+        if (!file || file.filestatus === fluid.uploader.fileStatusConstants.COMPLETE) {
+            return;
+        }
+        removeFileAndRow(that, file, row);
+    };
+    
+    var removeRowForFile = function (that, file) {
+        var row = rowForFile(that, file);
+        removeFileAndRow(that, file, row);
+    };
+    
+    var bindHover = function (row, styles) {
+        var over = function () {
+            if (row.hasClass(styles.ready) && !row.hasClass(styles.uploading)) {
+                row.addClass(styles.hover);
+            }
+        };
+        
+        var out = function () {
+            if (row.hasClass(styles.ready) && !row.hasClass(styles.uploading)) {
+                row.removeClass(styles.hover);
+            }   
+        };
+        row.hover(over, out);
+    };
+    
+    var bindDeleteKey = function (that, row) {
+        var deleteHandler = function () {
+            removeFileForRow(that, row);
+        };
+       
+        fluid.activatable(row, null, {
+            additionalBindings: [{
+                key: $.ui.keyCode.DELETE, 
+                activateHandler: deleteHandler
+            }]
+        });
+    };
+    
+    var bindRowHandlers = function (that, row) {
+        if ($.browser.msie && $.browser.version < 7) {
+            bindHover(row, that.options.styles);
+        }
+        
+        that.locate("fileIconBtn", row).click(function () {
+            removeFileForRow(that, row);
+        });
+        
+        bindDeleteKey(that, row);
+    };
+    
+    var renderRowFromTemplate = function (that, file) {
+        var row = that.rowTemplate.clone(),
+            fileName = file.name,
+            fileSize = fluid.uploader.formatFileSize(file.size);
+        
+        row.removeClass(that.options.styles.hiddenTemplate);
+        that.locate("fileName", row).text(fileName);
+        that.locate("fileSize", row).text(fileSize);
+        that.locate("fileIconBtn", row).addClass(that.options.styles.remove);
+        row.attr("id", file.id);
+        row.addClass(that.options.styles.ready);
+        bindRowHandlers(that, row);
+        fluid.updateAriaLabel(row, fileName + " " + fileSize);
+        return row;    
+    };
+    
+    var createProgressorFromTemplate = function (that, row) {
+        // create a new progress bar for the row and position it
+        var rowProgressor = that.rowProgressorTemplate.clone();
+        var rowId = row.attr("id");
+        var progressId = rowId + "_progress";
+        rowProgressor.attr("id", progressId);
+        rowProgressor.css("top", row.position().top);
+        rowProgressor.height(row.height()).width(5);
+        that.container.after(rowProgressor);
+       
+        that.fileProgressors[progressId] = fluid.progress(that.options.uploaderContainer, {
+            selectors: {
+                progressBar: "#" + rowId,
+                displayElement: "#" + progressId,
+                label: "#" + progressId + " .fl-uploader-file-progress-text",
+                indicator: "#" + progressId
+            }
+        });
+    };
+    
+    var addFile = function (that, file) {
+        var row = renderRowFromTemplate(that, file);
+        /* FLUID-2720 - do not hide the row under IE8 */
+        if (!($.browser.msie && ($.browser.version >= 8))) {
+            row.hide();
+        }
+        that.container.append(row);
+        row.attr("title", that.options.strings.status.remove);
+        row.fadeIn("slow");
+        that.scroller.scrollBottom();
+        createProgressorFromTemplate(that, row);
+
+        that.refreshView();
+    };
+    
+    var prepareForUpload = function (that) {
+        var rowButtons = that.locate("fileIconBtn", that.locate("fileRows"));
+        rowButtons.attr("disabled", "disabled");
+        rowButtons.addClass(that.options.styles.dim);    
+    };
+
+    var refreshAfterUpload = function (that) {
+        var rowButtons = that.locate("fileIconBtn", that.locate("fileRows"));
+        rowButtons.removeAttr("disabled");
+        rowButtons.removeClass(that.options.styles.dim);    
+    };
+        
+    var changeRowState = function (that, row, newState) {
+        row.removeClass(that.options.styles.ready).removeClass(that.options.styles.error).addClass(newState);
+    };
+    
+    var markRowAsComplete = function (that, file) {
+        // update styles and keyboard bindings for the file row
+        var row = rowForFile(that, file);
+        changeRowState(that, row, that.options.styles.uploaded);
+        row.attr("title", that.options.strings.status.success);
+        fluid.enabled(row, false);
+        
+        // update the click event and the styling for the file delete button
+        var removeRowBtn = that.locate("fileIconBtn", row);
+        removeRowBtn.unbind("click");
+        removeRowBtn.removeClass(that.options.styles.remove);
+        removeRowBtn.attr("title", that.options.strings.status.success); 
+    };
+    
+    var renderErrorInfoRowFromTemplate = function (that, fileRow, error) {
+        // Render the row by cloning the template and binding its id to the file.
+        var errorRow = that.errorInfoRowTemplate.clone();
+        errorRow.attr("id", fileRow.attr("id") + "_error");
+        
+        // Look up the error message and render it.
+        var errorType = fluid.keyForValue(fluid.uploader.errorConstants, error);
+        var errorMsg = that.options.strings.errors[errorType];
+        that.locate("errorText", errorRow).text(errorMsg);
+        fileRow.after(errorRow);
+        that.scroller.scrollTo(errorRow);
+    };
+    
+    var showErrorForFile = function (that, file, error) {
+        hideFileProgress(that, file);
+        if (file.filestatus === fluid.uploader.fileStatusConstants.ERROR) {
+            var fileRowElm = rowForFile(that, file);
+            changeRowState(that, fileRowElm, that.options.styles.error);
+            renderErrorInfoRowFromTemplate(that, fileRowElm, error);
+        }
+    };
+    
+    var bindModelEvents = function (that) {
+        that.returnedOptions = {
+            listeners: {
+                afterFileQueued: that.addFile,
+                onUploadStart: that.prepareForUpload,
+                onFileStart: that.showFileProgress,
+                onFileProgress: that.updateFileProgress,
+                onFileSuccess: that.markFileComplete,
+                onFileError: that.showErrorForFile,
+                afterFileComplete: that.hideFileProgress,
+                afterUploadComplete: that.refreshAfterUpload
+            }
+        };
+    };
+    
+    var addKeyboardNavigation = function (that) {
+        fluid.tabbable(that.container);
+        that.selectableContext = fluid.selectable(that.container, {
+            selectableSelector: that.options.selectors.fileRows,
+            onSelect: function (itemToSelect) {
+                $(itemToSelect).addClass(that.options.styles.selected);
+            },
+            onUnselect: function (selectedItem) {
+                $(selectedItem).removeClass(that.options.styles.selected);
+            }
+        });
+    };
+    
+    var prepareTemplateElements = function (that) {
+        // Grab our template elements out of the DOM.  
+        that.rowTemplate = that.locate("rowTemplate").remove();
+        that.errorInfoRowTemplate = that.locate("errorInfoRowTemplate").remove();
+        that.errorInfoRowTemplate.removeClass(that.options.styles.hiddenTemplate);
+        that.rowProgressorTemplate = that.locate("rowProgressorTemplate", that.options.uploaderContainer).remove();
+    };
+    
+    var setupFileQueue = function (that) {
+        fluid.initDependents(that);
+        prepareTemplateElements(that);         
+        addKeyboardNavigation(that); 
+        bindModelEvents(that);
+    };
+    
+    /**
+     * Creates a new File Queue view.
+     * 
+     * @param {jQuery|selector} container the file queue's container DOM element
+     * @param {fileQueue} queue a file queue model instance
+     * @param {Object} options configuration options for the view
+     */
+    fluid.uploader.fileQueueView = function (container, events, options) {
+        var that = fluid.initView("fluid.uploader.fileQueueView", container, options);
+        that.fileProgressors = {};
+        that.model = that.options.model;
+        that.events = events;
+        
+        that.addFile = function (file) {
+            addFile(that, file);
+        };
+        
+        that.removeFile = function (file) {
+            removeRowForFile(that, file);
+        };
+        
+        that.prepareForUpload = function () {
+            prepareForUpload(that);
+        };
+        
+        that.refreshAfterUpload = function () {
+            refreshAfterUpload(that);
+        };
+
+        that.showFileProgress = function (file) {
+            startFileProgress(that, file);
+        };
+        
+        that.updateFileProgress = function (file, fileBytesComplete, fileTotalBytes) {
+            updateFileProgress(that, file, fileBytesComplete, fileTotalBytes); 
+        };
+        
+        that.markFileComplete = function (file) {
+            progressorForFile(that, file).update(100, "100%");
+            markRowAsComplete(that, file);
+        };
+        
+        that.showErrorForFile = function (file, error) {
+            showErrorForFile(that, file, error);
+        };
+        
+        that.hideFileProgress = function (file) {
+            hideFileProgress(that, file);
+        };
+        
+        that.refreshView = function () {
+            that.scroller.refreshView();
+            that.selectableContext.refresh();
+        };
+        
+        setupFileQueue(that);     
+        return that;
+    };
+    
+    fluid.demands("fluid.uploader.fileQueueView", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.uploader.fileQueueView",
+        args: [
+            "{multiFileUploader}.dom.fileQueue",
+            {
+                onFileRemoved: "{multiFileUploader}.events.onFileRemoved"
+            },
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    fluid.demands("fluid.scroller", "fluid.uploader.fileQueueView", {
+        funcName: "fluid.scroller",
+        args: [
+            "{fileQueueView}.container"
+        ]
+    });
+    
+    fluid.defaults("fluid.uploader.fileQueueView", {
+        components: {
+            scroller: {
+                type: "fluid.scroller"
+            }
+        },
+        
+        selectors: {
+            fileRows: ".flc-uploader-file",
+            fileName: ".flc-uploader-file-name",
+            fileSize: ".flc-uploader-file-size",
+            fileIconBtn: ".flc-uploader-file-action",      
+            errorText: ".flc-uploader-file-error",
+            
+            rowTemplate: ".flc-uploader-file-tmplt",
+            errorInfoRowTemplate: ".flc-uploader-file-error-tmplt",
+            rowProgressorTemplate: ".flc-uploader-file-progressor-tmplt"
+        },
+        
+        styles: {
+            hover: "fl-uploader-file-hover",
+            selected: "fl-uploader-file-focus",
+            ready: "fl-uploader-file-state-ready",
+            uploading: "fl-uploader-file-state-uploading",
+            uploaded: "fl-uploader-file-state-uploaded",
+            error: "fl-uploader-file-state-error",
+            remove: "fl-uploader-file-action-remove",
+            dim: "fl-uploader-dim",
+            hiddenTemplate: "fl-uploader-hidden-templates"
+        },
+        
+        strings: {
+            progress: {
+                toUploadLabel: "To upload: %fileCount %fileLabel (%totalBytes)", 
+                singleFile: "file",
+                pluralFiles: "files"
+            },
+            status: {
+                success: "File Uploaded",
+                error: "File Upload Error",
+                remove: "Press Delete key to remove file"
+            }, 
+            errors: {
+                HTTP_ERROR: "File upload error: a network error occured or the file was rejected (reason unknown).",
+                IO_ERROR: "File upload error: a network error occured.",
+                UPLOAD_LIMIT_EXCEEDED: "File upload error: you have uploaded as many files as you are allowed during this session",
+                UPLOAD_FAILED: "File upload error: the upload failed for an unknown reason.",
+                QUEUE_LIMIT_EXCEEDED: "You have as many files in the queue as can be added at one time. Removing files from the queue may allow you to add different files.",
+                FILE_EXCEEDS_SIZE_LIMIT: "One or more of the files that you attempted to add to the queue exceeded the limit of %fileSizeLimit.",
+                ZERO_BYTE_FILE: "One or more of the files that you attempted to add contained no data.",
+                INVALID_FILETYPE: "One or more files were not added to the queue because they were of the wrong type."
+            }
+        },
+        
+        mergePolicy: {
+            model: "preserve",
+            events: "preserve"
+        }
+    });
+   
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/uploader/js/Flash9UploaderSupport.js b/docs/jscripts/infusion/components/uploader/js/Flash9UploaderSupport.js
new file mode 100644 (file)
index 0000000..f35fd1f
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    fluid.uploader = fluid.uploader || {};
+    fluid.uploader.swfUploadStrategy = fluid.uploader.swfUploadStrategy || {};
+    
+    /**********************************************************************************
+     * The functions in this file, which provide support for Flash 9 in the Uploader, *
+     * have been deprecated as of Infusion 1.3.                                       * 
+     **********************************************************************************/
+    
+    fluid.uploader.swfUploadStrategy.flash9SetupDOM = function (styles) {
+        var container = $("<div><span></span></div>");
+        container.addClass(styles.flash9Container);
+        $("body").append(container);
+        return container;       
+    };
+
+    fluid.demands("fluid.uploader.swfUploadStrategy.setupDOM", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.9"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash9SetupDOM",
+        args: [
+            "{engine}.options.styles"
+        ]
+    });
+
+    fluid.uploader.swfUploadStrategy.flash9SetupConfig = function (config, events) {
+        return fluid.uploader.swfUploadStrategy.convertConfigForSWFUpload(config, events);
+    };
+
+    fluid.demands("fluid.uploader.swfUploadStrategy.setupConfig", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.9"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash9SetupConfig",
+        args: [
+            "{engine}.config",
+            "{multiFileUploader}.events"
+        ]
+    });
+
+    fluid.uploader.swfUploadStrategy.flash9EventBinder = function (model, events, local, browseButton) {
+        browseButton.click(function (e) {        
+            local.browse();
+            e.preventDefault();
+        });
+        fluid.uploader.swfUploadStrategy.bindFileEventListeners(model, events);
+    };
+
+    fluid.demands("fluid.uploader.swfUploadStrategy.eventBinder", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.9"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash9EventBinder",
+        args: [
+            "{multiFileUploader}.queue.files",
+            "{multiFileUploader}.events",
+            "{local}",
+            "{multiFileUploader}.dom.browseButton"
+        ]
+    });
+
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/uploader/js/FlashUploaderSupport.js b/docs/jscripts/infusion/components/uploader/js/FlashUploaderSupport.js
new file mode 100644 (file)
index 0000000..d76ebf7
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true, SWFUpload, swfobject */
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    fluid.uploader = fluid.uploader || {};
+    
+    fluid.demands("fluid.uploader.impl", ["fluid.uploader", "fluid.uploader.swfUpload"], {
+        funcName: "fluid.uploader.multiFileUploader"
+    });
+    
+    /**********************
+     * uploader.swfUpload *
+     **********************/
+    
+    fluid.uploader.swfUploadStrategy = function (options) {
+        var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy", options);
+        fluid.initDependents(that);
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.swfUploadStrategy", {
+        components: {
+            engine: {
+                type: "fluid.uploader.swfUploadStrategy.engine",
+                options: {
+                    queueSettings: "{multiFileUploader}.options.queueSettings",
+                    flashMovieSettings: "{swfUploadStrategy}.options.flashMovieSettings"
+                }
+            },
+            
+            local: {
+                type: "fluid.uploader.swfUploadStrategy.local"
+            },
+            
+            remote: {
+                type: "fluid.uploader.remote"
+            }
+        },
+        
+        // TODO: Rename this to "flashSettings" and remove the "flash" prefix from each option
+        flashMovieSettings: {
+            flashURL: "../../../lib/swfupload/flash/swfupload.swf",
+            flashButtonPeerId: "",
+            flashButtonAlwaysVisible: false,
+            flashButtonTransparentEvenInIE: true,
+            flashButtonImageURL: "../images/browse.png", // Used only when the Flash movie is visible.
+            flashButtonCursorEffect: SWFUpload.CURSOR.HAND,
+            debug: false
+        },
+
+        styles: {
+            browseButtonOverlay: "fl-uploader-browse-overlay",
+            flash9Container: "fl-uploader-flash9-container",
+            uploaderWrapperFlash10: "fl-uploader-flash10-wrapper"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.uploader.swfUploadStrategy",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    fluid.demands("fluid.uploader.progressiveStrategy", "fluid.uploader.swfUpload", {
+        funcName: "fluid.uploader.swfUploadStrategy",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    
+    fluid.uploader.swfUploadStrategy.remote = function (swfUpload, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy.remote", options);
+        that.swfUpload = swfUpload;
+        
+        that.start = function () {
+            that.swfUpload.startUpload();
+        };
+        
+        that.stop = function () {
+            that.swfUpload.stopUpload();
+        };
+        return that;
+    };
+    
+    fluid.demands("fluid.uploader.remote", "fluid.uploader.swfUploadStrategy", {
+        funcName: "fluid.uploader.swfUploadStrategy.remote",
+        args: [
+            "{engine}.swfUpload",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+
+    
+    fluid.uploader.swfUploadStrategy.local = function (swfUpload, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy.local", options);
+        that.swfUpload = swfUpload;
+        
+        that.browse = function () {
+            if (that.options.file_queue_limit === 1) {
+                that.swfUpload.selectFile();
+            } else {
+                that.swfUpload.selectFiles();
+            }    
+        };
+        
+        that.removeFile = function (file) {
+            that.swfUpload.cancelUpload(file.id);
+        };
+        
+        that.enableBrowseButton = function () {
+            that.swfUpload.setButtonDisabled(false);
+        };
+        
+        that.disableBrowseButton = function () {
+            that.swfUpload.setButtonDisabled(true);
+        };
+        
+        return that;
+    };
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.local", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.uploader.swfUploadStrategy.local",
+        args: [
+            "{engine}.swfUpload",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    fluid.uploader.swfUploadStrategy.engine = function (options) {
+        var that = fluid.initLittleComponent("fluid.uploader.swfUploadStrategy.engine", options);
+        
+        // Get the Flash version from swfobject and setup a new context so that the appropriate
+        // Flash 9/10 strategies are selected.
+        var flashVersion = swfobject.getFlashPlayerVersion().major;
+        that.flashVersionContext = fluid.typeTag("fluid.uploader.flash." + flashVersion);
+        
+        // Merge Uploader's generic queue options with our Flash-specific options.
+        that.config = $.extend({}, that.options.queueSettings, that.options.flashMovieSettings);
+        
+        // Configure the SWFUpload subsystem.
+        fluid.initDependents(that);
+        that.flashContainer = that.setupDOM();
+        that.swfUploadConfig = that.setupConfig();
+        that.swfUpload = new SWFUpload(that.swfUploadConfig);
+        that.bindEvents();
+        
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.swfUploadStrategy.engine", {
+        invokers: {
+            setupDOM: "fluid.uploader.swfUploadStrategy.setupDOM",
+            setupConfig: "fluid.uploader.swfUploadStrategy.setupConfig",
+            bindEvents: "fluid.uploader.swfUploadStrategy.eventBinder"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.engine", "fluid.uploader.swfUploadStrategy", {
+        funcName: "fluid.uploader.swfUploadStrategy.engine",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    
+    /**********************
+     * swfUpload.setupDOM *
+     **********************/
+    
+    fluid.uploader.swfUploadStrategy.flash10SetupDOM = function (uploaderContainer, browseButton, styles) {
+        // Wrap the whole uploader first.
+        uploaderContainer.wrap("<div class='" + styles.uploaderWrapperFlash10 + "'></div>");
+
+        // Then create a container and placeholder for the Flash movie as a sibling to the uploader.
+        var flashContainer = $("<div><span></span></div>");
+        flashContainer.addClass(styles.browseButtonOverlay);
+        uploaderContainer.after(flashContainer);
+        
+        browseButton.attr("tabindex", -1);        
+        return flashContainer;   
+    };
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.setupDOM", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.10"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash10SetupDOM",
+        args: [
+            "{multiFileUploader}.container",
+            "{multiFileUploader}.dom.browseButton",
+            "{swfUploadStrategy}.options.styles"
+        ]
+    });
+     
+     
+    /*********************************
+     * swfUpload.setupConfig *
+     *********************************/
+      
+    // Maps SWFUpload's setting names to our component's setting names.
+    var swfUploadOptionsMap = {
+        uploadURL: "upload_url",
+        flashURL: "flash_url",
+        postParams: "post_params",
+        fileSizeLimit: "file_size_limit",
+        fileTypes: "file_types",
+        fileUploadLimit: "file_upload_limit",
+        fileQueueLimit: "file_queue_limit",
+        flashButtonPeerId: "button_placeholder_id",
+        flashButtonImageURL: "button_image_url",
+        flashButtonHeight: "button_height",
+        flashButtonWidth: "button_width",
+        flashButtonWindowMode: "button_window_mode",
+        flashButtonCursorEffect: "button_cursor",
+        debug: "debug"
+    };
+
+    // Maps SWFUpload's callback names to our component's callback names.
+    var swfUploadEventMap = {
+        afterReady: "swfupload_loaded_handler",
+        onFileDialog: "file_dialog_start_handler",
+        afterFileQueued: "file_queued_handler",
+        onQueueError: "file_queue_error_handler",
+        afterFileDialog: "file_dialog_complete_handler",
+        onFileStart: "upload_start_handler",
+        onFileProgress: "upload_progress_handler",
+        onFileComplete: "upload_complete_handler",
+        onFileError: "upload_error_handler",
+        onFileSuccess: "upload_success_handler"
+    };
+    
+    var mapNames = function (nameMap, source, target) {
+        var result = target || {};
+        for (var key in source) {
+            var mappedKey = nameMap[key];
+            if (mappedKey) {
+                result[mappedKey] = source[key];
+            }
+        }
+        
+        return result;
+    };
+    
+    // For each event type, hand the fire function to SWFUpload so it can fire the event at the right time for us.
+    // TODO: Refactor out duplication with mapNames()--should be able to use Engage's mapping tool
+    var mapSWFUploadEvents = function (nameMap, events, target) {
+        var result = target || {};
+        for (var eventType in events) {
+            var fireFn = events[eventType].fire;
+            var mappedName = nameMap[eventType];
+            if (mappedName) {
+                result[mappedName] = fireFn;
+            }   
+        }
+        return result;
+    };
+    
+    fluid.uploader.swfUploadStrategy.convertConfigForSWFUpload = function (config, events) {
+        // Map the event and settings names to SWFUpload's expectations.
+        var convertedConfig = mapNames(swfUploadOptionsMap, config);
+        return mapSWFUploadEvents(swfUploadEventMap, events, convertedConfig);
+    };
+    
+    fluid.uploader.swfUploadStrategy.flash10SetupConfig = function (config, events, flashContainer, browseButton) {
+        config.flashButtonPeerId = fluid.allocateSimpleId(flashContainer.children().eq(0));
+        var isTransparent = config.flashButtonAlwaysVisible ? false : (!$.browser.msie || config.flashButtonTransparentEvenInIE);
+        config.flashButtonImageURL = isTransparent ? undefined : config.flashButtonImageURL;
+        config.flashButtonHeight = config.flashButtonHeight || browseButton.outerHeight();
+        config.flashButtonWidth = config.flashButtonWidth || browseButton.outerWidth();
+        config.flashButtonWindowMode = isTransparent ? SWFUpload.WINDOW_MODE.TRANSPARENT : SWFUpload.WINDOW_MODE.OPAQUE;
+        return fluid.uploader.swfUploadStrategy.convertConfigForSWFUpload(config, events);
+    };
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.setupConfig", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.10"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash10SetupConfig",
+        args: [
+            "{engine}.config",
+            "{multiFileUploader}.events",
+            "{engine}.flashContainer",
+            "{multiFileUploader}.dom.browseButton"
+        ]
+    });
+
+     
+    /*********************************
+     * swfUpload.eventBinder *
+     *********************************/
+     
+    var unbindSWFUploadSelectFiles = function () {
+        // There's a bug in SWFUpload 2.2.0b3 that causes the entire browser to crash 
+        // if selectFile() or selectFiles() is invoked. Remove them so no one will accidently crash their browser.
+        var emptyFunction = function () {};
+        SWFUpload.prototype.selectFile = emptyFunction;
+        SWFUpload.prototype.selectFiles = emptyFunction;
+    };
+    
+    fluid.uploader.swfUploadStrategy.bindFileEventListeners = function (model, events) {
+        // Manually update our public model to keep it in sync with SWFUpload's insane,
+        // always-changing references to its internal model.        
+        var manualModelUpdater = function (file) {
+            fluid.find(model, function (potentialMatch) {
+                if (potentialMatch.id === file.id) {
+                    potentialMatch.filestatus = file.filestatus;
+                    return true;
+                }
+            });
+        };
+        
+        events.onFileStart.addListener(manualModelUpdater);
+        events.onFileProgress.addListener(manualModelUpdater);
+        events.onFileError.addListener(manualModelUpdater);
+        events.onFileSuccess.addListener(manualModelUpdater);
+    };
+    
+    fluid.uploader.swfUploadStrategy.flash10EventBinder = function (model, events, local) {
+        unbindSWFUploadSelectFiles();      
+              
+        events.onUploadStart.addListener(function () {
+            local.disableBrowseButton();
+        });
+        
+        events.afterUploadComplete.addListener(function () {
+            local.enableBrowseButton();            
+        });
+        
+        fluid.uploader.swfUploadStrategy.bindFileEventListeners(model, events);
+    };
+    
+    fluid.demands("fluid.uploader.swfUploadStrategy.eventBinder", [
+        "fluid.uploader.swfUploadStrategy.engine",
+        "fluid.uploader.flash.10"
+    ], {
+        funcName: "fluid.uploader.swfUploadStrategy.flash10EventBinder",
+        args: [
+            "{multiFileUploader}.queue.files",
+            "{multiFileUploader}.events",
+            "{swfUploadStrategy}.local"
+        ]
+    });
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/uploader/js/HTML5UploaderSupport.js b/docs/jscripts/infusion/components/uploader/js/HTML5UploaderSupport.js
new file mode 100644 (file)
index 0000000..e033e54
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+Copyright 2010 OCAD University 
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true, FormData*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    fluid.uploader = fluid.uploader || {};
+    
+    fluid.demands("fluid.uploader.impl", ["fluid.uploader", "fluid.uploader.html5"], {
+        funcName: "fluid.uploader.multiFileUploader"
+    });
+    
+    fluid.uploader.html5Strategy = function (options) {
+        var that = fluid.initLittleComponent("fluid.uploader.html5Strategy", options);
+        fluid.initDependents(that);
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.html5Strategy", {
+        components: {
+            local: {
+                type: "fluid.uploader.html5Strategy.local",
+                options: {
+                    queueSettings: "{multiFileUploader}.options.queueSettings",
+                    browseButton: "{multiFileUploader}.dom.browseButton",
+                    events: "{multiFileUploader}.events"
+                }
+            },
+            
+            remote: {
+                type: "fluid.uploader.remote",
+                options: {
+                    queueSettings: "{multiFileUploader}.options.queueSettings",
+                    events: "{multiFileUploader}.events"
+                }
+            }
+        },
+        
+        mergePolicy: {
+            events: "preserve",
+            browseButton: "preserve"
+        }
+    });
+
+    fluid.demands("fluid.uploader.html5Strategy", "fluid.multiFileUploader", {
+        funcName: "fluid.uploader.html5Strategy",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    fluid.demands("fluid.uploader.progressiveStrategy", "fluid.uploader.html5", {
+        funcName: "fluid.uploader.html5Strategy",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    
+    // TODO: The following two or three functions probably ultimately belong on a that responsible for
+    // coordinating with the XHR. A fileConnection object or something similar.
+    
+    fluid.uploader.html5Strategy.fileSuccessHandler = function (file, events) {
+        events.onFileSuccess.fire(file);
+        events.onFileComplete.fire(file);
+    };
+    
+    fluid.uploader.html5Strategy.progressTracker = function () {
+        var that = {
+            previousBytesLoaded: 0
+        };
+        
+        that.getChunkSize = function (bytesLoaded) {
+            var chunkSize = bytesLoaded - that.previousBytesLoaded;
+            that.previousBytesLoaded = bytesLoaded;
+            return chunkSize;
+        };
+        
+        return that;
+    };
+    
+    var createFileUploadXHR = function (file, events) {
+        var xhr = new XMLHttpRequest();
+        xhr.onreadystatechange = function () {
+            if (xhr.readyState === 4) {
+                fluid.uploader.html5Strategy.fileSuccessHandler(file, events);
+            }
+        };
+
+        var progressTracker = fluid.uploader.html5Strategy.progressTracker();
+        xhr.upload.onprogress = function (pe) {
+            events.onFileProgress.fire(file, progressTracker.getChunkSize(pe.loaded), pe.total);
+        };
+        
+        return xhr;
+    };
+    
+    // Set additional POST parameters for xhr  
+    var setPostParams =  function (formData, postParams) {
+        $.each(postParams,  function (key, value) {
+            formData.append(key, value);
+        });
+    };
+    
+    fluid.uploader.html5Strategy.remote = function (queue, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.html5Strategy.remote", options);
+        that.queue = queue;
+        that.queueSettings = that.options.queueSettings;
+        that.events = that.options.events;
+        
+        // Upload files in the current batch without exceeding the fileUploadLimit
+        // and the fileSizeLimit.  The fileSizeLimit is scaled to KBs.
+        that.start = function () {
+            var files = that.queue.currentBatch.files;
+            var fileUploadLimit = that.queueSettings.fileUploadLimit;
+            
+            for (var i = 0; i < files.length; i++) {
+                var file = files[i];
+                if (fileUploadLimit === 0 ||
+                        that.queue.currentBatch.numFilesCompleted < fileUploadLimit &&
+                        file.size < (that.queueSettings.fileSizeLimit * 1000)) {
+                    that.uploadFile(file);
+                }
+            }
+            that.events.afterUploadComplete.fire(files);
+        };
+        
+        that.uploadFile = function (file) {
+            that.events.onFileStart.fire(file);
+            that.currentXHR = createFileUploadXHR(file, that.events);
+            that.doUpload(file, that.queueSettings, that.currentXHR);            
+        };
+
+        that.stop = function () {
+            var batch = that.queue.currentBatch,
+                file = that.queue.files[batch.fileIdx];
+            
+            file.filestatus = fluid.uploader.fileStatusConstants.CANCELLED;
+            that.queue.shouldStop = true;
+            that.currentXHR.abort();
+            that.events.onUploadStop.fire();
+        };
+        
+        fluid.initDependents(that);
+        that.events.afterReady.fire();
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.html5Strategy.remote", {
+        invokers: {
+            doUpload: "fluid.uploader.html5Strategy.doUpload"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.remote", "fluid.uploader.html5Strategy", {
+        funcName: "fluid.uploader.html5Strategy.remote",
+        args: [
+            "{multiFileUploader}.queue", 
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    var CRLF = "\r\n";
+    
+    /** 
+     * Firefox 4  implementation.  FF4 has implemented a FormData function which
+     * conveniently provides easy construct of set key/value pairs representing 
+     * form fields and their values.  The FormData is then easily sent using the 
+     * XMLHttpRequest send() method.  
+     */
+    fluid.uploader.html5Strategy.doFormDataUpload = function (file, queueSettings, xhr) {
+        var formData = new FormData();
+        formData.append("file", file);
+        
+        setPostParams(formData, queueSettings.postParams);
+        
+        // set post params here.
+        xhr.open("POST", queueSettings.uploadURL, true);
+        xhr.send(formData);
+    };
+    
+    var generateMultipartBoundary = function () {
+        var boundary = "---------------------------";
+        boundary += Math.floor(Math.random() * 32768);
+        boundary += Math.floor(Math.random() * 32768);
+        boundary += Math.floor(Math.random() * 32768);
+        return boundary;
+    };
+    
+    fluid.uploader.html5Strategy.generateMultiPartContent = function (boundary, file) {
+        var multipart = " ";
+        multipart += "--" + boundary + CRLF;
+        multipart += "Content-Disposition: form-data;" +
+                     " name=\"fileData\";" + 
+                     " filename=\"" + file.name + 
+                     "\"" + CRLF;
+        multipart += "Content-Type: " + file.type + CRLF + CRLF;
+        multipart += file.getAsBinary(); // TODO: Ack, concatting binary data to JS String!
+        multipart += CRLF + "--" + boundary + "--" + CRLF;
+        return multipart;
+    };
+    
+    /*
+     * Create the multipart/form-data content by hand to send the file
+     */
+    fluid.uploader.html5Strategy.doManualMultipartUpload = function (file, queueSettings, xhr) {
+        var boundary = generateMultipartBoundary();
+        var multipart = fluid.uploader.html5Strategy.generateMultiPartContent(boundary, file);
+        
+        xhr.open("POST", queueSettings.uploadURL, true);
+        xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
+        xhr.sendAsBinary(multipart);
+    };
+    
+    // Default configuration for older browsers that don't support FormData
+    fluid.demands("fluid.uploader.html5Strategy.doUpload", "fluid.uploader.html5Strategy.remote", {
+        funcName: "fluid.uploader.html5Strategy.doManualMultipartUpload",
+        args: ["@0", "@1", "@2"]
+    });
+    
+    // Configuration for FF4, Chrome, and Safari 4+, all of which support FormData correctly.
+    fluid.demands("fluid.uploader.html5Strategy.doUpload", [
+        "fluid.uploader.html5Strategy.remote", 
+        "fluid.browser.supportsFormData"
+    ], {
+        funcName: "fluid.uploader.html5Strategy.doFormDataUpload",
+        args: ["@0", "@1", "@2"]
+    });
+    
+    
+    /*
+     * Return the active multi-file input from the input stack
+     */
+    var getActiveMultiFileInput = function (browseButton) {
+        var inputs = browseButton.children();
+        return inputs.eq(inputs.length - 1);
+    };
+    
+    fluid.uploader.html5Strategy.local = function (queue, options) {
+        var that = fluid.initLittleComponent("fluid.uploader.html5Strategy.local", options);
+        that.queue = queue;
+        that.events = that.options.events;
+        that.queueSettings = that.options.queueSettings;
+
+        // Add files to the file queue without exceeding the fileQueueLimit 
+        that.addFiles = function (files) {
+            var filesToUpload = files.length;
+            var fileQueueLimit = that.queueSettings.fileQueueLimit;
+            var filesInQueue = that.queue.files.length - that.queue.getUploadedFiles().length;
+            
+            if (fileQueueLimit !== 0 && (filesToUpload + filesInQueue) > fileQueueLimit) { 
+                filesToUpload = fileQueueLimit - filesInQueue;
+            } 
+            
+            for (var i = 0; i < filesToUpload; i++) {
+                var file = files[i];
+                file.filestatus = fluid.uploader.fileStatusConstants.QUEUED;
+                file.id = "file-" + fluid.allocateGuid();
+                that.events.afterFileQueued.fire(file);
+            }
+            
+            that.events.afterFileDialog.fire(files.length);    
+        };
+        
+        that.removeFile = function (file) {
+        };
+        
+        that.enableBrowseButton = function () {
+            var activeMultiFileInput = getActiveMultiFileInput(that.options.browseButton);
+            activeMultiFileInput.removeAttr("disabled");
+        };
+        
+        that.disableBrowseButton = function () {
+            var activeMultiFileInput = getActiveMultiFileInput(that.options.browseButton);
+            activeMultiFileInput.attr("disabled", "disabled");
+        };
+        
+        fluid.initDependents(that);
+        return that;
+    };
+    
+    
+    fluid.defaults("fluid.uploader.html5Strategy.local", {
+        components: {
+            browseHandler: {
+                type: "fluid.uploader.html5Strategy.browseHandler",
+                options: {
+                    browseButton: "{multiFileUploader}.dom.browseButton",
+                    queueSettings: "{multiFileUploader}.options.queueSettings",
+                    events: "{multiFileUploader}.events",
+                    addFilesFn: "{local}.addFiles"
+                }
+            }
+        },
+        mergePolicy: {
+            browseButton: "preserve",
+            events: "preserve",
+            // TODO: This is awkward--refactor
+            addFilesFn: "preserve"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.html5Strategy.local", "fluid.uploader.html5Strategy", {
+        funcName: "fluid.uploader.html5Strategy.local",
+        args: [
+            "{multiFileUploader}.queue",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+    
+    var bindEventsToFileInput = function (that, fileInput) {
+        fileInput.click(function () {
+            that.events.onFileDialog.fire();
+        });
+        
+        fileInput.change(function () {
+            var files = fileInput[0].files;
+            that.options.addFilesFn.apply(null, [files]);
+            that.renderFreshMultiFileInput();
+        });
+        
+        fileInput.focus(function () {
+            that.options.browseButton.addClass("focus");
+        });
+        
+        fileInput.blur(function () {
+            that.options.browseButton.removeClass("focus");
+        });
+    };
+    
+    var renderMultiFileInput = function (that) {
+        var multiFileInput = $(that.options.multiFileInputMarkup);
+        var fileTypes = (that.options.queueSettings.fileTypes).replace(/\;/g, ',');       
+        multiFileInput.attr("accept", fileTypes);
+        that.inputs.push(multiFileInput);
+        bindEventsToFileInput(that, multiFileInput);
+        return multiFileInput;
+    };
+    
+    var setupBrowseHandler = function (that) {
+        var multiFileInput = renderMultiFileInput(that);        
+        that.options.browseButton.append(multiFileInput);
+        that.options.browseButton.attr("tabindex", -1);
+    };
+    
+    fluid.uploader.html5Strategy.browseHandler = function (options) {
+        var that = fluid.initLittleComponent("fluid.uploader.html5Strategy.browseHandler", options);
+        that.inputs = [];
+        that.events = that.options.events;
+        
+        that.renderFreshMultiFileInput = function () {
+            // Update the stack of multi file input elements we have in the DOM.
+            var previousInput = that.inputs[that.inputs.length - 1];
+            previousInput.hide();
+            previousInput.attr("tabindex", -1);
+            var newInput = renderMultiFileInput(that);
+            previousInput.after(newInput);
+        };
+        
+        setupBrowseHandler(that);
+        return that;
+    };
+    
+    fluid.defaults("fluid.uploader.html5Strategy.browseHandler", {
+        multiFileInputMarkup: "<input type='file' multiple='' class='fl-hidden'/>"
+    });
+    
+    fluid.demands("fluid.uploader.html5Strategy.browseHandler", "fluid.uploader.html5Strategy.local", {
+        funcName: "fluid.uploader.html5Strategy.browseHandler",
+        args: [
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+
+})(jQuery, fluid_1_3);    
diff --git a/docs/jscripts/infusion/components/uploader/js/Scroller.js b/docs/jscripts/infusion/components/uploader/js/Scroller.js
new file mode 100644 (file)
index 0000000..1dd5f9b
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    var refreshView = function (that) {
+        var maxHeight = that.options.maxHeight;
+        var isOverMaxHeight = (that.scrollingElm.children().eq(0).height() > maxHeight);
+        var setHeight = (isOverMaxHeight) ? maxHeight : "";
+        that.scrollingElm.height(setHeight);
+    };
+    
+    var scrollBottom = function (that) {
+        that.scrollingElm[0].scrollTop = that.scrollingElm[0].scrollHeight;
+    };
+    
+    var scrollTo = function (that, element) {
+        if (!element || element.length < 1) {
+            return;
+        }
+        
+        var padTop = 0;
+        var padBottom = 0;
+        
+        var elmPosTop = element[0].offsetTop;
+        var elmHeight = element.height();
+        var containerScrollTop = that.scrollingElm[0].scrollTop;
+        var containerHeight = that.scrollingElm.height();
+        
+        if (that.options.padScroll) {
+            // if the combined height of the elements is greater than the 
+            // viewport then then scrollTo element would not be in view
+            var prevElmHeight = element.prev().height();
+            padTop = (prevElmHeight + elmHeight <= containerHeight) ? prevElmHeight : 0;
+            var nextElmHeight = element.next().height();
+            padBottom =  (nextElmHeight + elmHeight <= containerHeight) ? nextElmHeight : 0;
+        }
+        
+        // if the top of the row is ABOVE the view port move the row into position
+        if ((elmPosTop - padTop) < containerScrollTop) {
+            that.scrollingElm[0].scrollTop = elmPosTop - padTop;
+        }
+        
+        // if the bottom of the row is BELOW the viewport then scroll it into position
+        if (((elmPosTop + elmHeight) + padBottom) > (containerScrollTop + containerHeight)) {
+            elmHeight = (elmHeight < containerHeight) ? elmHeight : containerHeight;
+            that.scrollingElm[0].scrollTop = (elmPosTop - containerHeight + elmHeight + padBottom);
+        }
+    };
+    
+    var setupScroller = function (that) {
+        that.scrollingElm = that.container.parents(that.options.selectors.wrapper);
+        
+        // We should render our own sensible default if the scrolling element is missing.
+        if (!that.scrollingElm.length) {
+            fluid.fail({
+                name: "Missing Scroller",
+                message: "The scroller wrapper element was not found."
+            });
+        }
+        
+        // set the height of the scroller unless this is IE6
+        if (!$.browser.msie || $.browser.version > 6) {
+            that.scrollingElm.css("max-height", that.options.maxHeight);
+        }
+    };
+    
+    /**
+     * Creates a new Scroller component.
+     * 
+     * @param {Object} container the element containing the collection of things to make scrollable 
+     * @param {Object} options configuration options for the component
+     */
+    fluid.scroller = function (container, options) {
+        var that = fluid.initView("fluid.scroller", container, options);
+        setupScroller(that);
+
+        /**
+         * Scrolls the specified element into view
+         * 
+         * @param {jQuery} element the element to scroll into view
+         */
+        that.scrollTo = function (element) {
+            scrollTo(that, element);
+        };
+        
+        /**
+         * Scrolls to the bottom of the view.
+         */
+        that.scrollBottom = function () {
+            scrollBottom(that);
+        };
+        
+        /**
+         * Refreshes the scroller's appearance based on any changes to the document.
+         */
+        that.refreshView = function () {
+            if ($.browser.msie && $.browser.version < 7) {
+                refreshView(that);
+            }
+        };
+        
+        that.refreshView();
+        return that;
+    };
+    
+    fluid.defaults("fluid.scroller", {  
+        selectors: {
+            wrapper: ".flc-scroller"
+        },
+        
+        maxHeight: 180,
+        
+        padScroll: true
+    });
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/uploader/js/Uploader.js b/docs/jscripts/infusion/components/uploader/js/Uploader.js
new file mode 100644 (file)
index 0000000..dd79cf6
--- /dev/null
@@ -0,0 +1,665 @@
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2008-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true, window, swfobject*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+/************
+ * Uploader *
+ ************/
+
+(function ($, fluid) {
+    
+    var fileOrFiles = function (that, numFiles) {
+        return (numFiles === 1) ? that.options.strings.progress.singleFile : 
+                                  that.options.strings.progress.pluralFiles;
+    };
+    
+    var enableElement = function (that, elm) {
+        elm.removeAttr("disabled");
+        elm.removeClass(that.options.styles.dim);
+    };
+    
+    var disableElement = function (that, elm) {
+        elm.attr("disabled", "disabled");
+        elm.addClass(that.options.styles.dim);
+    };
+    
+    var showElement = function (that, elm) {
+        elm.removeClass(that.options.styles.hidden);
+    };
+     
+    var hideElement = function (that, elm) {
+        elm.addClass(that.options.styles.hidden);
+    };
+    
+    var setTotalProgressStyle = function (that, didError) {
+        didError = didError || false;
+        var indicator = that.totalProgress.indicator;
+        indicator.toggleClass(that.options.styles.totalProgress, !didError);
+        indicator.toggleClass(that.options.styles.totalProgressError, didError);
+    };
+    
+    var setStateEmpty = function (that) {
+        disableElement(that, that.locate("uploadButton"));
+        
+        // If the queue is totally empty, treat it specially.
+        if (that.queue.files.length === 0) { 
+            that.locate("browseButtonText").text(that.options.strings.buttons.browse);
+            that.locate("browseButton").removeClass(that.options.styles.browseButton);
+            showElement(that, that.locate("instructions"));
+        }
+    };
+    
+    var setStateDone = function (that) {
+        disableElement(that, that.locate("uploadButton"));
+        enableElement(that, that.locate("browseButton"));
+        that.strategy.local.enableBrowseButton();
+        hideElement(that, that.locate("pauseButton"));
+        showElement(that, that.locate("uploadButton"));
+    };
+
+    var setStateLoaded = function (that) {
+        that.locate("browseButtonText").text(that.options.strings.buttons.addMore);
+        that.locate("browseButton").addClass(that.options.styles.browseButton);
+        hideElement(that, that.locate("pauseButton"));
+        showElement(that, that.locate("uploadButton"));
+        enableElement(that, that.locate("uploadButton"));
+        enableElement(that, that.locate("browseButton"));
+        that.strategy.local.enableBrowseButton();
+        hideElement(that, that.locate("instructions"));
+        that.totalProgress.hide();
+    };
+    
+    var setStateUploading = function (that) {
+        that.totalProgress.hide(false, false);
+        setTotalProgressStyle(that);
+        hideElement(that, that.locate("uploadButton"));
+        disableElement(that, that.locate("browseButton"));
+        that.strategy.local.disableBrowseButton();
+        enableElement(that, that.locate("pauseButton"));
+        showElement(that, that.locate("pauseButton"));
+        that.locate(that.options.focusWithEvent.afterUploadStart).focus();
+    };    
+    
+    var renderUploadTotalMessage = function (that) {
+        // Render template for the total file status message.
+        var numReadyFiles = that.queue.getReadyFiles().length;
+        var bytesReadyFiles = that.queue.sizeOfReadyFiles();
+        var fileLabelStr = fileOrFiles(that, numReadyFiles);
+                                                   
+        var totalStateStr = fluid.stringTemplate(that.options.strings.progress.toUploadLabel, {
+            fileCount: numReadyFiles, 
+            fileLabel: fileLabelStr, 
+            totalBytes: fluid.uploader.formatFileSize(bytesReadyFiles)
+        });
+        that.locate("totalFileStatusText").html(totalStateStr);
+    };
+        
+    var updateTotalProgress = function (that) {
+        var batch = that.queue.currentBatch;
+        var totalPercent = fluid.uploader.derivePercent(batch.totalBytesUploaded, batch.totalBytes);
+        var numFilesInBatch = batch.files.length;
+        var fileLabelStr = fileOrFiles(that, numFilesInBatch);
+        
+        var totalProgressStr = fluid.stringTemplate(that.options.strings.progress.totalProgressLabel, {
+            curFileN: batch.fileIdx + 1, 
+            totalFilesN: numFilesInBatch, 
+            fileLabel: fileLabelStr,
+            currBytes: fluid.uploader.formatFileSize(batch.totalBytesUploaded), 
+            totalBytes: fluid.uploader.formatFileSize(batch.totalBytes)
+        });  
+        that.totalProgress.update(totalPercent, totalProgressStr);
+    };
+    
+    var updateTotalAtCompletion = function (that) {
+        var numErroredFiles = that.queue.getErroredFiles().length;
+        var numTotalFiles = that.queue.files.length;
+        var fileLabelStr = fileOrFiles(that, numTotalFiles);
+        
+        var errorStr = "";
+        
+        // if there are errors then change the total progress bar
+        // and set up the errorStr so that we can use it in the totalProgressStr
+        if (numErroredFiles > 0) {
+            var errorLabelString = (numErroredFiles === 1) ? that.options.strings.progress.singleError : 
+                                                             that.options.strings.progress.pluralErrors;
+            setTotalProgressStyle(that, true);
+            errorStr = fluid.stringTemplate(that.options.strings.progress.numberOfErrors, {
+                errorsN: numErroredFiles,
+                errorLabel: errorLabelString
+            });
+        }
+        
+        var totalProgressStr = fluid.stringTemplate(that.options.strings.progress.completedLabel, {
+            curFileN: that.queue.getUploadedFiles().length, 
+            totalFilesN: numTotalFiles,
+            errorString: errorStr,
+            fileLabel: fileLabelStr,
+            totalCurrBytes: fluid.uploader.formatFileSize(that.queue.sizeOfUploadedFiles())
+        });
+        
+        that.totalProgress.update(100, totalProgressStr);
+    };
+
+    /*
+     * Summarizes the status of all the files in the file queue.  
+     */
+    var updateQueueSummaryText = function (that) {
+        var fileQueueTable = that.locate("fileQueue");
+        
+        if (that.queue.files.length === 0) {
+            fileQueueTable.attr("summary", that.options.strings.queue.emptyQueue);
+        }
+        else {
+            var queueSummary = fluid.stringTemplate(that.options.strings.queue.queueSummary, {
+                totalUploaded: that.queue.getUploadedFiles().length, 
+                totalInUploadQueue: that.queue.files.length - that.queue.getUploadedFiles().length
+            });        
+            
+            fileQueueTable.attr("summary", queueSummary);
+        }
+    };
+    
+    var bindDOMEvents = function (that) {
+        that.locate("uploadButton").click(function () {
+            that.start();
+        });
+
+        that.locate("pauseButton").click(function () {
+            that.stop();
+        });
+    };
+
+    var updateStateAfterFileDialog = function (that) {
+        if (that.queue.getReadyFiles().length > 0) {
+            setStateLoaded(that);
+            renderUploadTotalMessage(that);
+            that.locate(that.options.focusWithEvent.afterFileDialog).focus();
+            updateQueueSummaryText(that);
+        }
+    };
+    
+    var updateStateAfterFileRemoval = function (that) {
+        if (that.queue.getReadyFiles().length === 0) {
+            setStateEmpty(that);
+        }
+        renderUploadTotalMessage(that);
+        updateQueueSummaryText(that);
+    };
+    
+    var updateStateAfterCompletion = function (that) {
+        if (that.queue.getReadyFiles().length === 0) {
+            setStateDone(that);
+        } else {
+            setStateLoaded(that);
+        }
+        updateTotalAtCompletion(that);
+        updateQueueSummaryText(that);
+    }; 
+    
+    var bindEvents = function (that) {       
+        that.events.afterFileDialog.addListener(function () {
+            updateStateAfterFileDialog(that);
+        });
+        
+        that.events.afterFileQueued.addListener(function (file) {
+            that.queue.addFile(file); 
+        });
+        
+        that.events.onFileRemoved.addListener(function (file) {
+            that.removeFile(file);
+        });
+        
+        that.events.afterFileRemoved.addListener(function () {
+            updateStateAfterFileRemoval(that);
+        });
+        
+        that.events.onUploadStart.addListener(function () {
+            setStateUploading(that);
+        });
+        
+        that.events.onUploadStop.addListener(function () {
+            that.locate(that.options.focusWithEvent.afterUploadStop).focus();
+        });
+        
+        that.events.onFileStart.addListener(function (file) {
+            file.filestatus = fluid.uploader.fileStatusConstants.IN_PROGRESS;
+            that.queue.startFile();
+        });
+        
+        that.events.onFileProgress.addListener(function (file, currentBytes, totalBytes) {
+            that.queue.updateBatchStatus(currentBytes);
+            updateTotalProgress(that); 
+        });
+        
+        that.events.onFileComplete.addListener(function (file) {
+            that.queue.finishFile(file);
+            that.events.afterFileComplete.fire(file); 
+            
+            if (that.queue.shouldUploadNextFile()) {
+                that.strategy.remote.start();
+            } else {
+                if (that.queue.shouldStop) {
+                    that.strategy.remote.stop();
+                }
+
+                that.events.afterUploadComplete.fire(that.queue.currentBatch.files);
+                that.queue.clearCurrentBatch();
+            }
+        });
+        
+        that.events.onFileSuccess.addListener(function (file) {
+            file.filestatus = fluid.uploader.fileStatusConstants.COMPLETE;
+            if (that.queue.currentBatch.bytesUploadedForFile === 0) {
+                that.queue.currentBatch.totalBytesUploaded += file.size;
+            }
+            
+            updateTotalProgress(that); 
+        });
+        
+        that.events.onFileError.addListener(function (file, error) {
+            file.filestatus = fluid.uploader.fileStatusConstants.ERROR;
+            if (error === fluid.uploader.errorConstants.UPLOAD_STOPPED) {
+                that.queue.isUploading = false;
+            } else if (that.queue.isUploading) {
+                that.queue.currentBatch.totalBytesUploaded += file.size;
+                that.queue.currentBatch.numFilesErrored++;
+            }
+        });
+
+        that.events.afterUploadComplete.addListener(function () {
+            that.queue.isUploading = false;
+            updateStateAfterCompletion(that);
+        });
+    };
+    
+    var setupUploader = function (that) {
+        // Setup the environment appropriate if we're in demo mode.
+        if (that.options.demo) {
+            that.demo = fluid.typeTag("fluid.uploader.demo");
+        }
+        
+        fluid.initDependents(that);                 
+
+        // Upload button should not be enabled until there are files to upload
+        disableElement(that, that.locate("uploadButton"));
+        bindDOMEvents(that);
+        bindEvents(that);
+        
+        updateQueueSummaryText(that);
+        that.statusUpdater();
+        
+        // Uploader uses application-style keyboard conventions, so give it a suitable role.
+        that.container.attr("role", "application");
+    };
+    
+    /**
+     * Instantiates a new Uploader component.
+     * 
+     * @param {Object} container the DOM element in which the Uploader lives
+     * @param {Object} options configuration options for the component.
+     */
+    fluid.uploader = function (container, options) {
+        var that = fluid.typeTag("fluid.uploader");
+        // Set up the environment for progressive enhancement.
+        if (fluid.progressiveChecker) {
+            fluid.staticEnvironment.uploaderContext = fluid.invoke("fluid.progressiveChecker", 
+                                                                   null, 
+                                                                   that);
+        }
+        
+        // Invoke an Uploader implementation, which will be specifically resolved using IoC 
+        // based on the static environment configured by the progressiveChecker above.
+        return fluid.invoke("fluid.uploader.impl", [container, options], that);
+    };
+    
+    fluid.demands("fluid.progressiveChecker", "fluid.uploader", {
+        funcName: "fluid.progressiveChecker",
+        args: [{
+            checks: [
+                {
+                    feature: "{fluid.browser.supportsBinaryXHR}",
+                    contextName: "fluid.uploader.html5"
+                },
+                {
+                    feature: "{fluid.browser.supportsFlash}",
+                    contextName: "fluid.uploader.swfUpload"
+                }
+            ],
+
+            defaultTypeTag: fluid.typeTag("fluid.uploader.singleFile")
+        }]
+    });
+    
+    // This method has been deprecated as of Infusion 1.3. Use fluid.uploader() instead, 
+    // which now includes built-in support for progressive enhancement.
+    fluid.progressiveEnhanceableUploader = function (container, enhanceable, options) {
+        return fluid.uploader(container, options);
+    };
+
+    /**
+     * Multiple file Uploader implementation. Use fluid.uploader() for IoC-resolved, progressively
+     * enhanceable Uploader, or call this directly if you don't want support for old-style single uploads
+     *
+     * @param {jQueryable} container the component's container
+     * @param {Object} options configuration options
+     */
+    fluid.uploader.multiFileUploader = function (container, options) {
+        var that = fluid.initView("fluid.uploader.multiFileUploader", container, options);
+        that.queue = fluid.uploader.fileQueue();
+        
+        /**
+         * Opens the native OS browse file dialog.
+         */
+        that.browse = function () {
+            if (!that.queue.isUploading) {
+                that.strategy.local.browse();
+            }
+        };
+        
+        /**
+         * Removes the specified file from the upload queue.
+         * 
+         * @param {File} file the file to remove
+         */
+        that.removeFile = function (file) {
+            that.queue.removeFile(file);
+            that.strategy.local.removeFile(file);
+            that.events.afterFileRemoved.fire(file);
+        };
+        
+        /**
+         * Starts uploading all queued files to the server.
+         */
+        that.start = function () {
+            that.queue.start();
+            that.events.onUploadStart.fire(that.queue.currentBatch.files); 
+            that.strategy.remote.start();
+        };
+        
+        /**
+         * Cancels an in-progress upload.
+         */
+        that.stop = function () {
+            /* FLUID-822: while stopping the upload cycle while a file is in mid-upload should be possible
+             * in practice, it sets up a state where when the upload cycle is restarted SWFUpload will get stuck
+             * therefore we only stop the upload after a file has completed but before the next file begins. 
+             */
+            that.queue.shouldStop = true;
+            that.events.onUploadStop.fire();
+        };
+        
+        setupUploader(that);
+        return that;  
+    };
+    
+    fluid.defaults("fluid.uploader.multiFileUploader", {
+        components: {
+            strategy: {
+                type: "fluid.uploader.progressiveStrategy"
+            },
+            
+            fileQueueView: {
+                type: "fluid.uploader.fileQueueView",
+                options: {
+                    model: "{multiFileUploader}.queue.files",
+                    uploaderContainer: "{multiFileUploader}.container",
+                    events: "{multiFileUploader}.events"
+                }
+            },
+            
+            totalProgress: {
+                type: "fluid.uploader.totalProgressBar",
+                options: {
+                    selectors: {
+                        progressBar: ".flc-uploader-queue-footer",
+                        displayElement: ".flc-uploader-total-progress", 
+                        label: ".flc-uploader-total-progress-text",
+                        indicator: ".flc-uploader-total-progress",
+                        ariaElement: ".flc-uploader-total-progress"
+                    }
+                }
+            }
+        },
+        
+        invokers: {
+            statusUpdater: "fluid.uploader.ariaLiveRegionUpdater"
+        },
+        
+        queueSettings: {
+            uploadURL: "",
+            postParams: {},
+            fileSizeLimit: "20480",
+            fileTypes: "*",
+            fileTypesDescription: null,
+            fileUploadLimit: 0,
+            fileQueueLimit: 0
+        },
+
+        demo: false,
+        
+        selectors: {
+            fileQueue: ".flc-uploader-queue",
+            browseButton: ".flc-uploader-button-browse",
+            browseButtonText: ".flc-uploader-button-browse-text",
+            uploadButton: ".flc-uploader-button-upload",
+            pauseButton: ".flc-uploader-button-pause",
+            totalFileStatusText: ".flc-uploader-total-progress-text",
+            instructions: ".flc-uploader-browse-instructions",
+            statusRegion: ".flc-uploader-status-region"
+        },
+
+        // Specifies a selector name to move keyboard focus to when a particular event fires.
+        // Event listeners must already be implemented to use these options.
+        focusWithEvent: {
+            afterFileDialog: "uploadButton",
+            afterUploadStart: "pauseButton",
+            afterUploadStop: "uploadButton"
+        },
+        
+        styles: {
+            disabled: "fl-uploader-disabled",
+            hidden: "fl-uploader-hidden",
+            dim: "fl-uploader-dim",
+            totalProgress: "fl-uploader-total-progress-okay",
+            totalProgressError: "fl-uploader-total-progress-errored",
+            browseButton: "fl-uploader-browseMore"
+        },
+        
+        events: {
+            afterReady: null,
+            onFileDialog: null,
+            afterFileQueued: null,
+            onFileRemoved: null,
+            afterFileRemoved: null,
+            onQueueError: null,
+            afterFileDialog: null,
+            onUploadStart: null,
+            onUploadStop: null,
+            onFileStart: null,
+            onFileProgress: null,
+            onFileError: null,
+            onFileSuccess: null,
+            onFileComplete: null,
+            afterFileComplete: null,
+            afterUploadComplete: null
+        },
+
+        strings: {
+            progress: {
+                toUploadLabel: "To upload: %fileCount %fileLabel (%totalBytes)", 
+                totalProgressLabel: "Uploading: %curFileN of %totalFilesN %fileLabel (%currBytes of %totalBytes)", 
+                completedLabel: "Uploaded: %curFileN of %totalFilesN %fileLabel (%totalCurrBytes)%errorString",
+                numberOfErrors: ", %errorsN %errorLabel",
+                singleFile: "file",
+                pluralFiles: "files",
+                singleError: "error",
+                pluralErrors: "errors"
+            },
+            buttons: {
+                browse: "Browse Files",
+                addMore: "Add More",
+                stopUpload: "Stop Upload",
+                cancelRemaning: "Cancel remaining Uploads",
+                resumeUpload: "Resume Upload"
+            },
+            queue: {
+                emptyQueue: "File list: No files waiting to be uploaded.",
+                queueSummary: "File list:  %totalUploaded files uploaded, %totalInUploadQueue file waiting to be uploaded." 
+            }
+        },
+        
+        mergePolicy: {
+            model: "preserve"
+        }
+    });
+    
+    fluid.demands("fluid.uploader.impl", "fluid.uploader", {
+        funcName: "fluid.uploader.multiFileUploader"
+    });
+    
+    fluid.demands("fluid.uploader.totalProgressBar", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.progress",
+        args: [
+            "{multiFileUploader}.container",
+            fluid.COMPONENT_OPTIONS
+        ]
+    });
+    
+        
+   /**
+    * Pretty prints a file's size, converting from bytes to kilobytes or megabytes.
+    * 
+    * @param {Number} bytes the files size, specified as in number bytes.
+    */
+    fluid.uploader.formatFileSize = function (bytes) {
+        if (typeof(bytes) === "number") {
+            if (bytes === 0) {
+                return "0.0 KB";
+            } else if (bytes > 0) {
+                if (bytes < 1048576) {
+                    return (Math.ceil(bytes / 1024 * 10) / 10).toFixed(1) + " KB";
+                }
+                else {
+                    return (Math.ceil(bytes / 1048576 * 10) / 10).toFixed(1) + " MB";
+                }
+            }
+        }
+        return "";
+    };
+
+    fluid.uploader.derivePercent = function (num, total) {
+        return Math.round((num * 100) / total);
+    };
+     
+    // TODO: Refactor this to be a general ARIA utility
+    fluid.uploader.ariaLiveRegionUpdater = function (statusRegion, totalFileStatusText, events) {
+        statusRegion.attr("role", "log");     
+        statusRegion.attr("aria-live", "assertive");
+        statusRegion.attr("aria-relevant", "text");
+        statusRegion.attr("aria-atomic", "true");
+
+        var regionUpdater = function () {
+            statusRegion.text(totalFileStatusText.text());
+        };
+
+        events.afterFileDialog.addListener(regionUpdater);
+        events.afterFileRemoved.addListener(regionUpdater);
+        events.afterUploadComplete.addListener(regionUpdater);
+    };
+    
+    fluid.demands("fluid.uploader.ariaLiveRegionUpdater", "fluid.uploader.multiFileUploader", {
+        funcName: "fluid.uploader.ariaLiveRegionUpdater",
+        args: [
+            "{multiFileUploader}.dom.statusRegion",
+            "{multiFileUploader}.dom.totalFileStatusText",
+            "{multiFileUploader}.events"
+        ]
+    });
+
+    
+    /**************************************************
+     * Error constants for the Uploader               *
+     * TODO: These are SWFUpload-specific error codes *
+     **************************************************/
+     
+    fluid.uploader.errorConstants = {
+        HTTP_ERROR: -200,
+        MISSING_UPLOAD_URL: -210,
+        IO_ERROR: -220,
+        SECURITY_ERROR: -230,
+        UPLOAD_LIMIT_EXCEEDED: -240,
+        UPLOAD_FAILED: -250,
+        SPECIFIED_FILE_ID_NOT_FOUND: -260,
+        FILE_VALIDATION_FAILED: -270,
+        FILE_CANCELLED: -280,
+        UPLOAD_STOPPED: -290
+    };
+    
+    fluid.uploader.fileStatusConstants = {
+        QUEUED: -1,
+        IN_PROGRESS: -2,
+        ERROR: -3,
+        COMPLETE: -4,
+        CANCELLED: -5
+    };
+
+
+    var toggleVisibility = function (toShow, toHide) {
+        // For FLUID-2789: hide() doesn't work in Opera
+        if (window.opera) { 
+            toShow.show().removeClass("hideUploaderForOpera");
+            toHide.show().addClass("hideUploaderForOpera");
+        } else {
+            toShow.show();
+            toHide.hide();
+        }
+    };
+
+    // TODO: Need to resolve the issue of the gracefully degraded view being outside of the component's
+    // container. Perhaps we can embed a standard HTML 5 file input element right in the template, 
+    // and hide everything else?
+    var determineContainer = function (options) {
+        var defaults = fluid.defaults("fluid.uploader.singleFileStrategy");
+        return (options && options.container) ? options.container : defaults.container;
+    };
+
+
+    /**
+     * Single file Uploader implementation. Use fluid.uploader() for IoC-resolved, progressively
+     * enhanceable Uploader, or call this directly if you only want a standard single file uploader.
+     * But why would you want that?
+     *
+     * @param {jQueryable} container the component's container
+     * @param {Object} options configuration options
+     */
+    fluid.uploader.singleFileUploader = function (container, options) {
+        var that = fluid.initView("fluid.uploader.singleFileUploader", container, options);
+        // TODO: direct DOM fascism that will fail with multiple uploaders on a single page.
+        toggleVisibility($(that.options.selectors.basicUpload), that.container);
+        return that;
+    };
+
+    fluid.defaults("fluid.uploader.singleFileUploader", {
+        selectors: {
+            basicUpload: ".fl-progEnhance-basic"
+        }
+    });
+
+    fluid.demands("fluid.uploader.impl", ["fluid.uploader", "fluid.uploader.singleFile"], {
+        funcName: "fluid.uploader.singleFileUploader"
+    });
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/components/uploader/js/UploaderCompatibility-Infusion1.2.js b/docs/jscripts/infusion/components/uploader/js/UploaderCompatibility-Infusion1.2.js
new file mode 100644 (file)
index 0000000..c682595
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery, fluid_1_3:true*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+/*********************************************************************************************
+ * Note: this file should not be included in any Infusion build.                             *
+ * Instead, users can choose to add this file manually if they need backwards compatibility. *
+ *********************************************************************************************/
+(function ($, fluid) {
+    
+    fluid.registerNamespace("fluid.compat.fluid_1_2.uploader");
+    fluid.staticEnvironment.uploaderCompatibility = fluid.typeTag("fluid.uploader.fluid_1_2");
+
+    fluid.compat.fluid_1_2.uploader.optionsRules = {
+        "components": {
+            expander: {
+                type: "fluid.model.transform.firstValue",
+                values: [
+                    {
+                        expander: {
+                            type: "fluid.model.transform.value",
+                            path: "components"
+                        }
+                    },
+                    {
+                        expander: {
+                            type: "fluid.model.transform.value",
+                            value: {
+                                "strategy": {
+                                    "options": {
+                                        "flashMovieSettings": {
+                                            expander: {
+                                                type: "fluid.model.transform.value",
+                                                value: {
+                                                    "flashURL": "uploadManager.options.flashURL",
+                                                    "flashButtonPeerId": "decorators.0.options.flashButtonPeerId",
+                                                    "flashButtonAlwaysVisible": "decorators.0.options.flashButtonAlwaysVisible",
+                                                    "flashButtonTransparentEvenInIE": "decorators.0.options.flashButtonTransparentEvenInIE",
+                                                    "flashButtonImageURL": "decorators.0.options.flashButtonImageURL",
+                                                    "flashButtonCursorEffect": "decorators.0.options.flashButtonCursorEffect",
+                                                    "debug": "decorators.0.options.debug"
+                                                }
+                                            }
+                                        },
+                                        "styles": "decorators.0.options.styles"
+                                    }
+                                },
+                                "fileQueueView": "fileQueueView",
+                                "totalProgressBar": "totalProgressBar"
+                            }
+                        }
+                    }
+                ]
+            }
+        },
+        "invokers": "invokers",
+        "queueSettings": "uploadManager.options",
+        "demo": "demo",
+        "selectors": "selectors",
+        "focusWithEvent": "focusWithEvent",
+        "styles": "styles",
+        "listeners": "listeners",
+        "strings": "strings",
+        "mergePolicy": "mergePolicy"
+    };
+    
+    // Monkey patch fluid.uploader with an options-chewing wrapper.
+    // TODO: Replace this with an IoC-resolved solution.
+    var multiFileImpl = fluid.uploader.multiFileUploader;
+    fluid.uploader.multiFileUploader = function (container, options) {
+        options = fluid.model.transformWithRules(options, fluid.compat.fluid_1_2.uploader.optionsRules);
+        return multiFileImpl(container, options);
+    };
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/DataBinding.js b/docs/jscripts/infusion/framework/core/js/DataBinding.js
new file mode 100644 (file)
index 0000000..50ed241
--- /dev/null
@@ -0,0 +1,589 @@
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2009 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.BINDING_ROOT_KEY = "fluid-binding-root";
+    
+    /** Recursively find any data stored under a given name from a node upwards
+     * in its DOM hierarchy **/
+     
+    fluid.findData = function(elem, name) {
+        while (elem) {
+            var data = $.data(elem, name);
+            if (data) {return data;}
+            elem = elem.parentNode;
+            }
+        };
+  
+    fluid.bindFossils = function(node, data, fossils) {
+        $.data(node, fluid.BINDING_ROOT_KEY, {data: data, fossils: fossils});
+        };
+        
+    fluid.boundPathForNode = function(node, fossils) {
+        node = fluid.unwrap(node);
+        var key = node.name || node.id;
+        var record = fossils[key];
+        return record? record.EL: null;
+    };
+  
+    fluid.findForm = function (node) {
+      return fluid.findAncestor(node, 
+          function(element) {return element.nodeName.toLowerCase() === "form";});
+    };
+    
+    /** A generalisation of jQuery.val to correctly handle the case of acquiring and
+     * setting the value of clustered radio button/checkbox sets, potentially, given
+     * a node corresponding to just one element.
+     */
+    fluid.value = function (nodeIn, newValue) {
+        var node = fluid.unwrap(nodeIn);
+        var multiple = false;
+        if (node.nodeType === undefined && node.length > 1) {
+            node = node[0];
+            multiple = true;
+        }
+        if ("input" !== node.nodeName.toLowerCase()
+           || ! /radio|checkbox/.test(node.type)) {return $(node).val(newValue);}
+        var name = node.name;
+        if (name === undefined) {
+            fluid.fail("Cannot acquire value from node " + fluid.dumpEl(node) + " which does not have name attribute set");
+        }
+        var elements;
+        if (multiple) {
+            elements = nodeIn;
+        }
+        else {
+            elements = document.getElementsByName(name);
+            var scope = fluid.findForm(node);
+            elements = $.grep(elements, 
+              function(element) {
+                if (element.name !== name) {return false;}
+                return !scope || fluid.dom.isContainer(scope, element);
+              });
+        }
+        if (newValue !== undefined) {
+            if (typeof(newValue) === "boolean") {
+                newValue = (newValue? "true" : "false");
+            }
+          // jQuery gets this partially right, but when dealing with radio button array will
+          // set all of their values to "newValue" rather than setting the checked property
+          // of the corresponding control. 
+            $.each(elements, function() {
+               this.checked = (newValue instanceof Array? 
+                 $.inArray(this.value, newValue) !== -1 : newValue === this.value);
+            });
+        }
+        else { // this part jQuery will not do - extracting value from <input> array
+            var checked = $.map(elements, function(element) {
+                return element.checked? element.value : null;
+            });
+            return node.type === "radio"? checked[0] : checked;
+            }
+       };
+    
+    /** "Automatically" apply to whatever part of the data model is
+     * relevant, the changed value received at the given DOM node*/
+    fluid.applyChange = function(node, newValue, applier) {
+        node = fluid.unwrap(node);
+        if (newValue === undefined) {
+            newValue = fluid.value(node);
+        }
+        if (node.nodeType === undefined && node.length > 0) {node = node[0];} // assume here that they share name and parent
+        var root = fluid.findData(node, fluid.BINDING_ROOT_KEY);
+        if (!root) {
+            fluid.fail("Bound data could not be discovered in any node above " + fluid.dumpEl(node));
+        }
+        var name = node.name;
+        var fossil = root.fossils[name];
+        if (!fossil) {
+            fluid.fail("No fossil discovered for name " + name + " in fossil record above " + fluid.dumpEl(node));
+        }
+        if (typeof(fossil.oldvalue) === "boolean") { // deal with the case of an "isolated checkbox"
+            newValue = newValue[0]? true: false;
+        }
+        var EL = root.fossils[name].EL;
+        if (applier) {
+            applier.fireChangeRequest({path: EL, value: newValue, source: node.id});
+        }
+        else {
+            fluid.set(root.data, EL, newValue);
+        }    
+        };
+   
+    fluid.pathUtil = {};
+   
+    var getPathSegmentImpl = function(accept, path, i) {
+        var segment = null; // TODO: rewrite this with regexes and replaces
+        if (accept) {
+            segment = "";
+        }
+        var escaped = false;
+        var limit = path.length;
+        for (; i < limit; ++i) {
+            var c = path.charAt(i);
+            if (!escaped) {
+                if (c === '.') {
+                    break;
+                    }
+                else if (c === '\\') {
+                    escaped = true;
+                    }
+                else if (segment !== null) {
+                    segment += c;
+                }
+            }
+            else {
+                escaped = false;
+                if (segment !== null) {
+                    accept += c;
+                }
+            }
+        }
+        if (segment !== null) {
+            accept[0] = segment;
+        }
+        return i;
+        };
+    
+    var globalAccept = []; // TODO: serious reentrancy risk here, why is this impl like this?
+    
+    fluid.pathUtil.getPathSegment = function(path, i) {
+        getPathSegmentImpl(globalAccept, path, i);
+        return globalAccept[0];
+        }; 
+  
+    fluid.pathUtil.getHeadPath = function(path) {
+        return fluid.pathUtil.getPathSegment(path, 0);
+        };
+  
+    fluid.pathUtil.getFromHeadPath = function(path) {
+        var firstdot = getPathSegmentImpl(null, path, 0);
+        return firstdot === path.length ? null
+            : path.substring(firstdot + 1);
+        };
+    
+    function lastDotIndex(path) {
+        // TODO: proper escaping rules
+        return path.lastIndexOf(".");
+        }
+    
+    fluid.pathUtil.getToTailPath = function(path) {
+        var lastdot = lastDotIndex(path);
+        return lastdot == -1 ? null : path.substring(0, lastdot);
+        };
+
+  /** Returns the very last path component of a bean path */
+    fluid.pathUtil.getTailPath = function(path) {
+        var lastdot = lastDotIndex(path);
+        return fluid.pathUtil.getPathSegment(path, lastdot + 1);
+        };
+    
+    var composeSegment = function(prefix, toappend) {
+        for (var i = 0; i < toappend.length; ++i) {
+            var c = toappend.charAt(i);
+            if (c === '.' || c === '\\' || c === '}') {
+                prefix += '\\';
+            }
+            prefix += c;
+        }
+        return prefix;
+    };
+    
+    /**
+     * Compose a prefix and suffix EL path, where the prefix is already escaped.
+     * Prefix may be empty, but not null. The suffix will become escaped.
+     */
+    fluid.pathUtil.composePath = function(prefix, suffix) {
+        if (prefix.length !== 0) {
+            prefix += '.';
+        }
+        return composeSegment(prefix, suffix);
+        };    
+   
+    fluid.pathUtil.matchPath = function(spec, path) {
+        var togo = "";
+        while (true) {
+          if (!spec || path === "") {break;}
+          if (!path) {return null;}
+          var spechead = fluid.pathUtil.getHeadPath(spec);
+          var pathhead = fluid.pathUtil.getHeadPath(path);
+          // if we fail to match on a specific component, fail.
+          if (spechead !== "*" && spechead !== pathhead) {
+              return null;
+          }
+          togo = fluid.pathUtil.composePath(togo, pathhead);
+          spec = fluid.pathUtil.getFromHeadPath(spec);
+          path = fluid.pathUtil.getFromHeadPath(path);
+        }
+        return togo;
+      };
+    
+    fluid.model.mergeModel = function(target, source, applier) {
+        var copySource = fluid.copy(source);
+        applier = applier || fluid.makeChangeApplier(source);
+        applier.fireChangeRequest({type: "ADD", path: "", value: target});
+        applier.fireChangeRequest({type: "MERGE", path: "", value: copySource});
+        return source; 
+    };
+        
+      
+    fluid.model.isNullChange = function(model, request, resolverGetConfig) {
+        if (request.type === "ADD") {
+            var existing = fluid.get(model, request.path, resolverGetConfig);
+            if (existing === request.value) {
+                return true;
+            }
+        }
+    };
+    /** Applies the supplied ChangeRequest object directly to the supplied model.
+     */
+    fluid.model.applyChangeRequest = function(model, request, resolverSetConfig) {
+        var pen = fluid.model.getPenultimate(model, request.path, resolverSetConfig || fluid.model.defaultSetConfig);
+        
+        if (request.type === "ADD" || request.type === "MERGE") {
+            if (pen.last === "" || request.type === "MERGE") {
+               if (request.type === "ADD") {
+                   fluid.clear(pen.root);
+               }
+               $.extend(true, pen.last === ""? pen.root: pen.root[pen.last], request.value);
+            }
+            else {
+                pen.root[pen.last] = request.value;
+            }
+        }
+        else if (request.type === "DELETE") {
+            if (pen.last === "") {
+                fluid.clear(pen.root);
+            }
+            else {
+                delete pen.root[pen.last];
+            }
+        }
+    };
+    
+    // Utility shared between changeApplier and superApplier
+    
+    function bindRequestChange(that) {
+        that.requestChange = function(path, value, type) {
+            var changeRequest = {
+                path: path,
+                value: value,
+                type: type
+            };
+        that.fireChangeRequest(changeRequest);
+        };
+    }
+    
+  
+    fluid.makeChangeApplier = function(model, options) {
+        options = options || {};
+        var baseEvents = {
+            guards: fluid.event.getEventFirer(false, true),
+            postGuards: fluid.event.getEventFirer(false, true),
+            modelChanged: fluid.event.getEventFirer(false, false)
+        };
+        var that = {
+            model: model
+        };
+        
+        function makeGuardWrapper(cullUnchanged) {
+            if (!cullUnchanged) {
+                return null;
+            }
+            var togo = function(guard) {
+                return function(model, changeRequest, internalApplier) {
+                    var oldRet = guard(model, changeRequest, internalApplier);
+                    if (oldRet === false) { return false;}
+                    else {
+                        if (fluid.model.isNullChange(model, changeRequest)) {
+                            togo.culled = true;
+                            return false;
+                        }
+                    }
+                };
+            };
+            return togo;
+        }
+
+        function wrapListener(listener, spec) {
+             var pathSpec = spec;
+             var transactional = false;
+             var priority = Number.MAX_VALUE;
+             if (typeof (spec) !== "string") {
+                 pathSpec = spec.path;
+                 transactional = spec.transactional;
+                 if (spec.priority !== undefined) {
+                     priority = spec.priority;
+                     }
+                 }
+             else {
+                 if (pathSpec.charAt(0) === "!") {
+                     transactional = true;
+                     pathSpec = pathSpec.substring(1);
+                 }
+             }
+             return function(changePath, fireSpec, accum) {
+                 var guid = fluid.event.identifyListener(listener);
+                 var exist = fireSpec.guids[guid];
+                 if (!exist) {
+                     var match = fluid.pathUtil.matchPath(pathSpec, changePath);
+                     if (match !== null) {
+                         var record = {
+                             changePath: changePath,
+                             pathSpec: pathSpec,
+                             listener: listener,
+                             priority: priority,
+                             transactional: transactional
+                             };
+                         if (accum) {
+                             record.accumulate = [accum];
+                         }
+                         fireSpec.guids[guid] = record;
+                         var collection = transactional? "transListeners": "listeners";
+                         fireSpec[collection].push(record);
+                         fireSpec.all.push(record);
+                     }
+                 }
+                 else if (accum) {
+                     if (!exist.accumulate) {
+                        exist.accumulate = [];
+                     }
+                     exist.accumulate.push(accum);
+                 }
+           };
+        }
+        
+        function fireFromSpec(name, fireSpec, args, category, wrapper) {
+            return baseEvents[name].fireToListeners(fireSpec[category], args, wrapper);
+        }
+        
+        function fireComparator(recA, recB) {
+            return recA.priority - recB.priority;
+        }
+
+        function prepareFireEvent(name, changePath, fireSpec, accum) {
+            baseEvents[name].fire(changePath, fireSpec, accum);
+            fireSpec.all.sort(fireComparator);
+            fireSpec.listeners.sort(fireComparator);
+            fireSpec.transListeners.sort(fireComparator);
+        }
+        
+        function makeFireSpec() {
+            return {guids: {}, all: [], listeners: [], transListeners: []};
+        }
+        
+        function getFireSpec(name, changePath) {
+            var fireSpec = makeFireSpec();
+            prepareFireEvent(name, changePath, fireSpec);
+            return fireSpec;
+        }
+        
+        function fireEvent(name, changePath, args, wrapper) {
+            var fireSpec = getFireSpec(name, changePath);
+            return fireFromSpec(name, fireSpec, args, "all", wrapper);
+        }
+        
+        function adaptListener(that, name) {
+            that[name] = {
+                addListener: function(spec, listener, namespace) {
+                    baseEvents[name].addListener(wrapListener(listener, spec), namespace);
+                },
+                removeListener: function(listener) {
+                    baseEvents[name].removeListener(listener);
+                }
+            };
+        }
+        adaptListener(that, "guards");
+        adaptListener(that, "postGuards");
+        adaptListener(that, "modelChanged");
+        
+        function preFireChangeRequest(changeRequest) {
+            if (!changeRequest.type) {
+                changeRequest.type = "ADD";
+            }
+        }
+
+        var bareApplier = {
+            fireChangeRequest: function(changeRequest) {
+                that.fireChangeRequest(changeRequest, true);
+            }
+        };
+        bindRequestChange(bareApplier);
+
+        that.fireChangeRequest = function(changeRequest, defeatGuards) {
+            preFireChangeRequest(changeRequest);
+            var guardFireSpec = defeatGuards? null : getFireSpec("guards", changeRequest.path);
+            if (guardFireSpec && guardFireSpec.transListeners.length > 0) {
+                var ation = that.initiate();
+                ation.fireChangeRequest(changeRequest, guardFireSpec);
+                ation.commit();
+            }
+            else {
+                if (!defeatGuards) {
+                    // TODO: this use of "listeners" seems pointless since we have just verified that there are no transactional listeners
+                    var prevent = fireFromSpec("guards", guardFireSpec, [model, changeRequest, bareApplier], "listeners");
+                    if (prevent === false) {
+                        return false;
+                    }
+                }
+                var oldModel = model;
+                if (!options.thin) {
+                    oldModel = {};
+                    fluid.model.copyModel(oldModel, model);                    
+                }
+                fluid.model.applyChangeRequest(model, changeRequest, options.resolverSetConfig);
+                fireEvent("modelChanged", changeRequest.path, [model, oldModel, [changeRequest]]);
+            }
+        };
+        
+        bindRequestChange(that);
+
+        function fireAgglomerated(eventName, formName, changes, args, accpos) {
+            var fireSpec = makeFireSpec();
+            for (var i = 0; i < changes.length; ++ i) {
+                prepareFireEvent(eventName, changes[i].path, fireSpec, changes[i]);
+                }
+            for (var i = 0; i < fireSpec[formName].length; ++ i) {
+                var spec = fireSpec[formName][i];
+                if (accpos) {
+                    args[accpos] = spec.accumulate;
+                }
+                var ret = spec.listener.apply(null, args);
+                if (ret === false) {
+                    return false;
+                }
+            }
+        }
+
+        that.initiate = function(newModel) {
+            var cancelled = false;
+            var changes = [];
+            if (options.thin) {
+                newModel = model;
+            }
+            else {
+                newModel = newModel || {};
+                fluid.model.copyModel(newModel, model);
+            }
+            // the guard in the inner world is given a private applier to "fast track"
+            // and glob collateral changes it requires
+            var internalApplier = 
+              {fireChangeRequest: function(changeRequest) {
+                    preFireChangeRequest(changeRequest);
+                    fluid.model.applyChangeRequest(newModel, changeRequest, options.resolverSetConfig);
+                    changes.push(changeRequest);
+                }};
+            bindRequestChange(internalApplier);
+            var ation = {
+                commit: function() {
+                    var oldModel;
+                    if (cancelled) {
+                        return false;
+                    }
+                    var ret = fireAgglomerated("postGuards", "transListeners", changes, [newModel, null, internalApplier], 1);
+                    if (ret === false) {
+                        return false;
+                    }
+                    if (options.thin) {
+                        oldModel = model;
+                    }
+                    else {
+                        oldModel = {};
+                        fluid.model.copyModel(oldModel, model);
+                        fluid.clear(model);
+                        fluid.model.copyModel(model, newModel);
+                    }
+                    fireAgglomerated("modelChanged", "all", changes, [model, oldModel, null], 2);
+                },
+                fireChangeRequest: function(changeRequest) {
+                     preFireChangeRequest(changeRequest);
+                     if (options.cullUnchanged && fluid.model.isNullChange(model, changeRequest, options.resolverGetConfig)) {
+                         return;
+                     } 
+                     var wrapper = makeGuardWrapper(options.cullUnchanged);
+                     var prevent = fireEvent("guards", changeRequest.path, [newModel, changeRequest, internalApplier], wrapper);
+                     if (prevent === false && !(wrapper && wrapper.culled)) {
+                         cancelled = true;
+                     }
+                     if (!cancelled) {
+                         if (!(wrapper && wrapper.culled)) {
+                             fluid.model.applyChangeRequest(newModel, changeRequest, options.resolverSetConfig);
+                             changes.push(changeRequest);
+                         }
+                     }
+                }
+            };
+            bindRequestChange(ation);
+
+            return ation;
+        };
+        
+        return that;
+    };
+    
+    fluid.makeSuperApplier = function() {
+        var subAppliers = [];
+        var that = {};
+        that.addSubApplier = function(path, subApplier) {
+            subAppliers.push({path: path, subApplier: subApplier});
+        };
+        that.fireChangeRequest = function(request) {
+            for (var i = 0; i < subAppliers.length; ++ i) {
+                var path = subAppliers[i].path;
+                if (request.path.indexOf(path) === 0) {
+                    var subpath = request.path.substring(path.length + 1);
+                    var subRequest = fluid.copy(request);
+                    subRequest.path = subpath;
+                    // TODO: Deal with the as yet unsupported case of an EL rvalue DAR
+                    subAppliers[i].subApplier.fireChangeRequest(subRequest);
+                }
+            }
+        };
+        bindRequestChange(that);
+        return that;
+    };
+    
+    fluid.attachModel = function(baseModel, path, model) {
+        var segs = fluid.model.parseEL(path);
+        for (var i = 0; i < segs.length - 1; ++ i) {
+            var seg = segs[i];
+            var subModel = baseModel[seg];
+            if (!subModel) {
+                baseModel[seg] = subModel = {};
+            }
+            baseModel = subModel;
+        }
+        baseModel[segs[segs.length - 1]] = model;
+    };
+    
+    fluid.assembleModel = function (modelSpec) {
+       var model = {};
+       var superApplier = fluid.makeSuperApplier();
+       var togo = {model: model, applier: superApplier};
+       for (var path in modelSpec) {
+           var rec = modelSpec[path];
+           fluid.attachModel(model, path, rec.model);
+           if (rec.applier) {
+              superApplier.addSubApplier(path, rec.applier);
+           }
+       }
+       return togo;
+    };
+
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/Fluid.js b/docs/jscripts/infusion/framework/core/js/Fluid.js
new file mode 100644 (file)
index 0000000..da343db
--- /dev/null
@@ -0,0 +1,1254 @@
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2007-2009 University of Toronto
+Copyright 2007-2009 University of California, Berkeley
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery, YAHOO, opera*/
+
+var fluid_1_3 = fluid_1_3 || {};
+var fluid = fluid || fluid_1_3;
+
+(function ($, fluid) {
+    
+    fluid.version = "Infusion 1.3";
+    
+    fluid.environment = {
+        fluid: fluid
+    };
+    var globalObject = window || {};
+    
+    /**
+     * Causes an error message to be logged to the console and a real runtime error to be thrown.
+     * 
+     * @param {String|Error} message the error message to log
+     */
+    fluid.fail = function (message) {
+        fluid.setLogging(true);
+        fluid.log(message.message? message.message : message);
+        throw new Error(message);
+        //message.fail(); // Intentionally cause a browser error by invoking a nonexistent function.
+    };
+    
+    // Logging
+    var logging;
+    /** method to allow user to enable logging (off by default) */
+    fluid.setLogging = function (enabled) {
+        if (typeof enabled === "boolean") {
+            logging = enabled;
+        } else {
+            logging = false;
+        }
+    };
+
+    /** Log a message to a suitable environmental console. If the standard "console" 
+     * stream is available, the message will be sent there - otherwise either the
+     * YAHOO logger or the Opera "postError" stream will be used. Logging must first
+     * be enabled with a call fo the fluid.setLogging(true) function.
+     */
+    fluid.log = function (str) {
+        if (logging) {
+            str = fluid.renderTimestamp(new Date()) + ":  " + str;
+            if (typeof(console) !== "undefined") {
+                if (console.debug) {
+                    console.debug(str);
+                } else {
+                    console.log(str);
+                }
+            }
+            else if (typeof(YAHOO) !== "undefined") {
+                YAHOO.log(str);
+            }
+            else if (typeof(opera) !== "undefined") {
+                opera.postError(str);
+            }
+        }
+    };
+    
+    /**
+     * Wraps an object in a jQuery if it isn't already one. This function is useful since
+     * it ensures to wrap a null or otherwise falsy argument to itself, rather than the
+     * often unhelpful jQuery default of returning the overall document node.
+     * 
+     * @param {Object} obj the object to wrap in a jQuery
+     */
+    fluid.wrap = function (obj) {
+        return ((!obj || obj.jquery) ? obj : $(obj)); 
+    };
+    
+    /**
+     * If obj is a jQuery, this function will return the first DOM element within it.
+     * 
+     * @param {jQuery} obj the jQuery instance to unwrap into a pure DOM element
+     */
+    fluid.unwrap = function (obj) {
+        return obj && obj.jquery && obj.length === 1 ? obj[0] : obj; // Unwrap the element if it's a jQuery.
+    };
+    
+    // Functional programming utilities.
+            
+    /** Return an empty container as the same type as the argument (either an
+     * array or hash */
+    fluid.freshContainer = function(tocopy) {
+        return fluid.isArrayable(tocopy)? [] : {};   
+    };
+    
+    /** Performs a deep copy (clone) of its argument **/
+    
+    fluid.copy = function (tocopy) {
+        if (fluid.isPrimitive(tocopy)) {
+            return tocopy;
+        }
+        return $.extend(true, fluid.freshContainer(tocopy), tocopy);
+    };
+    
+    /** A basic utility that returns its argument unchanged */
+    
+    fluid.identity = function(arg) {
+        return arg;
+    };
+    
+    // Framework and instantiation functions.
+
+    
+    /** Returns true if the argument is a primitive type **/
+    fluid.isPrimitive = function (value) {
+        var valueType = typeof(value);
+        return !value || valueType === "string" || valueType === "boolean" || valueType === "number" || valueType === "function";
+    };
+    
+    /** Determines whether the supplied object can be treated as an array, by 
+     * iterating an index towards its length. The test functions by detecting
+     * a property named "length" which is of type "number", but excluding objects
+     * which are themselves of primitive types (in particular functions and strings)
+     */
+    fluid.isArrayable = function(totest) {
+        return totest && !fluid.isPrimitive(totest) && typeof(totest.length) === "number";
+    };
+    
+            
+    /** Corrected version of jQuery makearray that returns an empty array on undefined rather than crashing **/
+    fluid.makeArray = function(arg) {
+        if (arg === null || arg === undefined) {
+            return [];
+        }
+        else {
+            return $.makeArray(arg);
+        }
+    };
+    
+    function transformInternal(source, togo, key, args) {
+        var transit = source[key];
+        for (var j = 0; j < args.length - 1; ++ j) {
+            transit = args[j + 1](transit, key);
+        }
+        togo[key] = transit; 
+    }
+    
+    /** Return a list or hash of objects, transformed by one or more functions. Similar to
+     * jQuery.map, only will accept an arbitrary list of transformation functions and also
+     * works on non-arrays.
+     * @param source {Array or Object} The initial container of objects to be transformed.
+     * @param fn1, fn2, etc. {Function} An arbitrary number of optional further arguments,
+     * all of type Function, accepting the signature (object, index), where object is the
+     * list member to be transformed, and index is its list index. Each function will be
+     * applied in turn to each list member, which will be replaced by the return value
+     * from the function.
+     * @return The finally transformed list, where each member has been replaced by the
+     * original member acted on by the function or functions.
+     */
+    fluid.transform = function (source) {
+        var togo = fluid.freshContainer(source);
+        if (fluid.isArrayable(source)) {
+            for (var i = 0; i < source.length; ++ i) {
+                transformInternal(source, togo, i, arguments);
+            }
+        }
+        else {
+            for (var key in source) {
+                transformInternal(source, togo, key, arguments);
+            }
+        }  
+        return togo;
+    };
+    
+    /** Better jQuery.each which works on hashes as well as having the arguments
+     * the right way round. 
+     * @param source {Arrayable or Object} The container to be iterated over
+     * @param func {Function} A function accepting (value, key) for each iterated
+     * object. This function may return a value to terminate the iteration
+     */
+    fluid.each = function (source, func) {
+        if (fluid.isArrayable(source)) {
+            for (var i = 0; i < source.length; ++ i) {
+                func(source[i], i);
+            }
+        }
+        else {
+            for (var key in source) {
+                func(source[key], key);
+            }
+        }
+    };
+    
+    /** Scan through a list or hash of objects, terminating on the first member which
+     * matches a predicate function.
+     * @param source {Arrayable or Object} The list or hash of objects to be searched.
+     * @param func {Function} A predicate function, acting on a member. A predicate which
+     * returns any value which is not <code>null</code> or <code>undefined</code> will terminate
+     * the search. The function accepts (object, index).
+     * @param deflt {Object} A value to be returned in the case no predicate function matches
+     * a list member. The default will be the natural value of <code>undefined</code>
+     * @return The first return value from the predicate function which is not <code>null</code>
+     * or <code>undefined</code>
+     */
+    fluid.find = function (source, func, deflt) {
+        if (fluid.isArrayable(source)) {
+            for (var i = 0; i < source.length; ++ i) {
+                var disp = func(source[i], i);
+                if (disp !== undefined) { return disp;}
+            }
+        }
+        else {
+            for (var key in source) {
+                var disp = func(source[key], key);
+                if (disp !== undefined) { return disp;}
+            }
+        }
+        return deflt;
+    };
+    
+    /** Scan through a list of objects, "accumulating" a value over them 
+     * (may be a straightforward "sum" or some other chained computation).
+     * @param list {Array} The list of objects to be accumulated over.
+     * @param fn {Function} An "accumulation function" accepting the signature (object, total, index) where
+     * object is the list member, total is the "running total" object (which is the return value from the previous function),
+     * and index is the index number.
+     * @param arg {Object} The initial value for the "running total" object.
+     * @return {Object} the final running total object as returned from the final invocation of the function on the last list member.
+     */
+    fluid.accumulate = function (list, fn, arg) {
+        for (var i = 0; i < list.length; ++ i) {
+            arg = fn(list[i], arg, i);
+        }
+        return arg;
+    };
+    
+    /** Can through a list of objects, removing those which match a predicate. Similar to
+     * jQuery.grep, only acts on the list in-place by removal, rather than by creating
+     * a new list by inclusion.
+     * @param source {Array|Object} The list of objects to be scanned over.
+     * @param fn {Function} A predicate function determining whether an element should be
+     * removed. This accepts the standard signature (object, index) and returns a "truthy"
+     * result in order to determine that the supplied object should be removed from the list.
+     * @return The list, transformed by the operation of removing the matched elements. The
+     * supplied list is modified by this operation.
+     */
+    fluid.remove_if = function (source, fn) {
+        if (fluid.isArrayable(source)) {
+            for (var i = 0; i < source.length; ++i) {
+                if (fn(source[i], i)) {
+                    source.splice(i, 1);
+                    --i;
+                }
+            }
+        }
+        else {
+            for (var key in source) {
+                if (fn(source[key], key)) {
+                    delete source[key];
+                }
+            }
+        }
+        return source;
+    };
+    
+    
+    /** 
+     * Searches through the supplied object for the first value which matches the one supplied.
+     * @param obj {Object} the Object to be searched through
+     * @param value {Object} the value to be found. This will be compared against the object's
+     * member using === equality.
+     * @return {String} The first key whose value matches the one supplied, or <code>null</code> if no
+     * such key is found.
+     */
+    fluid.keyForValue = function (obj, value) {
+        return fluid.find(obj, function(thisValue, key) {
+            if (value === thisValue) {
+                return key;
+            }
+        });
+    };
+    
+    /**
+     * This method is now deprecated and will be removed in a future release of Infusion. 
+     * See fluid.keyForValue instead.
+     */
+    fluid.findKeyInObject = fluid.keyForValue;
+    
+    /** 
+     * Clears an object or array of its contents. For objects, each property is deleted.
+     * 
+     * @param {Object|Array} target the target to be cleared
+     */
+    fluid.clear = function (target) {
+        if (target instanceof Array) {
+            target.length = 0;
+        }
+        else {
+            for (var i in target) {
+                delete target[i];
+            }
+        }
+    };
+        
+    // Model functions
+    fluid.model = {}; // cannot call registerNamespace yet since it depends on fluid.model
+       
+    /** Another special "marker object" representing that a distinguished 
+     * (probably context-dependent) value should be substituted.
+     */
+    fluid.VALUE = {type: "fluid.marker", value: "VALUE"};
+    
+    /** Another special "marker object" representing that no value is present (where
+     * signalling using the value "undefined" is not possible) */
+    fluid.NO_VALUE = {type: "fluid.marker", value: "NO_VALUE"};
+    
+    /** Determine whether an object is any marker, or a particular marker - omit the
+     * 2nd argument to detect any marker
+     */
+    fluid.isMarker = function(totest, type) {
+        if (!totest || typeof (totest) !== 'object' || totest.type !== "fluid.marker") return false;
+        if (!type) return true;
+        return totest.value === type || totest.value === type.value;
+    };
+   
+    /** Copy a source "model" onto a target **/
+    fluid.model.copyModel = function (target, source) {
+        fluid.clear(target);
+        $.extend(true, target, source);
+    };
+    
+    /** Parse an EL expression separated by periods (.) into its component segments.
+     * @param {String} EL The EL expression to be split
+     * @return {Array of String} the component path expressions.
+     * TODO: This needs to be upgraded to handle (the same) escaping rules (as RSF), so that
+     * path segments containing periods and backslashes etc. can be processed.
+     */
+    fluid.model.parseEL = function (EL) {
+        return String(EL).split('.');
+    };
+    
+    /** Compose an EL expression from two separate EL expressions. The returned 
+     * expression will be the one that will navigate the first expression, and then
+     * the second, from the value reached by the first. Either prefix or suffix may be
+     * the empty string **/
+    
+    fluid.model.composePath = function (prefix, suffix) {
+        return prefix === ""? suffix : (suffix === ""? prefix : prefix + "." + suffix);
+    };
+    
+    /** Compose any number of path segments, none of which may be empty **/
+    fluid.model.composeSegments = function () {
+        return $.makeArray(arguments).join(".");
+    };
+
+    /** Standard strategies for resolving path segments **/
+    fluid.model.environmentStrategy = function(initEnvironment) {
+        return {
+            init: function() {
+                var environment = initEnvironment;
+                return function(root, segment, index) {
+                    if (environment && environment[segment]) {
+                        var togo = environment[segment];
+                    }
+                    environment = null;
+                    return togo; 
+                };
+            }
+        };
+    };
+
+    fluid.model.defaultCreatorStrategy = function(root, segment) {
+        if (root[segment] === undefined) {
+            return root[segment] = {};
+            }
+        };
+    
+    fluid.model.defaultFetchStrategy = function(root, segment) {
+        return root[segment];
+        };
+        
+    fluid.model.funcResolverStrategy = function(root, segment) {
+        if (root.resolvePathSegment) {
+            return root.resolvePathSegment(segment);
+        }
+    };
+    
+    fluid.model.makeResolver = function(root, EL, config) {
+        var that = {
+            segs: fluid.model.parseEL(EL),
+            root: root,
+            index: 0,
+            strategies: fluid.transform(config, function(figel) {
+                return figel.init? figel.init() : figel;
+            })
+        };
+        that.next = function() {
+            if (!that.root) {return;}
+            var accepted;
+            for (var i = 0; i < that.strategies.length; ++ i) {
+                var value = that.strategies[i](that.root, that.segs[that.index], that.index);
+                if (accepted === undefined) {
+                    accepted = value;
+                }
+            }
+            if (accepted === fluid.NO_VALUE) {
+                accepted = undefined;
+            }
+            that.root = accepted;
+            ++that.index;
+        };
+        that.step = function(limit) {
+            for (var i = 0; i < limit; ++ i) {
+                that.next();
+            }
+            that.last = that.segs[that.index];
+        };
+        return that;
+    }    
+
+    fluid.model.defaultSetConfig = [fluid.model.funcResolverStrategy, fluid.model.defaultFetchStrategy, fluid.model.defaultCreatorStrategy];
+    
+    fluid.model.getPenultimate = function(root, EL, config) {
+        config = config || fluid.model.defaultGetConfig;
+        var resolver = fluid.model.makeResolver(root, EL, config);
+        resolver.step(resolver.segs.length - 1);
+        return resolver;
+    }
+    
+    fluid.set = function(root, EL, newValue, config) {
+        config = config || fluid.model.defaultSetConfig;
+        var resolver = fluid.model.getPenultimate(root, EL, config);
+        resolver.root[resolver.last] = newValue;
+    };
+    
+    fluid.model.defaultGetConfig = [fluid.model.funcResolverStrategy, fluid.model.defaultFetchStrategy];
+    
+    /** Evaluates an EL expression by fetching a dot-separated list of members
+     * recursively from a provided root.
+     * @param root The root data structure in which the EL expression is to be evaluated
+     * @param {string} EL The EL expression to be evaluated
+     * @param environment An optional "environment" which, if it contains any members
+     * at top level, will take priority over the root data structure.
+     * @return The fetched data value.
+     */
+    
+    fluid.get = function (root, EL, config) {
+        if (EL === "" || EL === null || EL === undefined) {
+            return root;
+        }
+        config = config || fluid.model.defaultGetConfig;
+        var resolver = fluid.model.makeResolver(root, EL, config);
+        resolver.step(resolver.segs.length);
+        return resolver.root;
+    };
+
+    // This backward compatibility will be maintained for a number of releases, probably until Fluid 2.0
+    fluid.model.setBeanValue = fluid.set;
+    fluid.model.getBeanValue = fluid.get;
+    
+    fluid.getGlobalValue = function(path, env) {
+        if (path) {
+            env = env || fluid.environment;
+            var envFetcher = fluid.model.environmentStrategy(env);
+            return fluid.get(globalObject, path, [envFetcher].concat(fluid.model.defaultGetConfig));
+        }
+    };
+    
+    /**
+     * Allows for the calling of a function from an EL expression "functionPath", with the arguments "args", scoped to an framework version "environment".
+     * @param {Object} functionPath - An EL expression
+     * @param {Object} args - An array of arguments to be applied to the function, specified in functionPath
+     * @param {Object} environment - (optional) The object to scope the functionPath to  (typically the framework root for version control)
+     */
+    fluid.invokeGlobalFunction = function (functionPath, args, environment) {
+        var func = fluid.getGlobalValue(functionPath, environment);
+        if (!func) {
+            fluid.fail("Error invoking global function: " + functionPath + " could not be located");
+        } else {
+            return func.apply(null, args);
+        }
+    };
+    
+    /** Registers a new global function at a given path (currently assumes that
+     * it lies within the fluid namespace)
+     */
+    
+    fluid.registerGlobalFunction = function (functionPath, func, env) {
+        env = env || fluid.environment;
+        var envFetcher = fluid.model.environmentStrategy(env);
+        fluid.set(globalObject, functionPath, func, [envFetcher].concat(fluid.model.defaultSetConfig));
+    };
+    
+    fluid.setGlobalValue = fluid.registerGlobalFunction;
+    
+    /** Ensures that an entry in the global namespace exists **/
+    fluid.registerNamespace = function (naimspace, env) {
+        env = env || fluid.environment;
+        var existing = fluid.getGlobalValue(naimspace, env);
+        if (!existing) {
+            existing = {};
+            fluid.setGlobalValue(naimspace, existing, env);
+        }
+        return existing;
+    };
+    
+    fluid.registerNamespace("fluid.event");
+    
+    fluid.event.addListenerToFirer = function(firer, value, namespace) {
+        if (typeof(value) === "function") {
+            firer.addListener(value, namespace);
+        }
+        else if (value && typeof(value) === "object") {
+            firer.addListener(value.listener, namespace, value.predicate, value.priority);
+        }
+    };
+    /**
+     * Attaches the user's listeners to a set of events.
+     * 
+     * @param {Object} events a collection of named event firers
+     * @param {Object} listeners optional listeners to add
+     */
+    fluid.mergeListeners = function (events, listeners) {
+        fluid.each(listeners, function(value, key) {
+            var keydot = key.indexOf(".");
+            var namespace;
+            if (keydot !== -1) {
+                namespace = key.substring(keydot + 1);
+                key = key.substring(0, keydot);
+            }
+            if (!events[key]) {
+                events[key] = fluid.event.getEventFirer();
+            }
+            var firer = events[key];
+            if (fluid.isArrayable(value)) {
+                for (var i = 0; i < value.length; ++ i) {
+                    fluid.event.addListenerToFirer(firer, value[i], namespace); 
+                }
+            }
+            else {
+                fluid.event.addListenerToFirer(firer, value, namespace);
+            } 
+        });
+    };
+    
+    /**
+     * Sets up a component's declared events.
+     * Events are specified in the options object by name. There are three different types of events that can be
+     * specified: 
+     * 1. an ordinary multicast event, specified by "null". 
+     * 2. a unicast event, which allows only one listener to be registered
+     * 3. a preventable event
+     * 
+     * @param {Object} that the component
+     * @param {Object} options the component's options structure, containing the declared event names and types
+     */
+    fluid.instantiateFirers = function (that, options) {
+        that.events = {};
+        if (options.events) {
+            for (var event in options.events) {
+                var eventType = options.events[event];
+                that.events[event] = fluid.event.getEventFirer(eventType === "unicast", eventType === "preventable");
+            }
+        }
+        fluid.mergeListeners(that.events, options.listeners);
+    };
+    
+        
+    // stubs for two functions in FluidDebugging.js
+    fluid.dumpEl = fluid.identity;
+    fluid.renderTimestamp = fluid.identity;
+    
+    /**
+     * Retreives and stores a component's default settings centrally.
+     * @param {boolean} (options) if true, manipulate a global option (for the head
+     *   component) rather than instance options.
+     * @param {String} componentName the name of the component
+     * @param {Object} (optional) an container of key/value pairs to set
+     * 
+     */
+    var defaultsStore = {};
+    var globalDefaultsStore = {};
+    fluid.defaults = function () {
+        var offset = 0;
+        var store = defaultsStore;
+        if (typeof arguments[0] === "boolean") {
+            store = globalDefaultsStore;
+            offset = 1;
+        }
+        var componentName = arguments[offset];
+        var defaultsObject = arguments[offset + 1];
+        if (defaultsObject !== undefined) {
+            store[componentName] = defaultsObject;   
+            return defaultsObject;
+        }
+        
+        return store[componentName];
+    };
+    
+                
+    fluid.mergePolicyIs = function(policy, test) {
+        return typeof(policy) === "string" && policy.indexOf(test) !== -1;
+    };
+    
+    function mergeImpl(policy, basePath, target, source, thisPolicy) {
+        if (typeof(thisPolicy) === "function") {
+            thisPolicy.apply(null, target, source);
+            return target;
+        }
+        if (fluid.mergePolicyIs(thisPolicy, "replace")) {
+            fluid.clear(target);
+        }
+      
+        for (var name in source) {
+            var path = (basePath? basePath + ".": "") + name;
+            var newPolicy = policy && typeof(policy) !== "string"? policy[path] : policy;
+            var thisTarget = target[name];
+            var thisSource = source[name];
+            var primitiveTarget = fluid.isPrimitive(thisTarget);
+    
+            if (thisSource !== undefined) {
+                if (thisSource !== null && typeof thisSource === 'object' &&
+                      !thisSource.nodeType && !thisSource.jquery && thisSource !== fluid.VALUE 
+                       && !fluid.mergePolicyIs(newPolicy, "preserve")) {
+                    if (primitiveTarget) {
+                        target[name] = thisTarget = thisSource instanceof Array? [] : {};
+                    }
+                    mergeImpl(policy, path, thisTarget, thisSource, newPolicy);
+                }
+                else {
+                    if (typeof(newPolicy) === "function") {
+                        newPolicy.call(null, target, source, name);
+                    }
+                    else if (thisTarget === null || thisTarget === undefined || !fluid.mergePolicyIs(newPolicy, "reverse")) {
+                        // TODO: When "grades" are implemented, grandfather in any paired applier to perform these operations
+                        // NB: mergePolicy of "preserve" now creates dependency on DataBinding.js
+                        target[name] = fluid.mergePolicyIs(newPolicy, "preserve")? fluid.model.mergeModel(thisTarget, thisSource) : thisSource;
+                    }
+                }
+            }
+        }
+        return target;
+    }
+    
+    /** Merge a collection of options structures onto a target, following an optional policy.
+     * This function is typically called automatically, as a result of an invocation of
+     * <code>fluid.iniView</code>. The behaviour of this function is explained more fully on
+     * the page http://wiki.fluidproject.org/display/fluid/Options+Merging+for+Fluid+Components .
+     * @param policy {Object/String} A "policy object" specifiying the type of merge to be performed.
+     * If policy is of type {String} it should take on the value "reverse" or "replace" representing
+     * a static policy. If it is an
+     * Object, it should contain a mapping of EL paths onto these String values, representing a
+     * fine-grained policy. If it is an Object, the values may also themselves be EL paths 
+     * representing that a default value is to be taken from that path.
+     * @param target {Object} The options structure which is to be modified by receiving the merge results.
+     * @param options1, options2, .... {Object} an arbitrary list of options structure which are to
+     * be merged "on top of" the <code>target</code>. These will not be modified.    
+     */
+    
+    fluid.merge = function (policy, target) {
+        var path = "";
+        
+        for (var i = 2; i < arguments.length; ++i) {
+            var source = arguments[i];
+            if (source !== null && source !== undefined) {
+                mergeImpl(policy, path, target, source, policy ? policy[""] : null);
+            }
+        }
+        if (policy && typeof(policy) !== "string") {
+            for (var key in policy) {
+                var elrh = policy[key];
+                if (typeof(elrh) === 'string' && elrh !== "replace") {
+                    var oldValue = fluid.get(target, key);
+                    if (oldValue === null || oldValue === undefined) {
+                        var value = fluid.get(target, elrh);
+                        fluid.set(target, key, value);
+                    }
+                }
+            }
+        }
+        return target;     
+    };
+
+    /**
+     * Merges the component's declared defaults, as obtained from fluid.defaults(),
+     * with the user's specified overrides.
+     * 
+     * @param {Object} that the instance to attach the options to
+     * @param {String} componentName the unique "name" of the component, which will be used
+     * to fetch the default options from store. By recommendation, this should be the global
+     * name of the component's creator function.
+     * @param {Object} userOptions the user-specified configuration options for this component
+     */
+    fluid.mergeComponentOptions = function (that, componentName, userOptions) {
+        var defaults = fluid.defaults(componentName); 
+        if (fluid.expandOptions) {
+            defaults = fluid.expandOptions(fluid.copy(defaults), that);
+        }
+        that.options = fluid.merge(defaults? defaults.mergePolicy: null, {}, defaults, userOptions);    
+    };
+    
+        
+    /** A special "marker object" which is recognised as one of the arguments to 
+     * fluid.initSubcomponents. This object is recognised by reference equality - 
+     * where it is found, it is replaced in the actual argument position supplied
+     * to the specific subcomponent instance, with the particular options block
+     * for that instance attached to the overall "that" object.
+     */
+    fluid.COMPONENT_OPTIONS = {type: "fluid.marker", value: "COMPONENT_OPTIONS"};
+    
+    /** Construct a dummy or "placeholder" subcomponent, that optionally provides empty
+     * implementations for a set of methods.
+     */
+    fluid.emptySubcomponent = function (options) {
+        var that = {};
+        options = $.makeArray(options);
+        var empty = function () {};
+        for (var i = 0; i < options.length; ++ i) {
+            that[options[i]] = empty;
+        }
+        return that;
+    };
+    
+    /** Compute a "nickname" given a fully qualified typename, by returning the last path
+     * segment.
+     */
+    
+    fluid.computeNickName = function(typeName) {
+        var segs = fluid.model.parseEL(typeName);
+        return segs[segs.length - 1];
+    };
+    
+    /**
+     * Creates a new "little component": a that-ist object with options merged into it by the framework.
+     * This method is a convenience for creating small objects that have options but don't require full
+     * View-like features such as the DOM Binder or events
+     * 
+     * @param {Object} name the name of the little component to create
+     * @param {Object} options user-supplied options to merge with the defaults
+     */
+    fluid.initLittleComponent = function(name, options) {
+        var that = {typeName: name, id: fluid.allocateGuid()};
+        // TODO: nickName must be available earlier than other merged options so that component may resolve to itself
+        that.nickName = options && options.nickName? options.nickName: fluid.computeNickName(that.typeName);
+        fluid.mergeComponentOptions(that, name, options);
+        return that;
+    };
+
+
+    // The Model Events system.
+    
+    var fluid_guid = 1;
+    
+    /** Allocate an integer value that will be unique for this session **/
+    
+    fluid.allocateGuid = function() {
+        return fluid_guid++;
+    };
+    
+    fluid.event.identifyListener = function(listener) {
+        if (!listener.$$guid) {
+            listener.$$guid = fluid.allocateGuid();
+        }
+        return listener.$$guid;
+    };
+    
+    fluid.event.mapPriority = function(priority) {
+        return (priority === null || priority === undefined? 0 : 
+           (priority === "last" ? -Number.MAX_VALUE :
+              (priority === "first" ? Number.MAX_VALUE : priority)));
+    };
+    
+    fluid.event.listenerComparator = function(recA, recB) {
+        return recB.priority - recA.priority;
+    };
+    
+    fluid.event.sortListeners = function(listeners) {
+        var togo = [];
+        fluid.each(listeners, function(listener) {
+            togo.push(listener);
+        });
+        return togo.sort(fluid.event.listenerComparator);
+    };
+    /** Construct an "event firer" object which can be used to register and deregister 
+     * listeners, to which "events" can be fired. These events consist of an arbitrary
+     * function signature. General documentation on the Fluid events system is at
+     * http://wiki.fluidproject.org/display/fluid/The+Fluid+Event+System .
+     * @param {Boolean} unicast If <code>true</code>, this is a "unicast" event which may only accept
+     * a single listener.
+     * @param {Boolean} preventable If <code>true</code> the return value of each handler will 
+     * be checked for <code>false</code> in which case further listeners will be shortcircuited, and this
+     * will be the return value of fire()
+     */
+    
+    fluid.event.getEventFirer = function (unicast, preventable) {
+        var listeners = {};
+        var sortedListeners = [];
+        
+        function fireToListeners(listeners, args, wrapper) {
+            for (var i in listeners) {
+                var lisrec = listeners[i];
+                var listener = lisrec.listener;
+                if (lisrec.predicate && !lisrec.predicate(listener, args)) {
+                    continue;
+                }
+                try {
+                    var ret = (wrapper? wrapper(listener) : listener).apply(null, args);
+                    if (preventable && ret === false) {
+                        return false;
+                    }
+                }
+                catch (e) {
+                    fluid.log("FireEvent received exception " + e.message + " e " + e + " firing to listener " + i);
+                    throw (e);       
+                }
+            }
+        }
+        
+        return {
+           addListener: function (listener, namespace, predicate, priority) {
+                if (!listener) {
+                    return;
+                }
+                if (unicast) {
+                    namespace = "unicast";
+                }
+                if (!namespace) {
+                    namespace = fluid.event.identifyListener(listener);
+                }
+
+                listeners[namespace] = {listener: listener, predicate: predicate, priority: 
+                    fluid.event.mapPriority(priority)};
+                sortedListeners = fluid.event.sortListeners(listeners);
+            },
+
+            removeListener: function (listener) {
+                if (typeof(listener) === 'string') {
+                    delete listeners[listener];
+                }
+                else if (listener.$$guid) {
+                    delete listeners[listener.$$guid];
+                }
+                sortedListeners = fluid.event.sortListeners(listeners);
+            },
+            // NB - this method exists currently solely for the convenience of the new,
+            // transactional changeApplier. As it exists it is hard to imagine the function
+            // being helpful to any other client. We need to get more experience on the kinds
+            // of listeners that are useful, and ultimately factor this method away.
+            fireToListeners: function (listeners, args, wrapper) {
+                return fireToListeners(listeners, args, wrapper);
+            },
+            fire: function () {
+                return fireToListeners(sortedListeners, arguments);
+            }
+        };
+    };
+
+  // **** VIEW-DEPENDENT DEFINITIONS BELOW HERE
+
+    /**
+     * Fetches a single container element and returns it as a jQuery.
+     * 
+     * @param {String||jQuery||element} containerSpec an id string, a single-element jQuery, or a DOM element specifying a unique container
+     * @param {Boolean} fallible <code>true</code> if an empty container is to be reported as a valid condition
+     * @return a single-element jQuery of container
+     */
+    fluid.container = function (containerSpec, fallible) {
+        var container = fluid.wrap(containerSpec);
+        if (fallible && !container || container.length === 0) {
+            return null;
+        }
+        
+        // Throw an exception if we've got more or less than one element.
+        if (!container || !container.jquery || container.length !== 1) {
+            if (typeof(containerSpec) !== "string") {
+                containerSpec = container.selector;
+            }
+            var count = container.length !== undefined? container.length : 0;
+            fluid.fail({
+                name: "NotOne",
+                message: count > 1? "More than one (" + count + ") container elements were "
+                : "No container element was found for selector " + containerSpec
+            });
+        }
+        
+        return container;
+    };
+    
+    /**
+     * Creates a new DOM Binder instance, used to locate elements in the DOM by name.
+     * 
+     * @param {Object} container the root element in which to locate named elements
+     * @param {Object} selectors a collection of named jQuery selectors
+     */
+    fluid.createDomBinder = function (container, selectors) {
+        var cache = {}, that = {};
+        
+        function cacheKey(name, thisContainer) {
+            return fluid.allocateSimpleId(thisContainer) + "-" + name;
+        }
+
+        function record(name, thisContainer, result) {
+            cache[cacheKey(name, thisContainer)] = result;
+        }
+
+        that.locate = function (name, localContainer) {
+            var selector, thisContainer, togo;
+            
+            selector = selectors[name];
+            thisContainer = localContainer? localContainer: container;
+            if (!thisContainer) {
+                fluid.fail("DOM binder invoked for selector " + name + " without container");
+            }
+
+            if (!selector) {
+                return thisContainer;
+            }
+
+            if (typeof(selector) === "function") {
+                togo = $(selector.call(null, fluid.unwrap(thisContainer)));
+            } else {
+                togo = $(selector, thisContainer);
+            }
+            if (togo.get(0) === document) {
+                togo = [];
+                //fluid.fail("Selector " + name + " with value " + selectors[name] +
+                //            " did not find any elements with container " + fluid.dumpEl(container));
+            }
+            if (!togo.selector) {
+                togo.selector = selector;
+                togo.context = thisContainer;
+            }
+            togo.selectorName = name;
+            record(name, thisContainer, togo);
+            return togo;
+        };
+        that.fastLocate = function (name, localContainer) {
+            var thisContainer = localContainer? localContainer: container;
+            var key = cacheKey(name, thisContainer);
+            var togo = cache[key];
+            return togo? togo : that.locate(name, localContainer);
+        };
+        that.clear = function () {
+            cache = {};
+        };
+        that.refresh = function (names, localContainer) {
+            var thisContainer = localContainer? localContainer: container;
+            if (typeof names === "string") {
+                names = [names];
+            }
+            if (thisContainer.length === undefined) {
+                thisContainer = [thisContainer];
+            }
+            for (var i = 0; i < names.length; ++ i) {
+                for (var j = 0; j < thisContainer.length; ++ j) {
+                    that.locate(names[i], thisContainer[j]);
+                }
+            }
+        };
+        that.resolvePathSegment = that.locate;
+        
+        return that;
+    };
+    
+    /** Expect that an output from the DOM binder has resulted in a non-empty set of 
+     * results. If none are found, this function will fail with a diagnostic message, 
+     * with the supplied message prepended.
+     */
+    fluid.expectFilledSelector = function (result, message) {
+        if (result && result.length === 0 && result.jquery) {
+            fluid.fail(message + ": selector \"" + result.selector + "\" with name " + result.selectorName +
+                       " returned no results in context " + fluid.dumpEl(result.context));
+        }
+    };
+    
+    /** 
+     * The central initialiation method called as the first act of every Fluid
+     * component. This function automatically merges user options with defaults,
+     * attaches a DOM Binder to the instance, and configures events.
+     * 
+     * @param {String} componentName The unique "name" of the component, which will be used
+     * to fetch the default options from store. By recommendation, this should be the global
+     * name of the component's creator function.
+     * @param {jQueryable} container A specifier for the single root "container node" in the
+     * DOM which will house all the markup for this component.
+     * @param {Object} userOptions The configuration options for this component.
+     */
+    fluid.initView = function (componentName, container, userOptions) {
+        fluid.expectFilledSelector(container, "Error instantiating component with name \"" + componentName);
+        container = fluid.container(container, true);
+        if (!container) {
+            return null;
+        }
+        var that = fluid.initLittleComponent(componentName, userOptions); 
+        that.container = container;
+        fluid.initDomBinder(that);
+        
+        fluid.instantiateFirers(that, that.options);
+
+        return that;
+    };
+
+    
+    fluid.initSubcomponent = function (that, className, args) {
+        return fluid.initSubcomponents(that, className, args)[0];
+    };
+    
+    fluid.initSubcomponentImpl = function(that, entry, args) {
+        var togo;
+        if (typeof(entry) !== "function") {
+            var entryType = typeof(entry) === "string"? entry : entry.type;
+            var globDef = fluid.defaults(true, entryType);
+            fluid.merge("reverse", that.options, globDef);
+            togo = entryType === "fluid.emptySubcomponent"?
+               fluid.emptySubcomponent(entry.options) : 
+               fluid.invokeGlobalFunction(entryType, args);
+        }
+        else {
+            togo = entry.apply(null, args);
+        }
+
+        var returnedOptions = togo? togo.returnedOptions : null;
+        if (returnedOptions) {
+            fluid.merge(that.options.mergePolicy, that.options, returnedOptions);
+            if (returnedOptions.listeners) {
+                fluid.mergeListeners(that.events, returnedOptions.listeners);
+            }
+        }
+        return togo;
+    };
+    
+    /** Initialise all the "subcomponents" which are configured to be attached to 
+     * the supplied top-level component, which share a particular "class name".
+     * @param {Component} that The top-level component for which sub-components are
+     * to be instantiated. It contains specifications for these subcomponents in its
+     * <code>options</code> structure.
+     * @param {String} className The "class name" or "category" for the subcomponents to
+     * be instantiated. A class name specifies an overall "function" for a class of 
+     * subcomponents and represents a category which accept the same signature of
+     * instantiation arguments.
+     * @param {Array of Object} args The instantiation arguments to be passed to each 
+     * constructed subcomponent. These will typically be members derived from the
+     * top-level <code>that</code> or perhaps globally discovered from elsewhere. One
+     * of these arguments may be <code>fluid.COMPONENT_OPTIONS</code> in which case this
+     * placeholder argument will be replaced by instance-specific options configured
+     * into the member of the top-level <code>options</code> structure named for the
+     * <code>className</code>
+     * @return {Array of Object} The instantiated subcomponents, one for each member
+     * of <code>that.options[className]</code>.
+     */
+    
+    fluid.initSubcomponents = function (that, className, args) {
+        var entry = that.options[className];
+        if (!entry) {
+            return;
+        }
+        var entries = $.makeArray(entry);
+        var optindex = -1;
+        var togo = [];
+        args = $.makeArray(args);
+        for (var i = 0; i < args.length; ++ i) {
+            if (args[i] === fluid.COMPONENT_OPTIONS) {
+                optindex = i;
+            }
+        }
+        for (i = 0; i < entries.length; ++ i) {
+            entry = entries[i];
+            if (optindex !== -1) {
+                args[optindex] = entry.options;
+            }
+            togo[i] = fluid.initSubcomponentImpl(that, entry, args);
+        }
+        return togo;
+    };
+    
+    /**
+     * Creates a new DOM Binder instance for the specified component and mixes it in.
+     * 
+     * @param {Object} that the component instance to attach the new DOM Binder to
+     */
+    fluid.initDomBinder = function (that) {
+        that.dom = fluid.createDomBinder(that.container, that.options.selectors);
+        that.locate = that.dom.locate;      
+    };
+
+
+
+    // DOM Utilities.
+    
+    /**
+     * Finds the nearest ancestor of the element that passes the test
+     * @param {Element} element DOM element
+     * @param {Function} test A function which takes an element as a parameter and return true or false for some test
+     */
+    fluid.findAncestor = function (element, test) {
+        element = fluid.unwrap(element);
+        while (element) {
+            if (test(element)) {
+                return element;
+            }
+            element = element.parentNode;
+        }
+    };
+    
+    /**
+     * Returns a jQuery object given the id of a DOM node. In the case the element
+     * is not found, will return an empty list.
+     */
+    fluid.jById = function (id, dokkument) {
+        dokkument = dokkument && dokkument.nodeType === 9? dokkument : document;
+        var element = fluid.byId(id, dokkument);
+        var togo = element? $(element) : [];
+        togo.selector = "#" + id;
+        togo.context = dokkument;
+        return togo;
+    };
+    
+    /**
+     * Returns an DOM element quickly, given an id
+     * 
+     * @param {Object} id the id of the DOM node to find
+     * @param {Document} dokkument the document in which it is to be found (if left empty, use the current document)
+     * @return The DOM element with this id, or null, if none exists in the document.
+     */
+    fluid.byId = function (id, dokkument) {
+        dokkument = dokkument && dokkument.nodeType === 9? dokkument : document;
+        var el = dokkument.getElementById(id);
+        if (el) {
+            if (el.getAttribute("id") !== id) {
+                fluid.fail("Problem in document structure - picked up element " +
+                    fluid.dumpEl(el) + " for id " + id +
+                    " without this id - most likely the element has a name which conflicts with this id");
+            }
+            return el;
+        }
+        else {
+            return null;
+        }
+    };
+    
+    /**
+     * Returns the id attribute from a jQuery or pure DOM element.
+     * 
+     * @param {jQuery||Element} element the element to return the id attribute for
+     */
+    fluid.getId = function (element) {
+        return fluid.unwrap(element).getAttribute("id");
+    };
+    
+    /** 
+     * Allocate an id to the supplied element if it has none already, by a simple
+     * scheme resulting in ids "fluid-id-nnnn" where nnnn is an increasing integer.
+     */
+    
+    fluid.allocateSimpleId = function (element) {
+        element = fluid.unwrap(element);
+        if (!element.id) {
+            element.id = "fluid-id-" + fluid.allocateGuid(); 
+        }
+        return element.id;
+    };
+    
+
+    // Message resolution and templating
+    
+    /**
+     * Simple string template system. 
+     * Takes a template string containing tokens in the form of "%value".
+     * Returns a new string with the tokens replaced by the specified values.
+     * Keys and values can be of any data type that can be coerced into a string. Arrays will work here as well.
+     * 
+     * @param {String}    template    a string (can be HTML) that contains tokens embedded into it
+     * @param {object}    values        a collection of token keys and values
+     */
+    fluid.stringTemplate = function (template, values) {
+        var newString = template;
+        for (var key in values) {
+            var searchStr = "%" + key;
+            newString = newString.replace(searchStr, values[key]);
+        }
+        return newString;
+    };
+    
+
+    fluid.messageResolver = function (options) {
+        var that = fluid.initLittleComponent("fluid.messageResolver", options);
+        that.messageBase = that.options.parseFunc(that.options.messageBase);
+        
+        that.lookup = function(messagecodes) {
+            var resolved = fluid.messageResolver.resolveOne(that.messageBase, messagecodes);
+            if (resolved === undefined) {
+                return fluid.find(that.options.parents, function(parent) {
+                    return parent.lookup(messagecodes);
+                });
+            }
+            else {
+                return {template: resolved, resolveFunc: that.options.resolveFunc};
+            }
+        };
+        that.resolve = function(messagecodes, args) {
+            if (!messagecodes) {
+                return "[No messagecodes provided]";
+            }
+            messagecodes = fluid.makeArray(messagecodes);
+            var looked = that.lookup(messagecodes);
+            return looked? looked.resolveFunc(looked.template, args)
+                :"[Message string for key " + messagecodes[0] + " not found]" 
+        };
+        
+        return that;  
+    };
+    
+    fluid.defaults("fluid.messageResolver", {
+        mergePolicy: {
+            messageBase: "preserve"  
+        },
+        resolveFunc: fluid.stringTemplate,
+        parseFunc: fluid.identity,
+        messageBase: {},
+        parents: []
+    });
+    
+    fluid.messageResolver.resolveOne = function(messageBase, messagecodes) {
+        for (var i = 0; i < messagecodes.length; ++ i) {
+            var code = messagecodes[i];
+            var message = messageBase[code];
+            if (message !== undefined) {
+                return message;
+            }
+        }
+    };
+          
+    /** Converts a data structure consisting of a mapping of keys to message strings,
+     * into a "messageLocator" function which maps an array of message codes, to be 
+     * tried in sequence until a key is found, and an array of substitution arguments,
+     * into a substituted message string.
+     */
+    fluid.messageLocator = function (messageBase, resolveFunc) {
+        var resolver = fluid.messageResolver({messageBase: messageBase, resolveFunc: resolveFunc});
+        return function(messagecodes, args) {
+            return resolver.resolve(messagecodes, args);
+        };
+    };
+
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/FluidDOMUtilities.js b/docs/jscripts/infusion/framework/core/js/FluidDOMUtilities.js
new file mode 100644 (file)
index 0000000..9afe81d
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2009 University of Toronto
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery */
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.dom = fluid.dom || {};
+    
+    // Node walker function for iterateDom.
+    var getNextNode = function (iterator) {
+        if (iterator.node.firstChild) {
+            iterator.node = iterator.node.firstChild;
+            iterator.depth += 1;
+            return iterator;
+        }
+        while (iterator.node) {
+            if (iterator.node.nextSibling) {
+                iterator.node = iterator.node.nextSibling;
+                return iterator;
+            }
+            iterator.node = iterator.node.parentNode;
+            iterator.depth -= 1;
+        }
+        return iterator;
+    };
+    
+    /**
+     * Walks the DOM, applying the specified acceptor function to each element.
+     * There is a special case for the acceptor, allowing for quick deletion of elements and their children.
+     * Return "delete" from your acceptor function if you want to delete the element in question.
+     * Return "stop" to terminate iteration.
+     * 
+     * @param {Element} node the node to start walking from
+     * @param {Function} acceptor the function to invoke with each DOM element
+     * @param {Boolean} allnodes Use <code>true</code> to call acceptor on all nodes, 
+     * rather than just element nodes (type 1)
+     */
+    fluid.dom.iterateDom = function (node, acceptor, allNodes) {
+        var currentNode = {node: node, depth: 0};
+        var prevNode = node;
+        var condition;
+        while (currentNode.node !== null && currentNode.depth >= 0 && currentNode.depth < fluid.dom.iterateDom.DOM_BAIL_DEPTH) {
+            condition = null;
+            if (currentNode.node.nodeType === 1 || allNodes) {
+                condition = acceptor(currentNode.node, currentNode.depth);
+            }
+            if (condition) {
+                if (condition === "delete") {
+                    currentNode.node.parentNode.removeChild(currentNode.node);
+                    currentNode.node = prevNode;
+                }
+                else if (condition === "stop") {
+                    return currentNode.node;
+                }
+            }
+            prevNode = currentNode.node;
+            currentNode = getNextNode(currentNode);
+        }
+    };
+    
+    // Work around IE circular DOM issue. This is the default max DOM depth on IE.
+    // http://msdn2.microsoft.com/en-us/library/ms761392(VS.85).aspx
+    fluid.dom.iterateDom.DOM_BAIL_DEPTH = 256;
+    
+    /**
+     * Checks if the sepcified container is actually the parent of containee.
+     * 
+     * @param {Element} container the potential parent
+     * @param {Element} containee the child in question
+     */
+    fluid.dom.isContainer = function (container, containee) {
+        for (; containee; containee = containee.parentNode) {
+            if (container === containee) {
+                return true;
+            }
+        }
+        return false;
+    };
+       
+    /** Return the element text from the supplied DOM node as a single String */
+    fluid.dom.getElementText = function(element) {
+        var nodes = element.childNodes;
+        var text = "";
+        for (var i = 0; i < nodes.length; ++ i) {
+          var child = nodes[i];
+          if (child.nodeType == 3) {
+            text = text + child.nodeValue;
+            }
+          }
+        return text; 
+    };
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/FluidDebugging.js b/docs/jscripts/infusion/framework/core/js/FluidDebugging.js
new file mode 100644 (file)
index 0000000..0d18a82
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2007-2009 University of Toronto
+Copyright 2007-2009 University of California, Berkeley
+Copyright 2010 OCAD University
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery, YAHOO, opera*/
+
+var fluid_1_3 = fluid_1_3 || {};
+var fluid = fluid || fluid_1_3;
+
+(function ($, fluid) {
+       
+    fluid.renderTimestamp = function (date) {
+        var zeropad = function (num, width) {
+             if (!width) width = 2;
+             var numstr = (num == undefined? "" : num.toString());
+             return "00000".substring(5 - width + numstr.length) + numstr;
+             }
+        return zeropad(date.getHours()) + ":" + zeropad(date.getMinutes()) + ":" + zeropad(date.getSeconds()) + "." + zeropad(date.getMilliseconds(), 3);
+    };
+    
+    function generate(c, count) {
+        var togo = "";
+        for (var i = 0; i < count; ++ i) {
+            togo += c;
+        }
+        return togo;
+    }
+    
+    function printImpl(obj, small, options) {
+        var big = small + options.indentChars;
+        if (obj === null) {
+            return "null";
+        }
+        else if (fluid.isPrimitive(obj)) {
+            return JSON.stringify(obj);
+        }
+        else {
+            var j = [];
+            if (fluid.isArrayable(obj)) {
+                if (obj.length === 0) {
+                    return "[]";
+                }
+                for (var i = 0; i < obj.length; ++ i) {
+                    j[i] = printImpl(obj[i], big, options);
+                }
+                return "[\n" + big + j.join(",\n" + big) + "\n" + small + "]";
+                }
+            else {
+                var i = 0;
+                fluid.each(obj, function(value, key) {
+                    j[i++] = JSON.stringify(key) + ": " + printImpl(value, big, options);
+                });
+                return "{\n" + big + j.join(",\n" + big) + "\n" + small + "}"; 
+            }
+        }
+    }
+    
+    fluid.prettyPrintJSON = function(obj, options) {
+        options = $.extend({indent: 4}, options);
+        options.indentChars = generate(" ", options.indent);
+        return printImpl(obj, "", options);
+    }
+        
+    /** 
+     * Dumps a DOM element into a readily recognisable form for debugging - produces a
+     * "semi-selector" summarising its tag name, class and id, whichever are set.
+     * 
+     * @param {jQueryable} element The element to be dumped
+     * @return A string representing the element.
+     */
+    fluid.dumpEl = function (element) {
+        var togo;
+        
+        if (!element) {
+            return "null";
+        }
+        if (element.nodeType === 3 || element.nodeType === 8) {
+            return "[data: " + element.data + "]";
+        } 
+        if (element.nodeType === 9) {
+            return "[document: location " + element.location + "]";
+        }
+        if (!element.nodeType && fluid.isArrayable(element)) {
+            togo = "[";
+            for (var i = 0; i < element.length; ++ i) {
+                togo += fluid.dumpEl(element[i]);
+                if (i < element.length - 1) {
+                    togo += ", ";
+                }
+            }
+            return togo + "]";
+        }
+        element = $(element);
+        togo = element.get(0).tagName;
+        if (element.attr("id")) {
+            togo += "#" + element.attr("id");
+        }
+        if (element.attr("class")) {
+            togo += "." + element.attr("class");
+        }
+        return togo;
+    };
+        
+})(jQuery, fluid_1_3);
+    
\ No newline at end of file
diff --git a/docs/jscripts/infusion/framework/core/js/FluidDocument.js b/docs/jscripts/infusion/framework/core/js/FluidDocument.js
new file mode 100644 (file)
index 0000000..aa93f79
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2007-2009 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/** This file contains functions which depend on the presence of a DOM document
+ * but which do not depend on the contents of Fluid.js **/
+
+// Declare dependencies.
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    // Private constants.
+    var NAMESPACE_KEY = "fluid-scoped-data";
+
+    /**
+     * Gets stored state from the jQuery instance's data map.
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.getScopedData = function(target, key) {
+        var data = $(target).data(NAMESPACE_KEY);
+        return data ? data[key] : undefined;
+    };
+
+    /**
+     * Stores state in the jQuery instance's data map. Unlike jQuery's version,
+     * accepts multiple-element jQueries.
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.setScopedData = function(target, key, value) {
+        $(target).each(function() {
+            var data = $.data(this, NAMESPACE_KEY) || {};
+            data[key] = value;
+
+            $.data(this, NAMESPACE_KEY, data);
+        });
+    };
+
+    /** Global focus manager - makes use of "focusin" event supported in jquery 1.4.2 or later.
+     */
+
+    var lastFocusedElement = null;
+    
+    $(document).bind("focusin", function(event){
+        lastFocusedElement = event.target;
+    });
+    
+    fluid.getLastFocusedElement = function () {
+        return lastFocusedElement;
+    }
+
+
+    var ENABLEMENT_KEY = "enablement";
+
+    /** Queries or sets the enabled status of a control. An activatable node
+     * may be "disabled" in which case its keyboard bindings will be inoperable
+     * (but still stored) until it is reenabled again.
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+     
+    fluid.enabled = function(target, state) {
+        target = $(target);
+        if (state === undefined) {
+            return fluid.getScopedData(target, ENABLEMENT_KEY) !== false;
+        }
+        else {
+            $("*", target).add(target).each(function() {
+                if (fluid.getScopedData(this, ENABLEMENT_KEY) !== undefined) {
+                    fluid.setScopedData(this, ENABLEMENT_KEY, state);
+                }
+                else if (/select|textarea|input/i.test(this.nodeName)) {
+                    $(this).attr("disabled", !state);
+                }
+            });
+            fluid.setScopedData(target, ENABLEMENT_KEY, state);
+        }
+    };
+    
+    fluid.initEnablement = function(target) {
+        fluid.setScopedData(target, ENABLEMENT_KEY, true);
+    }
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/FluidIoC.js b/docs/jscripts/infusion/framework/core/js/FluidIoC.js
new file mode 100644 (file)
index 0000000..6ec72a2
--- /dev/null
@@ -0,0 +1,722 @@
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    /** The Fluid "IoC System proper" - resolution of references and 
+     * completely automated instantiation of declaratively defined
+     * component trees */ 
+    
+    var inCreationMarker = "__CURRENTLY_IN_CREATION__";
+    
+    var findMatchingComponent = function(that, visitor, except) {
+        for (var name in that) {
+            var component = that[name];
+            if (!component || component === except || !component.typeName) {continue;}
+            if (visitor(component, name)) {
+                return true;
+            }
+            findMatchingComponent(component, visitor);
+         }
+    };
+    
+    // thatStack contains an increasing list of MORE SPECIFIC thats.
+    var visitComponents = function(thatStack, visitor) {
+        var lastDead;
+        for (var i = thatStack.length - 1; i >= 0; -- i) {
+            var that = thatStack[i];
+            if (that.options && that.options.fireBreak) { // TODO: formalise this
+               return;
+            }
+            if (that.typeName) {
+                if (visitor(that, "")) {
+                    return;
+                }
+            }
+            if (findMatchingComponent(that, visitor, lastDead)) {
+                return;
+            }
+            lastDead = that;
+        }
+    };
+    
+    // An EL segment resolver strategy that will attempt to trigger creation of
+    // components that it discovers along the EL path, if they have been defined but not yet
+    // constructed. Spring, eat your heart out! Wot no SPR-2048?
+    
+    function makeGingerStrategy(thatStack) {
+        return function(component, thisSeg) {
+            var atval = component[thisSeg];
+            if (atval !== undefined) {
+                if (atval[inCreationMarker] && atval !== thatStack[0]) {
+                    fluid.fail("Component of type " + 
+                    atval.typeName + " cannot be used for lookup of path " + segs.join(".") +
+                    " since it is still in creation. Please reorganise your dependencies so that they no longer contain circular references");
+                }
+            }
+            else {
+                if (component.options && component.options.components && component.options.components[thisSeg]) {
+                    fluid.initDependent(component, thisSeg, thatStack);
+                    atval = component[thisSeg];
+                }
+            };
+            return atval;
+        }
+    };
+
+    function makeStackFetcher(thatStack, directModel) {
+        var fetchStrategies = [fluid.model.funcResolverStrategy, makeGingerStrategy(thatStack)]; 
+        var fetcher = function(parsed) {
+            var context = parsed.context;
+            var foundComponent;
+            visitComponents(thatStack, function(component, name) {
+                if (context === name || context === component.typeName || context === component.nickName) {
+                    foundComponent = component;
+                    return true; // YOUR VISIT IS AT AN END!!
+                }
+                if (component.options && component.options.components && component.options.components[context] && !component[context]) {
+                    foundComponent = fluid.get(component, context, fetchStrategies);
+                    return true;
+                }
+            });
+                // TODO: we used to get a helpful diagnostic when we failed to match a context name before we fell back
+                // to the environment for FLUID-3818
+                //fluid.fail("No context matched for name " + context + " from root of type " + thatStack[0].typeName);
+            return fluid.get(foundComponent, parsed.path, fetchStrategies);
+        };
+        return fetcher;
+    }
+     
+    function makeStackResolverOptions(thatStack, directModel) {
+        return $.extend({}, fluid.defaults("fluid.resolveEnvironment"), {
+            noCopy: true,
+            fetcher: makeStackFetcher(thatStack, directModel)
+            }); 
+    } 
+     
+    function resolveRvalue(thatStack, arg, initArgs, componentOptions) {
+        var directModel = thatStack[0].model; // TODO: this convention may not always be helpful
+        var options = makeStackResolverOptions(thatStack, directModel);
+        options.model = directModel;
+        
+        if (fluid.isMarker(arg, fluid.COMPONENT_OPTIONS)) {
+            arg = fluid.expander.expandLight(componentOptions, options);
+        }
+        else {
+            if (typeof(arg) === "string" && arg.charAt(0) === "@") { // Test cases for i) single-args, ii) composite args
+                var argpos = arg.substring(1);
+                arg = initArgs[argpos];
+            }
+            else {
+                arg = fluid.expander.expandLight(arg, options); // fluid.resolveEnvironment(arg, directModel, options);
+            }
+        }
+        return arg;
+    }
+    
+    
+    /** Given a concrete argument list and/or options, determine the final concrete
+     * "invocation specification" which is coded by the supplied demandspec in the 
+     * environment "thatStack" - the return is a package of concrete global function name
+     * and argument list which is suitable to be executed directly by fluid.invokeGlobalFunction.
+     */
+    fluid.embodyDemands = function(thatStack, demandspec, initArgs, options) {
+        var demands = $.makeArray(demandspec.args);
+        options = options || {};
+        if (demands.length === 0) {
+            if (options.componentOptions) { // Guess that it is meant to be a subcomponent TODO: component grades
+               demands = [fluid.COMPONENT_OPTIONS];
+            }
+            else if (options.passArgs) {
+                demands = fluid.transform(initArgs, function(arg, index) {
+                    return "@"+index;
+                });
+            }
+        }
+        var args = [];
+        if (demands) {
+            for (var i = 0; i < demands.length; ++ i) {
+                var arg = demands[i];
+                if (typeof(arg) === "object" && !fluid.isMarker(arg)) {
+                    var resolvedOptions = {};
+                    for (var key in arg) {
+                        var ref = arg[key];
+                        var rvalue = resolveRvalue(thatStack, ref, initArgs, options.componentOptions);
+                        fluid.set(resolvedOptions, key, rvalue);
+                    }
+                    args[i] = resolvedOptions;
+                }
+                else {
+                    var resolvedArg = resolveRvalue(thatStack, arg, initArgs, options.componentOptions);
+                    args[i] = resolvedArg;
+                }
+                if (i === demands.length - 1 && args[i] && typeof(args[i]) === "object" && !args[i].typeName && !args[i].targetTypeName) {
+                    args[i].targetTypeName = demandspec.funcName; // TODO: investigate the general sanity of this
+                }
+            }
+        }
+        else {
+            args = initArgs? initArgs: [];
+        }
+
+        var togo = {
+            args: args,
+            funcName: demandspec.funcName
+        };
+        return togo;
+    };
+   
+    var dependentStore = {};
+    
+    function searchDemands(demandingName, contextNames) {
+        var exist = dependentStore[demandingName] || [];
+        outer: for (var i = 0; i < exist.length; ++ i) {
+            var rec = exist[i];
+            for (var j = 0; j < contextNames.length; ++ j) {
+                 if (rec.contexts[j] !== contextNames[j]) {
+                     continue outer;
+                 }
+            }
+            return rec.spec;   
+        }
+    }
+    
+    fluid.demands = function(demandingName, contextName, spec) {
+        var contextNames = $.makeArray(contextName).sort(); 
+        if (!spec) {
+            return searchDemands(demandingName, contextNames);
+        }
+        else if (spec.length) {
+            spec = {args: spec};
+        }
+        var exist = dependentStore[demandingName];
+        if (!exist) {
+            exist = [];
+            dependentStore[demandingName] = exist;
+        }
+        exist.push({contexts: contextNames, spec: spec});
+    };
+    
+    fluid.getEnvironmentalThatStack = function() {
+         return [fluid.staticEnvironment];
+    };
+    
+    fluid.getDynamicEnvironmentalThatStack = function() {
+        var root = fluid.threadLocal();
+        var dynamic = root["fluid.initDependents"]
+        return dynamic? dynamic : fluid.getEnvironmentalThatStack();
+    };
+
+    fluid.locateDemands = function(demandingNames, thatStack) {
+        var searchStack = fluid.getEnvironmentalThatStack().concat(thatStack); // TODO: put in ThreadLocal "instance" too, and also accelerate lookup
+        var contextNames = {};
+        visitComponents(searchStack, function(component) {
+            contextNames[component.typeName] = true;
+        });
+        var matches = [];
+        for (var i = 0; i < demandingNames.length; ++ i) {
+            var rec = dependentStore[demandingNames[i]] || [];
+            for (var j = 0; j < rec.length; ++ j) {
+                var spec = rec[j];
+                var record = {spec: spec.spec, intersect: 0, uncess: 0};
+                for (var k = 0; k < spec.contexts.length; ++ k) {
+                    record[contextNames[spec.contexts[k]]? "intersect" : "uncess"] += 2;
+                }
+                if (spec.contexts.length === 0) { // allow weak priority for contextless matches
+                    record.intersect ++;
+                }
+                // TODO: Potentially more subtle algorithm here - also ambiguity reports  
+                matches.push(record); 
+            }
+        }
+        matches.sort(function(speca, specb) {
+            var p1 = specb.intersect - speca.intersect; 
+            return p1 === 0? speca.uncess - specb.uncess : p1;
+            });
+        return matches.length === 0 || matches[0].intersect === 0? null : matches[0].spec;
+    };
+    
+    /** Determine the appropriate demand specification held in the fluid.demands environment 
+     * relative to "thatStack" for the function name(s) funcNames.
+     */
+    fluid.determineDemands = function (thatStack, funcNames) {
+        var that = thatStack[thatStack.length - 1];
+        funcNames = $.makeArray(funcNames);
+        var demandspec = fluid.locateDemands(funcNames, thatStack);
+   
+        if (!demandspec) {
+            demandspec = {};
+        }
+        var newFuncName = funcNames[0];
+        if (demandspec.funcName) {
+            newFuncName = demandspec.funcName;
+           /**    TODO: "redirects" disabled pending further thought
+            var demandspec2 = fluid.fetchDirectDemands(funcNames[0], that.typeName);
+            if (demandspec2) {
+                demandspec = demandspec2; // follow just one redirect
+            } **/
+        }
+        var mergeArgs = [];
+        if (demandspec.parent) {
+            var parent = searchDemands(funcNames[0], $.makeArray(demandspec.parent).sort());
+            if (parent) {
+                mergeArgs = parent.args; // TODO: is this really a necessary feature?
+            }
+        }
+        var args = [];
+        fluid.merge(null, args, $.makeArray(mergeArgs), $.makeArray(demandspec.args)); // TODO: avoid so much copying
+        return {funcName: newFuncName, args: args};
+    };
+    
+    fluid.resolveDemands = function (thatStack, funcNames, initArgs, options) {
+        var demandspec = fluid.determineDemands(thatStack, funcNames);
+        return fluid.embodyDemands(thatStack, demandspec, initArgs, options);
+    };
+    
+    // TODO: make a *slightly* more performant version of fluid.invoke that perhaps caches the demands
+    // after the first successful invocation
+    fluid.invoke = function(functionName, args, that, environment) {
+        args = fluid.makeArray(args);
+        return fluid.withNewComponent(that || {typeName: functionName}, function(thatStack) {
+            var invokeSpec = fluid.resolveDemands(thatStack, functionName, args, {passArgs: true});
+            return fluid.invokeGlobalFunction(invokeSpec.funcName, invokeSpec.args, environment);
+        });
+    };
+    
+    /** Make a function which performs only "static redispatch" of the supplied function name - 
+     * that is, taking only account of the contents of the "static environment". Since the static
+     * environment is assumed to be constant, the dispatch of the call will be evaluated at the
+     * time this call is made, as an optimisation.
+     */
+    
+    fluid.makeFreeInvoker = function(functionName, environment) {
+        var demandSpec = fluid.determineDemands([fluid.staticEnvironment], functionName);
+        return function() {
+            var invokeSpec = fluid.embodyDemands(fluid.staticEnvironment, demandSpec, arguments);
+            return fluid.invokeGlobalFunction(invokeSpec.funcName, invokeSpec.args, environment);
+        };
+    };
+    
+    fluid.makeInvoker = function(thatStack, demandspec, functionName, environment) {
+        demandspec = demandspec || fluid.determineDemands(thatStack, functionName);
+        thatStack = $.makeArray(thatStack); // take a copy of this since it will most likely go away
+        return function() {
+            var invokeSpec = fluid.embodyDemands(thatStack, demandspec, arguments);
+            return fluid.invokeGlobalFunction(invokeSpec.funcName, invokeSpec.args, environment);
+        };
+    };
+    
+    fluid.addBoiledListener = function(thatStack, eventName, listener, namespace, predicate) {
+        thatStack = $.makeArray(thatStack);
+        var topThat = thatStack[thatStack.length - 1];
+        topThat.events[eventName].addListener(function(args) {
+            var resolved = fluid.resolveDemands(thatStack, eventName, args);
+            listener.apply(null, resolved.args);
+        }, namespace, predicate);
+    };
+    
+        
+    fluid.registerNamespace("fluid.expander");
+    
+    /** rescue that part of a component's options which should not be subject to
+     * options expansion via IoC - this initially consists of "components" and "mergePolicy" 
+     * but will be expanded by the set of paths specified as "noexpand" within "mergePolicy" 
+     */
+    
+    fluid.expander.preserveFromExpansion = function(options) {
+        var preserve = {};
+        var preserveList = ["mergePolicy", "components", "invokers"];
+        fluid.each(options.mergePolicy, function(value, key) {
+            if (fluid.mergePolicyIs(value, "noexpand")) {
+                preserveList.push(key);
+            }
+        });
+        fluid.each(preserveList, function(path) {
+            var pen = fluid.model.getPenultimate(options, path);
+            var value = pen.root[pen.last];
+            delete pen.root[pen.last];
+            fluid.set(preserve, path, value);  
+        });
+        return {
+            restore: function(target) {
+                fluid.each(preserveList, function(path) {
+                    var preserved = fluid.get(preserve, path);
+                    if (preserved !== undefined) {
+                        fluid.set(target, path, preserved)
+                    }
+                });
+            }
+        };
+    };
+    
+    /** Expand a set of component options with respect to a set of "expanders" (essentially only
+     *  deferredCall) -  This substitution is destructive since it is assumed that the options are already "live" as the
+     *  result of environmental substitutions. Note that options contained inside "components" will not be expanded
+     *  by this call directly to avoid linearly increasing expansion depth if this call is occuring as a result of
+     *  "initDependents" */
+     // TODO: This needs to be integrated with "embodyDemands" above which makes a call to "resolveEnvironment" directly
+     // but with very similarly derived options (makeStackResolverOptions)
+     // The whole merge/expansion pipeline needs an overhaul once we have "grades" to allow merging and
+     // defaulting to occur smoothly across a "demands" stack - right now, "demanded" options have exactly
+     // the same status as user options whereas they should slot into the right place between 
+     // "earlyDefaults"/"defaults"/"demands"/"user options". Demands should be allowed to say whether they
+     // integrate with or override defaults.
+    fluid.expandOptions = function(args, that) {
+        if (fluid.isPrimitive(args)) {
+            return args;
+        }
+        return fluid.withNewComponent(that, function(thatStack) {
+            var expandOptions = makeStackResolverOptions(thatStack);
+            expandOptions.noCopy = true; // It is still possible a model may be fetched even though it is preserved
+            if (!fluid.isArrayable(args)) {
+                var pres = fluid.expander.preserveFromExpansion(args);
+            }
+            var expanded = fluid.expander.expandLight(args, expandOptions);
+            if (pres) {
+                pres.restore(expanded);
+            }
+            return expanded;
+        });
+    };
+    
+    fluid.initDependent = function(that, name, thatStack) {
+        if (!that || that[name]) { return; }
+        var component = that.options.components[name];
+        var invokeSpec = fluid.resolveDemands(thatStack, [component.type, name], [], {componentOptions: component.options});
+        // TODO: only want to expand "options" or all args? See "component rescuing" in expandOptions above
+        //invokeSpec.args = fluid.expandOptions(invokeSpec.args, thatStack, true); 
+        var instance = fluid.initSubcomponentImpl(that, {type: invokeSpec.funcName}, invokeSpec.args);
+        if (instance) { // TODO: more fallibility
+            that[name] = instance;
+        }
+    };
+    
+    // NON-API function    
+    fluid.withNewComponent = function(that, func) {
+        if (that) {
+            that[inCreationMarker] = true;
+        }
+        // push a dynamic stack of "currently resolving components" onto the current thread
+        var root = fluid.threadLocal();
+        var thatStack = root["fluid.initDependents"];
+        if (that) {
+            if (!thatStack) {
+                thatStack = [that];
+                root["fluid.initDependents"] = thatStack;
+            }
+            else {
+                thatStack.push(that);
+            }
+        }
+        var fullStack = [fluid.staticEnvironment, fluid.threadLocal()].concat(fluid.makeArray(thatStack));
+        try {
+            return func(fullStack);
+        }
+        finally {
+            thatStack.pop();
+            delete that[inCreationMarker];
+        }              
+    };
+        
+    fluid.initDependents = function(that) {
+        var options = that.options;
+        fluid.withNewComponent(that, function(thatStack) {
+            var components = options.components || {};
+            for (var name in components) {
+                fluid.initDependent(that, name, thatStack);
+            }
+            var invokers = options.invokers || {};
+            for (var name in invokers) {
+                var invokerec = invokers[name];
+                var funcName = typeof(invokerec) === "string"? invokerec : null;
+                that[name] = fluid.makeInvoker(thatStack, funcName? null : invokerec, funcName);
+            }
+        });
+    };
+    
+    // Standard Fluid component types
+    
+    fluid.typeTag = function(name) {
+        return {
+            typeName: name
+        };
+    };
+    
+    fluid.standardComponent = function(name) {
+        return function(container, options) {
+            var that = fluid.initView(name, container, options);
+            fluid.initDependents(that);
+            return that;
+        };
+    };
+    
+    fluid.littleComponent = function(name) {
+        return function(options) {
+            var that = fluid.initLittleComponent(name, options);
+            fluid.initDependents(that);
+            return that;
+        };
+    };
+    
+    fluid.makeComponents = function(components, env) {
+        if (!env) {
+            env = fluid.environment;
+        }
+        for (var name in components) {
+            fluid.setGlobalValue(name, 
+                fluid.invokeGlobalFunction(components[name], [name], env), env);
+        }
+    };
+    
+        
+    fluid.staticEnvironment = fluid.typeTag("fluid.staticEnvironment");
+    
+    fluid.staticEnvironment.environmentClass = fluid.typeTag("fluid.browser");
+    
+    // fluid.environmentalRoot.environmentClass = fluid.typeTag("fluid.rhino");
+    
+    fluid.demands("fluid.threadLocal", "fluid.browser", {funcName: "fluid.singleThreadLocal"});
+
+    var singleThreadLocal = fluid.typeTag("fluid.dynamicEnvironment");
+    
+    fluid.singleThreadLocal = function() {
+        return singleThreadLocal;
+    };
+
+    fluid.threadLocal = fluid.makeFreeInvoker("fluid.threadLocal");
+
+    fluid.withEnvironment = function(envAdd, func) {
+        var root = fluid.threadLocal();
+        try {
+            $.extend(root, envAdd);
+            return func();
+        }
+        finally {
+            for (var key in envAdd) {
+               delete root[key];
+            }
+        }
+    };
+    
+    fluid.extractEL = function(string, options) {
+        if (options.ELstyle === "ALL") {
+            return string;
+        }
+        else if (options.ELstyle.length === 1) {
+            if (string.charAt(0) === options.ELstyle) {
+                return string.substring(1);
+            }
+        }
+        else if (options.ELstyle === "${}") {
+            var i1 = string.indexOf("${");
+            var i2 = string.lastIndexOf("}");
+            if (i1 === 0 && i2 !== -1) {
+                return string.substring(2, i2);
+            }
+        }
+    };
+    
+    fluid.extractELWithContext = function(string, options) {
+        var EL = fluid.extractEL(string, options);
+        if (EL && EL.charAt(0) === "{") {
+            return fluid.parseContextReference(EL, 0);
+        }
+        return EL? {path: EL} : EL;
+    };
+
+    /* An EL extraction utility suitable for context expressions which occur in 
+     * expanding component trees. It assumes that any context expressions refer
+     * to EL paths that are to be referred to the "true (direct) model" - since
+     * the context during expansion may not agree with the context during rendering.
+     * It satisfies the same contract as fluid.extractEL, in that it will either return
+     * an EL path, or undefined if the string value supplied cannot be interpreted
+     * as an EL path with respect to the supplied options.
+     */
+        
+    fluid.extractContextualPath = function (string, options, env) {
+        var parsed = fluid.extractELWithContext(string, options);
+        if (parsed) {
+            if (parsed.context) {
+                var fetched = env[parsed.context];
+                if (typeof(fetched) !== "string") {
+                    fluid.fail("Could not look up context path named " + parsed.context + " to string value");
+                }
+                return fluid.model.composePath(fetched, parsed.path);
+            }
+            else {
+                return parsed.path;
+            }
+        }
+    };
+
+    fluid.parseContextReference = function(reference, index, delimiter) {
+        var endcpos = reference.indexOf("}", index + 1);
+        if (endcpos === -1) {
+            fluid.fail("Malformed context reference without }");
+        }
+        var context = reference.substring(index + 1, endcpos);
+        var endpos = delimiter? reference.indexOf(delimiter, endcpos + 1) : reference.length;
+        var path = reference.substring(endcpos + 1, endpos);
+        if (path.charAt(0) === ".") {
+            path = path.substring(1);
+        }
+        return {context: context, path: path, endpos: endpos};
+    };
+    
+    fluid.fetchContextReference = function(parsed, directModel, env) {
+        var base = parsed.context? env[parsed.context] : directModel;
+        if (!base) {
+            return base;
+        }
+        return fluid.get(base, parsed.path);
+    };
+    
+    fluid.resolveContextValue = function(string, options) {
+        if (options.bareContextRefs && string.charAt(0) === "{") {
+            var parsed = fluid.parseContextReference(string, 0);
+            return options.fetcher(parsed);        
+        }
+        else if (options.ELstyle && options.ELstyle !== "${}") {
+            var parsed = fluid.extractELWithContext(string, options);
+            if (parsed) {
+                return options.fetcher(parsed);
+            }
+        }
+        while (typeof(string) === "string") {
+            var i1 = string.indexOf("${");
+            var i2 = string.indexOf("}", i1 + 2);
+            var all = (i1 === 0 && i2 === string.length - 1); 
+            if (i1 !== -1 && i2 !== -1) {
+                var parsed;
+                if (string.charAt(i1 + 2) === "{") {
+                    parsed = fluid.parseContextReference(string, i1 + 2, "}");
+                    i2 = parsed.endpos;
+                }
+                else {
+                    parsed = {path: string.substring(i1 + 2, i2)};
+                }
+                var subs = options.fetcher(parsed);
+                // TODO: test case for all undefined substitution
+                if (subs === undefined || subs === null) {
+                    return subs;
+                    }
+                string = all? subs : string.substring(0, i1) + subs + string.substring(i2 + 1);
+            }
+            else {
+                break;
+            }
+        }
+        return string;
+    };
+    
+    function resolveEnvironmentImpl(obj, options) {
+        function recurse(arg) {
+            return resolveEnvironmentImpl(arg, options);
+        }
+        if (typeof(obj) === "string" && !options.noValue) {
+            return fluid.resolveContextValue(obj, options);
+        }
+        else if (fluid.isPrimitive(obj) || obj.nodeType !== undefined || obj.jquery) {
+            return obj;
+        }
+        else if (options.filter) {
+            return options.filter(obj, recurse, options);
+        }
+        else {
+            return (options.noCopy? fluid.each : fluid.transform)(obj, function(value, key) {
+                return resolveEnvironmentImpl(value, options);
+            });
+        }
+    }
+    
+    fluid.defaults("fluid.resolveEnvironment", 
+        {ELstyle:     "${}",
+         bareContextRefs: true});
+    
+    fluid.environmentFetcher = function(directModel) {
+        var env = fluid.threadLocal();
+        return function(parsed) {
+            return fluid.fetchContextReference(parsed, directModel, env);
+        };
+    };
+    
+    fluid.resolveEnvironment = function(obj, directModel, userOptions) {
+        directModel = directModel || {};
+        var options = fluid.merge(null, {}, fluid.defaults("fluid.resolveEnvironment"), userOptions);
+        if (!options.fetcher) {
+            options.fetcher = fluid.environmentFetcher(directModel);
+        }
+        return resolveEnvironmentImpl(obj, options);
+    };
+
+    /** "light" expanders, starting with support functions for the "deferredFetcher" expander **/
+
+    fluid.expander.deferredCall = function(target, source, recurse) {
+        var expander = source.expander;
+        var args = (!expander.args || fluid.isArrayable(expander.args))? expander.args : $.makeArray(expander.args);
+        args = recurse(args); 
+        return fluid.invokeGlobalFunction(expander.func, args);
+    };
+    
+    fluid.deferredCall = fluid.expander.deferredCall; // put in top namespace for convenience
+    
+    fluid.deferredInvokeCall = function(target, source, recurse) {
+        var expander = source.expander;
+        var args = (!expander.args || fluid.isArrayable(expander.args))? expander.args : $.makeArray(expander.args);
+        args = recurse(args);  
+        return fluid.invoke(expander.func, args);
+    };
+    
+    // The "noexpand" expander which simply unwraps one level of expansion and ceases.
+    fluid.expander.noexpand = function(target, source) {
+        return $.extend(target, source.expander.tree);
+    };
+  
+    fluid.noexpand = fluid.expander.noexpand; // TODO: check naming and namespacing
+  
+    fluid.expander.lightFilter = function (obj, recurse, options) {
+          var togo;
+          if (fluid.isArrayable(obj)) {
+              togo = options.noCopy? obj : [];
+              fluid.each(obj, function(value, key) {togo[key] = recurse(value);});
+          }
+          else {
+              togo = options.noCopy? obj : {};
+              for (var key in obj) {
+                  var value = obj[key];
+                  var expander;
+                  if (key === "expander" && !(options.expandOnly && options.expandOnly[value.type])){
+                      expander = fluid.getGlobalValue(value.type);  
+                      if (expander) {
+                          return expander.call(null, togo, obj, recurse);
+                      }
+                  }
+                  if (key !== "expander" || !expander) {
+                      togo[key] = recurse(value);
+                  }
+              }
+          }
+          return options.noCopy? obj : togo;
+      };
+      
+    fluid.expander.expandLight = function (source, expandOptions) {
+        var options = $.extend({}, expandOptions);
+        options.filter = fluid.expander.lightFilter;
+        return fluid.resolveEnvironment(source, options.model, options);       
+    };
+          
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/FluidRequests.js b/docs/jscripts/infusion/framework/core/js/FluidRequests.js
new file mode 100644 (file)
index 0000000..40d103c
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+Copyright 2007-2010 University of Cambridge
+Copyright 2007-2009 University of Toronto
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+// Declare dependencies.
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    /** Framework-global caching state for fluid.fetchResources **/
+
+    var resourceCache = {};
+  
+    var pendingClass = {};
+    /** Accepts a hash of structures with free keys, where each entry has either
+     * href/url or nodeId set - on completion, callback will be called with the populated
+     * structure with fetched resource text in the field "resourceText" for each
+     * entry. Each structure may contain "options" holding raw options to be forwarded
+     * to jQuery.ajax().
+     */
+  
+    fluid.fetchResources = function(resourceSpecs, callback, options) {
+        var that = fluid.initLittleComponent("fluid.fetchResources", options);
+        that.resourceSpecs = resourceSpecs;
+        that.callback = callback;
+        that.operate = function() {
+            fluid.fetchResources.fetchResourcesImpl(that);
+        };
+        fluid.each(resourceSpecs, function(resourceSpec) {
+             resourceSpec.recurseFirer = fluid.event.getEventFirer();
+             resourceSpec.recurseFirer.addListener(that.operate);
+             if (resourceSpec.url && !resourceSpec.href) {
+                resourceSpec.href = resourceSpec.url;
+             }
+        });
+        if (that.options.amalgamateClasses) {
+            fluid.fetchResources.amalgamateClasses(resourceSpecs, that.options.amalgamateClasses, that.operate);
+        }
+        that.operate();
+        return that;
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    // Add "synthetic" elements of *this* resourceSpec list corresponding to any
+    // still pending elements matching the PROLEPTICK CLASS SPECIFICATION supplied 
+    fluid.fetchResources.amalgamateClasses = function(specs, classes, operator) {
+        fluid.each(classes, function(clazz) {
+            var pending = pendingClass[clazz];
+            fluid.each(pending, function(pendingrec, canon) {
+                specs[clazz+"!"+canon] = pendingrec;
+                pendingrec.recurseFirer.addListener(operator);
+            });
+        });
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.timeSuccessCallback = function(resourceSpec) {
+        if (resourceSpec.timeSuccess && resourceSpec.options && resourceSpec.options.success) {
+            var success = resourceSpec.options.success;
+            resourceSpec.options.success = function() {
+            var startTime = new Date();
+            var ret = success.apply(null, arguments);
+            fluid.log("External callback for URL " + resourceSpec.href + " completed - callback time: " + 
+                    (new Date().getTime() - startTime.getTime()) + "ms");
+            return ret;
+            };
+        }
+    };
+    
+    // TODO: Integrate punch-through from old Engage implementation
+    function canonUrl(url) {
+        return url;
+    }
+    
+    fluid.fetchResources.clearResourceCache = function(url) {
+        if (url) {
+            delete resourceCache[canonUrl(url)];
+        }
+        else {
+            fluid.clear(resourceCache);
+        }  
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.handleCachedRequest = function(resourceSpec, response) {
+         var canon = canonUrl(resourceSpec.href);
+         var cached = resourceCache[canon];
+         if (cached.$$firer$$) {
+             fluid.log("Handling request for " + canon + " from cache");
+             var fetchClass = resourceSpec.fetchClass;
+             if (fetchClass && pendingClass[fetchClass]) {
+                 fluid.log("Clearing pendingClass entry for class " + fetchClass);
+                 delete pendingClass[fetchClass][canon];
+             }
+             resourceCache[canon] = response;      
+             cached.fire(response);
+         }
+    };
+    
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.completeRequest = function(thisSpec, recurseCall) {
+        thisSpec.queued = false;
+        thisSpec.completeTime = new Date();
+        fluid.log("Request to URL " + thisSpec.href + " completed - total elapsed time: " + 
+            (thisSpec.completeTime.getTime() - thisSpec.initTime.getTime()) + "ms");
+        thisSpec.recurseFirer.fire();
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.makeResourceCallback = function(thisSpec) {
+        return {
+            success: function(response) {
+                thisSpec.resourceText = response;
+                thisSpec.resourceKey = thisSpec.href;
+                if (thisSpec.forceCache) {
+                    fluid.fetchResources.handleCachedRequest(thisSpec, response);
+                }
+                fluid.fetchResources.completeRequest(thisSpec);
+            },
+            error: function(response, textStatus, errorThrown) {
+                thisSpec.fetchError = {
+                    status: response.status,
+                    textStatus: response.textStatus,
+                    errorThrown: errorThrown
+                };
+                fluid.fetchResources.completeRequest(thisSpec);
+            }
+            
+        };
+    };
+    
+        
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.issueCachedRequest = function(resourceSpec, options) {
+         var canon = canonUrl(resourceSpec.href);
+         var cached = resourceCache[canon];
+         if (!cached) {
+             fluid.log("First request for cached resource with url " + canon);
+             cached = fluid.event.getEventFirer();
+             cached.$$firer$$ = true;
+             resourceCache[canon] = cached;
+             var fetchClass = resourceSpec.fetchClass;
+             if (fetchClass) {
+                 if (!pendingClass[fetchClass]) {
+                     pendingClass[fetchClass] = {};
+                 }
+                 pendingClass[fetchClass][canon] = resourceSpec;
+             }
+             options.cache = false; // TODO: Getting weird "not modified" issues on Firefox
+             $.ajax(options);
+         }
+         else {
+             if (!cached.$$firer$$) {
+                 options.success(cached);
+             }
+             else {
+                 fluid.log("Request for cached resource which is in flight: url " + canon);
+                 cached.addListener(function(response) {
+                     options.success(response);
+                 });
+             }
+         }
+    };
+    
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    // Compose callbacks in such a way that the 2nd, marked "external" will be applied
+    // first if it exists, but in all cases, the first, marked internal, will be 
+    // CALLED WITHOUT FAIL
+    fluid.fetchResources.composeCallbacks = function(internal, external) {
+        return external? function() {
+            try {
+                external.apply(null, arguments);
+            }
+            catch (e) {
+                fluid.log("Exception applying external fetchResources callback: " + e);
+            }
+            internal.apply(null, arguments); // call the internal callback without fail
+        } : internal;
+    };
+    
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.composePolicy = function(target, source, key) {
+        target[key] = fluid.fetchResources.composeCallbacks(target[key], source[key]);
+    };
+    
+    fluid.defaults("fluid.fetchResources.issueRequest", {
+        mergePolicy: {
+            success: fluid.fetchResources.composePolicy,
+            error: fluid.fetchResources.composePolicy,
+            url: "reverse"
+        }
+    });
+    
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.fetchResources.issueRequest = function(resourceSpec, key) {
+        var thisCallback = fluid.fetchResources.makeResourceCallback(resourceSpec);
+        var options = {  
+             url:     resourceSpec.href,
+             success: thisCallback.success, 
+             error:   thisCallback.error};
+        fluid.fetchResources.timeSuccessCallback(resourceSpec);
+        fluid.merge(fluid.defaults("fluid.fetchResources.issueRequest").mergePolicy,
+                      options, resourceSpec.options);
+        resourceSpec.queued = true;
+        resourceSpec.initTime = new Date();
+        fluid.log("Request with key " + key + " queued for " + resourceSpec.href);
+
+        if (resourceSpec.forceCache) {
+            fluid.fetchResources.issueCachedRequest(resourceSpec, options);
+        }
+        else {
+            $.ajax(options);
+        }
+    };
+    
+    fluid.fetchResources.fetchResourcesImpl = function(that) {
+        var complete = true;
+        var allSync = true;
+        var resourceSpecs = that.resourceSpecs;
+        for (var key in resourceSpecs) {
+            var resourceSpec = resourceSpecs[key];
+            if (!resourceSpec.options || resourceSpec.options.async) {
+                allSync = false;
+            }
+            if (resourceSpec.href && !resourceSpec.completeTime) {
+                 if (!resourceSpec.queued) {
+                     fluid.fetchResources.issueRequest(resourceSpec, key);  
+                 }
+                 if (resourceSpec.queued) {
+                     complete = false;
+                 }
+            }
+            else if (resourceSpec.nodeId && !resourceSpec.resourceText) {
+                var node = document.getElementById(resourceSpec.nodeId);
+                // upgrade this to somehow detect whether node is "armoured" somehow
+                // with comment or CDATA wrapping
+                resourceSpec.resourceText = fluid.dom.getElementText(node);
+                resourceSpec.resourceKey = resourceSpec.nodeId;
+            }
+        }
+        if (complete && that.callback && !that.callbackCalled) {
+            that.callbackCalled = true;
+            if ($.browser.mozilla && !allSync) {
+                // Defer this callback to avoid debugging problems on Firefox
+                setTimeout(function() {
+                    that.callback(resourceSpecs);
+                    }, 1);
+            }
+            else {
+                that.callback(resourceSpecs);
+            }
+        }
+    };
+    
+    fluid.fetchResources.primeCacheFromResources = function(componentName) {
+        var resources = fluid.defaults(componentName).resources;
+        var expanded = (fluid.expandOptions? fluid.expandOptions : fluid.identity)(fluid.copy(resources));
+        fluid.fetchResources(expanded);
+    };
+    
+    /** Utilities invoking requests for expansion **/
+    fluid.registerNamespace("fluid.expander");
+      
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.expander.makeDefaultFetchOptions = function (successdisposer, failid, options) {
+        return $.extend(true, {dataType: "text"}, options, {
+            success: function(response, environmentdisposer) {
+                var json = JSON.parse(response);
+                environmentdisposer(successdisposer(json));
+            },
+            error: function(response, textStatus) {
+                fluid.log("Error fetching " + failid + ": " + textStatus);
+            }
+        });
+    };
+  
+    /*
+     * This function is unsupported: It is not really intended for use by implementors.
+     */
+    fluid.expander.makeFetchExpander = function (options) {
+        return { expander: {
+            type: "fluid.expander.deferredFetcher",
+            href: options.url,
+            options: fluid.expander.makeDefaultFetchOptions(options.disposer, options.url, options.options),
+            resourceSpecCollector: "{resourceSpecCollector}",
+            fetchKey: options.fetchKey
+        }};
+    };
+    
+    fluid.expander.deferredFetcher = function(target, source) {
+        var expander = source.expander;
+        var spec = fluid.copy(expander);
+        // fetch the "global" collector specified in the external environment to receive
+        // this resourceSpec
+        var collector = fluid.resolveEnvironment(expander.resourceSpecCollector);
+        delete spec.type;
+        delete spec.resourceSpecCollector;
+        delete spec.fetchKey;
+        var environmentdisposer = function(disposed) {
+            $.extend(target, disposed);
+        };
+        // replace the callback which is there (taking 2 arguments) with one which
+        // directly responds to the request, passing in the result and OUR "disposer" - 
+        // which once the user has processed the response (say, parsing JSON and repackaging)
+        // finally deposits it in the place of the expander in the tree to which this reference
+        // has been stored at the point this expander was evaluated.
+        spec.options.success = function(response) {
+             expander.options.success(response, environmentdisposer);
+        };
+        var key = expander.fetchKey || fluid.allocateGuid();
+        collector[key] = spec;
+        return target;
+    };
+    
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/FluidView.js b/docs/jscripts/infusion/framework/core/js/FluidView.js
new file mode 100644 (file)
index 0000000..85c3725
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/** This file contains functions which depend on the presence of a DOM document
+ *  and which depend on the contents of Fluid.js **/
+
+// Declare dependencies.
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+
+    fluid.defaults("fluid.ariaLabeller", {
+        labelAttribute: "aria-label",
+        liveRegionMarkup: "<div class=\"liveRegion fl-offScreen-hidden\" aria-live=\"polite\"></div>",
+        liveRegionId: "fluid-ariaLabeller-liveRegion",
+        invokers: {
+            generateLiveElement: {funcName: "fluid.ariaLabeller.generateLiveElement", args: ["{ariaLabeller}"]}
+        }
+    });
+    fluid.ariaLabeller = function(element, options) {
+        var that = fluid.initView("fluid.ariaLabeller", element, options);
+        fluid.initDependents(that);
+
+        that.update = function(newOptions) {
+            newOptions = newOptions || that.options;
+            that.container.attr(that.options.labelAttribute, newOptions.text);
+            if (newOptions.dynamicLabel) {
+                var live = fluid.jById(that.options.liveRegionId); 
+                if (live.length === 0) {
+                    live = that.generateLiveElement();
+                }
+                live.text(newOptions.text);
+            }
+        }
+        that.update();
+        return that;
+    };
+    
+    fluid.ariaLabeller.generateLiveElement = function(that) {
+        var liveEl = $(that.options.liveRegionMarkup);
+        liveEl.attr("id", that.options.liveRegionId);
+        $("body").append(liveEl);
+        return liveEl;
+    };
+    
+    var LABEL_KEY = "aria-labelling";
+    
+    fluid.getAriaLabeller = function(element) {
+        element = $(element);
+        var that = fluid.getScopedData(element, LABEL_KEY);
+        return that;      
+    };
+    
+    /** Manages an ARIA-mediated label attached to a given DOM element. An
+     * aria-labelledby attribute and target node is fabricated in the document
+     * if they do not exist already, and a "little component" is returned exposing a method
+     * "update" that allows the text to be updated. */
+    
+    fluid.updateAriaLabel = function(element, text, options) {
+        var options = $.extend({}, options || {}, {text: text});
+        var that = fluid.getAriaLabeller(element);
+        if (!that) {
+            that = fluid.ariaLabeller(element, options);
+            fluid.setScopedData(element, LABEL_KEY, that);
+        }
+        else that.update(options);
+        return that;
+    };
+    
+    /** Sets an interation on a target control, which morally manages a "blur" for
+     * a possibly composite region.
+     * A timed blur listener is set on the control, which waits for a short period of
+     * time (options.delay, defaults to 150ms) to discover whether the reason for the 
+     * blur interaction is that either a focus or click is being serviced on a nominated
+     * set of "exclusions" (options.exclusions, a free hash of elements or jQueries). 
+     * If no such event is received within the window, options.handler will be called
+     * with the argument "control", to service whatever interaction is required of the
+     * blur.
+     */
+    
+    fluid.deadMansBlur = function (control, options) {
+        var that = fluid.initLittleComponent(control, options);
+        that.blurPending = false;
+        $(control).blur(function () {
+            that.blurPending = true;
+            setTimeout(function () {
+                if (that.blurPending) {
+                    that.options.handler(control);
+                }
+            }, that.options.delay);
+        });
+        that.canceller = function () {
+            that.blurPending = false; 
+        };
+        fluid.each(that.options.exclusions, function(exclusion) {
+            var exclusion = $(exclusion);
+            exclusion.focusin(that.canceller);
+            exclusion.click(that.canceller);
+        });
+        return that;
+    };
+
+    fluid.defaults("fluid.deadMansBlur", {
+        delay: 150,
+    });
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/JavaProperties.js b/docs/jscripts/infusion/framework/core/js/JavaProperties.js
new file mode 100644 (file)
index 0000000..2dcd33c
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2009 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+/*global fluid_1_3*/
+
+fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+      
+  var unUnicode = /(\\u[\dabcdef]{4}|\\x[\dabcdef]{2})/g;
+  
+  fluid.unescapeProperties = function (string) {
+    string = string.replace(unUnicode, function(match) {
+      var code = match.substring(2);
+      var parsed = parseInt(code, 16);
+      return String.fromCharCode(parsed);
+      }
+    );
+    var pos = 0;
+    while (true) {
+        var backpos = string.indexOf("\\", pos);
+        if (backpos === -1) {
+            break;
+        }
+        if (backpos === string.length - 1) {
+          return [string.substring(0, string.length - 1), true];
+        }
+        var replace = string.charAt(backpos + 1);
+        if (replace === "n") replace = "\n";
+        if (replace === "r") replace = "\r";
+        if (replace === "t") replace = "\t";
+        string = string.substring(0, backpos) + replace + string.substring(backpos + 2);
+        pos = backpos + 1;
+    }
+    return [string, false];
+  };
+  
+  var breakPos = /[^\\][\s:=]/;
+  
+  fluid.parseJavaProperties = function(text) {
+    // File format described at http://java.sun.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)
+    var togo = {};
+    text = text.replace(/\r\n/g, "\n");
+    text = text.replace(/\r/g, "\n");
+    lines = text.split("\n");
+    var contin, key, valueComp, valueRaw, valueEsc;
+    for (var i = 0; i < lines.length; ++ i) {
+      var line = $.trim(lines[i]);
+      if (!line || line.charAt(0) === "#" || line.charAt(0) === '!') {
+          continue;
+      }
+      if (!contin) {
+        valueComp = "";
+        var breakpos = line.search(breakPos);
+        if (breakpos === -1) {
+          key = line;
+          valueRaw = "";
+          }
+        else {
+          key = $.trim(line.substring(0, breakpos + 1)); // +1 since first char is escape exclusion
+          valueRaw = $.trim(line.substring(breakpos + 2));
+          if (valueRaw.charAt(0) === ":" || valueRaw.charAt(0) === "=") {
+            valueRaw = $.trim(valueRaw.substring(1));
+          }
+        }
+      
+        key = fluid.unescapeProperties(key)[0];
+        valueEsc = fluid.unescapeProperties(valueRaw);
+      }
+      else {
+        valueEsc = fluid.unescapeProperties(line);
+      }
+
+      contin = valueEsc[1];
+      if (!valueEsc[1]) { // this line was not a continuation line - store the value
+        togo[key] = valueComp + valueEsc[0];
+      }
+      else {
+        valueComp += valueEsc[0];
+      }
+    }
+    return togo;
+  };
+      
+    /** 
+     * Expand a message string with respect to a set of arguments, following a basic
+     * subset of the Java MessageFormat rules. 
+     * http://java.sun.com/j2se/1.4.2/docs/api/java/text/MessageFormat.html
+     * 
+     * The message string is expected to contain replacement specifications such
+     * as {0}, {1}, {2}, etc.
+     * @param messageString {String} The message key to be expanded
+     * @param args {String/Array of String} An array of arguments to be substituted into the message.
+     * @return The expanded message string. 
+     */
+    fluid.formatMessage = function (messageString, args) {
+        if (!args) {
+            return messageString;
+        } 
+        if (typeof(args) === "string") {
+            args = [args];
+        }
+        for (var i = 0; i < args.length; ++ i) {
+            messageString = messageString.replace("{" + i + "}", args[i]);
+        }
+        return messageString;
+    };
+      
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/ModelTransformations.js b/docs/jscripts/infusion/framework/core/js/ModelTransformations.js
new file mode 100644 (file)
index 0000000..ae0d430
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+Copyright 2010 University of Toronto
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global fluid, mccord, jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+var fluid = fluid || fluid_1_3;
+
+(function ($) {
+
+    fluid.model = fluid.model || {};
+    fluid.model.transform = fluid.model.transform || {};
+    
+    
+    /******************************
+     * General Model Transformers *
+     ******************************/
+    
+    fluid.model.transform.value = function (model, expandSpec, recurse) {
+        var val;
+        if (expandSpec.path) {
+            val = fluid.get(model, expandSpec.path);
+            
+            if (typeof(val) !== "undefined") {
+                return val;
+            }
+        }
+        
+        return typeof(expandSpec.value) === "object" ? recurse(model, expandSpec.value) : expandSpec.value;    
+    };
+    
+    fluid.model.transform.arrayValue = function (model, expandSpec, recurse) {
+        return fluid.makeArray(fluid.model.transform.value(model, expandSpec));
+    };
+     
+    fluid.model.transform.count = function (model, expandSpec, recurse) {
+        var value = fluid.get(model, expandSpec.path);
+        return fluid.makeArray(value).length;
+    };
+     
+    fluid.model.transform.firstValue = function (model, expandSpec, recurse) {
+        var result;
+        for (var i = 0; i < expandSpec.values.length; i++) {
+            var value = expandSpec.values[i];
+            if (typeof(value) === "string") {
+                value = fixupExpandSpec(value);
+            }
+            result = fluid.model.transform.value(model, value.expander, recurse);
+            if (typeof(result) !== "undefined") {
+                break;
+            }
+        }
+        return result;
+    };
+    
+    var getOrRecurse = function (model, value, recurse) {
+        return typeof(value) === "string" ? fluid.get(model, value) : recurse(model, value, recurse);
+    };
+    
+    fluid.model.transform.merge = function (model, expandSpec, recurse) {
+        var left = getOrRecurse(model, expandSpec.left, recurse);
+        var right = getOrRecurse(model, expandSpec.right, recurse);
+        
+        if (typeof(left) !== "object" || typeof(right) !== "object") {
+            return left;
+        }
+        
+        return fluid.merge(expandSpec.policy ? expandSpec.policy : null, {}, left, right);
+    };
+     
+    var fixupExpandSpec = function (expandSpec) {
+        return {
+            expander: {
+                type: "fluid.model.transform.value",
+                path: expandSpec
+            }
+        };
+    };
+    
+    var expandRule = function (model, targetPath, rule) {
+        var expanded = {};
+        for (var key in rule) {
+            var value = rule[key];
+            if (key === "expander") {
+                var expanderFn = fluid.getGlobalValue(value.type);
+                if (expanderFn) {
+                    expanded = expanderFn.call(null, model, value, fluid.model.transformWithRules);
+                }
+            } else {
+                expanded[key] = fluid.model.transformWithRules(model, value);
+            }
+        }
+        return expanded;
+    };
+    
+    /**
+     * Transforms a model based on a specified expansion rules objects.
+     * Rules objects take the form of:
+     *   {
+     *       "target.path": "value.el.path" || {
+     *          expander: {
+     *              type: "expander.function.path",
+     *               ...
+     *           }
+     *       }
+     *   }
+     *
+     * @param {Object} model the model to transform
+     * @param {Object} rules a rules object containing instructions on how to transform the model
+     */
+    fluid.model.transformWithRules = function (model, rules) {
+        var transformed = {};
+        for (var targetPath in rules) {
+            var rule = rules[targetPath];
+            
+            if (typeof(rule) === "string") {
+                rule = fixupExpandSpec(rule);
+            }
+            
+            var expanded = expandRule(model, targetPath, rule);
+            if (typeof(expanded) !== "undefined") {
+                fluid.set(transformed, targetPath, expanded);
+            }
+        };
+        return transformed;
+    };
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/core/js/jquery.keyboard-a11y.js b/docs/jscripts/infusion/framework/core/js/jquery.keyboard-a11y.js
new file mode 100644 (file)
index 0000000..0a62fd4
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+Copyright 2008-2010 University of Cambridge
+Copyright 2008-2010 University of Toronto
+Copyright 2010 Lucendo Development Ltd.
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+var fluid = fluid || fluid_1_3;
+
+(function ($, fluid) {
+
+    // $().fluid("selectable", args)
+    // $().fluid("selectable".that()
+    // $().fluid("pager.pagerBar", args)
+    // $().fluid("reorderer", options)
+
+/** Create a "bridge" from code written in the Fluid standard "that-ist" style,
+ *  to the standard JQuery UI plugin architecture specified at http://docs.jquery.com/UI/Guidelines .
+ *  Every Fluid component corresponding to the top-level standard signature (JQueryable, options)
+ *  will automatically convert idiomatically to the JQuery UI standard via this adapter. 
+ *  Any return value which is a primitive or array type will become the return value
+ *  of the "bridged" function - however, where this function returns a general hash
+ *  (object) this is interpreted as forming part of the Fluid "return that" pattern,
+ *  and the function will instead be bridged to "return this" as per JQuery standard,
+ *  permitting chaining to occur. However, as a courtesy, the particular "this" returned
+ *  will be augmented with a function that() which will allow the original return
+ *  value to be retrieved if desired.
+ *  @param {String} name The name under which the "plugin space" is to be injected into
+ *  JQuery
+ *  @param {Object} peer The root of the namespace corresponding to the peer object.
+ */
+
+    fluid.thatistBridge = function (name, peer) {
+
+        var togo = function(funcname) {
+            var segs = funcname.split(".");
+            var move = peer;
+            for (var i = 0; i < segs.length; ++i) {
+                move = move[segs[i]];
+            }
+            var args = [this];
+            if (arguments.length === 2) {
+                args = args.concat($.makeArray(arguments[1]));
+            }
+            var ret = move.apply(null, args);
+            this.that = function() {
+                return ret;
+            }
+            var type = typeof(ret);
+            return !ret || type === "string" || type === "number" || type === "boolean"
+              || ret && ret.length !== undefined? ret: this;
+        };
+        $.fn[name] = togo;
+        return togo;
+    };
+
+    fluid.thatistBridge("fluid", fluid);
+    fluid.thatistBridge("fluid_1_3", fluid_1_3);
+
+/*************************************************************************
+ * Tabindex normalization - compensate for browser differences in naming
+ * and function of "tabindex" attribute and tabbing order.
+ */
+
+    // -- Private functions --
+    
+    
+    var normalizeTabindexName = function() {
+        return $.browser.msie ? "tabIndex" : "tabindex";
+    };
+
+    var canHaveDefaultTabindex = function(elements) {
+       if (elements.length <= 0) {
+           return false;
+       }
+
+       return $(elements[0]).is("a, input, button, select, area, textarea, object");
+    };
+    
+    var getValue = function(elements) {
+        if (elements.length <= 0) {
+            return undefined;
+        }
+
+        if (!fluid.tabindex.hasAttr(elements)) {
+            return canHaveDefaultTabindex(elements) ? Number(0) : undefined;
+        }
+
+        // Get the attribute and return it as a number value.
+        var value = elements.attr(normalizeTabindexName());
+        return Number(value);
+    };
+
+    var setValue = function(elements, toIndex) {
+        return elements.each(function(i, item) {
+            $(item).attr(normalizeTabindexName(), toIndex);
+        });
+    };
+    
+    // -- Public API --
+    
+    /**
+     * Gets the value of the tabindex attribute for the first item, or sets the tabindex value of all elements
+     * if toIndex is specified.
+     * 
+     * @param {String|Number} toIndex
+     */
+    fluid.tabindex = function(target, toIndex) {
+        target = $(target);
+        if (toIndex !== null && toIndex !== undefined) {
+            return setValue(target, toIndex);
+        } else {
+            return getValue(target);
+        }
+    };
+
+    /**
+     * Removes the tabindex attribute altogether from each element.
+     */
+    fluid.tabindex.remove = function(target) {
+        target = $(target);
+        return target.each(function(i, item) {
+            $(item).removeAttr(normalizeTabindexName());
+        });
+    };
+
+    /**
+     * Determines if an element actually has a tabindex attribute present.
+     */
+    fluid.tabindex.hasAttr = function(target) {
+        target = $(target);
+        if (target.length <= 0) {
+            return false;
+        }
+        var togo = target.map(
+            function() {
+                var attributeNode = this.getAttributeNode(normalizeTabindexName());
+                return attributeNode ? attributeNode.specified : false;
+            }
+            );
+        return togo.length === 1? togo[0] : togo;
+    };
+
+    /**
+     * Determines if an element either has a tabindex attribute or is naturally tab-focussable.
+     */
+    fluid.tabindex.has = function(target) {
+        target = $(target);
+        return fluid.tabindex.hasAttr(target) || canHaveDefaultTabindex(target);
+    };
+
+    // Keyboard navigation
+    // Public, static constants needed by the rest of the library.
+    fluid.a11y = $.a11y || {};
+
+    fluid.a11y.orientation = {
+        HORIZONTAL: 0,
+        VERTICAL: 1,
+        BOTH: 2
+    };
+
+    var UP_DOWN_KEYMAP = {
+        next: $.ui.keyCode.DOWN,
+        previous: $.ui.keyCode.UP
+    };
+
+    var LEFT_RIGHT_KEYMAP = {
+        next: $.ui.keyCode.RIGHT,
+        previous: $.ui.keyCode.LEFT
+    };
+
+    // Private functions.
+    var unwrap = function(element) {
+        return element.jquery ? element[0] : element; // Unwrap the element if it's a jQuery.
+    };
+
+
+    var makeElementsTabFocussable = function(elements) {
+        // If each element doesn't have a tabindex, or has one set to a negative value, set it to 0.
+        elements.each(function(idx, item) {
+            item = $(item);
+            if (!item.fluid("tabindex.has") || item.fluid("tabindex") < 0) {
+                item.fluid("tabindex", 0);
+            }
+        });
+    };
+
+    // Public API.
+    /**
+     * Makes all matched elements available in the tab order by setting their tabindices to "0".
+     */
+    fluid.tabbable = function(target) {
+        target = $(target);
+        makeElementsTabFocussable(target);
+    };
+
+    /*********************************************************************** 
+     * Selectable functionality - geometrising a set of nodes such that they
+     * can be navigated (by setting focus) using a set of directional keys
+     */
+
+    var CONTEXT_KEY = "selectionContext";
+    var NO_SELECTION = -32768;
+
+    var cleanUpWhenLeavingContainer = function(selectionContext) {
+        if (selectionContext.activeItemIndex !== NO_SELECTION) {
+            if (selectionContext.options.onLeaveContainer) {
+                selectionContext.options.onLeaveContainer(
+                  selectionContext.selectables[selectionContext.activeItemIndex]);
+            } else if (selectionContext.options.onUnselect) {
+                selectionContext.options.onUnselect(
+                selectionContext.selectables[selectionContext.activeItemIndex]);
+            }
+        }
+
+        if (!selectionContext.options.rememberSelectionState) {
+            selectionContext.activeItemIndex = NO_SELECTION;
+        }
+    };
+
+    /**
+     * Does the work of selecting an element and delegating to the client handler.
+     */
+    var drawSelection = function(elementToSelect, handler) {
+        if (handler) {
+            handler(elementToSelect);
+        }
+    };
+
+    /**
+     * Does does the work of unselecting an element and delegating to the client handler.
+     */
+    var eraseSelection = function(selectedElement, handler) {
+        if (handler && selectedElement) {
+            handler(selectedElement);
+        }
+    };
+
+    var unselectElement = function(selectedElement, selectionContext) {
+        eraseSelection(selectedElement, selectionContext.options.onUnselect);
+    };
+
+    var selectElement = function(elementToSelect, selectionContext) {
+        // It's possible that we're being called programmatically, in which case we should clear any previous selection.
+        unselectElement(selectionContext.selectedElement(), selectionContext);
+
+        elementToSelect = unwrap(elementToSelect);
+        var newIndex = selectionContext.selectables.index(elementToSelect);
+
+        // Next check if the element is a known selectable. If not, do nothing.
+        if (newIndex === -1) {
+           return;
+        }
+
+        // Select the new element.
+        selectionContext.activeItemIndex = newIndex;
+        drawSelection(elementToSelect, selectionContext.options.onSelect);
+    };
+
+    var selectableFocusHandler = function(selectionContext) {
+        return function(evt) {
+            // FLUID-3590: newer browsers (FF 3.6, Webkit 4) have a form of "bug" in that they will go bananas
+            // on attempting to move focus off an element which has tabindex dynamically set to -1.
+            $(evt.target).fluid("tabindex", 0);
+            selectElement(evt.target, selectionContext);
+
+            // Force focus not to bubble on some browsers.
+            return evt.stopPropagation();
+        };
+    };
+
+    var selectableBlurHandler = function(selectionContext) {
+        return function(evt) {
+            $(evt.target).fluid("tabindex", selectionContext.options.selectablesTabindex);
+            unselectElement(evt.target, selectionContext);
+
+            // Force blur not to bubble on some browsers.
+            return evt.stopPropagation();
+        };
+    };
+
+    var reifyIndex = function(sc_that) {
+        var elements = sc_that.selectables;
+        if (sc_that.activeItemIndex >= elements.length) {
+            sc_that.activeItemIndex = 0;
+        }
+        if (sc_that.activeItemIndex < 0 && sc_that.activeItemIndex !== NO_SELECTION) {
+            sc_that.activeItemIndex = elements.length - 1;
+        }
+        if (sc_that.activeItemIndex >= 0) {
+            $(elements[sc_that.activeItemIndex]).focus();
+        }
+    };
+
+    var prepareShift = function(selectionContext) {
+        // FLUID-3590: FF 3.6 and Safari 4.x won't fire blur() when programmatically moving focus.
+        var selElm = selectionContext.selectedElement();
+        if (selElm) {
+            selElm.blur();
+        }
+
+        unselectElement(selectionContext.selectedElement(), selectionContext);
+        if (selectionContext.activeItemIndex === NO_SELECTION) {
+          selectionContext.activeItemIndex = -1;
+        }
+    };
+
+    var focusNextElement = function(selectionContext) {
+        prepareShift(selectionContext);
+        ++selectionContext.activeItemIndex;
+        reifyIndex(selectionContext);
+    };
+
+    var focusPreviousElement = function(selectionContext) {
+        prepareShift(selectionContext);
+        --selectionContext.activeItemIndex;
+        reifyIndex(selectionContext);
+    };
+
+    var arrowKeyHandler = function(selectionContext, keyMap, userHandlers) {
+        return function(evt) {
+            if (evt.which === keyMap.next) {
+                focusNextElement(selectionContext);
+                evt.preventDefault();
+            } else if (evt.which === keyMap.previous) {
+                focusPreviousElement(selectionContext);
+                evt.preventDefault();
+            }
+        };
+    };
+
+    var getKeyMapForDirection = function(direction) {
+        // Determine the appropriate mapping for next and previous based on the specified direction.
+        var keyMap;
+        if (direction === fluid.a11y.orientation.HORIZONTAL) {
+            keyMap = LEFT_RIGHT_KEYMAP;
+        } 
+        else if (direction === fluid.a11y.orientation.VERTICAL) {
+            // Assume vertical in any other case.
+            keyMap = UP_DOWN_KEYMAP;
+        }
+
+        return keyMap;
+    };
+
+    var tabKeyHandler = function(selectionContext) {
+        return function(evt) {
+            if (evt.which !== $.ui.keyCode.TAB) {
+                return;
+            }
+            cleanUpWhenLeavingContainer(selectionContext);
+
+            // Catch Shift-Tab and note that focus is on its way out of the container.
+            if (evt.shiftKey) {
+                selectionContext.focusIsLeavingContainer = true;
+            }
+        };
+    };
+
+    var containerFocusHandler = function(selectionContext) {
+        return function(evt) {
+            var shouldOrig = selectionContext.options.autoSelectFirstItem;
+            var shouldSelect = typeof(shouldOrig) === "function" ? 
+                 shouldOrig() : shouldOrig;
+
+            // Override the autoselection if we're on the way out of the container.
+            if (selectionContext.focusIsLeavingContainer) {
+                shouldSelect = false;
+            }
+
+            // This target check works around the fact that sometimes focus bubbles, even though it shouldn't.
+            if (shouldSelect && evt.target === selectionContext.container.get(0)) {
+                if (selectionContext.activeItemIndex === NO_SELECTION) {
+                    selectionContext.activeItemIndex = 0;
+                }
+                $(selectionContext.selectables[selectionContext.activeItemIndex]).focus();
+            }
+
+           // Force focus not to bubble on some browsers.
+           return evt.stopPropagation();
+        };
+    };
+
+    var containerBlurHandler = function(selectionContext) {
+        return function(evt) {
+            selectionContext.focusIsLeavingContainer = false;
+
+            // Force blur not to bubble on some browsers.
+            return evt.stopPropagation();
+        };
+    };
+
+    var makeElementsSelectable = function(container, defaults, userOptions) {
+
+        var options = $.extend(true, {}, defaults, userOptions);
+
+        var keyMap = getKeyMapForDirection(options.direction);
+
+        var selectableElements = options.selectableElements? options.selectableElements :
+              container.find(options.selectableSelector);
+          
+        // Context stores the currently active item(undefined to start) and list of selectables.
+        var that = {
+            container: container,
+            activeItemIndex: NO_SELECTION,
+            selectables: selectableElements,
+            focusIsLeavingContainer: false,
+            options: options
+        };
+
+        that.selectablesUpdated = function(focusedItem) {
+          // Remove selectables from the tab order and add focus/blur handlers
+            if (typeof(that.options.selectablesTabindex) === "number") {
+                that.selectables.fluid("tabindex", that.options.selectablesTabindex);
+            }
+            that.selectables.unbind("focus." + CONTEXT_KEY);
+            that.selectables.unbind("blur." + CONTEXT_KEY);
+            that.selectables.bind("focus."+ CONTEXT_KEY, selectableFocusHandler(that));
+            that.selectables.bind("blur." + CONTEXT_KEY, selectableBlurHandler(that));
+            if (keyMap && that.options.noBubbleListeners) {
+                that.selectables.unbind("keydown."+CONTEXT_KEY);
+                that.selectables.bind("keydown."+CONTEXT_KEY, arrowKeyHandler(that, keyMap));
+            }
+            if (focusedItem) {
+                selectElement(focusedItem, that);
+            }
+            else {
+                reifyIndex(that);
+            }
+        };
+
+        that.refresh = function() {
+            if (!that.options.selectableSelector) {
+                throw("Cannot refresh selectable context which was not initialised by a selector");
+            }
+            that.selectables = container.find(options.selectableSelector);
+            that.selectablesUpdated();
+        };
+        
+        that.selectedElement = function() {
+            return that.activeItemIndex < 0? null : that.selectables[that.activeItemIndex];
+        };
+        
+        // Add various handlers to the container.
+        if (keyMap && !that.options.noBubbleListeners) {
+            container.keydown(arrowKeyHandler(that, keyMap));
+        }
+        container.keydown(tabKeyHandler(that));
+        container.focus(containerFocusHandler(that));
+        container.blur(containerBlurHandler(that));
+        
+        that.selectablesUpdated();
+
+        return that;
+    };
+
+    /**
+     * Makes all matched elements selectable with the arrow keys.
+     * Supply your own handlers object with onSelect: and onUnselect: properties for custom behaviour.
+     * Options provide configurability, including direction: and autoSelectFirstItem:
+     * Currently supported directions are jQuery.a11y.directions.HORIZONTAL and VERTICAL.
+     */
+    fluid.selectable = function(target, options) {
+        target = $(target);
+        var that = makeElementsSelectable(target, fluid.selectable.defaults, options);
+        fluid.setScopedData(target, CONTEXT_KEY, that);
+        return that;
+    };
+
+    /**
+     * Selects the specified element.
+     */
+    fluid.selectable.select = function(target, toSelect) {
+        $(toSelect).focus();
+    };
+
+    /**
+     * Selects the next matched element.
+     */
+    fluid.selectable.selectNext = function(target) {
+        target = $(target);
+        focusNextElement(fluid.getScopedData(target, CONTEXT_KEY));
+    };
+
+    /**
+     * Selects the previous matched element.
+     */
+    fluid.selectable.selectPrevious = function(target) {
+        target = $(target);
+        focusPreviousElement(fluid.getScopedData(target, CONTEXT_KEY));
+    };
+
+    /**
+     * Returns the currently selected item wrapped as a jQuery object.
+     */
+    fluid.selectable.currentSelection = function(target) {
+        target = $(target);
+        var that = fluid.getScopedData(target, CONTEXT_KEY);
+        return $(that.selectedElement());
+    };
+
+    fluid.selectable.defaults = {
+        direction: fluid.a11y.orientation.VERTICAL,
+        selectablesTabindex: -1,
+        autoSelectFirstItem: true,
+        rememberSelectionState: true,
+        selectableSelector: ".selectable",
+        selectableElements: null,
+        onSelect: null,
+        onUnselect: null,
+        onLeaveContainer: null
+    };
+
+    /********************************************************************
+     *  Activation functionality - declaratively associating actions with 
+     * a set of keyboard bindings.
+     */
+
+    var checkForModifier = function(binding, evt) {
+        // If no modifier was specified, just return true.
+        if (!binding.modifier) {
+            return true;
+        }
+
+        var modifierKey = binding.modifier;
+        var isCtrlKeyPresent = modifierKey && evt.ctrlKey;
+        var isAltKeyPresent = modifierKey && evt.altKey;
+        var isShiftKeyPresent = modifierKey && evt.shiftKey;
+
+        return isCtrlKeyPresent || isAltKeyPresent || isShiftKeyPresent;
+    };
+
+    /** Constructs a raw "keydown"-facing handler, given a binding entry. This
+     *  checks whether the key event genuinely triggers the event and forwards it
+     *  to any "activateHandler" registered in the binding. 
+     */
+    var makeActivationHandler = function(binding) {
+        return function(evt) {
+            var target = evt.target;
+            if (!fluid.enabled(evt.target)) {
+                return;
+            }
+// The following 'if' clause works in the real world, but there's a bug in the jQuery simulation
+// that causes keyboard simulation to fail in Safari, causing our tests to fail:
+//     http://ui.jquery.com/bugs/ticket/3229
+// The replacement 'if' clause works around this bug.
+// When this issue is resolved, we should revert to the original clause.
+//            if (evt.which === binding.key && binding.activateHandler && checkForModifier(binding, evt)) {
+            var code = evt.which? evt.which : evt.keyCode;
+            if (code === binding.key && binding.activateHandler && checkForModifier(binding, evt)) {
+                var event = $.Event("fluid-activate");
+                $(evt.target).trigger(event, [binding.activateHandler]);
+                if (event.isDefaultPrevented()) {
+                    evt.preventDefault();
+                }
+            }
+        };
+    };
+
+    var makeElementsActivatable = function(elements, onActivateHandler, defaultKeys, options) {
+        // Create bindings for each default key.
+        var bindings = [];
+        $(defaultKeys).each(function(index, key) {
+            bindings.push({
+                modifier: null,
+                key: key,
+                activateHandler: onActivateHandler
+            });
+        });
+
+        // Merge with any additional key bindings.
+        if (options && options.additionalBindings) {
+            bindings = bindings.concat(options.additionalBindings);
+        }
+
+        fluid.initEnablement(elements);
+
+        // Add listeners for each key binding.
+        for (var i = 0; i < bindings.length; ++ i) {
+            var binding = bindings[i];
+            elements.keydown(makeActivationHandler(binding));
+        }
+        elements.bind("fluid-activate", function(evt, handler) {
+            handler = handler || onActivateHandler;
+            return handler? handler(evt): null;
+        });
+    };
+
+    /**
+     * Makes all matched elements activatable with the Space and Enter keys.
+     * Provide your own handler function for custom behaviour.
+     * Options allow you to provide a list of additionalActivationKeys.
+     */
+    fluid.activatable = function(target, fn, options) {
+        target = $(target);
+        makeElementsActivatable(target, fn, fluid.activatable.defaults.keys, options);
+    };
+
+    /**
+     * Activates the specified element.
+     */
+    fluid.activate = function(target) {
+        $(target).trigger("fluid-activate");
+    };
+
+    // Public Defaults.
+    fluid.activatable.defaults = {
+        keys: [$.ui.keyCode.ENTER, $.ui.keyCode.SPACE]
+    };
+
+  
+  })(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/enhancement/js/ProgressiveEnhancement.js b/docs/jscripts/infusion/framework/enhancement/js/ProgressiveEnhancement.js
new file mode 100644 (file)
index 0000000..2a69013
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+Copyright 2008-2009 University of Toronto
+Copyright 2010 OCAD University
+
+Licensed under the Educational Community License (ECL), Version 2.0 or the New
+BSD license. You may not use this file except in compliance with one these
+Licenses.
+
+You may obtain a copy of the ECL 2.0 License and BSD License at
+https://source.fluidproject.org/svn/LICENSE.txt
+*/
+
+/*global window, swfobject, jQuery*/
+
+var fluid_1_3 = fluid_1_3 || {};
+
+(function ($, fluid) {
+    
+    fluid.browser = fluid.browser || {};
+    
+    fluid.browser.binaryXHR = function () {
+        var canSendBinary = window.FormData || XMLHttpRequest.prototype.sendAsBinary;
+        return canSendBinary ? fluid.typeTag("fluid.browser.supportsBinaryXHR") : undefined;
+    };
+    
+    fluid.browser.formData  = function () {
+        return window.FormData ? fluid.typeTag("fluid.browser.supportsFormData") : undefined;
+    };
+    
+    fluid.browser.flash = function () {
+        var hasModernFlash = (typeof(swfobject) !== "undefined") && (swfobject.getFlashPlayerVersion().major > 8);
+        return hasModernFlash ? fluid.typeTag("fluid.browser.supportsFlash") : undefined;
+    };
+    
+    fluid.progressiveChecker = function (options) {
+        // TODO: Replace with fluid.makeArray() when merged into trunk.
+        var checks = options.checks ? $.makeArray(options.checks) : [];
+        for (var x = 0; x < checks.length; x++) {
+            var check = checks[x];
+                            
+            if (check.feature) {
+                return fluid.typeTag(check.contextName);
+            }
+
+        }
+        return options.defaultTypeTag;
+    };
+    
+    fluid.defaults("fluid.progressiveChecker", {
+        checks: [], // [{"feature": "{IoC Expression}", "contextName": "context.name"}]
+        defaultTypeTag: undefined
+    });
+    
+    
+    /**********************************************************
+     * This code runs immediately upon inclusion of this file *
+     **********************************************************/
+    
+    // Use JavaScript to hide any markup that is specifically in place for cases when JavaScript is off.
+    // Note: the use of fl-ProgEnhance-basic is deprecated, and replaced by fl-progEnhance-basic.
+    // It is included here for backward compatibility only.
+    $("head").append("<style type='text/css'>.fl-progEnhance-basic, .fl-ProgEnhance-basic { display: none; }</style>");
+    
+    // Browser feature detection--adds corresponding type tags to the static environment,
+    // which can be used to define appropriate demands blocks for components using the IoC system.
+    var features = {
+        supportsBinaryXHR: fluid.browser.binaryXHR(),
+        supportsFormData: fluid.browser.formData(),
+        supportsFlash: fluid.browser.flash()
+    };
+    fluid.merge(null, fluid.staticEnvironment, features);
+    
+})(jQuery, fluid_1_3);
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-JSR168Bridge.css b/docs/jscripts/infusion/framework/fss/css/fss-JSR168Bridge.css
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-layout.css b/docs/jscripts/infusion/framework/fss/css/fss-layout.css
new file mode 100644 (file)
index 0000000..4b2ffb1
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+Generic containers for columns and layouts
+=============================================
+ */
+
+/* Container Utilities*/
+.fl-fix {overflow: auto; zoom:1;}
+.fl-push {clear:both;}
+
+/* Container alignment */
+ /* IE 6 needs inline display to prevent double margin bug (other browsers ignore it) */
+.fl-force-right {float:right; display:inline;}
+.fl-force-left {float:left; display:inline;}
+.fl-centered {margin-left:auto; margin-right:auto; display:block;}
+
+/* Generic container, proxy for other container effects */
+.fl-container {}
+
+/* Fixed width containers */
+.fl-container-50 {width: 50px;} 
+.fl-container-100 {width: 100px;} 
+.fl-container-150 {width: 150px;} 
+.fl-container-200 {width: 200px;} 
+.fl-container-250 {width: 250px;} 
+.fl-container-300 {width: 300px;} 
+.fl-container-350 {width: 350px;} 
+.fl-container-400 {width: 400px;} 
+.fl-container-450 {width: 450px;} 
+.fl-container-500 {width: 500px;} 
+.fl-container-550 {width: 550px;} 
+.fl-container-600 {width: 600px;} 
+.fl-container-650 {width: 650px;} 
+.fl-container-700 {width: 700px;} 
+.fl-container-750 {width: 750px;} 
+.fl-container-800 {width: 800px;} 
+.fl-container-850 {width: 850px;} 
+.fl-container-900 {width: 900px;} 
+.fl-container-950 {width: 950px;} 
+.fl-container-1000 {width: 1000px;} 
+
+/* Flex width containers */
+.fl-container-auto {width: auto;}
+.fl-container-flex {width: 100%; clear:both;}
+.fl-container-flex10 {width: 10%;}
+.fl-container-flex20 {width: 20%;}
+.fl-container-flex25 {width: 25%;}
+.fl-container-flex30 {width: 30%;}
+.fl-container-flex33 {width: 33%;}
+.fl-container-flex40 {width: 40%;}
+.fl-container-flex50 {width: 50%;}
+.fl-container-flex60 {width: 60%;}
+.fl-container-flex66 {width: 66%;}
+.fl-container-flex75 {width: 75%;}
+
+/* linearizable containers & columns */
+.fl-layout-linear *,
+.fl-layout-linear .fl-linearEnabled {
+    overflow:visible !important; 
+    clear: both !important; 
+    float:none !important;
+    margin-left:0 !important;  
+    margin-right:0 !important;
+}
+.fl-layout-linear .fl-container,
+.fl-layout-linear .fl-container-100,
+.fl-layout-linear .fl-container-150,
+.fl-layout-linear .fl-container-200,
+.fl-layout-linear .fl-container-250,
+.fl-layout-linear .fl-container-300,
+.fl-layout-linear .fl-container-400,
+.fl-layout-linear .fl-container-750,
+.fl-layout-linear .fl-container-950,
+.fl-layout-linear .fl-container-auto, 
+.fl-layout-linear .fl-container-flex25, 
+.fl-layout-linear .fl-container-flex30, 
+.fl-layout-linear .fl-container-flex33, 
+.fl-layout-linear .fl-container-flex50,
+.fl-layout-linear .fl-col,
+.fl-layout-linear .fl-col-side,
+.fl-layout-linear .fl-col-flex,
+.fl-layout-linear .fl-col-main,
+.fl-layout-linear .fl-col-fixed,
+.fl-layout-linear .fl-col-justified {width:100% !important; margin:auto; padding:0 !important;}
+
+.fl-layout-linear .fl-force-left, 
+.fl-layout-linear .fl-force-right,
+.fl-layout-linear li {display:block !important; float:none !important;}
+
+.fl-layout-linear .fl-linearEnabled {width:100% !important; /*position:relative;*/ display:block;} /* linearization opt in for special cases */
+
+.fl-layout-linear .fl-button-left,
+.fl-layout-linear .fl-button-right {padding:1em;}
+
+/*
+Layout Helpers
+=============================================
+*/
+/* Columns: A quick column grid system */
+/* Flex width columns (containers with margins and padding) */
+.fl-col-justified {float:left; display:inline; overflow:auto; text-align:justify;} /* redundant: text alignment now in fluid.text.css */
+
+.fl-col-flex2, .fl-col-flex3, .fl-col-flex4, .fl-col-flex5 {overflow:auto; zoom:1;}
+.fl-col {float:left; display:inline;}
+.fl-col-flex5 .fl-col {width:18.95%; margin-left:0.25%;margin-right:0.25%; padding-left:0.25%; padding-right:0.25%}
+.fl-col-flex4 .fl-col {width:24%; margin-left:0.25%;margin-right:0.25%; padding-left:0.25%; padding-right:0.25%}
+.fl-col-flex3 .fl-col {width:32.33%; margin-left:0.25%;margin-right:0.25%; padding-left:0.25%; padding-right:0.25%}
+.fl-col-flex2 .fl-col {width:48.85%; margin-left:0.25%;margin-right:0.25%; padding-left:0.25%; padding-right:0.25%}
+
+/* CHANGE TO LAYOUT not COL since this will become a layout helper */
+.fl-col-mixed,
+.fl-col-mixed2,
+.fl-col-mixed3 {overflow:auto; zoom:1;}
+
+/* Old System */
+.fl-col-mixed .fl-col-side {width:200px;}
+.fl-col-mixed .fl-col-side, 
+.fl-col-mixed .fl-col-main {padding:0 10px;}
+
+.fl-col-mixed2 .fl-col-side {width:200px; padding:0 10px; float:left;}
+.fl-col-mixed2 .fl-col-main {margin-left:220px; padding:0 10px;} /* margin goes on whichever side the fixed column goes */
+
+.fl-col-mixed3 .fl-col-main {margin:0 220px;} /* margin goes on whichever side the fixed column goes */
+
+/* New System, requires fl-force-XX on the fixed column, provides some basic numbers to start with */
+.fl-col-fixed, .fl-col-flex {padding:0 10px;}
+
+.fl-col-mixed .fl-col-fixed {width:200px; padding:0 10px;}
+.fl-col-mixed .fl-col-flex {margin-left:220px; padding:0 10px;}
+
+.fl-col-mixed-100 .fl-col-fixed {width:100px;}
+.fl-col-mixed-100 .fl-col-flex {margin-left:120px;}
+
+.fl-col-mixed-150 .fl-col-fixed {width:150px;}
+.fl-col-mixed-150 .fl-col-flex {margin-left:170px;}
+
+.fl-col-mixed-200 .fl-col-fixed {width:200px;}
+.fl-col-mixed-200 .fl-col-flex {margin-left:220px;}
+
+.fl-col-mixed-250 .fl-col-fixed {width:250px;}
+.fl-col-mixed-250 .fl-col-flex {margin-left:270px;}
+
+.fl-col-mixed-300 .fl-col-fixed {width:300px;}
+.fl-col-mixed-300 .fl-col-flex {margin-left:320px;}
+
+
+/*
+ * Tabs: a quick tab system
+ * Dependency: list-based markup ?
+ */
+.fl-tabs {margin:10px 0 0 0; border-bottom:1px solid #000; text-align:center; padding-bottom:2px;}
+.fl-tabs li {list-style-type:none; display:inline;}
+/* star hack to get IE 6+7 to behave perfectly */
+.fl-tabs li a {padding:3px 16px 2px; background-color:#fff; margin-left:-5px; *margin-bottom:-6px; zoom:1; border:1px solid #000; color:#999;}
+.fl-tabs li a:hover {}
+.fl-tabs-center {text-align:center;}
+.fl-tabs-left {text-align:left; padding-left:10px;}
+.fl-tabs-right {text-align:right; padding-right:15px;}
+.fl-tabs .fl-reorderer-dropMarker {padding:0 3px; background-color:#c00;margin:0 5px 0 -5px; zoom:1;}
+.fl-tabs .fl-tabs-active a {padding:3px 16px; border-bottom:none; color:#000;}
+.fl-tabs-content {padding:5px;}
+/* get webkit to behave perfectly - 
+ * unfortunately, Chrome gets caught in this too even though it applied the original padding values fine 
+ */
+@media screen and (-webkit-min-device-pixel-ratio:0){
+    .fl-tabs li a {padding:3px 16px 3px;} 
+    .fl-tabs .fl-tabs-active a {padding:3px 16px 4px;}
+}
+
+/*
+ * Menus: quick horizontal and vertical menu
+ * Requires list items with anchors
+ */ 
+.fl-listmenu, /* <=== fl-listmenu is DEPRECATED! */
+.fl-list-menu {padding:0px; margin:0; border-bottom-width:1px; border-bottom-style:solid;}
+
+.fl-listmenu li, /* <=== fl-listmenu is DEPRECATED! */
+.fl-list-menu li {margin:0; padding:0; list-style-type:none; border-width:1px; border-style:solid; border-bottom:none; overflow:auto;}
+
+.fl-listmenu a, /* <=== fl-listmenu is DEPRECATED! */
+.fl-list-menu a {padding:5px 5px; display:block; zoom:1; overflow:auto; outline:none;} /* list item needs layout (http://www.brunildo.org/test/IEWlispace.php) */
+
+/*
+ * Picture Grid: a quick picture grid layout
+ * Dependency: list-based markup
+ */
+ul.fl-grid, .fl-grid ul {padding:0; margin:0; overflow:auto;}
+.fl-grid li {list-style-type:none; display:inline;}
+.fl-grid li {float:left; width:19%; margin:0.5%; height:150px; overflow:hidden; position:relative; display:inline;}
+.fl-grid li img {display:block; margin:5px auto;}
+.fl-grid li .caption,  /*<=== DEPRECATED SYNTAX */
+.fl-grid li .fl-grid-caption {position:absolute; left:0px; bottom:0px; width:100%; text-align:center; height:1em; padding:3px 0;}
+
+/*
+ * Icons: quick accessible icon helper
+ */
+.fl-icon {
+    text-indent:-5000px; 
+    overflow:hidden; 
+    cursor:pointer; 
+    display:block; 
+    height:16px; 
+    width:16px; 
+    margin-left:5px; 
+    margin-right:5px;
+    background-position:center center; 
+    background-repeat:no-repeat;
+}
+input.fl-icon {padding-left:16px;}
+
+/*
+ * Buttons: quick sliding door buttons
+ * requires a container with a sub container
+ */
+.fl-button-left {float:left; margin-right:10px; padding:0 0 0 16px; background-position:left center; background-repeat:no-repeat;}
+.fl-button-right {float:right;  margin-left:10px; padding:0 0 0 16px; background-position:left center; background-repeat:no-repeat;}
+.fl-button-inner {float:left; padding:5px 16px 5px 0; cursor:pointer; background-position:right center; background-repeat:no-repeat;}
+
+/*
+ * Widgets: modelled after the mycamtools widget model
+ */
+.fl-widget {padding:5px; margin-bottom:10px;}
+.fl-widget .button {margin:0 5px;}
+.fl-grabbable .fl-widget-titlebar {background-position:center top; background-repeat:no-repeat; cursor:move;}
+.fl-widget .fl-widget-titlebar {}
+.fl-widget .fl-widget-titlebar h2 {padding:0; margin:0; font-size:105%;}
+.fl-widget .fl-widget-titlebar .fl-button-inner {
+    font-size:0.8em;
+    padding-bottom:0.2em;
+    padding-top:0.2em;
+}
+.fl-widget .fl-widget-controls {margin:-1.3em 0 1.5em 0;}
+.fl-widget .fl-widget-options {margin-top:5px; padding:0px 10px;}
+.fl-widget .fl-widget-options ul {margin:0; padding:0; overflow:hidden; zoom:1;} 
+.fl-widget .fl-widget-options li {list-style-type:none; float:left; display:inline; padding:0 5px 0 5px; margin-left:-5px;}
+.fl-widget .fl-widget-options a {margin-right:5px;}
+.fl-widget .fl-widget-content {zoom:1; margin:5px 0 0 0; overflow: auto;}
+.fl-widget .fl-widget-content ul {}
+.fl-widget .empty * {padding-top:10px; margin-left:auto; margin-right:auto; text-align:center;}
+
+
+/* Common widget sub-components */
+.fl-widget .menu       {margin:0;}
+.fl-widget .toggle  {width:32px;}
+.fl-widget .on                 {background-position:left top;}
+.fl-widget .off        {background-position:left bottom;}
+
+/*
+ * Forms and Form Controls
+ * Common arrangements for form inputs in a list
+ * Alignment requires list of fl-label elements placed before the control 
+ */
+.fl-controls-left li {list-style-type:none; text-align:left;}
+.fl-controls-left .fl-label {float:left; text-align:left; width:50%; margin-right:5px;}
+
+.fl-controls-right li {list-style-type:none; display:block; text-align:left;}
+.fl-controls-right .fl-label {float:left; text-align:right; width:50%; margin-right:5px;}
+
+.fl-controls-centered li {list-style-type:none; display:block; text-align:left;}
+.fl-controls-centered .fl-label {float:left; text-align:center; width:50%; margin-right:5px;}
+
+/**
+ * Knockout Background Images
+ */
+.fl-noBackgroundImages, .fl-noBackgroundImages * {
+    background-image:none !important;
+}
+.fl-noBackgroundImages .fl-icon {
+    text-indent:0 !important;
+    width:auto !important;
+    background-color:transparent !important;
+}
+
+/* Progressive Enhancement: JS will reverse the display setup if it is enabled */
+.fl-ProgEnhance-enhanced, /* << syntax breaks conventions and is Deprecated*/
+.fl-progEnhance-enhanced {display:none} 
+
+.fl-ProgEnhance-basic, /* << syntax breaks conventions and is Deprecated*/
+.fl-progEnhance-basic {}
+
+/* hide text for screen readers. */
+.fl-offScreen-hidden {position:absolute; left:-10000px; top:auto; width:1px; height:1px; overflow:hidden;}
+
+
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-mobile-layout.css b/docs/jscripts/infusion/framework/fss/css/fss-mobile-layout.css
new file mode 100644 (file)
index 0000000..d87ec46
--- /dev/null
@@ -0,0 +1,539 @@
+body {
+    margin:0;
+    padding:0;
+    font:normal 17px Helvetica, sans-serif; /* Base font rules */
+       -webkit-user-select: none; /* prevents child elements content from being selected - desired? */
+    -webkit-text-size-adjust:none; /* http://website-engineering.blogspot.com/2009/07/stop-adjusting-text-size-in-iphone-when.html */
+}
+h1, h2, h3, h4, h5, h6 {margin:0.25em;}
+h1 {font-size:1.5em;}
+h2 {font-size:1.1em;}
+h3 {font-size:1em;}
+h4 {}
+h5 {}
+h6 {}
+img {border:none;}
+
+
+/***************************************/
+/**
+ * Basic overrides for fss layout
+ */
+
+.fl-icon {
+    width:30px;
+    height:30px;
+    margin-top:-5px;
+    margin-right:5px;
+    -webkit-border-radius: 5px;
+}
+.fl-label {
+    width:25%;
+}
+
+/***************************************/
+/* iPhone general purpose gel buttons */
+/* effect is applied anywhere but the navbar, which has its own button look */
+
+.fl-button {
+    border-width: 10px;
+    text-align:center;
+    -webkit-border-radius:10px;
+    -webkit-background-origin: border;
+    -webkit-background-clip: border;
+}
+
+/*************************************************/
+/* Navigation Bar
+ */
+.fl-navbar {
+    width:100%;
+    text-align:center;
+       border-style:solid;
+       border-width:1px 0;
+}
+.fl-navbar .fl-table-row:first-child {
+       height:45px;
+}
+.fl-navbar .fl-table-cell:not(h1) {
+    width:1px;
+    white-space:nowrap;
+}
+
+.fl-navbar .fl-table-cell:first-child {
+    padding-left:5px;
+}
+.fl-navbar .fl-table-cell:last-child {
+    padding-right:5px;
+}
+.fl-navbar h1 {
+    padding:0;
+    text-align:center;
+    font-size:1.17em;
+    font-weight: bold;
+}
+.fl-navbar a {
+       text-decoration:none;
+       font-size: 0.7em;
+    font-weight:bold;
+}
+
+.fl-navbar [class*=fl-button] {
+    -webkit-border-radius:5px;
+    padding:0;
+    display:block;
+}
+
+.fl-navbar .fl-backButton {
+    /*
+     * To create the oddly shaped back button with fully customizable CSS colors, there are 2 main things:
+     * 1) The stencil for the shadow and hilights along the angled edge
+     * 2) The mask to crop the background along the angled edge
+     */
+    /* Angled effect is just a single border with a transaprent stencil image for the bevel */
+    border-width:0 0 0 15px;
+    -webkit-border-top-right-radius: 5px;
+    -webkit-border-bottom-right-radius: 5px;
+    -webkit-background-origin: border;
+    -webkit-background-clip: border;
+    -webkit-mask-repeat: no-repeat;
+    -webkit-mask-origin: border;
+    -webkit-mask-clip: border;
+}
+
+.fl-navbar .fl-backButton .fl-button-inner {
+    margin-left:-3px; /* close tiny gap between angled border + button-inner on Mobile Safari */
+    border-left-width:0;
+}
+.fl-navbar .fl-button-inner {
+    float:none;
+    padding:5px;
+    border-width:5px;
+    -webkit-background-origin: border;
+    -webkit-background-clip: border;
+}
+
+.fl-navbar img.fl-button-inner {
+    height:24px;
+    vertical-align:middle;
+    padding:0;    
+}
+
+/*************************************************/
+/* iPhone tabs: general purpose dividers, or fixed to the bottom of the screen
+ */
+/*************************************************/
+.fl-tabs {
+    margin:0;
+    padding:0;
+    border:none;
+    text-align:center;
+}
+.fl-tabs li {
+    display:inline-block;
+    margin-right:-5px;
+    border-width:5px 0;
+    text-align:center;
+}
+.fl-tabs li:first-child {
+    border-left-width:5px;
+    -webkit-border-top-left-radius:5px;
+    -webkit-border-bottom-left-radius:5px;
+}
+.fl-tabs li:last-child {
+    margin-right:0;
+    border-right-width:5px;
+    -webkit-border-top-right-radius:5px;
+    -webkit-border-bottom-right-radius:5px;
+}
+.fl-tabs li.fl-tabs-active a,
+.fl-tabs li a {
+    display:block;
+    margin:-4px 0;
+    text-decoration:none;
+    padding:0.35em 0.5em;
+    font-weight:bold;
+    background:none;
+    border-width:0 1px;
+    border-style:solid;
+}
+
+.fl-tabs li:last-child a {
+    border-right:none;
+}
+.fl-tabs li:first-child a {
+    border-left:none;
+}
+
+/*************************************************/
+/* iPhone list menu styles: Ordered lists, Unordererd lists, Thumbnail lists, Icon lists, Definition lists
+ * By default, all list formats fit to width
+ * When nested within a fl-container element, they are indented and therefore contain rounded corners
+ */
+/*************************************************/
+/* Default list system setup */
+[class*=fl-list] {
+    border: none;
+    list-style:none;
+    margin:0 0 10px;
+    padding:0;
+}
+[class*=fl-list] > li {
+       display:block;
+       padding: 12px 0px 12px 12px;
+       text-decoration: none;
+       font-weight: bold;
+       outline: none;
+       border-style:solid;
+       border-width:1px 0px 0 0px;
+    overflow:auto; /* to encapsulate floating elements within */
+}
+[class*=fl-list] > li:last-child {
+       border-bottom-width:1px;
+}
+[class*=fl-list] .fl-link-loading .fl-link-secondary {
+    display:none;
+}
+/* secondary link info behaviour */
+[class*=fl-list] > li .fl-link-secondary {
+    float:right;
+    margin-right:25px;
+    font-weight:normal;
+    font-size:.9em;
+}
+ /* summary link info ehaviour */
+[class*=fl-list] > li .fl-link-summary {
+    display:block;
+    clear:right;
+    margin:0 25px 0 0px;
+    font-weight:normal;
+    font-size:0.8em;
+}
+[class*=fl-list] li .fl-icon ~ .fl-link-summary {
+    margin-left:30px; /* default size of fl-icon */
+}
+/* icon behaviour */
+[class*=fl-list] li .fl-icon {
+    float:left;
+    margin-left:-6px;
+}
+
+/*************************************************/
+/**
+ * fl-listmenu is now the way to create a link list
+ * The links create a hotspot over the entire list item, just like how it was by default
+ */
+.fl-list-menu {
+}
+.fl-list-menu li {
+       padding:0;
+}
+.fl-list-menu li a {
+    display:block;
+    padding: 12px 0px 12px 12px;
+    text-decoration: none;
+    font-weight: bold;
+    outline: none;
+}
+
+
+/*************************************************/
+/* Thumbnail and Expanded Thumbnails list features */
+
+.fl-list-thumbnails > li {
+    margin-bottom:5px;
+    border-bottom-width:1px;
+}
+.fl-list-thumbnails > li a {
+    padding-top:6px;
+    overflow:auto;
+}
+/* summary behaviour */
+.fl-list-thumbnails > li a  .fl-icon ~ .fl-link-summary {
+       margin:0 25px -12px 42px;
+}
+.fl-list-thumbnails:not(.fl-thumbnails-expanded):not(.fl-list-brief) > li a > .fl-icon ~ .fl-link-summary {
+    padding-bottom:10px;
+}
+/* icon behaviour */
+.fl-list-thumbnails > li a  .fl-icon {
+    width:44px;
+    height:44px;
+    margin:-6px 10px -12px -12px;
+    -webkit-border-radius:0;
+}
+.fl-list-thumbnails.fl-thumbnails-expanded li {
+    margin:0;
+    border-bottom-width:0;
+}
+.fl-list-thumbnails.fl-thumbnails-expanded > li:last-child {
+    border-bottom-width:1px;
+}
+.fl-list-thumbnails.fl-thumbnails-expanded > li a {
+    -webkit-border-radius:0;
+    padding: 10px 0px 10px 10px;
+}
+.fl-list-thumbnails.fl-thumbnails-expanded > li a  .fl-icon {
+    width:60px;
+    height:60px;
+    margin:-5px 5px -5px -5px;
+    -webkit-border-radius:0;
+}
+.fl-list-thumbnails.fl-thumbnails-expanded > li a  .fl-icon ~ .fl-link-summary {
+    margin: 0px 0px -5px 60px;
+}
+.fl-list-thumbnails.fl-thumbnails-expanded > li a  .fl-link-secondary {
+    padding-top:0;
+}
+
+/*************************************************/
+/* Brief lists auto trim summary content + add ellipsis if necessary */
+
+.fl-list-brief > li a  {
+    padding-bottom:6px;
+}
+.fl-list-brief > li a > .fl-link-summary {
+    height:1.3em;
+    overflow:hidden;
+    text-overflow:ellipsis;
+    white-space:nowrap;
+}
+.fl-list-brief > li a > .fl-icon ~ .fl-link-summary {
+    margin-left:0;
+}
+.fl-list-brief.fl-list-thumbnails:not(.fl-thumbnails-expanded) > li a > .fl-icon {
+    margin:-6px 10px -6px -12px;
+}
+.fl-list-brief.fl-list-thumbnails:not(.fl-thumbnails-expanded) > li a > .fl-link-secondary {
+    padding-top:0px;
+}
+
+/*************************************************/
+/* Thumbnails in Grid layout, changes the entire layout of the list item and contents */
+.fl-list-thumbnails.fl-grid {
+       overflow:auto;
+}
+.fl-list-thumbnails.fl-grid li {
+       display:inline-block;
+    float:none;
+    vertical-align:top;
+       width:auto;
+       height:auto;
+       margin:4px;
+}
+.fl-list-thumbnails.fl-grid li a {
+       padding:0;
+       margin:0;
+       text-indent:-5000px;
+       overflow:hidden;
+       width:44px;
+       height:44px;
+}
+.fl-list-thumbnails.fl-grid li a .fl-caption {
+}
+.fl-list-thumbnails.fl-grid li a .fl-icon {
+       display:block;
+       float:none;
+       margin:0 auto;
+       padding:0;
+       width:auto;
+       height:auto;
+       max-height:44px;
+       max-width:44px;
+}
+.fl-list-thumbnails.fl-thumbnails-expanded.fl-grid li {
+       margin:0px;
+}
+.fl-list-thumbnails.fl-thumbnails-expanded.fl-grid li a {
+       height:64px;
+       width:64px;
+}
+.fl-list-thumbnails.fl-thumbnails-expanded.fl-grid > li a .fl-icon {
+       max-height:64px;
+       max-width:64px;
+}
+/* Reiterate the properties of fl-table-cell for specificities sake */
+.fl-list-thumbnails.fl-grid li .fl-table {
+}
+.fl-list-thumbnails.fl-grid li .fl-table .fl-table-cell {
+    display:table-cell;
+    vertical-align:middle;
+}
+.fl-list-thumbnails.fl-grid .fl-grid-caption {
+    position:relative;
+    height:auto;
+    text-align:center;
+    font-size:12px;
+}
+.fl-thumbnails-expanded.fl-grid .fl-grid-caption {
+    width:64px;
+}
+/*************************************************/
+/* Glossy icons (and thumbnails?) */
+
+/* Glossiness for 30x30 icons */
+.fl-list-glossy > li a::before {
+    position:absolute;
+    content:" ";
+    float:left;
+    width:28px;
+    height:28px;
+    margin:-4px 0 0 -5px;
+    -webkit-border-radius:3px;
+    -webkit-background-size: 30px 60px;
+    background-repeat:no-repeat;
+}
+/* Glossiness for 44x44 thumbnails */
+.fl-list-thumbnails.fl-list-glossy > li a::before {
+    width:42px;
+    height:42px;
+    margin:-11px;
+    -webkit-border-radius:0px;
+    -webkit-background-size: 44px 50px;
+}
+/* Glossiness for 60x60 thumbnails */
+.fl-list-thumbnails.fl-thumbnails-expanded.fl-list-glossy > li a::before {
+    width:58px;
+    height:58px;
+    margin:-4px;
+    -webkit-border-radius:0px;
+    -webkit-background-size: 60px 40px;
+}
+
+
+/*************************************************/
+/* Content Panels */
+/*************************************************/
+
+[class*=fl-container]:not(.fl-navbar) {
+       margin:10px;
+    width:auto;
+}
+
+/* Panel influence for list corners */
+[class*=fl-container] [class*=fl-list] > li {
+    border-right-width: 1px;
+    border-left-width: 1px;
+}
+[class*=fl-container] [class*=fl-list] > li:first-child,
+[class*=fl-container] [class*=fl-list] > li:first-child a {
+    -webkit-border-top-left-radius: 8px;
+    -webkit-border-top-right-radius: 8px;
+}
+
+[class*=fl-container] [class*=fl-list] > li:last-child,
+[class*=fl-container] [class*=fl-list] > li:last-child a {
+    -webkit-border-bottom-left-radius: 8px;
+    -webkit-border-bottom-right-radius: 8px;
+}
+
+[class*=fl-container] [class*=fl-list]:not(.fl-thumbnails-expanded) > li a .fl-icon {
+    -webkit-border-bottom-left-radius: 8px;
+    -webkit-border-top-left-radius: 8px;
+}
+
+[class*=fl-container] .fl-list-thumbnails:not(.fl-thumbnails-expanded) > li,
+[class*=fl-container] .fl-list-thumbnails:not(.fl-thumbnails-expanded) > li a {
+    -webkit-border-radius: 8px;
+}
+/* Panel Auto Headings */
+/* The first element found becomes the "heading" */
+.fl-container-autoHeading > *:first-child {
+       padding:10px;
+       margin:0;
+       -webkit-border-top-left-radius: 8px;
+       -webkit-border-top-right-radius: 8px;
+}
+/* The last element found becomes the "content" - list or otherwise */
+.fl-container-autoHeading > *:last-child {
+       margin:0;
+       -webkit-border-radius:0;
+       -webkit-border-bottom-left-radius:8px;
+       -webkit-border-bottom-right-radius:8px;
+}
+
+.fl-container-autoHeading [class*=fl-list] > li {
+       margin:0;
+}
+.fl-container-autoHeading [class*=fl-list] > li:first-child,
+.fl-container-autoHeading [class*=fl-list] > li:first-child a {
+       -webkit-border-radius:0;
+}
+.fl-container-autoHeading [class*=fl-list] > li:first-child:last-child,
+.fl-container-autoHeading [class*=fl-list] > li:first-child:last-child a {
+       -webkit-border-bottom-left-radius:8px;
+       -webkit-border-bottom-right-radius:8px;
+}
+
+/* Collapsing and expanding panels */
+.fl-container-collapsable {
+       max-height:900px;
+       overflow:hidden;
+       -webkit-border-radius:8px;
+}
+
+/***************************/
+.fl-table {
+    display:table;
+    border-collapse:collapse;
+}
+.fl-table-row {
+    display:table-row;
+}
+.fl-table-cell {
+    display:table-cell;
+    vertical-align:middle;
+}
+
+
+/************************************************************************************************/
+/*
+    Theme/Device specific layout adjustments
+    The base CSS is modelled off the iPhone WebKit environment, which is a good UI to emulate
+    The following CSS modifies the base slightly to be a little more device-friendly and native-ish for other Webkit environments
+*/
+
+.fl-theme-android {
+    font:normal 17px "Droid Sans", sans-serif;
+}
+.fl-theme-android [class*=fl-list] > li {
+    border-width:0px;
+}
+.fl-theme-android h1,
+.fl-theme-android h2,
+.fl-theme-android h3 {
+    padding:5px;
+    margin:0;
+}
+.fl-theme-android [class*=fl-container] h1,
+.fl-theme-android [class*=fl-container] h2,
+.fl-theme-android [class*=fl-container] h3 {
+    margin:0 -10px;
+}
+.fl-theme-android .fl-list-menu a {
+    font-weight:normal;
+}
+.fl-theme-android .fl-tabs li:not(:last-child) {
+    margin-right:-4px; /* for some reason, the use of Droid Sans font make the right border dissappear (on Chrome+XP). This 1px change fixes it */
+}
+/* Panel influence for list corners */
+.fl-theme-android [class*=fl-container] [class*=fl-list] > li:first-child,
+.fl-theme-android [class*=fl-container] [class*=fl-list] > li:first-child a {
+    -webkit-border-top-left-radius: 0px;
+    -webkit-border-top-right-radius: 0px;
+}
+.fl-theme-android [class*=fl-container]:not(.fl-container-autoHeading) [class*=fl-list] > li:last-child,
+.fl-theme-android [class*=fl-container]:not(.fl-container-autoHeading) [class*=fl-list] > li:last-child a {
+    -webkit-border-bottom-left-radius: 0px;
+    -webkit-border-bottom-right-radius: 0px;
+}
+.fl-theme-android .fl-list-thumbnails.fl-thumbnails-expanded > li:last-child {
+       border:0;       
+}
+.fl-theme-android .fl-container-autoHeading > :first-child {
+       margin-left:0;
+       margin-right:0;
+}
+.fl-theme-android .fl-container-autoHeading > :last-child {
+       border-width:0 1px 1px;
+}
\ No newline at end of file
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-mobile-theme-android.css b/docs/jscripts/infusion/framework/fss/css/fss-mobile-theme-android.css
new file mode 100644 (file)
index 0000000..315da6b
--- /dev/null
@@ -0,0 +1,279 @@
+/***************************************/
+/* general styles */
+.fl-theme-android {
+    background:#222;
+    color:#fff;
+}
+
+.fl-theme-android .fl-bevel-black {
+    text-shadow: rgba(0,0,0,0.35) 0px -1px 0px;
+}
+.fl-theme-android .fl-bevel-white {
+    text-shadow: rgba(200,200,200,0.85) 0px 1px 0px;
+}
+
+.fl-theme-android a {
+    -webkit-touch-callout:none; /* prevents iphone popup menu to copy / follow / bookmark a particular link */
+    -webkit-tap-highlight-color: rgba(0,0,0,0);
+    text-decoration:none;
+    color:#fff;
+}
+
+.fl-theme-android h1,
+.fl-theme-android h2,
+.fl-theme-android h3 {
+    text-shadow: rgba(255,255,255,1) 0px 1px 0px;
+    color:#000;
+    background: #999 -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#999)) no-repeat top left;
+}
+
+/***************************************/
+/* Navigation Bar */
+
+.fl-theme-android .fl-navbar {
+    border-top-color:#333;
+    border-bottom-color:#000;
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(#666),
+        color-stop(0.5, #333),
+        color-stop(0.50, #000),
+        to(#000)
+    );
+    -webkit-box-shadow: 0px 0px 5px rgba(0,0,0,0.5);
+}
+
+.fl-theme-android .fl-navbar {
+    color:#fff;    
+}
+
+.fl-theme-android .fl-navbar h1 {
+    color:#fff;
+    background:transparent;
+    text-shadow: rgba(0,0,0,0.5) 0px -1px 0px;
+}
+.fl-theme-android .fl-navbar a {
+       color: #fff;
+    text-shadow: rgba(0,0,0,0.5) 0px -1px 0px;
+}
+.fl-theme-android .fl-navbar .fl-button {
+    -webkit-border-image:none;
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(#444),
+        color-stop(0.5, #666),
+        color-stop(0.50, #333),
+        to(#000)
+    );
+}
+.fl-theme-android .fl-navbar [class*=fl-button]:active {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(#ffb347),
+        to(#ff702f)
+    );
+}
+
+.fl-theme-android .fl-navbar .fl-backButton {
+    /* See mobile layout for details */
+    -webkit-border-image: url(../images/themes/android/navbar_back_button_insetShadow.png) 0 15 stretch;
+    -webkit-mask-box-image: url(../images/themes/android/backbutton_mask.png) 0 15 stretch;
+}
+
+.fl-theme-android .fl-navbar .fl-button-inner {
+    -webkit-border-image: url(../images/themes/android/navbar_normal_button_insetShadow.png) 5 5 5 5 stretch;
+}
+
+/***************************************/
+/* general purpose gel buttons */
+/* effect is applied anywhere but the navbar, which has its own button look */
+.fl-theme-android .fl-button {
+    text-decoration:none;
+    font-weight:bold;
+    -webkit-border-image: url(../images/themes/android/button_bg_insetShadow.png) 10 stretch;
+}
+.fl-theme-android .fl-button.fl-bevel-white {
+    color:#333333;
+}
+.fl-theme-android .fl-button.fl-bevel-black {
+    color:#FFFFFF;
+}
+.fl-theme-android .fl-button-white {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(240,240,240,0.25)),
+        to(rgba(220,220,220,0.75)),
+        color-stop(0.5, rgba(240,240,240,1)),
+        color-stop(0.50, rgba(200,200,200,0.8))
+    );
+}
+.fl-theme-android .fl-button-black {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(106,106,106,0.25)),
+        to(rgba(00,00,00,0.75)),
+        color-stop(0.5, rgba(130,130,130,1)),
+        color-stop(0.50, rgba(75,75,75,0.8))
+    );
+}
+.fl-theme-android .fl-button-green {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(120,190,130,0.2)),
+        to(rgba(50,170,60,0.75)),
+        color-stop(0.5, rgba(120,190,130,1)),
+        color-stop(0.50, rgba(0,150,10,0.8))
+    );
+}
+.fl-theme-android .fl-button-blue {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(149, 184, 239,0.2)),
+        to(rgba(35,109,229,0.75)),
+        color-stop(0.5, rgba(149, 184, 239,1)),
+        color-stop(0.50, rgba(75,148,244,0.8))
+    );
+}
+/***************************************/
+/* tabs 1: small general purpose content dividers */
+.fl-theme-android .fl-tabs li {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(#666),
+        color-stop(0.5, #666),
+        color-stop(0.50, #000),
+        to(#000)
+    );
+    -webkit-border-image: url(../images/themes/android/navbar_normal_button_insetShadow.png) 5 5 5 5 stretch;
+    -webkit-border-left-image: none;
+    -webkit-background-origin: border;
+    -webkit-background-clip: border;
+}
+
+.fl-theme-android .fl-tabs li a {
+       color:#fff;
+}
+
+.fl-theme-android .fl-tabs li a,
+.fl-theme-android .fl-tabs .fl-tabs-active a {
+    border-right-color:rgba(255,255,255,0.35);
+    border-left-color:rgba(0,0,0,1);
+}
+
+.fl-theme-android .fl-tabs .fl-tabs-active {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(#ff702f),
+        to(#ffb347)
+    );
+}
+.fl-theme-android .fl-tabs .fl-tabs-active a {
+    color:#000;
+    text-shadow:none;
+}
+
+/*************************************************/
+/* Ordered lists, Unordererd lists, Thumbnail lists, Icon lists, Definition lists */
+/*************************************************/
+/* Default list system setup */
+
+.fl-theme-android .fl-list a {
+    color:#ffb347;
+}
+.fl-theme-android .fl-list-menu a {
+    color:#fff;
+}
+
+.fl-theme-android [class*=fl-list] > li {
+       color: #fff;
+    background:transparent -webkit-gradient(linear, left bottom, right bottom, 
+        from(rgba(0,0,0,0)), 
+        to(rgba(0,0,0,0)),
+        color-stop(0.35, rgba(255,255,255,1)),
+        color-stop(0.75, rgba(255,255,255,1))
+    ) no-repeat bottom center;
+    
+    -webkit-background-size: 100% 1px;
+}
+
+.fl-theme-android [class*=fl-list] > li:last-child {
+    background:transparent;
+}
+
+/* secondary link info behaviour */
+.fl-theme-android [class*=fl-list] > li .fl-link-secondary {
+    color: #fff;
+}
+
+ /* summary link info ehaviour */
+.fl-theme-android [class*=fl-list] > li .fl-link-summary {
+    color:#999;
+}
+
+.fl-theme-android [class*=fl-list]:not(.fl-list):not(.fl-grid) a {    
+}
+
+/* A simulation for a:active on the device, requires JS */
+.fl-theme-android [class*=fl-list]:not(.fl-list):not(.fl-grid) a:active,
+.fl-theme-android [class*=fl-list]:not(.fl-list):not(.fl-grid) a.fl-link-hilight {
+       color: #000;
+    background: -webkit-gradient(linear, left top, left bottom, from(#ffb347), to(#ff702f));
+}
+
+.fl-theme-android [class*=fl-list]:not(.fl-list) a:active,
+.fl-theme-android [class*=fl-list]:not(.fl-list) a:active .fl-link-secondary,
+.fl-theme-android [class*=fl-list]:not(.fl-list) a:active .fl-link-summary,
+.fl-theme-android [class*=fl-list]:not(.fl-list) a.fl-link-loading * {
+    color: #000;
+}
+
+.fl-theme-android [class*=fl-list]:not(.fl-list) a.fl-link-loading {
+    color: #fff;
+    background: url(../images/themes/android/listmenu_loader.gif) no-repeat 97% center,
+                       -webkit-gradient(linear, left top, left bottom, from(#ffb347), to(#ff702f));
+}
+
+/***************************************/
+/* Grid overrides, removes nav coloring */
+.fl-theme-android .fl-grid li {
+    background-color:transparent;
+}
+.fl-theme-android .fl-grid li,
+.fl-theme-android .fl-grid li a {
+    background-image:none;
+    border:none;
+}
+.fl-theme-android .fl-grid li a {
+    background-color:#333;
+}
+.fl-theme-android .fl-grid li a:active,
+.fl-theme-android .fl-grid .fl-link-hilight {
+       color: #fff;
+    background: -webkit-gradient(linear, left top, left bottom, from(#ffb347), to(#ff702f));
+}
+.fl-theme-android .fl-grid .fl-grid-caption {
+       font-weight:normal;
+}
+/***************************************/
+/* instructional text (usually embossed too)*/
+.fl-theme-android .fl-note {
+    color:#999;
+}
+
+/****************************/
+/* Collapsing and expanding panels */
+.fl-theme-android .fl-container-autoHeading > *:first-child {
+    color:#444;
+    background: -webkit-gradient(linear, left top, left bottom, from(#CCC), to(#999));
+       text-shadow:rgba(0, 0, 0, 0.292969) 0px 2px 2px;
+}
+.fl-theme-android .fl-container-autoHeading > *:first-child:focus {
+    color:#000;
+    background: -webkit-gradient(linear, left top, left bottom, from(#FFF), to(#CCC));
+}
+.fl-theme-android .fl-container-autoHeading > :last-child {
+       background: transparent -webkit-gradient(radial, 50% 0, 10, 50% 0%, 300,
+        from(rgba(100,100,100,1)), 
+        to(rgba(0,0,0,0))
+    );
+       border-color:#666;
+       border-style:solid;
+}
+
+/****************************/
+/* Gloss tint for glossy icons/thumbnails */
+
+.fl-theme-android .fl-list-glossy > li a::before {
+    background-image: -webkit-gradient(radial, 50% -15%, 10, 50% -50%, 45, from(rgba(255,255,255,1)), to(rgba(255,255,255,0)), color-stop(90%, rgba(255,255,255,.65)));
+}
\ No newline at end of file
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-mobile-theme-iphone.css b/docs/jscripts/infusion/framework/fss/css/fss-mobile-theme-iphone.css
new file mode 100644 (file)
index 0000000..ca3782c
--- /dev/null
@@ -0,0 +1,264 @@
+/***************************************/
+/* general styles */
+.fl-theme-iphone {
+    background: -webkit-gradient(linear, left top, right top,
+        from(#c5ccd3),
+        to(#cfd5dd),
+        color-stop(0.80, #c5ccd3),
+        color-stop(0.80, #cfd5dd));
+    -webkit-background-origin: padding-box;
+    -webkit-background-clip: content-box;
+    -webkit-background-size: 10px 1px;
+}
+
+.fl-theme-iphone .fl-bevel-black {
+    text-shadow: rgba(0,0,0,0.35) 0px -1px 0px;
+}
+.fl-theme-iphone .fl-bevel-white {
+    text-shadow: rgba(255,255,255,1) 0px 1px 0px;
+}
+
+.fl-theme-iphone a {
+    -webkit-touch-callout:none; /* prevents iphone popup menu to copy / follow / bookmark a particular link */
+    -webkit-tap-highlight-color: rgba(0,0,0,0);
+    text-decoration:none;
+    color:#000;
+}
+
+.fl-theme-iphone h1,
+.fl-theme-iphone h2,
+.fl-theme-iphone h3 {
+    text-shadow: rgba(255,255,255,1) 0px 1px 0px;
+    color:#4C566C;
+}
+
+/***************************************/
+/* Navigation Bar */
+
+/*body::before {*/
+.fl-theme-iphone .fl-navbar {
+    border-top-color:#ccd6e2;
+    border-bottom-color:#000;
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(#B0BCCD),
+        color-stop(0.5, #889BB3),
+        color-stop(0.50, #6D84A2),
+        to(#6D84A2)
+    );
+}
+.fl-theme-iphone .fl-navbar {
+    color:#fff;    
+}
+
+.fl-theme-iphone .fl-navbar h1 {
+    color:#fff;
+    text-shadow: rgba(0,0,0,0.5) 0px -1px 0px;
+}
+
+.fl-theme-iphone .fl-navbar a {
+       color: #fff;
+    text-shadow: rgba(0,0,0,0.5) 0px -1px 0px;
+}
+.fl-theme-iphone .fl-navbar .fl-button {
+    -webkit-border-image:none;
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(#9aafca),
+        color-stop(0.5, #6d8cb3),
+        color-stop(0.50, #4b6b90),
+        to(#4b6b90)
+    );
+}
+.fl-theme-iphone .fl-navbar [class*=fl-button]:active {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(149, 184, 239,1)),
+        to(rgba(35,109,229,1)),
+        color-stop(0.5, rgba(149, 184, 239,1)),
+        color-stop(0.50, rgba(75,148,244,1))
+    );
+}
+
+.fl-theme-iphone .fl-navbar .fl-backButton {
+    /* See mobile layout for details */
+    -webkit-border-image: url(../images/themes/iphone/navbar_back_button_insetShadow.png) 0 15 stretch;
+    -webkit-mask-box-image: url(../images/themes/iphone/backbutton_mask.png) 0 15 stretch;
+}
+
+.fl-theme-iphone .fl-navbar .fl-button-inner {
+    -webkit-border-image: url(../images/themes/iphone/navbar_normal_button_insetShadow.png) 5 5 5 5 stretch;
+}
+
+/***************************************/
+/* general purpose gel buttons */
+/* effect is applied anywhere but the navbar, which has its own button look */
+.fl-theme-iphone .fl-button {
+    text-decoration:none;
+    font-weight:bold;
+    -webkit-border-image: url(../images/themes/iphone/button_bg_insetShadow.png) 10 stretch;
+}
+.fl-theme-iphone .fl-button.fl-bevel-white {
+    color:#333333;
+}
+.fl-theme-iphone .fl-button.fl-bevel-black {
+    color:#FFFFFF;
+}
+.fl-theme-iphone .fl-button-white {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(240,240,240,0.25)),
+        to(rgba(220,220,220,0.75)),
+        color-stop(0.5, rgba(240,240,240,1)),
+        color-stop(0.50, rgba(200,200,200,0.8))
+    );
+}
+.fl-theme-iphone .fl-button-black {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(106,106,106,0.25)),
+        to(rgba(00,00,00,0.75)),
+        color-stop(0.5, rgba(130,130,130,1)),
+        color-stop(0.50, rgba(75,75,75,0.8))
+    );
+}
+.fl-theme-iphone .fl-button-green {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(120,190,130,0.2)),
+        to(rgba(50,170,60,0.75)),
+        color-stop(0.5, rgba(120,190,130,1)),
+        color-stop(0.50, rgba(0,150,10,0.8))
+    );
+}
+.fl-theme-iphone .fl-button-blue {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(149, 184, 239,0.2)),
+        to(rgba(35,109,229,0.75)),
+        color-stop(0.5, rgba(149, 184, 239,1)),
+        color-stop(0.50, rgba(75,148,244,0.8))
+    );
+}
+/***************************************/
+/* tabs 1: small general purpose content dividers */
+
+.fl-theme-iphone .fl-tabs li {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(#9aafca),
+        color-stop(0.5, #6d8cb3),
+        color-stop(0.50, #4b6b90),
+        to(#4b6b90)
+    );
+    -webkit-border-image: url(../images/themes/iphone/navbar_normal_button_insetShadow.png) 5 5 5 5 stretch;
+    -webkit-border-left-image: none;
+    -webkit-background-origin: border;
+    -webkit-background-clip: border;
+}
+
+.fl-theme-iphone .fl-tabs li a,
+.fl-theme-iphone .fl-tabs .fl-tabs-active a {
+       color:#fff;
+    border-right-color:rgba(255,255,255,0.35);
+    border-left-color:rgba(0,0,0,0.35);
+}
+
+.fl-theme-iphone .fl-tabs .fl-tabs-active {
+    background-image: -webkit-gradient(linear, left top, left bottom,
+        from(rgba(149, 184, 239,1)),
+        to(rgba(35,109,229,1)),
+        color-stop(0.5, rgba(149, 184, 239,1)),
+        color-stop(0.50, rgba(75,148,244,1))
+    );
+}
+
+/*************************************************/
+/* Ordered lists, Unordererd lists, Thumbnail lists, Icon lists, Definition lists */
+/*************************************************/
+/* Default list system setup */
+
+.fl-theme-iphone .fl-list a {
+    color:#4a94f4;
+}
+
+.fl-theme-iphone [class*=fl-list] > li {
+       color: #000;
+    border-color: rgb(169,173,176);
+       background-color:#fff;
+}
+
+/* secondary link info behaviour */
+.fl-theme-iphone [class*=fl-list] > li .fl-link-secondary {
+    color: rgb(50, 79, 133);
+}
+
+ /* summary link info ehaviour */
+.fl-theme-iphone [class*=fl-list] > li .fl-link-summary {
+    color:#999;
+}
+
+.fl-theme-iphone [class*=fl-list]:not(.fl-list):not(.fl-grid) a {
+    background: url(../images/themes/iphone/listmenu_arrow.png) no-repeat right 5px;
+}
+
+/* A simulation for a:active on the device, requires JS */
+/* since .fl-list is for mixed material lists, dont include them in these effects */
+.fl-theme-iphone [class*=fl-list]:not(.fl-list):not(.fl-grid) a:active,
+.fl-theme-iphone [class*=fl-list]:not(.fl-list):not(.fl-grid) a.fl-link-hilight {
+       color: #fff;
+    background: url(../images/themes/iphone/listmenu_arrow.png) no-repeat right -25px,
+                -webkit-gradient(linear, left top, left bottom, from(#4a94f4), to(#236de5));
+}
+
+.fl-theme-iphone [class*=fl-list]:not(.fl-list) a:active,
+.fl-theme-iphone [class*=fl-list]:not(.fl-list) a:active .fl-link-secondary,
+.fl-theme-iphone [class*=fl-list]:not(.fl-list) a:active .fl-link-summary,
+.fl-theme-iphone [class*=fl-list]:not(.fl-list) a.fl-link-loading * {
+    color: #fff;
+}
+
+.fl-theme-iphone [class*=fl-list]:not(.fl-list) a.fl-link-loading {
+    color: #fff;
+    background: url(../images/themes/iphone/listmenu_loader.gif) no-repeat 97% center,
+                       -webkit-gradient(linear, left top, left bottom, from(#4a94f4), to(#236de5));
+}
+
+/***************************************/
+/* Grid overrides, removes nav coloring */
+.fl-theme-iphone .fl-grid li {
+    background-color:transparent;
+}
+.fl-theme-iphone .fl-grid li,
+.fl-theme-iphone .fl-grid li a {
+    background-image:none;
+    border:none;
+}
+.fl-theme-iphone .fl-grid li a {
+    background-color:#333;
+}
+.fl-theme-iphone .fl-grid li a:active,
+.fl-theme-iphone .fl-grid .fl-link-hilight {
+       color: #fff;
+    background: -webkit-gradient(linear, left top, left bottom, from(#4a94f4), to(#236de5));
+}
+
+/***************************************/
+/* instructional text (usually embossed too)*/
+.fl-theme-iphone .fl-note {
+    color:#4C566C;
+}
+
+/****************************/
+/* Collapsing and expanding panels */
+.fl-theme-iphone .fl-container-autoHeading > *:first-child {
+       font-weight:normal;
+    color:rgba(255,255,255,1);
+       text-shadow: rgba(0,0,0, 1) 0px -1px 1px;
+    background: -webkit-gradient(linear, left top, left bottom, from(#999), to(#000));
+}
+.fl-theme-iphone .fl-container-autoHeading > *:first-child:focus {
+       font-weight:normal;
+    color:rgba(0,0,0,1);
+       text-shadow: rgba(0,0,0, .5) 0px 1px 2px;
+    background: -webkit-gradient(linear, left top, left bottom, from(#FFF), to(#CCC));
+}
+
+/****************************/
+/* Gloss tint for glossy icons/thumbnails */
+
+.fl-theme-iphone .fl-list-glossy > li a::before {
+    background-image: -webkit-gradient(radial, 50% -15%, 10, 50% -50%, 45, from(rgba(255,255,255,1)), to(rgba(255,255,255,0)), color-stop(90%, rgba(255,255,255,.65)));
+}
\ No newline at end of file
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-reset.css b/docs/jscripts/infusion/framework/fss/css/fss-reset.css
new file mode 100644 (file)
index 0000000..91092c4
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+YUI fonts, reset and base
+
+Copyright (c) 2008, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.5.2
+*/
+/**
+ * Percents could work for IE, but for backCompat purposes, we are using keywords.
+ * x-small is for IE6/7 quirks mode.
+ */
+body {}
+table {font-size:inherit;font:100%;}
+/**
+ * Bump up IE to get to 13px equivalent
+ */
+pre,code,kbd,samp,tt {font-family:monospace;*font-size:108%;line-height:100%;}
+
+html{color:#000;}
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,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;}
+li{list-style:none;}
+caption,th{text-align:left;}
+h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}
+q:before,q:after{content:'';}
+abbr,acronym {border:0;font-variant:normal;}
+/* to preserve line-height and selector appearance */
+sup {vertical-align:text-top;}
+sub {vertical-align:text-bottom;}
+
+/*because legend doesn't inherit in IE */
+legend{color:#000;}
+
+/* base.css, part of YUI's CSS Foundation */
+h1 {
+       /*18px via YUI Fonts CSS foundation*/
+       font-size:138.5%;  
+}
+h2 {
+       /*16px via YUI Fonts CSS foundation*/
+       font-size:123.1%; 
+}
+h3 {
+       /*14px via YUI Fonts CSS foundation*/
+       font-size:108%;  
+}
+h1,h2,h3 {
+       /* top & bottom margin based on font size */
+       margin:1em 0;
+}
+h1,h2,h3,h4,h5,h6,strong {
+       /*bringing boldness back to headers and the strong element*/
+       font-weight:bold; 
+}
+abbr,acronym {
+       /*indicating to users that more info is available */
+       border-bottom:1px dotted #000;
+       cursor:help;
+} 
+em {
+       /*bringing italics back to the em element*/
+       font-style:italic;
+}
+blockquote,ul,ol,dl {
+       /*giving blockquotes and lists room to breath*/
+       margin:1em;
+}
+ol,ul,dl {
+       /*bringing lists on to the page with breathing room */
+       margin-left:2em;
+}
+ol li {
+       /*giving OL's LIs generated numbers*/
+       list-style: decimal outside;    
+}
+ul li {
+       /*giving UL's LIs generated disc markers*/
+       list-style: disc outside;
+}
+dl dd {
+       /*giving UL's LIs generated numbers*/
+       margin-left:1em;
+}
+th,td {
+       /*borders and padding to make the table readable*/
+       border:1px solid #000;
+       padding:.5em;
+}
+th {
+       /*distinguishing table headers from data cells*/
+       font-weight:bold;
+       text-align:center;
+}
+caption {
+       /*coordinated margin to match cell's padding*/
+       margin-bottom:.5em;
+       /*centered so it doesn't blend in to other content*/
+       text-align:center;
+}
+p,fieldset,table,pre {
+       /*so things don't run into each other*/
+       margin-bottom:1em;
+}
+/* setting a consistent width, 160px; 
+   control of type=file still not possible */
+input[type=text],input[type=password],textarea{width:12.25em;*width:11.9em;}
+input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}
+
+/*************************************************************************************/
+/* Prevent unecessary scrollbars in IE */
+/* Put font data on the HTML element to allow overrides on the body */
+/* IE6 cant zoom text properly, so text size reset is a rough approximation */
+html {overflow:auto; font:13px/1.231 arial,helvetica,clean,sans-serif; *font-size:small;}
+
+/*to enable resizing for IE*/
+input,textarea,select{
+    *font-size:100%;
+    *font-family:sans-serif;
+}
+
+/* IE rendering fix for extra padding on buttons. Padding can now be set safely */
+input {
+    *overflow:visible; *padding:0 1em;
+}
+
+/* custom default focus indicator - theme overwrites this */
+:focus {    
+   outline: 2px solid black;
+}
+
+
+
+
+
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-text.css b/docs/jscripts/infusion/framework/fss/css/fss-text.css
new file mode 100644 (file)
index 0000000..e42f720
--- /dev/null
@@ -0,0 +1,192 @@
+.fl-font-size-70,
+.fl-font-size-70 body,
+.fl-font-size-70 input, 
+.fl-font-size-70 select, 
+.fl-font-size-70 textarea {font-size:0.7em !important; line-height:1em !important;}
+
+.fl-font-size-80,
+.fl-font-size-80 body, 
+.fl-font-size-80 input, 
+.fl-font-size-80 select, 
+.fl-font-size-80 textarea {font-size:0.8em !important; line-height:1.1em !important;}
+
+.fl-font-size-90,
+.fl-font-size-90 body,
+.fl-font-size-90 input,
+.fl-font-size-90 select,
+.fl-font-size-90 textarea {font-size:0.9em !important; line-height:1.2em !important;}
+
+.fl-font-size-100, 
+.fl-font-size-100 body,
+.fl-font-size-100 input, 
+.fl-font-size-100 select, 
+.fl-font-size-100 textarea {font-size:1em !important; line-height:1.3em !important;}
+
+.fl-font-size-110, 
+.fl-font-size-110 body, 
+.fl-font-size-110 input, 
+.fl-font-size-110 select, 
+.fl-font-size-110 textarea {font-size:1.1em !important; line-height:1.4em !important;}
+
+.fl-font-size-120, 
+.fl-font-size-120 body, 
+.fl-font-size-120 input, 
+.fl-font-size-120 select, 
+.fl-font-size-120 textarea {font-size:1.2em !important; line-height:1.5em !important;}
+
+.fl-font-size-130, 
+.fl-font-size-130 body, 
+.fl-font-size-130 input, 
+.fl-font-size-130 select, 
+.fl-font-size-130 textarea {font-size:1.3em !important; line-height:1.6em !important;}
+
+.fl-font-size-140, 
+.fl-font-size-140 body, 
+.fl-font-size-140 input, 
+.fl-font-size-140 select, 
+.fl-font-size-140 textarea {font-size:1.4em !important; line-height:1.7em !important;}
+
+.fl-font-size-150, 
+.fl-font-size-150 body,
+.fl-font-size-150 input, 
+.fl-font-size-150 select, 
+.fl-font-size-150 textarea {font-size:1.5em !important; line-height:1.8em !important;}
+
+/* fix for Safari 3 ignoring input font size */
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+    
+    [class~='fl-font-size-70'] input[type=submit],
+    [class~='fl-font-size-70'] input[type=button] {padding:0 1em}
+
+    [class~='fl-font-size-80'] input[type=submit],
+    [class~='fl-font-size-80'] input[type=button] {font-size:0.8em !important; padding:0 1em}
+
+    [class~='fl-font-size-90'] input[type=submit],
+    [class~='fl-font-size-90'] input[type=button] {font-size:0.9em !important; padding:0 1em}
+
+    [class~='fl-font-size-100'] input[type=submit],
+    [class~='fl-font-size-100'] input[type=button] {font-size:1em !important; padding:0 1em}
+
+    [class~='fl-font-size-110'] input[type=submit],
+    input[type=submit][class~='fl-font-size-110'],
+    [class~='fl-font-size-110'] input[type=button] {background-color:#fff; font-size:1.1em !important; padding:0 1em}
+
+    [class~='fl-font-size-120'] input[type=submit],
+    input[type=submit][class~='fl-font-size-120'],
+    [class~='fl-font-size-120'] input[type=button] {background-color:#fff; font-size:1.2em !important; padding:0 1em}
+
+    [class~='fl-font-size-130'] input[type=submit],
+    input[type=submit][class~='fl-font-size-130'],
+    [class~='fl-font-size-130'] input[type=button] {background-color:#fff; font-size:1.3em !important; padding:0 1em}
+
+    [class~='fl-font-size-140'] input[type=submit],
+    input[type=submit][class~='fl-font-size-140'],
+    [class~='fl-font-size-140'] input[type=button] {background-color:#fff; font-size:1.4em !important; padding:0 1em}
+
+    [class~='fl-font-size-150'] input[type=submit],
+    input[type=submit][class~='fl-font-size-150'],
+    [class~='fl-font-size-150'] input[type=button] {background-color:#fff; font-size:1.5em !important; padding:0 1em}
+    
+    [class~='fl-font-serif'] input[type=submit],
+    [class~='fl-font-sans'] input[type=submit],
+    [class~='fl-font-monospace'] input[type=submit],
+    [class~='fl-font-arial'] input[type=submit],
+    [class~='fl-font-verdana'] input[type=submit],
+    [class~='fl-font-times'] input[type=submit],
+    [class~='fl-font-courier'] input[type=submit] {background-color:#fff; padding:0 1em}
+}
+
+
+.fl-font-serif, .fl-font-serif * {font-family: Georgia, Times, "Times New Roman", "Book Antiqua", serif !important;}
+.fl-font-sans, .fl-font-sans * {font-family: Tahoma, Verdana, Helvetica, sans-serif !important;}
+.fl-font-monospace, .fl-font-monospace * {font-family: "Courier New, Courier", monospace !important;}
+
+.fl-font-arial, .fl-font-arial * {font-family: "Arial" !important;}
+.fl-font-verdana, .fl-font-verdana * {font-family: "Verdana" !important;}
+.fl-font-times, .fl-font-times *   {font-family: Georgia, Times, "Times New Roman", serif !important;}
+.fl-font-courier, .fl-font-courier * {font-family: "Courier New", Courier, monospace !important;}
+
+.fl-text-align-left {
+    text-align:left;
+}
+.fl-text-align-right {
+    text-align:right;
+}
+.fl-text-align-center {
+    text-align:center;
+}
+.fl-text-align-justify {
+    text-align:justify;
+}
+
+.fl-font-spacing-0, 
+.fl-font-spacing-0 body, 
+.fl-font-spacing-0 input,
+.fl-font-spacing-0 select,
+.fl-font-spacing-0 textarea {letter-spacing:0em}
+
+.fl-font-spacing-1, 
+.fl-font-spacing-1 body, 
+.fl-font-spacing-1 input,
+.fl-font-spacing-1 select,
+.fl-font-spacing-1 textarea {letter-spacing:0.1em}
+
+.fl-font-spacing-2, 
+.fl-font-spacing-2 body, 
+.fl-font-spacing-2 input,
+.fl-font-spacing-2 select,
+.fl-font-spacing-2 textarea {letter-spacing:0.2em}
+
+.fl-font-spacing-3, 
+.fl-font-spacing-3 body, 
+.fl-font-spacing-3 input,
+.fl-font-spacing-3 select,
+.fl-font-spacing-3 textarea {letter-spacing:0.3em}
+
+.fl-font-spacing-4, 
+.fl-font-spacing-4 body, 
+.fl-font-spacing-4 input,
+.fl-font-spacing-4 select,
+.fl-font-spacing-4 textarea {letter-spacing:0.4em}
+
+.fl-font-spacing-5, 
+.fl-font-spacing-5 body, 
+.fl-font-spacing-5 input,
+.fl-font-spacing-5 select,
+.fl-font-spacing-5 textarea {letter-spacing:0.5em}
+
+.fl-font-spacing-6, 
+.fl-font-spacing-6 body, 
+.fl-font-spacing-6 input,
+.fl-font-spacing-6 select,
+.fl-font-spacing-6 textarea {letter-spacing:0.6em}
+
+/* UI Enhancer "Esier to Find" link options */
+/* First pass strategy: apply classnames directky on the elements of interest, usually via JS */
+.fl-text-aqua {color: aqua !important;}
+.fl-text-black {color: black !important;}
+.fl-text-blue {color: blue !important;}
+.fl-text-fuchsia {color: fuchsia !important;}
+.fl-text-gray {color: gray !important;}
+.fl-text-green {color: green !important;}
+.fl-text-lime {color: lime !important;}
+.fl-text-maroon {color: maroon !important;}
+.fl-text-navy {color: navy !important;}
+.fl-text-olive {color: olive !important;}
+.fl-text-purple {color: purple !important;}
+.fl-text-red {color: red !important;}
+.fl-text-silver {color: silver !important;}
+.fl-text-teal {color: teal !important;}
+.fl-text-white {color:white !important;}
+.fl-text-yellow {color: yellow !important;}
+
+.fl-text-underline {text-decoration:underline !important;}
+.fl-text-bold {font-weight:bold !important;}
+.fl-text-larger {font-size:125% !important;}
+.fl-input-outline {border:2px solid;} /* leave out color? */
+
+.fl-highlight-yellow, .fl-highlight-hover-yellow:hover, .fl-highlight-focus-yellow:focus {background-color:#FF0 !important; background-image:none !important;}
+.fl-highlight-green, .fl-highlight-hover-green:hover, .fl-highlight-focus-green:focus {background-color:#0F0 !important; background-image:none !important;}
+.fl-highlight-blue, .fl-highlight-hover-blue:hover, .fl-highlight-focus-blue:focus {background-color:#00F !important; background-image:none !important;}
+
+
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-theme-coal.css b/docs/jscripts/infusion/framework/fss/css/fss-theme-coal.css
new file mode 100644 (file)
index 0000000..d72ca27
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+         Medium Contrast Greyscale
+=============================================
+ * Color Schema:
+ *     DEFINITIONS:
+ *         white     : #ffffff
+ *         grey      : #999999
+ *         charcoal  : #333333
+ *         black     : #000000
+ */
+
+
+
+
+.fl-theme-coal :focus,
+.fl-theme-coal .selectable  {
+   /* themed focus indicator */
+   outline: 0.1em solid #000;
+}
+
+.fl-theme-coal {color:#000000; background-color:#ebebeb; border-color:#000000;}
+.fl-theme-coal a {color: #333333; font-weight:bold;}
+.fl-theme-coal a:hover {color: #999999;}
+.fl-theme-coal h1 {color:#000000; border-bottom-width:0.2em; border-bottom-style:solid;} 
+.fl-theme-coal h2 {color: #666666;}
+.fl-theme-coal th {border:0.1em solid #ffffff; background-color:#dfefff !important;}
+.fl-theme-coal td {border:0.1em solid #999999 !important;}
+.fl-theme-coal .fl-textfield,
+.fl-theme-coal .fl-textarea {background-color:#ffffff; border:1px solid #000000;}
+.fl-theme-coal .fl-icon {background-color:#666666}
+
+/* temp selector to access inline edit input fields (should use a classname but now there is none applied) */
+.fl-theme-coal .fl-inlineEdit-edit {background-color:#ebebeb !important; border:0.1em solid #ffffff; margin:-0.1em;}
+
+/* Helper: buttons */
+.fl-theme-coal .fl-button-left, 
+.fl-theme-coal .fl-button-right {color:#FFFFFF !important; background-color:#333 !important;}
+.fl-theme-coal .fl-button-left {background-image:url(../images/themes/coal/buttons-med-cap.png);}
+.fl-theme-coal .fl-button-right {background-image:url(../images/themes/coal/buttons-med-cap.png);}
+.fl-theme-coal .fl-button-inner {background-image:url(../images/themes/coal/buttons-med-bg.png);}
+
+.fl-theme-coal a.fl-button-left:hover,
+.fl-theme-coal a.fl-button-right:hover {color:#ffffff !important}
+
+
+/* Helper: Tabs */
+.fl-theme-coal .fl-tabs {border-bottom-color:#333333;}
+.fl-theme-coal .fl-tabs li,
+.fl-theme-coal .fl-tabs li a  {font-weight:bold; color:#ffffff !important; border-color:#333333; border-bottom-color:#333333; background-color:#666666; text-decoration:none;}
+.fl-theme-coal .fl-tabs li a:hover {background-color:#333333; color:#fff !important;}
+/* Deprecated */
+.fl-theme-coal .fl-tabs li.fl-activeTab,
+.fl-theme-coal .fl-tabs li.fl-activeTab:hover,
+.fl-theme-coal .fl-tabs li.fl-activeTab a,
+.fl-theme-coal .fl-tabs li.fl-activeTab a:hover,
+/* New syntax */
+.fl-theme-coal .fl-tabs li.fl-tabs-active,
+.fl-theme-coal .fl-tabs li.fl-tabs-active a,
+.fl-theme-coal .fl-tabs li.fl-tabs-active a:hover {background-color: #ebebeb; border-bottom-color:#ebebeb; color:#000 !important;}
+.fl-theme-coal .fl-tabs-content {background-color:#ebebeb; color:#000; border:1px solid #999999; border-top:none;}
+
+/* Helper: Menu */
+.fl-theme-coal .fl-listmenu, 
+.fl-theme-coal .fl-list-menu {border:1px solid #333; border-bottom-width:2px; background-color:#ebebeb;}
+.fl-theme-coal .fl-listmenu li, 
+.fl-theme-coal .fl-list-menu li,
+.fl-theme-coal .fl-listmenu li a, 
+.fl-theme-coal .fl-list-menu li a  {font-weight:bold; background-color:#ebebeb; border-color:#333; text-decoration:none;}
+.fl-theme-coal .fl-listmenu a:hover, 
+.fl-theme-coal .fl-list-menu a:hover {background-color:#ffffff; color:#333333 !important;}
+.fl-theme-coal .fl-listmenu .fl-activemenu, 
+.fl-theme-coal .fl-list-menu .fl-activemenu,
+.fl-theme-coal .fl-listmenu .fl-activemenu:hover, 
+.fl-theme-coal .fl-list-menu .fl-activemenu:hover { background-color: #ffffff; border-bottom-color:#999999; color:#d9d9d9;}
+
+/* Helper: Thumbnail Grid */
+.fl-theme-coal .fl-grid {border:2px solid #000000; background-color:#ccc;}
+.fl-theme-coal .fl-grid li {background-color:#EDEDED; border:1px solid #000000;}
+.fl-theme-coal .fl-grid .fl-grid-caption {background-color:#666; color:#ffffff; }
+
+/* Helper: Widgets */
+/* Widget core */
+.fl-theme-coal .fl-widget {background:#333333 url(../images/themes/coal/widget-bg.png) repeat-x top left; border:1px solid #000000;}                        
+.fl-theme-coal .fl-widget ul {}
+.fl-theme-coal .fl-widget h2 {color:#ffffff;}
+.fl-theme-coal .fl-widget .fl-icon-more  {background-image:url('../images/themes/coal/icon-widget-More.png'); margin-left:0px;}
+.fl-theme-coal .fl-widget .fl-icon-close {background-image:url('../images/themes/coal/icon-widget-Close.png'); margin-right:0px;}
+
+/* Widget titlebar */
+.fl-theme-coal .fl-grabbable .fl-widget-titlebar {background-image:url('../images/themes/coal/icon-widget-gripper.png');}
+.fl-theme-coal .fl-widget-titlebar {background-position:center top; background-repeat:no-repeat;}
+.fl-theme-coal .fl-widget-titlebar .icon {background-position:center center;}
+.fl-theme-coal .fl-widget-titlebar .fl-button-right,
+.fl-theme-coal .fl-widget-titlebar .fl-button-left {
+    background-image:url(../images/themes/coal/buttons-titlebar-cap.png);
+    text-decoration:none;
+}
+.fl-theme-coal .fl-widget-titlebar .fl-button-inner {
+    background-image:url(../images/themes/coal/buttons-titlebar-bg.png);
+    padding-bottom:0.3em;
+    padding-top:0;
+}
+/* Widget options */
+.fl-theme-coal .fl-widget-options {}
+.fl-theme-coal .fl-widget-options ul {} 
+.fl-theme-coal .fl-widget-options li {border-left:1px solid #ccc;}
+.fl-theme-coal .fl-widget-options li a {color: #ebebeb !important;}
+.fl-theme-coal .fl-widget-options li a:hover {color: #ffffff !important;}
+.fl-theme-coal .fl-widget-options a.icon:hover {background-color:#ffffff; border-color:#000;}
+
+/* Widget content */
+.fl-theme-coal .fl-widget-content {background-color:#ffffff;}
+
+/* Component: Progressor */
+.fl-theme-coal .fl-progress-bounds {border-color:#333333; background-color:#ebebeb;}
+.fl-theme-coal .fl-progress-fill {color:#ffffff; background-color:#999999;}
+
+/* States */
+.fl-theme-coal .fl-reorderer-dropMarker {background-color:#f00 !important;}
+
+/* Enhaced Graphics Toggles */
+
+/* Regular Background */
+.fl-theme-coal .fl-tabs-content-enhanced {
+    background:#ebebeb url(../images/themes/coal/tabs-med-content-bg.png) repeat-x left top;
+    border:none;
+}
+.fl-theme-coal .fl-tabs-enhanced {
+    background:url(../images/themes/coal/tabs-med-container-bg.png) repeat-x left bottom;
+    border-bottom:none;
+    margin:10px 0 0;
+    padding:5px 0 6px;
+    *padding:0;
+}
+
+.fl-theme-coal .fl-tabs-enhanced li {
+    background: transparent url(../images/themes/coal/tabs-med-cap.png) no-repeat left top; 
+    padding:4px 0 6px 16px;
+    *padding-top:0;
+}
+.fl-theme-coal .fl-tabs-enhanced li a {
+    background: transparent url(../images/themes/coal/tabs-med-bg.png) no-repeat right top; 
+    border:none;
+    margin:0;
+    padding:4px 16px 6px 0;
+    color:#ebebeb !important;
+    *padding-bottom:4px;
+}
+.fl-theme-coal .fl-tabs-enhanced li.fl-tabs-active {
+    background:transparent url(../images/themes/coal/tabs-med-active-cap.png) no-repeat scroll left top;
+}
+.fl-theme-coal .fl-tabs-enhanced li.fl-tabs-active a {
+    background:transparent url(../images/themes/coal/tabs-med-active-bg.png) no-repeat scroll right top;
+    color:#ffffff !important;
+}
+
+/* Widget Content Background */
+.fl-theme-coal .fl-widget-content .fl-tabs-content-enhanced {
+    background:#ebebeb url(../images/themes/coal/tabs-light-content-bg.png) repeat-x left top;
+    border:none;
+}
+.fl-theme-coal .fl-widget-content .fl-tabs-enhanced {
+    background:url(../images/themes/coal/tabs-light-container-bg.png) repeat-x left bottom;
+    border-bottom:none;
+    margin:10px 0 0;
+    padding:5px 0 6px;
+    *padding:0;
+}
+
+.fl-theme-coal .fl-widget-content .fl-tabs-enhanced li {
+    background: transparent url(../images/themes/coal/tabs-light-cap.png) no-repeat left top; 
+    padding:4px 0 6px 16px;
+    *padding-top:0;
+}
+.fl-theme-coal .fl-widget-content .fl-tabs-enhanced li a {
+    background: transparent url(../images/themes/coal/tabs-light-bg.png) no-repeat right top; 
+    border:none;
+    margin:0;
+    padding:4px 16px 6px 0;
+    color:#ebebeb !important;
+    *padding-bottom:4px;
+}
+.fl-theme-coal .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active {
+    background:transparent url(../images/themes/coal/tabs-light-active-cap.png) no-repeat scroll left top;
+}
+.fl-theme-coal .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active a {
+    background:transparent url(../images/themes/coal/tabs-light-active-bg.png) no-repeat scroll right top;
+    color:#ffffff !important;
+}
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-theme-debug.css b/docs/jscripts/infusion/framework/fss/css/fss-theme-debug.css
new file mode 100644 (file)
index 0000000..b407c27
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+                Debug
+=============================================
+ */
+
+/* topographical debugging */
+.fl-theme-debug {color:#fff; background-color:rgb(0,0,0)}
+.fl-theme-debug a {color:#F00;}
+.outline * {outline:1px solid rgb(255,0,0)}
+
+.fl-theme-debug div {background-color:rgb(41,41,41);}
+.fl-theme-debug div div {background-color:rgb(82,82,82);}
+.fl-theme-debug div div div {background-color:rgb(123,123,123)}
+.fl-theme-debug div div div div {background-color:rgb(164,164,164)}
+.fl-theme-debug div div div div div {background-color:rgb(205,205,205)}
+.fl-theme-debug div div div div div div {background-color:rgb(246,246,246)}
+.fl-theme-debug div div div div div div div {background-color:rgb(250,250,250)}
+.fl-theme-debug div div div div div div div div {background-color:rgb(245,245,245)}
+.fl-theme-debug div div div div div div div div div {background-color:rgb(240,240,240)}
+.fl-theme-debug div div div div div div div div div div {background-color:rgb(235,235,235)}
+.fl-theme-debug div div div div div div div div div div div {background-color:rgb(230,230,230)}
+.fl-theme-debug div div div div div div div div div div div div {background-color:rgb(225,225,225)}
+.fl-theme-debug div div div div div div div div div div div div div {background-color:rgb(220,220,220)}
+.fl-theme-debug div div div div div div div div div div div div div div {background-color:rgb(215,215,215)}
+.fl-theme-debug div div div div div div div div div div div div div div div {background-color:rgb(210,210,210)}
\ No newline at end of file
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-theme-hc.css b/docs/jscripts/infusion/framework/fss/css/fss-theme-hc.css
new file mode 100644 (file)
index 0000000..e057a0a
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+              High Contrast
+=============================================
+ * Color Schema:
+ *     DEFINITIONS:
+ *         white     : #ffffff
+ *         black     : #000000
+
+ */
+.fl-theme-hc :focus,
+.fl-theme-hc .selectable  {
+   /* themed focus indicator */
+   outline: 0.2em solid #F00;
+}
+
+.fl-theme-hc {color:#000000 !important; background-color:#ffffff !important;}
+.fl-theme-hc div, .fl-theme-hc input {color:#000000; background-color:#ffffff; border-color:#000;}
+.fl-theme-hc .fl-knockout {background:transparent !important; color:#000;} /* a granular method for basic reset of appearance */
+
+/* Links */
+.fl-theme-hc a  {color: #000 !important; font-weight:bold; background-color:#fff !important;}
+.fl-theme-hc a:hover {color: #fff !important; background-color:#000 !important;}
+.fl-theme-hc a:hover * {color: #fff !important; background-color:#000 !important;}
+
+/* Headers */
+.fl-theme-hc h1,
+.fl-theme-hc h2,
+.fl-theme-hc h3,
+.fl-theme-hc h4,
+.fl-theme-hc h5,
+.fl-theme-hc h6 {color:#000 !important; background-color:#fff !important; border-color:#000 !important; border-bottom:0.1em dashed #000000 !important;}
+
+
+/* Tables */
+.fl-theme-hc th {border:0.1em solid #000; background-color:#000 !important; color:#fff !important;}
+.fl-theme-hc td {border:0.1em solid #000;}
+.fl-theme-hc .fl-textfield {}
+.fl-theme-hc .fl-textarea {}
+
+/* temp selector to access inline edit input fields (should use a classname but now there is none applied) */
+.fl-theme-hc .fl-inlineEdit-edit {background-color:#000 !important; color:#fff !important; border: 0.1em solid #fff; padding:0.1em; margin:-0.1em;}
+
+/* Helper: Tabs */
+.fl-theme-hc .fl-tabs {border-bottom-color:#000;}
+.fl-theme-hc .fl-tabs li,
+.fl-theme-hc .fl-tabs li *  {border-color:#000; border-bottom-color:#000; }
+.fl-theme-hc .fl-tabs li    {background-color:#000;}
+.fl-theme-hc .fl-tabs li *  {color:#fff !important; font-weight:bold; background-color:#000 !important;text-decoration:none;}
+
+.fl-theme-hc .fl-tabs li:hover,
+.fl-theme-hc .fl-tabs li:hover *,
+.fl-theme-hc .fl-tabs li a:hover {color:#000 !important; background-color:#fff !important;}
+
+.fl-theme-hc .fl-tabs li.fl-tabs-active,
+.fl-theme-hc .fl-tabs li.fl-tabs-active a,
+.fl-theme-hc .fl-tabs li.fl-tabs-active a:hover, 
+
+.fl-theme-hc .fl-tabs li.fl-activeTab,
+.fl-theme-hc .fl-tabs li.fl-activeTab:hover,
+.fl-theme-hc .fl-tabs li.fl-activeTab a,
+.fl-theme-hc .fl-tabs li.fl-activeTab a:hover {background-color: #fff !important; border-bottom-color:#fff; color:#000 !important;}
+.fl-theme-hc .fl-tab-content {background-color:#ffffff; color:#000;}
+
+/* Helper: Menu */
+.fl-theme-hc .fl-listmenu, 
+.fl-theme-hc .fl-list-menu {border:1px solid #000; border-bottom-width:2px; background-color:#fff;}
+.fl-theme-hc .fl-listmenu li, 
+.fl-theme-hc .fl-list-menu li,
+.fl-theme-hc .fl-listmenu li a, 
+.fl-theme-hc .fl-list-menu li a {font-weight:bold; color:#000000; background-color:#ffffff; border-color:#000000; text-decoration:none;}
+.fl-theme-hc .fl-listmenu a:hover, 
+.fl-theme-hc .fl-list-menu a:hover {background-color:#ffffff; color:#fff;}
+.fl-theme-hc .fl-listmenu .fl-activemenu, 
+.fl-theme-hc .fl-list-menu .fl-activemenu,
+.fl-theme-hc .fl-listmenu .fl-activemenu:hover, 
+.fl-theme-hc .fl-list-menu .fl-activemenu:hover { background-color: #fff; border-bottom-color:#fff; color:#508cc9;}
+
+
+/* Helper: Thumbnail Grid */
+.fl-theme-hc .fl-grid {}
+.fl-theme-hc .fl-grid li {}
+.fl-theme-hc .fl-grid .fl-grid-caption {}
+
+/* Helper: buttons */
+.fl-theme-hc .fl-button-right, 
+.fl-theme-hc .fl-button-left, 
+.fl-theme-hc .fl-button-inner {padding:0;}
+
+
+/* Widget core */
+.fl-theme-hc .fl-widget {background-color:#000; border:1px solid #fff;}
+.fl-theme-hc .fl-widget ul {}
+.fl-theme-hc .fl-widget h2 {color:#fff;}
+.fl-theme-hc .fl-widget a {color:#000;}
+.fl-theme-hc .fl-widget .fl-icon {color:#000; background-image:none; background-color:#fff; text-indent:0; width:auto; height:auto; margin-left:0px; padding:0 2px; display:inline; font-weight:bold;}
+        
+/* Widget titlebar */
+.fl-theme-hc .fl-widget .fl-widget-titlebar {margin-bottom:3px;}
+
+/* Widget options */
+.fl-theme-hc .fl-widget .fl-widget-options {padding:0px;}
+.fl-theme-hc .fl-widget .fl-widget-options ul {} 
+.fl-theme-hc .fl-widget .fl-widget-options li {border-left:1px solid #fff;}
+.fl-theme-hc .fl-widget .fl-widget-options a {color:#fff;}
+.fl-theme-hc .fl-widget .fl-widget-options .icon {}
+.fl-theme-hc .fl-widget .fl-widget-options .viewOptions {}
+.fl-theme-hc .fl-widget .fl-widget-options .toggle {}
+    
+/* Widget content */
+.fl-theme-hc .fl-widget .fl-widget-content {background-color:#fff;}
+
+/* Component: Progressor */
+.fl-theme-hc .fl-progress-bounds {border-color:#000; background-color:#fff;}
+.fl-theme-hc .fl-progress-fill {color:#fff; background-color:#000;}
+
+/* Component: Uploader */
+
+/* States */
+.fl-theme-hc .fl-reorderer-dropMarker {background-color:#f00 !important;}
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-theme-hci.css b/docs/jscripts/infusion/framework/fss/css/fss-theme-hci.css
new file mode 100644 (file)
index 0000000..4564540
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+              High Contrast
+=============================================
+ * Color Schema:
+ *     DEFINITIONS:
+ *         white     : #ffffff
+ *         black     : #000000
+
+ */
+.fl-theme-hci :focus,
+.fl-theme-hci .selectable  {
+   /* themed focus indicator */
+   outline: 0.2em solid #F00;
+}
+
+.fl-theme-hci {color:#fff !important; background-color:#000 !important;}
+.fl-theme-hci div, .fl-theme-hci input {color:#fff !important; background-color:#000 !important;}
+.fl-theme-hci .fl-knockout {background:transparent !important; color:#ffffff;} /* a granular method for basic reset of appearance */
+
+/* Links */
+.fl-theme-hci a  {color: #ffffff !important; font-weight:bold; background-color:#000000 !important;}
+.fl-theme-hci a:hover {color: #000000 !important; background-color:#ffffff !important;}
+.fl-theme-hci a:hover * {color: #000000 !important; background-color:#ffffff !important;}
+
+/* Headers */
+.fl-theme-hci h1,
+.fl-theme-hci h2,
+.fl-theme-hci h3,
+.fl-theme-hci h4,
+.fl-theme-hci h5,
+.fl-theme-hci h6 {color:#ffffff; background-color:#000000; border-color:#ffffff; border-bottom:0.1em dashed #ffffff;}
+
+/* Tables */
+.fl-theme-hci th {border:0.1em solid #ffffff; background-color:#ffffff !important; color:#000000 !important;}
+.fl-theme-hci td {border:0.1em solid #ffffff;}
+.fl-theme-hci .fl-textfield {}
+.fl-theme-hci .fl-textarea {}
+
+/* temp selector to access inline edit input fields (should use a classname but now there is none applied) */
+.fl-theme-hci .fl-inlineEdit-edit {background-color:#ffffff !important; color:#000000 !important; border: 0.1em solid #000000; padding:0.1em; margin:-0.1em;}
+
+/* Helper: Tabs */
+.fl-theme-hci .fl-tabs {border-bottom-color:#ffffff;}
+.fl-theme-hci .fl-tabs li,
+.fl-theme-hci .fl-tabs li *  {border-color:#ffffff; border-bottom-color:#ffffff; }
+.fl-theme-hci .fl-tabs li    {background-color:#ffffff;}
+.fl-theme-hci .fl-tabs li *  {color:#000000 !important; font-weight:bold; background-color:#ffffff !important;text-decoration:none;}
+
+.fl-theme-hci .fl-tabs li:hover,
+.fl-theme-hci .fl-tabs li:hover *,
+.fl-theme-hci .fl-tabs li a:hover {color:#ffffff !important; background-color:#000000 !important;}
+
+.fl-theme-hci .fl-tabs li.fl-tabs-active,
+.fl-theme-hci .fl-tabs li.fl-tabs-active a,
+.fl-theme-hci .fl-tabs li.fl-tabs-active a:hover, 
+
+.fl-theme-hci .fl-tabs li.fl-activeTab,
+.fl-theme-hci .fl-tabs li.fl-activeTab:hover,
+.fl-theme-hci .fl-tabs li.fl-activeTab a,
+.fl-theme-hci .fl-tabs li.fl-activeTab a:hover {background-color: #000000 !important; border-bottom-color:#000000; color:#ffffff !important;}
+.fl-theme-hci .fl-tab-content {background-color:#000000; border:1px solid #fff; border-top:none; color:#ffffff;}
+
+/* Helper: Menu */
+.fl-theme-hci .fl-listmenu, 
+.fl-theme-hci .fl-list-menu {border:1px solid #ffffff; border-bottom-width:2px; background-color:#000000;}
+.fl-theme-hci .fl-listmenu li, 
+.fl-theme-hci .fl-list-menu li,
+.fl-theme-hci .fl-listmenu li a, 
+.fl-theme-hci .fl-list-menu li a  {font-weight:bold; color:#ffffff; background-color:#dfefff; border-color:#ffffff; text-decoration:none;}
+.fl-theme-hci .fl-listmenu a:hover, 
+.fl-theme-hci .fl-list-menu a:hover {background-color:#5a95cf; color:#000000;}
+.fl-theme-hci .fl-listmenu .fl-activemenu, 
+.fl-theme-hci .fl-list-menu .fl-activemenu,
+.fl-theme-hci .fl-listmenu .fl-activemenu:hover, 
+.fl-theme-hci .fl-list-menu .fl-activemenu:hover { background-color: #000000; border-bottom-color:#000000; color:#508cc9;}
+
+/* Helper: Thumbnail Grid */
+.fl-theme-hci .fl-grid {}
+.fl-theme-hci .fl-grid li {}
+.fl-theme-hci .fl-grid .fl-grid-caption {}
+
+/* Helper: buttons */
+.fl-theme-hci .fl-button-right, 
+.fl-theme-hci .fl-button-left, 
+.fl-theme-hci .fl-button-inner {padding:0;}
+
+/* Widget core */
+.fl-theme-hci .fl-widget {background-color:#ffffff !important; border:1px solid #000000;}
+.fl-theme-hci .fl-widget ul {}
+.fl-theme-hci .fl-widget h2 {color:#000000; background-color:#ffffff;}
+.fl-theme-hci .fl-widget a {color:#ffffff;}
+.fl-theme-hci .fl-widget .fl-icon {color:#ffffff; background-image:none; background-color:#000000; text-indent:0; width:auto; height:auto; margin-left:0px; padding:0 2px; display:inline; font-weight:bold;}
+        
+/* Widget titlebar */
+.fl-theme-hci .fl-widget .fl-widget-titlebar {margin-bottom:3px;}
+
+/* Widget options */
+.fl-theme-hci .fl-widget .fl-widget-options {padding:0px;}
+.fl-theme-hci .fl-widget .fl-widget-options ul {} 
+.fl-theme-hci .fl-widget .fl-widget-options li {border-left:1px solid #000000;}
+.fl-theme-hci .fl-widget .fl-widget-options a {color:#000000;}
+.fl-theme-hci .fl-widget .fl-widget-options .icon {}
+.fl-theme-hci .fl-widget .fl-widget-options .viewOptions {}
+.fl-theme-hci .fl-widget .fl-widget-options .toggle {}
+    
+/* Widget content */
+.fl-theme-hci .fl-widget .fl-widget-content {background-color:#000000;}
+
+
+
+/* Component: Progressor */
+.fl-theme-hci .fl-progress-bounds {border-color:#ffffff; background-color:#000000;}
+.fl-theme-hci .fl-progress-fill {color:#000000; background-color:#ffffff;}
+
+/* Component: Uploader */
+
+/* States */
+.fl-theme-hci .fl-reorderer-dropMarker {background-color:#f00 !important;}
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-theme-mist.css b/docs/jscripts/infusion/framework/fss/css/fss-theme-mist.css
new file mode 100644 (file)
index 0000000..fd22e4d
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+                Mist
+=============================================
+ * Color Schema:
+ *     DEFINITIONS:
+ *         white     : #ffffff
+ *         light blue: #dfefff
+ *         med blue  : #5a95cf
+ *         dark blue : #4070a1
+ *         grey      : #999999
+ *         charcoal  : #333333
+ *         black     : #000000
+ */
+
+.fl-theme-mist :focus,
+.fl-theme-mist .selectable  {
+   /* themed focus indicator */
+   outline: 0.1em solid #F00;
+}
+
+.fl-theme-mist {color:#000000; background-color:#f4f4f4; border-color:#4070a1;}
+.fl-theme-mist a {color: #5a95cf; font-weight:bold;}
+.fl-theme-mist a:hover {color: #6DB5FB;}
+.fl-theme-mist h1 {color:#4070a1; border-bottom-width:0.2em; border-bottom-style:solid;} 
+.fl-theme-mist h2 {color: #5a95cf;}
+.fl-theme-mist th {border:0.1em solid #5a95cf; background-color:#dfefff !important;}
+.fl-theme-mist td {border:0.1em solid #999999 !important;}
+.fl-theme-mist .fl-textfield,
+.fl-theme-mist .fl-textarea {border:1px solid #5a95cf; border-bottom-color:#5a95cf; border-right-color:#5a95cf; background-color:#dfefff;}
+.fl-theme-mist .fl-icon {background-color:#5a95cf}
+
+/* temp selector to access inline edit input fields (should use a classname but now there is none applied) */
+.fl-theme-mist .fl-inlineEdit-edit {background-color:#dfefff !important; border:0.1em solid #5a95cf; margin:-0.1em;}
+
+/* Helper: Buttons */
+.fl-theme-mist .fl-button-left {background-image:url(../images/themes/mist/buttons-med-cap.png);}
+.fl-theme-mist .fl-button-right {background-image:url(../images/themes/mist/buttons-med-cap.png);}
+.fl-theme-mist .fl-button-inner {background-image:url(../images/themes/mist/buttons-med-bg.png);}
+
+/* Helper: Tabs */
+.fl-theme-mist .fl-tabs {border-bottom-color:#4070a1;}
+.fl-theme-mist .fl-tabs li,
+.fl-theme-mist .fl-tabs li a  {font-weight:bold; color:#4070a1; border-color:#4070a1; border-bottom-color:#4070a1; text-decoration:none; background-color:#dfefff;}
+.fl-theme-mist .fl-tabs li a:hover {background-color:#5a95cf; color:#fff !important;}
+/* Deprecated */
+.fl-theme-mist .fl-tabs li.fl-activeTab,
+.fl-theme-mist .fl-tabs li.fl-activeTab:hover,
+.fl-theme-mist .fl-tabs li.fl-activeTab a,
+.fl-theme-mist .fl-tabs li.fl-activeTab a:hover
+/* New syntax */
+.fl-theme-mist .fl-tabs li.fl-tabs-active,
+.fl-theme-mist .fl-tabs li.fl-tabs-active a,
+.fl-theme-mist .fl-tabs li.fl-tabs-active a:hover {background-color: #f4f4f4; border-bottom-color:#f4f4f4; color:#508cc9 !important;}
+.fl-theme-mist .fl-tabs-content {background-color:#f4f4f4; color:#000; border:1px solid #61c5ff; border-top:none; margin-top:-4px;}
+
+/* Helper: Menu */
+.fl-theme-mist .fl-listmenu, 
+.fl-theme-mist .fl-list-menu {border:1px solid #4070a1; border-bottom-width:2px; background-color:#fff;}
+.fl-theme-mist .fl-listmenu li, 
+.fl-theme-mist .fl-list-menu li,
+.fl-theme-mist .fl-listmenu li a, 
+.fl-theme-mist .fl-list-menu li a  {font-weight:bold; color:#4070a1; background-color:#dfefff; border-color:#4070a1; text-decoration:none;}
+.fl-theme-mist .fl-listmenu a:hover, 
+.fl-theme-mist .fl-list-menu a:hover {background-color:#5a95cf; color:#fff !important;}
+.fl-theme-mist .fl-listmenu .fl-activemenu, 
+.fl-theme-mist .fl-list-menu .fl-activemenu,
+.fl-theme-mist .fl-listmenu .fl-activemenu:hover, 
+.fl-theme-mist .fl-list-menu .fl-activemenu:hover { background-color: #fff; border-bottom-color:#fff; color:#508cc9;}
+
+/* Helper: Thumbnail Grid */
+.fl-theme-mist .fl-grid {border:2px solid #4070a1; background-color:#fff;}
+.fl-theme-mist .fl-grid li {background-color:#dfefff; border:1px solid #4070a1}
+.fl-theme-mist .fl-grid .fl-grid-caption {background-color:#dfefff; color:#5a95cf; }
+
+/* Helper: Widgets */
+/* Widget core */
+.fl-theme-mist .fl-widget {background:#efefef url(../images/themes/mist/widget-bg.png) repeat-x top left; border:1px solid #CCCCCC;}
+.fl-theme-mist .fl-widget ul {}
+.fl-theme-mist .fl-widget h2 {color:#000;}
+.fl-theme-mist .fl-widget .fl-icon-more  {background-image:url('../images/themes/mist/icon-widget-More.png'); margin-left:0px;}
+.fl-theme-mist .fl-widget .fl-icon-close {background-image:url('../images/themes/mist/icon-widget-Close.png'); margin-right:0px;}
+
+/* Widget titlebar */
+.fl-theme-mist .fl-grabbable .fl-widget-titlebar {background-image:url('../images/themes/mist/icon-widget-gripper.png');}
+.fl-theme-mist .fl-widget-titlebar .icon {background-position:center center;}
+.fl-theme-mist .fl-widget-titlebar .fl-button-right,
+.fl-theme-mist .fl-widget-titlebar .fl-button-left {
+    color:#4070a1 !important;
+    background-image:url(../images/themes/mist/buttons-titlebar-cap.png);
+    text-decoration:none;
+}
+.fl-theme-mist .fl-widget-titlebar .fl-button-inner {
+    background-image:url(../images/themes/mist/buttons-titlebar-bg.png);
+}
+
+/* Widget options */
+.fl-theme-mist .fl-widget-options {}
+.fl-theme-mist .fl-widget-options ul {} 
+.fl-theme-mist .fl-widget-options li {border-left:1px solid #ccc;}
+.fl-theme-mist .fl-widget-options a {}
+.fl-theme-mist .fl-widget-options a.icon:hover {background-color:#5a95cf; border-color:#000;}
+
+/* Widget content */
+.fl-theme-mist .fl-widget-content {background-color:#fff;}
+
+/* Component: Progressor */
+.fl-theme-mist .fl-progress-bounds {border-color:#999999; background-color:#fff;}
+.fl-theme-mist .fl-progress-fill {color:#4070a1; background-color:#000;}
+
+/* States */
+.fl-theme-mist .fl-reorderer-dropMarker {background-color:#f00 !important;}
+
+
+/* Regular Background */
+.fl-theme-mist .fl-tabs-enhanced {
+    background:url(../images/themes/mist/tabs-med-container-bg.png) repeat-x left bottom;
+    border-bottom:none;
+    margin:10px 1px 0;
+    padding:5px 0 6px;
+    *padding:2px 0;
+}
+
+.fl-theme-mist .fl-tabs-enhanced li {
+    background: transparent url(../images/themes/mist/tabs-med-cap.png) no-repeat left top; 
+    padding:4px 0 6px 16px;
+    *padding-top:0;
+}
+.fl-theme-mist .fl-tabs-enhanced li a {
+    background: transparent url(../images/themes/mist/tabs-med-bg.png) no-repeat right top; 
+    border:none;
+    margin:0;
+    padding:4px 16px 6px 0;
+    color:#666666 !important;
+    *padding-bottom:4px;
+}
+.fl-theme-mist .fl-tabs-enhanced li.fl-tabs-active {
+    background:transparent url(../images/themes/mist/tabs-med-active-cap.png) no-repeat scroll left top;
+}
+.fl-theme-mist .fl-tabs-enhanced li.fl-tabs-active a {
+    background:transparent url(../images/themes/mist/tabs-med-active-bg.png) no-repeat scroll right top;
+    color:#000000 !important;
+}
+
+/* Widget Content Background */
+.fl-theme-mist .fl-widget-content .fl-tabs-enhanced {
+    background:url(../images/themes/mist/tabs-light-container-bg.png) repeat-x left bottom;
+    border-bottom:none;
+    margin:10px 1px 0;
+    padding:5px 0 6px;
+    *padding:2px 0;
+}
+
+.fl-theme-mist .fl-widget-content .fl-tabs-enhanced li {
+    background: transparent url(../images/themes/mist/tabs-light-cap.png) no-repeat left top; 
+    padding:4px 0 6px 16px;
+    *padding-top:0;
+}
+.fl-theme-mist .fl-widget-content .fl-tabs-enhanced li a {
+    background: transparent url(../images/themes/mist/tabs-light-bg.png) no-repeat right top; 
+    border:none;
+    margin:0;
+    padding:4px 16px 6px 0;
+    color:#666666 !important;
+    *padding-bottom:4px;
+}
+.fl-theme-mist .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active {
+    background:transparent url(../images/themes/mist/tabs-light-active-cap.png) no-repeat scroll left top;
+}
+.fl-theme-mist .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active a {
+    background:transparent url(../images/themes/mist/tabs-light-active-bg.png) no-repeat scroll right top;
+    color:#000000 !important;
+}
+
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-theme-rust.css b/docs/jscripts/infusion/framework/fss/css/fss-theme-rust.css
new file mode 100644 (file)
index 0000000..6f41799
--- /dev/null
@@ -0,0 +1,139 @@
+/*                Rust
+============================================= 
+
+ * Color Schema:
+ *     DEFINITIONS:
+ *         wheat     : #F2E0B6
+ *         off brown : #917A61
+ *         mocha     : #916535
+ *         reddy     : #914E38
+ *         coffee    : #453A2E
+ *         white      : #ffffff
+ *         black      : #000000
+ *         
+ *     :focus & .selectable
+ *         text       : 
+ *         bg         : 
+ *         outline    : 
+ *     text           : 
+ *     links
+ *         :link      : 
+ *         :active    : 
+ *         :visited   : 
+ *         :hover     : 
+ *     headings
+ *         h1
+ *             text   : 
+ *             border : 
+ *             bg     : 
+ *         h2
+ *             text   : 
+ *         h3-6
+ *             text   : 
+ *     tables
+ *         headers
+ *             text    :
+ *             border  :
+ *             bg      :
+ *          cells
+ *              text   :
+ *              border :
+ *     forms    
+ *         input[text]
+ *             bg       :
+ *             border   :
+ *         textarea
+ *             bg       :
+ *             border   :
+ *     tab helper    
+ *         text         :
+ *         bg           :
+ *         hover        :
+ *         active text  :
+ *         active bg    :
+ *         active hover :
+ *         border       :
+ *     menu helper
+ *         text         :
+ *         bg           :
+ *         hover        :
+ *         active text  :
+ *         active bg    :
+ *         active hover :
+
+ */
+.fl-theme-rust :focus,
+.fl-theme-rust .selectable  {
+   /* themed focus indicator */
+   outline: 0.2em solid #662e0f;
+}
+
+.fl-theme-rust {color:#000000 !important; background-color:#F2E0B6 !important; border-color:#916535;}
+.fl-theme-rust .fl-knockout {background:transparent; color:#000;}
+
+.fl-theme-rust a {color: #916535 !important; /*font-weight:bold;*/}
+.fl-theme-rust a:hover {color: #914E38 !important;}
+.fl-theme-rust h1 {color:#000; border-bottom-width:1px; border-bottom-style:dotted;} 
+.fl-theme-rust h2 {color: #453A2E;}
+
+.fl-theme-rust th {border:0.1em solid #453A2E; background-color:#917A61 !important; color:#fff !important;}
+.fl-theme-rust td {border:0.1em solid #453A2E !important;}
+.fl-theme-rust .fl-textfield {}
+.fl-theme-rust .fl-textarea {}
+
+/* temp selector to access inline edit input fields (should use a classname but now there is none applied) */
+.fl-theme-rust .fl-inlineEdit-edit {background-color:#dfefff; border: 1px solid #5a95cf; margin:-1px;}
+
+/* Wrappers and Callouts  */
+.fl-theme-rust .fl-wrapper-widget { border:3px solid #dfefff; background-color:#333; color:#fff;}
+.fl-theme-rust .fl-wrapper-callout { border-color:#999999; background-color:#dfefff; }
+
+/* Helper: Tabs */
+.fl-theme-rust .fl-tabs {border-bottom-color:#916535;}
+.fl-theme-rust .fl-tabs li  {background-color:#F2E0B6;}
+.fl-theme-rust .fl-tabs li,
+.fl-theme-rust .fl-tabs li a  {font-weight:bold; border-color:#916535; border-bottom-color:#916535; text-decoration:none;}
+.fl-theme-rust .fl-tabs li:hover,
+.fl-theme-rust .fl-tabs li:hover a,
+.fl-theme-rust .fl-tabs li a:hover {background-color:#662e0f; color:#fff !important;}
+
+.fl-theme-rust .fl-tabs li.fl-tabs-active,
+.fl-theme-rust .fl-tabs li.fl-tabs-active a,
+.fl-theme-rust .fl-tabs li.fl-tabs-active a:hover, 
+
+.fl-theme-rust .fl-tabs li.fl-activeTab,
+.fl-theme-rust .fl-tabs li.fl-activeTab:hover,
+.fl-theme-rust .fl-tabs li.fl-activeTab a,
+.fl-theme-rust .fl-tabs li.fl-activeTab a:hover {background-color: #FFFBC2; border-bottom-color:#FFFBC2; color:#916535 !important;}
+.fl-theme-rust .fl-tab-content {background-color:#FFFBC2; color:#000; border:1px solid #916535; border-top:none;}
+
+/* Helper: Menu */
+.fl-theme-rust .fl-listmenu {border:1px solid #4070a1; background-color:#fff;}
+.fl-theme-rust .fl-listmenu li,
+.fl-theme-rust .fl-listmenu li a  {font-weight:bold; color:#4070a1; background-color:#fff; border-color:#4070a1; text-decoration:none;}
+
+.fl-theme-rust .fl-listmenu a:hover {background-color:#5a95cf; color:#fff;}
+.fl-theme-rust .fl-listmenu .fl-activemenu,
+.fl-theme-rust .fl-listmenu .fl-activemenu:hover { background-color: #fff; border-bottom-color:#fff; color:#508cc9;}
+
+
+/* Widget core */
+.fl-theme-rust .fl-widget {background:#662e0f url(../images/themes/rust/widget-earmark.png) no-repeat top left;}
+    .fl-theme-rust .fl-widget ul {}
+    .fl-theme-rust .fl-widget h2 {color:#FFFBC2;}
+    .fl-theme-rust .grabbable {background-image:url('../images/themes/rust/gripper.png');}
+
+/* Widget titlebar */
+.fl-theme-rust .fl-widget-titlebar {/*margin:-5px -5px 0 -5px;*/}
+    .fl-theme-rust .fl-widget-titlebar .icon {background-position:center center;}
+
+/* Widget options */
+.fl-theme-rust .fl-widget-options {}
+    .fl-theme-rust .fl-widget-options ul {} 
+    .fl-theme-rust .fl-widget-options li {border-left:1px solid #cf923e;}
+    .fl-theme-rust .fl-widget-options a {color:#cf923e !important;}
+    .fl-theme-rust .fl-widget-options .icon {}
+    .fl-theme-rust .fl-widget-options a.icon:hover {background-color:#cf923e; border-color:#fffbc2;}
+
+/* Widget content */
+.fl-theme-rust .fl-widget-content {background-color:#FFFBC2 !important;}
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-theme-slate.css b/docs/jscripts/infusion/framework/fss/css/fss-theme-slate.css
new file mode 100644 (file)
index 0000000..e64d883
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+          Low Contrast Grey scale ()
+=============================================
+ * Color Schema:
+ *     DEFINITIONS:
+ *         white     : #ffffff
+ *         grey      : #cccccc
+ *         dark grey : #999999
+ *         deep grey : #666666
+ */
+
+
+
+
+.fl-theme-slate :focus,
+.fl-theme-slate .selectable  {
+   /* themed focus indicator */
+   outline: 0.1em solid #000;
+}
+
+.fl-theme-slate {color:#000000; background-color:#cccccc; border-color:#999999;}
+.fl-theme-slate a {color: #ebebeb; font-weight:bold;}
+.fl-theme-slate a:hover {color: #ffffff;}
+.fl-theme-slate h1 {color:#999999; border-bottom-width:0.2em; border-bottom-style:solid;} 
+.fl-theme-slate h2 {color: #ffffff;}
+.fl-theme-slate th {border:0.1em solid #ffffff; background-color:#dfefff !important;}
+.fl-theme-slate td {border:0.1em solid #999999 !important;}
+.fl-theme-slate .fl-textfield,
+.fl-theme-slate .fl-textarea {background-color:#ccc; border:1px solid #666;}
+.fl-theme-slate .fl-icon {background-color:#ebebeb}
+
+/* temp selector to access inline edit input fields (should use a classname but now there is none applied) */
+.fl-theme-slate .fl-inlineEdit-edit {background-color:#dfefff !important; border:0.1em solid #ffffff; margin:-0.1em;}
+
+/* Helper: Buttons */
+.fl-theme-slate .fl-button-left,
+.fl-theme-slate .fl-button-right {color:#FFFFFF !important; background-color:#999 !important;}
+.fl-theme-slate .fl-button-left {background-image:url(../images/themes/slate/buttons-med-cap.png);}
+.fl-theme-slate .fl-button-right {background-image:url(../images/themes/slate/buttons-med-cap.png);}
+.fl-theme-slate .fl-button-inner {background-image:url(../images/themes/slate/buttons-med-bg.png);}
+
+/* Helper: Tabs */
+.fl-theme-slate .fl-tabs {border-bottom-color:#999999;}
+.fl-theme-slate .fl-tabs li,
+.fl-theme-slate .fl-tabs li a  {font-weight:bold; color:#999999; border-color:#999999; border-bottom-color:#999999; background-color:#666; text-decoration:none;}
+.fl-theme-slate .fl-tabs li a:hover {background-color:#999; color:#fff !important;}
+/* Deprecated */
+.fl-theme-slate .fl-tabs li.fl-activeTab,
+.fl-theme-slate .fl-tabs li.fl-activeTab:hover,
+.fl-theme-slate .fl-tabs li.fl-activeTab a,
+.fl-theme-slate .fl-tabs li.fl-activeTab a:hover,
+/* New syntax */
+.fl-theme-slate .fl-tabs li.fl-tabs-active,
+.fl-theme-slate .fl-tabs li.fl-tabs-active a,
+.fl-theme-slate .fl-tabs li.fl-tabs-active a:hover {background-color: #ebebeb; border-bottom-color:#ccc; color:#000 !important;}
+.fl-theme-slate .fl-tabs-content {background-color:#ebebeb; color:#000; border:1px solid #999999; border-top:none;}
+
+/* Helper: Menu */
+.fl-theme-slate .fl-listmenu, 
+.fl-theme-slate .fl-list-menu {border:1px solid #999999; border-bottom-width:2px; background-color:#ccc;}
+.fl-theme-slate .fl-listmenu li, 
+.fl-theme-slate .fl-list-menu li,
+.fl-theme-slate .fl-listmenu li a, 
+.fl-theme-slate .fl-list-menu li a  {font-weight:bold; background-color:#999; border-color:#ccc; text-decoration:none;}
+.fl-theme-slate .fl-listmenu a:hover, 
+.fl-theme-slate .fl-list-menu a:hover {background-color:#ebebeb; color:#000 !important;}
+.fl-theme-slate .fl-listmenu .fl-activemenu, 
+.fl-theme-slate .fl-list-menu .fl-activemenu,
+.fl-theme-slate .fl-listmenu .fl-activemenu:hover, 
+.fl-theme-slate .fl-list-menu .fl-activemenu:hover { background-color: #ccc; border-bottom-color:#ccc; color:#d9d9d9;}
+
+/* Helper: Thumbnail Grid */
+.fl-theme-slate .fl-grid {border:2px solid #999999; background-color:#ccc;}
+.fl-theme-slate .fl-grid li {background-color:#999; border:1px solid #999999}
+.fl-theme-slate .fl-grid .fl-grid-caption {background-color:#666; color:#ffffff; }
+
+/* Helper: Widgets */
+/* Widget core */
+.fl-theme-slate .fl-widget {background:#cccccc url(../images/themes/slate/widget-bg.png) repeat-x top left; border:1px solid #666666;}                        
+.fl-theme-slate .fl-widget ul {}
+.fl-theme-slate .fl-widget h2 {color:#ccc;}
+.fl-theme-slate .fl-widget .fl-icon-more  {background-image:url('../images/themes/slate/icon-widget-More.png'); margin-left:0px;}
+.fl-theme-slate .fl-widget .fl-icon-close {background-image:url('../images/themes/slate/icon-widget-Close.png'); margin-right:0px;}
+
+/* Widget titlebar */
+.fl-theme-slate .fl-grabbable .fl-widget-titlebar {background-image:url('../images/themes/slate/icon-widget-gripper.png');}
+.fl-theme-slate .fl-widget-titlebar .icon {background-position:center center;}
+.fl-theme-slate .fl-widget-titlebar .fl-button-right,
+.fl-theme-slate .fl-widget-titlebar .fl-button-left {
+    color:#333 !important;
+    background-image:url(../images/themes/slate/buttons-titlebar-cap.png);
+    text-decoration:none;
+}
+.fl-theme-slate .fl-widget-titlebar .fl-button-inner {
+    background-image:url(../images/themes/slate/buttons-titlebar-bg.png);
+    padding-bottom:0.3em;
+    padding-top:0;
+}
+
+/* Widget options */
+.fl-theme-slate .fl-widget-options {}
+.fl-theme-slate .fl-widget-options ul {} 
+.fl-theme-slate .fl-widget-options li {border-left:1px solid #ccc;}
+.fl-theme-slate .fl-widget-options a {}
+.fl-theme-slate .fl-widget-options a.icon:hover {background-color:#ffffff; border-color:#000;}
+
+/* Widget content */
+.fl-theme-slate .fl-widget-content {background-color:#bfbfbf;}
+
+/* Component: Progressor */
+.fl-theme-slate .fl-progress-bounds {border-color:#999999; background-color:#ccc;}
+.fl-theme-slate .fl-progress-fill {color:#999999; background-color:#000;}
+
+/* States */
+.fl-theme-slate .fl-reorderer-dropMarker {background-color:#f00 !important;}
+
+/* Enhaced Graphics Toggles */
+
+/* Regular Background */
+.fl-theme-slate .fl-tabs-enhanced {
+    background:url(../images/themes/slate/tabs-light-container-bg.png) repeat-x left bottom;
+    border-bottom:none;
+    margin:10px 1px 0;
+    padding:5px 0 6px;
+    *padding:0;
+}
+
+.fl-theme-slate .fl-tabs-enhanced li {
+    background: transparent url(../images/themes/slate/tabs-light-cap.png) no-repeat left top; 
+    padding:4px 0 6px 16px;
+    *padding-top:0;
+}
+.fl-theme-slate .fl-tabs-enhanced li a {
+    background: transparent url(../images/themes/slate/tabs-light-bg.png) no-repeat right top; 
+    border:none;
+    margin:0;
+    padding:4px 16px 6px 0;
+    color:#666666 !important;
+    *padding-bottom:4px;
+}
+.fl-theme-slate .fl-tabs-enhanced li.fl-tabs-active {
+    background:transparent url(../images/themes/slate/tabs-light-active-cap.png) no-repeat scroll left top;
+}
+.fl-theme-slate .fl-tabs-enhanced li.fl-tabs-active a {
+    background:transparent url(../images/themes/slate/tabs-light-active-bg.png) no-repeat scroll right top;
+    color:#000000 !important;
+}
+
+/* Widget Content Background */
+.fl-theme-slate .fl-widget-content .fl-tabs-enhanced {
+    background:url(../images/themes/slate/tabs-med-container-bg.png) repeat-x left bottom;
+    border-bottom:none;
+    margin:10px 1px 0;
+    padding:5px 0 6px;
+    *padding:0;
+}
+
+.fl-theme-slate .fl-widget-content .fl-tabs-enhanced li {
+    background: transparent url(../images/themes/slate/tabs-med-cap.png) no-repeat left top; 
+    padding:4px 0 6px 16px;
+    *padding-top:0;
+}
+.fl-theme-slate .fl-widget-content .fl-tabs-enhanced li a {
+    background: transparent url(../images/themes/slate/tabs-med-bg.png) no-repeat right top; 
+    border:none;
+    margin:0;
+    padding:4px 16px 6px 0;
+    color:#666666 !important;
+    *padding-bottom:4px;
+}
+.fl-theme-slate .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active {
+    background:transparent url(../images/themes/slate/tabs-med-active-cap.png) no-repeat scroll left top;
+}
+.fl-theme-slate .fl-widget-content .fl-tabs-enhanced li.fl-tabs-active a {
+    background:transparent url(../images/themes/slate/tabs-med-active-bg.png) no-repeat scroll right top;
+    color:#000000 !important;
+}
diff --git a/docs/jscripts/infusion/framework/fss/css/fss-transitions.css b/docs/jscripts/infusion/framework/fss/css/fss-transitions.css
new file mode 100644 (file)
index 0000000..1ec6c1a
--- /dev/null
@@ -0,0 +1,35 @@
+/***************************************/
+/* Transition Library */
+.fl-transition-slide {
+    -webkit-transition: -webkit-transform 0.75s ease-in-out;
+}
+.fl-transition-grow {
+    -webkit-transition: height 0.75s ease-in-out;
+}
+.fl-transition-fade {
+    -webkit-transition: opacity 0.75s linear;
+}
+
+
+/***************************************/
+/* Animation Library */
+@-webkit-keyframes fadeOut {
+       from {
+               opacity : 1;
+       }
+       to {
+               opacity : 0;
+       }
+}
+
+@-webkit-keyframes loading_spinner {
+    from {
+        -webkit-transform: rotate(0deg);
+        -webkit-animation-timing-function: linear
+    }
+    to {
+        -webkit-transform: rotate(360deg);
+        -webkit-animation-timing-function: linear
+    }
+}
+/**************************************/
diff --git a/docs/jscripts/infusion/framework/fss/images/exclamation.png b/docs/jscripts/infusion/framework/fss/images/exclamation.png
new file mode 100644 (file)
index 0000000..056f680
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/exclamation.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/gripper.png b/docs/jscripts/infusion/framework/fss/images/gripper.png
new file mode 100644 (file)
index 0000000..5e7cc70
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/gripper.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/_common/exclamation.png b/docs/jscripts/infusion/framework/fss/images/themes/_common/exclamation.png
new file mode 100644 (file)
index 0000000..056f680
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/_common/exclamation.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/_common/gloss_25_repeater.png b/docs/jscripts/infusion/framework/fss/images/themes/_common/gloss_25_repeater.png
new file mode 100644 (file)
index 0000000..5e7c7a7
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/_common/gloss_25_repeater.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/_common/gripper.png b/docs/jscripts/infusion/framework/fss/images/themes/_common/gripper.png
new file mode 100644 (file)
index 0000000..5e7cc70
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/_common/gripper.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/android/backbutton_mask.png b/docs/jscripts/infusion/framework/fss/images/themes/android/backbutton_mask.png
new file mode 100644 (file)
index 0000000..5191bd0
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/android/backbutton_mask.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/android/button_bg_insetShadow.png b/docs/jscripts/infusion/framework/fss/images/themes/android/button_bg_insetShadow.png
new file mode 100644 (file)
index 0000000..1662147
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/android/button_bg_insetShadow.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_arrow.png b/docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_arrow.png
new file mode 100644 (file)
index 0000000..8a4e37f
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_arrow.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.gif b/docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.gif
new file mode 100644 (file)
index 0000000..0c8c1d8
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.gif differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.png b/docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.png
new file mode 100644 (file)
index 0000000..5ce9ec2
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/android/listmenu_loader.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/android/navbar_back_button_insetShadow.png b/docs/jscripts/infusion/framework/fss/images/themes/android/navbar_back_button_insetShadow.png
new file mode 100644 (file)
index 0000000..6179fba
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/android/navbar_back_button_insetShadow.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/android/navbar_normal_button_insetShadow.png b/docs/jscripts/infusion/framework/fss/images/themes/android/navbar_normal_button_insetShadow.png
new file mode 100644 (file)
index 0000000..305ea1b
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/android/navbar_normal_button_insetShadow.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-light-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-light-bg.png
new file mode 100644 (file)
index 0000000..d661752
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-light-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-light-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-light-cap.png
new file mode 100644 (file)
index 0000000..9b4c6cb
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-light-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-bg.png
new file mode 100644 (file)
index 0000000..5d25958
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-cap.png
new file mode 100644 (file)
index 0000000..61eeecb
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-med-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-bg.png
new file mode 100644 (file)
index 0000000..4979328
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-cap.png
new file mode 100644 (file)
index 0000000..20acddc
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/buttons-titlebar-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-menu-Delete.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-menu-Delete.png
new file mode 100644 (file)
index 0000000..85e3070
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-menu-Delete.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Close.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Close.png
new file mode 100644 (file)
index 0000000..1d8bab3
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Close.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Less.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Less.png
new file mode 100644 (file)
index 0000000..395067b
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-Less.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-More.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-More.png
new file mode 100644 (file)
index 0000000..6ed2263
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-More.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-ShowSettings.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-ShowSettings.png
new file mode 100644 (file)
index 0000000..7f43e1d
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-ShowSettings.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-gripper.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-gripper.png
new file mode 100644 (file)
index 0000000..ce26271
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/icon-widget-gripper.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-bg.png
new file mode 100644 (file)
index 0000000..16c34a6
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-cap.png
new file mode 100644 (file)
index 0000000..8c8b587
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-active-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-bg.png
new file mode 100644 (file)
index 0000000..ceffc5e
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-cap.png
new file mode 100644 (file)
index 0000000..877c88c
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-container-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-container-bg.png
new file mode 100644 (file)
index 0000000..e585983
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-container-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-content-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-content-bg.png
new file mode 100644 (file)
index 0000000..cc5368a
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-light-content-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-bg.png
new file mode 100644 (file)
index 0000000..40c7d14
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-cap.png
new file mode 100644 (file)
index 0000000..6be7f9a
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-active-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-bg.png
new file mode 100644 (file)
index 0000000..37e20ca
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-cap.png
new file mode 100644 (file)
index 0000000..bab78aa
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-container-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-container-bg.png
new file mode 100644 (file)
index 0000000..6d0919d
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-container-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-content-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-content-bg.png
new file mode 100644 (file)
index 0000000..cc5368a
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/tabs-med-content-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/coal/widget-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/coal/widget-bg.png
new file mode 100644 (file)
index 0000000..4579055
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/coal/widget-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/iphone/backbutton_mask.png b/docs/jscripts/infusion/framework/fss/images/themes/iphone/backbutton_mask.png
new file mode 100644 (file)
index 0000000..5191bd0
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/iphone/backbutton_mask.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/iphone/button_bg_insetShadow.png b/docs/jscripts/infusion/framework/fss/images/themes/iphone/button_bg_insetShadow.png
new file mode 100644 (file)
index 0000000..1662147
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/iphone/button_bg_insetShadow.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_arrow.png b/docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_arrow.png
new file mode 100644 (file)
index 0000000..8a4e37f
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_arrow.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.gif b/docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.gif
new file mode 100644 (file)
index 0000000..95294c9
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.gif differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.png b/docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.png
new file mode 100644 (file)
index 0000000..5ce9ec2
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/iphone/listmenu_loader.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/iphone/navbar_back_button_insetShadow.png b/docs/jscripts/infusion/framework/fss/images/themes/iphone/navbar_back_button_insetShadow.png
new file mode 100644 (file)
index 0000000..6179fba
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/iphone/navbar_back_button_insetShadow.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/iphone/navbar_normal_button_insetShadow.png b/docs/jscripts/infusion/framework/fss/images/themes/iphone/navbar_normal_button_insetShadow.png
new file mode 100644 (file)
index 0000000..305ea1b
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/iphone/navbar_normal_button_insetShadow.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-bg.png
new file mode 100644 (file)
index 0000000..086c50a
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-cap.png
new file mode 100644 (file)
index 0000000..42e7286
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-light-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-med-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-med-bg.png
new file mode 100644 (file)
index 0000000..3ff61be
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-med-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-med-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-med-cap.png
new file mode 100644 (file)
index 0000000..8313382
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-med-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-bg.png
new file mode 100644 (file)
index 0000000..50cd421
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-cap.png
new file mode 100644 (file)
index 0000000..8730c71
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/buttons-titlebar-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-menu-Delete.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-menu-Delete.png
new file mode 100644 (file)
index 0000000..85e3070
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-menu-Delete.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-options-ListOrGrid.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-options-ListOrGrid.png
new file mode 100644 (file)
index 0000000..b39267f
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-options-ListOrGrid.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Close.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Close.png
new file mode 100644 (file)
index 0000000..347e288
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Close.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Less.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Less.png
new file mode 100644 (file)
index 0000000..fdd1a3d
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-Less.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-More.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-More.png
new file mode 100644 (file)
index 0000000..164ee2b
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-More.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-ShowSettings.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-ShowSettings.png
new file mode 100644 (file)
index 0000000..7f43e1d
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-ShowSettings.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-gripper.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-gripper.png
new file mode 100644 (file)
index 0000000..c0b6b97
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/icon-widget-gripper.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-bg.png
new file mode 100644 (file)
index 0000000..96036b2
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-cap.png
new file mode 100644 (file)
index 0000000..29adc21
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-active-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-bg.png
new file mode 100644 (file)
index 0000000..ca91cc1
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-cap.png
new file mode 100644 (file)
index 0000000..6bbcd54
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-container-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-container-bg.png
new file mode 100644 (file)
index 0000000..eda424e
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-light-container-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-bg.png
new file mode 100644 (file)
index 0000000..ab495c6
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-cap.png
new file mode 100644 (file)
index 0000000..218603a
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-active-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-bg.png
new file mode 100644 (file)
index 0000000..907e1ec
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-cap.png
new file mode 100644 (file)
index 0000000..15524c4
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-container-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-container-bg.png
new file mode 100644 (file)
index 0000000..888fb86
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/tabs-med-container-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/mist/widget-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/mist/widget-bg.png
new file mode 100644 (file)
index 0000000..a7beb3d
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/mist/widget-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/rust/gripper.png b/docs/jscripts/infusion/framework/fss/images/themes/rust/gripper.png
new file mode 100644 (file)
index 0000000..84d3710
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/rust/gripper.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-menu-Delete.png b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-menu-Delete.png
new file mode 100644 (file)
index 0000000..b1ec1ca
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-menu-Delete.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-options-ListOrGrid.png b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-options-ListOrGrid.png
new file mode 100644 (file)
index 0000000..b39267f
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-options-ListOrGrid.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Close.png b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Close.png
new file mode 100644 (file)
index 0000000..9c12532
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Close.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-More.png b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-More.png
new file mode 100644 (file)
index 0000000..21d40de
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-More.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Settings.png b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Settings.png
new file mode 100644 (file)
index 0000000..1dbedbb
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/rust/icon-widget-Settings.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/rust/menu-hover.png b/docs/jscripts/infusion/framework/fss/images/themes/rust/menu-hover.png
new file mode 100644 (file)
index 0000000..e398151
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/rust/menu-hover.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/rust/widget-earmark.png b/docs/jscripts/infusion/framework/fss/images/themes/rust/widget-earmark.png
new file mode 100644 (file)
index 0000000..3f2b612
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/rust/widget-earmark.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-light-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-light-bg.png
new file mode 100644 (file)
index 0000000..7a49729
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-light-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-light-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-light-cap.png
new file mode 100644 (file)
index 0000000..1b61357
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-light-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-bg.png
new file mode 100644 (file)
index 0000000..011549f
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-cap.png
new file mode 100644 (file)
index 0000000..af12703
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-med-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-bg.png
new file mode 100644 (file)
index 0000000..299b57e
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-cap.png
new file mode 100644 (file)
index 0000000..f17f7f6
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/buttons-titlebar-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Close.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Close.png
new file mode 100644 (file)
index 0000000..df8ba52
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Close.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Less.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Less.png
new file mode 100644 (file)
index 0000000..afb4355
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-Less.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-More.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-More.png
new file mode 100644 (file)
index 0000000..7eaf519
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-More.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-gripper.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-gripper.png
new file mode 100644 (file)
index 0000000..d944f44
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/icon-widget-gripper.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/sprites.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/sprites.png
new file mode 100644 (file)
index 0000000..45b5a94
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/sprites.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-bg.png
new file mode 100644 (file)
index 0000000..e1a3e64
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-cap.png
new file mode 100644 (file)
index 0000000..a150b58
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-active-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-bg.png
new file mode 100644 (file)
index 0000000..132027f
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-cap.png
new file mode 100644 (file)
index 0000000..ee50821
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-container-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-container-bg.png
new file mode 100644 (file)
index 0000000..2ad596e
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-light-container-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-bg.png
new file mode 100644 (file)
index 0000000..0d87fcc
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-cap.png
new file mode 100644 (file)
index 0000000..c49b497
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-active-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-bg.png
new file mode 100644 (file)
index 0000000..1d35340
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-cap.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-cap.png
new file mode 100644 (file)
index 0000000..27bb1d4
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-cap.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-container-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-container-bg.png
new file mode 100644 (file)
index 0000000..a64706d
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/tabs-med-container-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/widget-bg.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/widget-bg.png
new file mode 100644 (file)
index 0000000..1f60b14
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/widget-bg.png differ
diff --git a/docs/jscripts/infusion/framework/fss/images/themes/slate/widget-earmark.png b/docs/jscripts/infusion/framework/fss/images/themes/slate/widget-earmark.png
new file mode 100644 (file)
index 0000000..f286c20
Binary files /dev/null and b/docs/jscripts/infusion/framework/fss/images/themes/slate/widget-earmark.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/core/js/jquery.js b/docs/jscripts/infusion/lib/jquery/core/js/jquery.js
new file mode 100644 (file)
index 0000000..fff6776
--- /dev/null
@@ -0,0 +1,6240 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function( window, undefined ) {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+               // The jQuery object is actually just the init constructor 'enhanced'
+               return new jQuery.fn.init( selector, context );
+       },
+
+       // Map over jQuery in case of overwrite
+       _jQuery = window.jQuery,
+
+       // Map over the $ in case of overwrite
+       _$ = window.$,
+
+       // Use the correct document accordingly with window argument (sandbox)
+       document = window.document,
+
+       // A central reference to the root jQuery(document)
+       rootjQuery,
+
+       // A simple way to check for HTML strings or ID strings
+       // (both of which we optimize for)
+       quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
+
+       // Is it a simple selector
+       isSimple = /^.[^:#\[\.,]*$/,
+
+       // Check if a string has a non-whitespace character in it
+       rnotwhite = /\S/,
+
+       // Used for trimming whitespace
+       rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
+
+       // Match a standalone tag
+       rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+       // Keep a UserAgent string for use with jQuery.browser
+       userAgent = navigator.userAgent,
+
+       // For matching the engine and version of the browser
+       browserMatch,
+       
+       // Has the ready events already been bound?
+       readyBound = false,
+       
+       // The functions to execute on DOM ready
+       readyList = [],
+
+       // The ready event handler
+       DOMContentLoaded,
+
+       // Save a reference to some core methods
+       toString = Object.prototype.toString,
+       hasOwnProperty = Object.prototype.hasOwnProperty,
+       push = Array.prototype.push,
+       slice = Array.prototype.slice,
+       indexOf = Array.prototype.indexOf;
+
+jQuery.fn = jQuery.prototype = {
+       init: function( selector, context ) {
+               var match, elem, ret, doc;
+
+               // Handle $(""), $(null), or $(undefined)
+               if ( !selector ) {
+                       return this;
+               }
+
+               // Handle $(DOMElement)
+               if ( selector.nodeType ) {
+                       this.context = this[0] = selector;
+                       this.length = 1;
+                       return this;
+               }
+               
+               // The body element only exists once, optimize finding it
+               if ( selector === "body" && !context ) {
+                       this.context = document;
+                       this[0] = document.body;
+                       this.selector = "body";
+                       this.length = 1;
+                       return this;
+               }
+
+               // Handle HTML strings
+               if ( typeof selector === "string" ) {
+                       // Are we dealing with HTML string or an ID?
+                       match = quickExpr.exec( selector );
+
+                       // Verify a match, and that no context was specified for #id
+                       if ( match && (match[1] || !context) ) {
+
+                               // HANDLE: $(html) -> $(array)
+                               if ( match[1] ) {
+                                       doc = (context ? context.ownerDocument || context : document);
+
+                                       // If a single string is passed in and it's a single tag
+                                       // just do a createElement and skip the rest
+                                       ret = rsingleTag.exec( selector );
+
+                                       if ( ret ) {
+                                               if ( jQuery.isPlainObject( context ) ) {
+                                                       selector = [ document.createElement( ret[1] ) ];
+                                                       jQuery.fn.attr.call( selector, context, true );
+
+                                               } else {
+                                                       selector = [ doc.createElement( ret[1] ) ];
+                                               }
+
+                                       } else {
+                                               ret = buildFragment( [ match[1] ], [ doc ] );
+                                               selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
+                                       }
+                                       
+                                       return jQuery.merge( this, selector );
+                                       
+                               // HANDLE: $("#id")
+                               } else {
+                                       elem = document.getElementById( match[2] );
+
+                                       if ( elem ) {
+                                               // Handle the case where IE and Opera return items
+                                               // by name instead of ID
+                                               if ( elem.id !== match[2] ) {
+                                                       return rootjQuery.find( selector );
+                                               }
+
+                                               // Otherwise, we inject the element directly into the jQuery object
+                                               this.length = 1;
+                                               this[0] = elem;
+                                       }
+
+                                       this.context = document;
+                                       this.selector = selector;
+                                       return this;
+                               }
+
+                       // HANDLE: $("TAG")
+                       } else if ( !context && /^\w+$/.test( selector ) ) {
+                               this.selector = selector;
+                               this.context = document;
+                               selector = document.getElementsByTagName( selector );
+                               return jQuery.merge( this, selector );
+
+                       // HANDLE: $(expr, $(...))
+                       } else if ( !context || context.jquery ) {
+                               return (context || rootjQuery).find( selector );
+
+                       // HANDLE: $(expr, context)
+                       // (which is just equivalent to: $(context).find(expr)
+                       } else {
+                               return jQuery( context ).find( selector );
+                       }
+
+               // HANDLE: $(function)
+               // Shortcut for document ready
+               } else if ( jQuery.isFunction( selector ) ) {
+                       return rootjQuery.ready( selector );
+               }
+
+               if (selector.selector !== undefined) {
+                       this.selector = selector.selector;
+                       this.context = selector.context;
+               }
+
+               return jQuery.makeArray( selector, this );
+       },
+
+       // Start with an empty selector
+       selector: "",
+
+       // The current version of jQuery being used
+       jquery: "1.4.2",
+
+       // The default length of a jQuery object is 0
+       length: 0,
+
+       // The number of elements contained in the matched element set
+       size: function() {
+               return this.length;
+       },
+
+       toArray: function() {
+               return slice.call( this, 0 );
+       },
+
+       // Get the Nth element in the matched element set OR
+       // Get the whole matched element set as a clean array
+       get: function( num ) {
+               return num == null ?
+
+                       // Return a 'clean' array
+                       this.toArray() :
+
+                       // Return just the object
+                       ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
+       },
+
+       // Take an array of elements and push it onto the stack
+       // (returning the new matched element set)
+       pushStack: function( elems, name, selector ) {
+               // Build a new jQuery matched element set
+               var ret = jQuery();
+
+               if ( jQuery.isArray( elems ) ) {
+                       push.apply( ret, elems );
+               
+               } else {
+                       jQuery.merge( ret, elems );
+               }
+
+               // Add the old object onto the stack (as a reference)
+               ret.prevObject = this;
+
+               ret.context = this.context;
+
+               if ( name === "find" ) {
+                       ret.selector = this.selector + (this.selector ? " " : "") + selector;
+               } else if ( name ) {
+                       ret.selector = this.selector + "." + name + "(" + selector + ")";
+               }
+
+               // Return the newly-formed element set
+               return ret;
+       },
+
+       // Execute a callback for every element in the matched set.
+       // (You can seed the arguments with an array of args, but this is
+       // only used internally.)
+       each: function( callback, args ) {
+               return jQuery.each( this, callback, args );
+       },
+       
+       ready: function( fn ) {
+               // Attach the listeners
+               jQuery.bindReady();
+
+               // If the DOM is already ready
+               if ( jQuery.isReady ) {
+                       // Execute the function immediately
+                       fn.call( document, jQuery );
+
+               // Otherwise, remember the function for later
+               } else if ( readyList ) {
+                       // Add the function to the wait list
+                       readyList.push( fn );
+               }
+
+               return this;
+       },
+       
+       eq: function( i ) {
+               return i === -1 ?
+                       this.slice( i ) :
+                       this.slice( i, +i + 1 );
+       },
+
+       first: function() {
+               return this.eq( 0 );
+       },
+
+       last: function() {
+               return this.eq( -1 );
+       },
+
+       slice: function() {
+               return this.pushStack( slice.apply( this, arguments ),
+                       "slice", slice.call(arguments).join(",") );
+       },
+
+       map: function( callback ) {
+               return this.pushStack( jQuery.map(this, function( elem, i ) {
+                       return callback.call( elem, i, elem );
+               }));
+       },
+       
+       end: function() {
+               return this.prevObject || jQuery(null);
+       },
+
+       // For internal use only.
+       // Behaves like an Array's method, not like a jQuery method.
+       push: push,
+       sort: [].sort,
+       splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+       // copy reference to target object
+       var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
+
+       // Handle a deep copy situation
+       if ( typeof target === "boolean" ) {
+               deep = target;
+               target = arguments[1] || {};
+               // skip the boolean and the target
+               i = 2;
+       }
+
+       // Handle case when target is a string or something (possible in deep copy)
+       if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+               target = {};
+       }
+
+       // extend jQuery itself if only one argument is passed
+       if ( length === i ) {
+               target = this;
+               --i;
+       }
+
+       for ( ; i < length; i++ ) {
+               // Only deal with non-null/undefined values
+               if ( (options = arguments[ i ]) != null ) {
+                       // Extend the base object
+                       for ( name in options ) {
+                               src = target[ name ];
+                               copy = options[ name ];
+
+                               // Prevent never-ending loop
+                               if ( target === copy ) {
+                                       continue;
+                               }
+
+                               // Recurse if we're merging object literal values or arrays
+                               if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
+                                       var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
+                                               : jQuery.isArray(copy) ? [] : {};
+
+                                       // Never move original objects, clone them
+                                       target[ name ] = jQuery.extend( deep, clone, copy );
+
+                               // Don't bring in undefined values
+                               } else if ( copy !== undefined ) {
+                                       target[ name ] = copy;
+                               }
+                       }
+               }
+       }
+
+       // Return the modified object
+       return target;
+};
+
+jQuery.extend({
+       noConflict: function( deep ) {
+               window.$ = _$;
+
+               if ( deep ) {
+                       window.jQuery = _jQuery;
+               }
+
+               return jQuery;
+       },
+       
+       // Is the DOM ready to be used? Set to true once it occurs.
+       isReady: false,
+       
+       // Handle when the DOM is ready
+       ready: function() {
+               // Make sure that the DOM is not already loaded
+               if ( !jQuery.isReady ) {
+                       // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+                       if ( !document.body ) {
+                               return setTimeout( jQuery.ready, 13 );
+                       }
+
+                       // Remember that the DOM is ready
+                       jQuery.isReady = true;
+
+                       // If there are functions bound, to execute
+                       if ( readyList ) {
+                               // Execute all of them
+                               var fn, i = 0;
+                               while ( (fn = readyList[ i++ ]) ) {
+                                       fn.call( document, jQuery );
+                               }
+
+                               // Reset the list of functions
+                               readyList = null;
+                       }
+
+                       // Trigger any bound ready events
+                       if ( jQuery.fn.triggerHandler ) {
+                               jQuery( document ).triggerHandler( "ready" );
+                       }
+               }
+       },
+       
+       bindReady: function() {
+               if ( readyBound ) {
+                       return;
+               }
+
+               readyBound = true;
+
+               // Catch cases where $(document).ready() is called after the
+               // browser event has already occurred.
+               if ( document.readyState === "complete" ) {
+                       return jQuery.ready();
+               }
+
+               // Mozilla, Opera and webkit nightlies currently support this event
+               if ( document.addEventListener ) {
+                       // Use the handy event callback
+                       document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+                       
+                       // A fallback to window.onload, that will always work
+                       window.addEventListener( "load", jQuery.ready, false );
+
+               // If IE event model is used
+               } else if ( document.attachEvent ) {
+                       // ensure firing before onload,
+                       // maybe late but safe also for iframes
+                       document.attachEvent("onreadystatechange", DOMContentLoaded);
+                       
+                       // A fallback to window.onload, that will always work
+                       window.attachEvent( "onload", jQuery.ready );
+
+                       // If IE and not a frame
+                       // continually check to see if the document is ready
+                       var toplevel = false;
+
+                       try {
+                               toplevel = window.frameElement == null;
+                       } catch(e) {}
+
+                       if ( document.documentElement.doScroll && toplevel ) {
+                               doScrollCheck();
+                       }
+               }
+       },
+
+       // See test/unit/core.js for details concerning isFunction.
+       // Since version 1.3, DOM methods and functions like alert
+       // aren't supported. They return false on IE (#2968).
+       isFunction: function( obj ) {
+               return toString.call(obj) === "[object Function]";
+       },
+
+       isArray: function( obj ) {
+               return toString.call(obj) === "[object Array]";
+       },
+
+       isPlainObject: function( obj ) {
+               // Must be an Object.
+               // Because of IE, we also have to check the presence of the constructor property.
+               // Make sure that DOM nodes and window objects don't pass through, as well
+               if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
+                       return false;
+               }
+               
+               // Not own constructor property must be Object
+               if ( obj.constructor
+                       && !hasOwnProperty.call(obj, "constructor")
+                       && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
+                       return false;
+               }
+               
+               // Own properties are enumerated firstly, so to speed up,
+               // if last one is own, then all properties are own.
+       
+               var key;
+               for ( key in obj ) {}
+               
+               return key === undefined || hasOwnProperty.call( obj, key );
+       },
+
+       isEmptyObject: function( obj ) {
+               for ( var name in obj ) {
+                       return false;
+               }
+               return true;
+       },
+       
+       error: function( msg ) {
+               throw msg;
+       },
+       
+       parseJSON: function( data ) {
+               if ( typeof data !== "string" || !data ) {
+                       return null;
+               }
+
+               // Make sure leading/trailing whitespace is removed (IE can't handle it)
+               data = jQuery.trim( data );
+               
+               // Make sure the incoming data is actual JSON
+               // Logic borrowed from http://json.org/json2.js
+               if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
+                       .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
+                       .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
+
+                       // Try to use the native JSON parser first
+                       return window.JSON && window.JSON.parse ?
+                               window.JSON.parse( data ) :
+                               (new Function("return " + data))();
+
+               } else {
+                       jQuery.error( "Invalid JSON: " + data );
+               }
+       },
+
+       noop: function() {},
+
+       // Evalulates a script in a global context
+       globalEval: function( data ) {
+               if ( data && rnotwhite.test(data) ) {
+                       // Inspired by code by Andrea Giammarchi
+                       // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
+                       var head = document.getElementsByTagName("head")[0] || document.documentElement,
+                               script = document.createElement("script");
+
+                       script.type = "text/javascript";
+
+                       if ( jQuery.support.scriptEval ) {
+                               script.appendChild( document.createTextNode( data ) );
+                       } else {
+                               script.text = data;
+                       }
+
+                       // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+                       // This arises when a base node is used (#2709).
+                       head.insertBefore( script, head.firstChild );
+                       head.removeChild( script );
+               }
+       },
+
+       nodeName: function( elem, name ) {
+               return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+       },
+
+       // args is for internal usage only
+       each: function( object, callback, args ) {
+               var name, i = 0,
+                       length = object.length,
+                       isObj = length === undefined || jQuery.isFunction(object);
+
+               if ( args ) {
+                       if ( isObj ) {
+                               for ( name in object ) {
+                                       if ( callback.apply( object[ name ], args ) === false ) {
+                                               break;
+                                       }
+                               }
+                       } else {
+                               for ( ; i < length; ) {
+                                       if ( callback.apply( object[ i++ ], args ) === false ) {
+                                               break;
+                                       }
+                               }
+                       }
+
+               // A special, fast, case for the most common use of each
+               } else {
+                       if ( isObj ) {
+                               for ( name in object ) {
+                                       if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+                                               break;
+                                       }
+                               }
+                       } else {
+                               for ( var value = object[0];
+                                       i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
+                       }
+               }
+
+               return object;
+       },
+
+       trim: function( text ) {
+               return (text || "").replace( rtrim, "" );
+       },
+
+       // results is for internal usage only
+       makeArray: function( array, results ) {
+               var ret = results || [];
+
+               if ( array != null ) {
+                       // The window, strings (and functions) also have 'length'
+                       // The extra typeof function check is to prevent crashes
+                       // in Safari 2 (See: #3039)
+                       if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
+                               push.call( ret, array );
+                       } else {
+                               jQuery.merge( ret, array );
+                       }
+               }
+
+               return ret;
+       },
+
+       inArray: function( elem, array ) {
+               if ( array.indexOf ) {
+                       return array.indexOf( elem );
+               }
+
+               for ( var i = 0, length = array.length; i < length; i++ ) {
+                       if ( array[ i ] === elem ) {
+                               return i;
+                       }
+               }
+
+               return -1;
+       },
+
+       merge: function( first, second ) {
+               var i = first.length, j = 0;
+
+               if ( typeof second.length === "number" ) {
+                       for ( var l = second.length; j < l; j++ ) {
+                               first[ i++ ] = second[ j ];
+                       }
+               
+               } else {
+                       while ( second[j] !== undefined ) {
+                               first[ i++ ] = second[ j++ ];
+                       }
+               }
+
+               first.length = i;
+
+               return first;
+       },
+
+       grep: function( elems, callback, inv ) {
+               var ret = [];
+
+               // Go through the array, only saving the items
+               // that pass the validator function
+               for ( var i = 0, length = elems.length; i < length; i++ ) {
+                       if ( !inv !== !callback( elems[ i ], i ) ) {
+                               ret.push( elems[ i ] );
+                       }
+               }
+
+               return ret;
+       },
+
+       // arg is for internal usage only
+       map: function( elems, callback, arg ) {
+               var ret = [], value;
+
+               // Go through the array, translating each of the items to their
+               // new value (or values).
+               for ( var i = 0, length = elems.length; i < length; i++ ) {
+                       value = callback( elems[ i ], i, arg );
+
+                       if ( value != null ) {
+                               ret[ ret.length ] = value;
+                       }
+               }
+
+               return ret.concat.apply( [], ret );
+       },
+
+       // A global GUID counter for objects
+       guid: 1,
+
+       proxy: function( fn, proxy, thisObject ) {
+               if ( arguments.length === 2 ) {
+                       if ( typeof proxy === "string" ) {
+                               thisObject = fn;
+                               fn = thisObject[ proxy ];
+                               proxy = undefined;
+
+                       } else if ( proxy && !jQuery.isFunction( proxy ) ) {
+                               thisObject = proxy;
+                               proxy = undefined;
+                       }
+               }
+
+               if ( !proxy && fn ) {
+                       proxy = function() {
+                               return fn.apply( thisObject || this, arguments );
+                       };
+               }
+
+               // Set the guid of unique handler to the same of original handler, so it can be removed
+               if ( fn ) {
+                       proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+               }
+
+               // So proxy can be declared as an argument
+               return proxy;
+       },
+
+       // Use of jQuery.browser is frowned upon.
+       // More details: http://docs.jquery.com/Utilities/jQuery.browser
+       uaMatch: function( ua ) {
+               ua = ua.toLowerCase();
+
+               var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+                       /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
+                       /(msie) ([\w.]+)/.exec( ua ) ||
+                       !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
+                       [];
+
+               return { browser: match[1] || "", version: match[2] || "0" };
+       },
+
+       browser: {}
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+       jQuery.browser[ browserMatch.browser ] = true;
+       jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+       jQuery.browser.safari = true;
+}
+
+if ( indexOf ) {
+       jQuery.inArray = function( elem, array ) {
+               return indexOf.call( array, elem );
+       };
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+       DOMContentLoaded = function() {
+               document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+               jQuery.ready();
+       };
+
+} else if ( document.attachEvent ) {
+       DOMContentLoaded = function() {
+               // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+               if ( document.readyState === "complete" ) {
+                       document.detachEvent( "onreadystatechange", DOMContentLoaded );
+                       jQuery.ready();
+               }
+       };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+       if ( jQuery.isReady ) {
+               return;
+       }
+
+       try {
+               // If IE is used, use the trick by Diego Perini
+               // http://javascript.nwbox.com/IEContentLoaded/
+               document.documentElement.doScroll("left");
+       } catch( error ) {
+               setTimeout( doScrollCheck, 1 );
+               return;
+       }
+
+       // and execute any waiting functions
+       jQuery.ready();
+}
+
+function evalScript( i, elem ) {
+       if ( elem.src ) {
+               jQuery.ajax({
+                       url: elem.src,
+                       async: false,
+                       dataType: "script"
+               });
+       } else {
+               jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
+       }
+
+       if ( elem.parentNode ) {
+               elem.parentNode.removeChild( elem );
+       }
+}
+
+// Mutifunctional method to get and set values to a collection
+// The value/s can be optionally by executed if its a function
+function access( elems, key, value, exec, fn, pass ) {
+       var length = elems.length;
+       
+       // Setting many attributes
+       if ( typeof key === "object" ) {
+               for ( var k in key ) {
+                       access( elems, k, key[k], exec, fn, value );
+               }
+               return elems;
+       }
+       
+       // Setting one attribute
+       if ( value !== undefined ) {
+               // Optionally, function values get executed if exec is true
+               exec = !pass && exec && jQuery.isFunction(value);
+               
+               for ( var i = 0; i < length; i++ ) {
+                       fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+               }
+               
+               return elems;
+       }
+       
+       // Getting an attribute
+       return length ? fn( elems[0], key ) : undefined;
+}
+
+function now() {
+       return (new Date).getTime();
+}
+(function() {
+
+       jQuery.support = {};
+
+       var root = document.documentElement,
+               script = document.createElement("script"),
+               div = document.createElement("div"),
+               id = "script" + now();
+
+       div.style.display = "none";
+       div.innerHTML = "   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+       var all = div.getElementsByTagName("*"),
+               a = div.getElementsByTagName("a")[0];
+
+       // Can't get basic test support
+       if ( !all || !all.length || !a ) {
+               return;
+       }
+
+       jQuery.support = {
+               // IE strips leading whitespace when .innerHTML is used
+               leadingWhitespace: div.firstChild.nodeType === 3,
+
+               // Make sure that tbody elements aren't automatically inserted
+               // IE will insert them into empty tables
+               tbody: !div.getElementsByTagName("tbody").length,
+
+               // Make sure that link elements get serialized correctly by innerHTML
+               // This requires a wrapper element in IE
+               htmlSerialize: !!div.getElementsByTagName("link").length,
+
+               // Get the style information from getAttribute
+               // (IE uses .cssText insted)
+               style: /red/.test( a.getAttribute("style") ),
+
+               // Make sure that URLs aren't manipulated
+               // (IE normalizes it by default)
+               hrefNormalized: a.getAttribute("href") === "/a",
+
+               // Make sure that element opacity exists
+               // (IE uses filter instead)
+               // Use a regex to work around a WebKit issue. See #5145
+               opacity: /^0.55$/.test( a.style.opacity ),
+
+               // Verify style float existence
+               // (IE uses styleFloat instead of cssFloat)
+               cssFloat: !!a.style.cssFloat,
+
+               // Make sure that if no value is specified for a checkbox
+               // that it defaults to "on".
+               // (WebKit defaults to "" instead)
+               checkOn: div.getElementsByTagName("input")[0].value === "on",
+
+               // Make sure that a selected-by-default option has a working selected property.
+               // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+               optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
+
+               parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
+
+               // Will be defined later
+               deleteExpando: true,
+               checkClone: false,
+               scriptEval: false,
+               noCloneEvent: true,
+               boxModel: null
+       };
+
+       script.type = "text/javascript";
+       try {
+               script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
+       } catch(e) {}
+
+       root.insertBefore( script, root.firstChild );
+
+       // Make sure that the execution of code works by injecting a script
+       // tag with appendChild/createTextNode
+       // (IE doesn't support this, fails, and uses .text instead)
+       if ( window[ id ] ) {
+               jQuery.support.scriptEval = true;
+               delete window[ id ];
+       }
+
+       // Test to see if it's possible to delete an expando from an element
+       // Fails in Internet Explorer
+       try {
+               delete script.test;
+       
+       } catch(e) {
+               jQuery.support.deleteExpando = false;
+       }
+
+       root.removeChild( script );
+
+       if ( div.attachEvent && div.fireEvent ) {
+               div.attachEvent("onclick", function click() {
+                       // Cloning a node shouldn't copy over any
+                       // bound event handlers (IE does this)
+                       jQuery.support.noCloneEvent = false;
+                       div.detachEvent("onclick", click);
+               });
+               div.cloneNode(true).fireEvent("onclick");
+       }
+
+       div = document.createElement("div");
+       div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
+
+       var fragment = document.createDocumentFragment();
+       fragment.appendChild( div.firstChild );
+
+       // WebKit doesn't clone checked state correctly in fragments
+       jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
+
+       // Figure out if the W3C box model works as expected
+       // document.body must exist before we can do this
+       jQuery(function() {
+               var div = document.createElement("div");
+               div.style.width = div.style.paddingLeft = "1px";
+
+               document.body.appendChild( div );
+               jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
+               document.body.removeChild( div ).style.display = 'none';
+
+               div = null;
+       });
+
+       // Technique from Juriy Zaytsev
+       // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+       var eventSupported = function( eventName ) { 
+               var el = document.createElement("div"); 
+               eventName = "on" + eventName; 
+
+               var isSupported = (eventName in el); 
+               if ( !isSupported ) { 
+                       el.setAttribute(eventName, "return;"); 
+                       isSupported = typeof el[eventName] === "function"; 
+               } 
+               el = null; 
+
+               return isSupported; 
+       };
+       
+       jQuery.support.submitBubbles = eventSupported("submit");
+       jQuery.support.changeBubbles = eventSupported("change");
+
+       // release memory in IE
+       root = script = div = all = a = null;
+})();
+
+jQuery.props = {
+       "for": "htmlFor",
+       "class": "className",
+       readonly: "readOnly",
+       maxlength: "maxLength",
+       cellspacing: "cellSpacing",
+       rowspan: "rowSpan",
+       colspan: "colSpan",
+       tabindex: "tabIndex",
+       usemap: "useMap",
+       frameborder: "frameBorder"
+};
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+       cache: {},
+       
+       expando:expando,
+
+       // The following elements throw uncatchable exceptions if you
+       // attempt to add expando properties to them.
+       noData: {
+               "embed": true,
+               "object": true,
+               "applet": true
+       },
+
+       data: function( elem, name, data ) {
+               if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+                       return;
+               }
+
+               elem = elem == window ?
+                       windowData :
+                       elem;
+
+               var id = elem[ expando ], cache = jQuery.cache, thisCache;
+
+               if ( !id && typeof name === "string" && data === undefined ) {
+                       return null;
+               }
+
+               // Compute a unique ID for the element
+               if ( !id ) { 
+                       id = ++uuid;
+               }
+
+               // Avoid generating a new cache unless none exists and we
+               // want to manipulate it.
+               if ( typeof name === "object" ) {
+                       elem[ expando ] = id;
+                       thisCache = cache[ id ] = jQuery.extend(true, {}, name);
+
+               } else if ( !cache[ id ] ) {
+                       elem[ expando ] = id;
+                       cache[ id ] = {};
+               }
+
+               thisCache = cache[ id ];
+
+               // Prevent overriding the named cache with undefined values
+               if ( data !== undefined ) {
+                       thisCache[ name ] = data;
+               }
+
+               return typeof name === "string" ? thisCache[ name ] : thisCache;
+       },
+
+       removeData: function( elem, name ) {
+               if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+                       return;
+               }
+
+               elem = elem == window ?
+                       windowData :
+                       elem;
+
+               var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];
+
+               // If we want to remove a specific section of the element's data
+               if ( name ) {
+                       if ( thisCache ) {
+                               // Remove the section of cache data
+                               delete thisCache[ name ];
+
+                               // If we've removed all the data, remove the element's cache
+                               if ( jQuery.isEmptyObject(thisCache) ) {
+                                       jQuery.removeData( elem );
+                               }
+                       }
+
+               // Otherwise, we want to remove all of the element's data
+               } else {
+                       if ( jQuery.support.deleteExpando ) {
+                               delete elem[ jQuery.expando ];
+
+                       } else if ( elem.removeAttribute ) {
+                               elem.removeAttribute( jQuery.expando );
+                       }
+
+                       // Completely remove the data cache
+                       delete cache[ id ];
+               }
+       }
+});
+
+jQuery.fn.extend({
+       data: function( key, value ) {
+               if ( typeof key === "undefined" && this.length ) {
+                       return jQuery.data( this[0] );
+
+               } else if ( typeof key === "object" ) {
+                       return this.each(function() {
+                               jQuery.data( this, key );
+                       });
+               }
+
+               var parts = key.split(".");
+               parts[1] = parts[1] ? "." + parts[1] : "";
+
+               if ( value === undefined ) {
+                       var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+                       if ( data === undefined && this.length ) {
+                               data = jQuery.data( this[0], key );
+                       }
+                       return data === undefined && parts[1] ?
+                               this.data( parts[0] ) :
+                               data;
+               } else {
+                       return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
+                               jQuery.data( this, key, value );
+                       });
+               }
+       },
+
+       removeData: function( key ) {
+               return this.each(function() {
+                       jQuery.removeData( this, key );
+               });
+       }
+});
+jQuery.extend({
+       queue: function( elem, type, data ) {
+               if ( !elem ) {
+                       return;
+               }
+
+               type = (type || "fx") + "queue";
+               var q = jQuery.data( elem, type );
+
+               // Speed up dequeue by getting out quickly if this is just a lookup
+               if ( !data ) {
+                       return q || [];
+               }
+
+               if ( !q || jQuery.isArray(data) ) {
+                       q = jQuery.data( elem, type, jQuery.makeArray(data) );
+
+               } else {
+                       q.push( data );
+               }
+
+               return q;
+       },
+
+       dequeue: function( elem, type ) {
+               type = type || "fx";
+
+               var queue = jQuery.queue( elem, type ), fn = queue.shift();
+
+               // If the fx queue is dequeued, always remove the progress sentinel
+               if ( fn === "inprogress" ) {
+                       fn = queue.shift();
+               }
+
+               if ( fn ) {
+                       // Add a progress sentinel to prevent the fx queue from being
+                       // automatically dequeued
+                       if ( type === "fx" ) {
+                               queue.unshift("inprogress");
+                       }
+
+                       fn.call(elem, function() {
+                               jQuery.dequeue(elem, type);
+                       });
+               }
+       }
+});
+
+jQuery.fn.extend({
+       queue: function( type, data ) {
+               if ( typeof type !== "string" ) {
+                       data = type;
+                       type = "fx";
+               }
+
+               if ( data === undefined ) {
+                       return jQuery.queue( this[0], type );
+               }
+               return this.each(function( i, elem ) {
+                       var queue = jQuery.queue( this, type, data );
+
+                       if ( type === "fx" && queue[0] !== "inprogress" ) {
+                               jQuery.dequeue( this, type );
+                       }
+               });
+       },
+       dequeue: function( type ) {
+               return this.each(function() {
+                       jQuery.dequeue( this, type );
+               });
+       },
+
+       // Based off of the plugin by Clint Helfers, with permission.
+       // http://blindsignals.com/index.php/2009/07/jquery-delay/
+       delay: function( time, type ) {
+               time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
+               type = type || "fx";
+
+               return this.queue( type, function() {
+                       var elem = this;
+                       setTimeout(function() {
+                               jQuery.dequeue( elem, type );
+                       }, time );
+               });
+       },
+
+       clearQueue: function( type ) {
+               return this.queue( type || "fx", [] );
+       }
+});
+var rclass = /[\n\t]/g,
+       rspace = /\s+/,
+       rreturn = /\r/g,
+       rspecialurl = /href|src|style/,
+       rtype = /(button|input)/i,
+       rfocusable = /(button|input|object|select|textarea)/i,
+       rclickable = /^(a|area)$/i,
+       rradiocheck = /radio|checkbox/;
+
+jQuery.fn.extend({
+       attr: function( name, value ) {
+               return access( this, name, value, true, jQuery.attr );
+       },
+
+       removeAttr: function( name, fn ) {
+               return this.each(function(){
+                       jQuery.attr( this, name, "" );
+                       if ( this.nodeType === 1 ) {
+                               this.removeAttribute( name );
+                       }
+               });
+       },
+
+       addClass: function( value ) {
+               if ( jQuery.isFunction(value) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               self.addClass( value.call(this, i, self.attr("class")) );
+                       });
+               }
+
+               if ( value && typeof value === "string" ) {
+                       var classNames = (value || "").split( rspace );
+
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               var elem = this[i];
+
+                               if ( elem.nodeType === 1 ) {
+                                       if ( !elem.className ) {
+                                               elem.className = value;
+
+                                       } else {
+                                               var className = " " + elem.className + " ", setClass = elem.className;
+                                               for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+                                                       if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
+                                                               setClass += " " + classNames[c];
+                                                       }
+                                               }
+                                               elem.className = jQuery.trim( setClass );
+                                       }
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       removeClass: function( value ) {
+               if ( jQuery.isFunction(value) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               self.removeClass( value.call(this, i, self.attr("class")) );
+                       });
+               }
+
+               if ( (value && typeof value === "string") || value === undefined ) {
+                       var classNames = (value || "").split(rspace);
+
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               var elem = this[i];
+
+                               if ( elem.nodeType === 1 && elem.className ) {
+                                       if ( value ) {
+                                               var className = (" " + elem.className + " ").replace(rclass, " ");
+                                               for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
+                                                       className = className.replace(" " + classNames[c] + " ", " ");
+                                               }
+                                               elem.className = jQuery.trim( className );
+
+                                       } else {
+                                               elem.className = "";
+                                       }
+                               }
+                       }
+               }
+
+               return this;
+       },
+
+       toggleClass: function( value, stateVal ) {
+               var type = typeof value, isBool = typeof stateVal === "boolean";
+
+               if ( jQuery.isFunction( value ) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
+                       });
+               }
+
+               return this.each(function() {
+                       if ( type === "string" ) {
+                               // toggle individual class names
+                               var className, i = 0, self = jQuery(this),
+                                       state = stateVal,
+                                       classNames = value.split( rspace );
+
+                               while ( (className = classNames[ i++ ]) ) {
+                                       // check each className given, space seperated list
+                                       state = isBool ? state : !self.hasClass( className );
+                                       self[ state ? "addClass" : "removeClass" ]( className );
+                               }
+
+                       } else if ( type === "undefined" || type === "boolean" ) {
+                               if ( this.className ) {
+                                       // store className if set
+                                       jQuery.data( this, "__className__", this.className );
+                               }
+
+                               // toggle whole className
+                               this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
+                       }
+               });
+       },
+
+       hasClass: function( selector ) {
+               var className = " " + selector + " ";
+               for ( var i = 0, l = this.length; i < l; i++ ) {
+                       if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       },
+
+       val: function( value ) {
+               if ( value === undefined ) {
+                       var elem = this[0];
+
+                       if ( elem ) {
+                               if ( jQuery.nodeName( elem, "option" ) ) {
+                                       return (elem.attributes.value || {}).specified ? elem.value : elem.text;
+                               }
+
+                               // We need to handle select boxes special
+                               if ( jQuery.nodeName( elem, "select" ) ) {
+                                       var index = elem.selectedIndex,
+                                               values = [],
+                                               options = elem.options,
+                                               one = elem.type === "select-one";
+
+                                       // Nothing was selected
+                                       if ( index < 0 ) {
+                                               return null;
+                                       }
+
+                                       // Loop through all the selected options
+                                       for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
+                                               var option = options[ i ];
+
+                                               if ( option.selected ) {
+                                                       // Get the specifc value for the option
+                                                       value = jQuery(option).val();
+
+                                                       // We don't need an array for one selects
+                                                       if ( one ) {
+                                                               return value;
+                                                       }
+
+                                                       // Multi-Selects return an array
+                                                       values.push( value );
+                                               }
+                                       }
+
+                                       return values;
+                               }
+
+                               // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+                               if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
+                                       return elem.getAttribute("value") === null ? "on" : elem.value;
+                               }
+                               
+
+                               // Everything else, we just grab the value
+                               return (elem.value || "").replace(rreturn, "");
+
+                       }
+
+                       return undefined;
+               }
+
+               var isFunction = jQuery.isFunction(value);
+
+               return this.each(function(i) {
+                       var self = jQuery(this), val = value;
+
+                       if ( this.nodeType !== 1 ) {
+                               return;
+                       }
+
+                       if ( isFunction ) {
+                               val = value.call(this, i, self.val());
+                       }
+
+                       // Typecast each time if the value is a Function and the appended
+                       // value is therefore different each time.
+                       if ( typeof val === "number" ) {
+                               val += "";
+                       }
+
+                       if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
+                               this.checked = jQuery.inArray( self.val(), val ) >= 0;
+
+                       } else if ( jQuery.nodeName( this, "select" ) ) {
+                               var values = jQuery.makeArray(val);
+
+                               jQuery( "option", this ).each(function() {
+                                       this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+                               });
+
+                               if ( !values.length ) {
+                                       this.selectedIndex = -1;
+                               }
+
+                       } else {
+                               this.value = val;
+                       }
+               });
+       }
+});
+
+jQuery.extend({
+       attrFn: {
+               val: true,
+               css: true,
+               html: true,
+               text: true,
+               data: true,
+               width: true,
+               height: true,
+               offset: true
+       },
+               
+       attr: function( elem, name, value, pass ) {
+               // don't set attributes on text and comment nodes
+               if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+                       return undefined;
+               }
+
+               if ( pass && name in jQuery.attrFn ) {
+                       return jQuery(elem)[name](value);
+               }
+
+               var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
+                       // Whether we are setting (or getting)
+                       set = value !== undefined;
+
+               // Try to normalize/fix the name
+               name = notxml && jQuery.props[ name ] || name;
+
+               // Only do all the following if this is a node (faster for style)
+               if ( elem.nodeType === 1 ) {
+                       // These attributes require special treatment
+                       var special = rspecialurl.test( name );
+
+                       // Safari mis-reports the default selected property of an option
+                       // Accessing the parent's selectedIndex property fixes it
+                       if ( name === "selected" && !jQuery.support.optSelected ) {
+                               var parent = elem.parentNode;
+                               if ( parent ) {
+                                       parent.selectedIndex;
+       
+                                       // Make sure that it also works with optgroups, see #5701
+                                       if ( parent.parentNode ) {
+                                               parent.parentNode.selectedIndex;
+                                       }
+                               }
+                       }
+
+                       // If applicable, access the attribute via the DOM 0 way
+                       if ( name in elem && notxml && !special ) {
+                               if ( set ) {
+                                       // We can't allow the type property to be changed (since it causes problems in IE)
+                                       if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
+                                               jQuery.error( "type property can't be changed" );
+                                       }
+
+                                       elem[ name ] = value;
+                               }
+
+                               // browsers index elements by id/name on forms, give priority to attributes.
+                               if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
+                                       return elem.getAttributeNode( name ).nodeValue;
+                               }
+
+                               // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+                               // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+                               if ( name === "tabIndex" ) {
+                                       var attributeNode = elem.getAttributeNode( "tabIndex" );
+
+                                       return attributeNode && attributeNode.specified ?
+                                               attributeNode.value :
+                                               rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+                                                       0 :
+                                                       undefined;
+                               }
+
+                               return elem[ name ];
+                       }
+
+                       if ( !jQuery.support.style && notxml && name === "style" ) {
+                               if ( set ) {
+                                       elem.style.cssText = "" + value;
+                               }
+
+                               return elem.style.cssText;
+                       }
+
+                       if ( set ) {
+                               // convert the value to a string (all browsers do this but IE) see #1070
+                               elem.setAttribute( name, "" + value );
+                       }
+
+                       var attr = !jQuery.support.hrefNormalized && notxml && special ?
+                                       // Some attributes require a special call on IE
+                                       elem.getAttribute( name, 2 ) :
+                                       elem.getAttribute( name );
+
+                       // Non-existent attributes return null, we normalize to undefined
+                       return attr === null ? undefined : attr;
+               }
+
+               // elem is actually elem.style ... set the style
+               // Using attr for specific style information is now deprecated. Use style instead.
+               return jQuery.style( elem, name, value );
+       }
+});
+var rnamespaces = /\.(.*)$/,
+       fcleanup = function( nm ) {
+               return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
+                       return "\\" + ch;
+               });
+       };
+
+/*
+ * A number of helper functions used for managing events.
+ * Many of the ideas behind this code originated from
+ * Dean Edwards' addEvent library.
+ */
+jQuery.event = {
+
+       // Bind an event to an element
+       // Original by Dean Edwards
+       add: function( elem, types, handler, data ) {
+               if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+                       return;
+               }
+
+               // For whatever reason, IE has trouble passing the window object
+               // around, causing it to be cloned in the process
+               if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
+                       elem = window;
+               }
+
+               var handleObjIn, handleObj;
+
+               if ( handler.handler ) {
+                       handleObjIn = handler;
+                       handler = handleObjIn.handler;
+               }
+
+               // Make sure that the function being executed has a unique ID
+               if ( !handler.guid ) {
+                       handler.guid = jQuery.guid++;
+               }
+
+               // Init the element's event structure
+               var elemData = jQuery.data( elem );
+
+               // If no elemData is found then we must be trying to bind to one of the
+               // banned noData elements
+               if ( !elemData ) {
+                       return;
+               }
+
+               var events = elemData.events = elemData.events || {},
+                       eventHandle = elemData.handle, eventHandle;
+
+               if ( !eventHandle ) {
+                       elemData.handle = eventHandle = function() {
+                               // Handle the second event of a trigger and when
+                               // an event is called after a page has unloaded
+                               return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
+                                       jQuery.event.handle.apply( eventHandle.elem, arguments ) :
+                                       undefined;
+                       };
+               }
+
+               // Add elem as a property of the handle function
+               // This is to prevent a memory leak with non-native events in IE.
+               eventHandle.elem = elem;
+
+               // Handle multiple events separated by a space
+               // jQuery(...).bind("mouseover mouseout", fn);
+               types = types.split(" ");
+
+               var type, i = 0, namespaces;
+
+               while ( (type = types[ i++ ]) ) {
+                       handleObj = handleObjIn ?
+                               jQuery.extend({}, handleObjIn) :
+                               { handler: handler, data: data };
+
+                       // Namespaced event handlers
+                       if ( type.indexOf(".") > -1 ) {
+                               namespaces = type.split(".");
+                               type = namespaces.shift();
+                               handleObj.namespace = namespaces.slice(0).sort().join(".");
+
+                       } else {
+                               namespaces = [];
+                               handleObj.namespace = "";
+                       }
+
+                       handleObj.type = type;
+                       handleObj.guid = handler.guid;
+
+                       // Get the current list of functions bound to this event
+                       var handlers = events[ type ],
+                               special = jQuery.event.special[ type ] || {};
+
+                       // Init the event handler queue
+                       if ( !handlers ) {
+                               handlers = events[ type ] = [];
+
+                               // Check for a special event handler
+                               // Only use addEventListener/attachEvent if the special
+                               // events handler returns false
+                               if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+                                       // Bind the global event handler to the element
+                                       if ( elem.addEventListener ) {
+                                               elem.addEventListener( type, eventHandle, false );
+
+                                       } else if ( elem.attachEvent ) {
+                                               elem.attachEvent( "on" + type, eventHandle );
+                                       }
+                               }
+                       }
+                       
+                       if ( special.add ) { 
+                               special.add.call( elem, handleObj ); 
+
+                               if ( !handleObj.handler.guid ) {
+                                       handleObj.handler.guid = handler.guid;
+                               }
+                       }
+
+                       // Add the function to the element's handler list
+                       handlers.push( handleObj );
+
+                       // Keep track of which events have been used, for global triggering
+                       jQuery.event.global[ type ] = true;
+               }
+
+               // Nullify elem to prevent memory leaks in IE
+               elem = null;
+       },
+
+       global: {},
+
+       // Detach an event or set of events from an element
+       remove: function( elem, types, handler, pos ) {
+               // don't do events on text and comment nodes
+               if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+                       return;
+               }
+
+               var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
+                       elemData = jQuery.data( elem ),
+                       events = elemData && elemData.events;
+
+               if ( !elemData || !events ) {
+                       return;
+               }
+
+               // types is actually an event object here
+               if ( types && types.type ) {
+                       handler = types.handler;
+                       types = types.type;
+               }
+
+               // Unbind all events for the element
+               if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
+                       types = types || "";
+
+                       for ( type in events ) {
+                               jQuery.event.remove( elem, type + types );
+                       }
+
+                       return;
+               }
+
+               // Handle multiple events separated by a space
+               // jQuery(...).unbind("mouseover mouseout", fn);
+               types = types.split(" ");
+
+               while ( (type = types[ i++ ]) ) {
+                       origType = type;
+                       handleObj = null;
+                       all = type.indexOf(".") < 0;
+                       namespaces = [];
+
+                       if ( !all ) {
+                               // Namespaced event handlers
+                               namespaces = type.split(".");
+                               type = namespaces.shift();
+
+                               namespace = new RegExp("(^|\\.)" + 
+                                       jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
+                       }
+
+                       eventType = events[ type ];
+
+                       if ( !eventType ) {
+                               continue;
+                       }
+
+                       if ( !handler ) {
+                               for ( var j = 0; j < eventType.length; j++ ) {
+                                       handleObj = eventType[ j ];
+
+                                       if ( all || namespace.test( handleObj.namespace ) ) {
+                                               jQuery.event.remove( elem, origType, handleObj.handler, j );
+                                               eventType.splice( j--, 1 );
+                                       }
+                               }
+
+                               continue;
+                       }
+
+                       special = jQuery.event.special[ type ] || {};
+
+                       for ( var j = pos || 0; j < eventType.length; j++ ) {
+                               handleObj = eventType[ j ];
+
+                               if ( handler.guid === handleObj.guid ) {
+                                       // remove the given handler for the given type
+                                       if ( all || namespace.test( handleObj.namespace ) ) {
+                                               if ( pos == null ) {
+                                                       eventType.splice( j--, 1 );
+                                               }
+
+                                               if ( special.remove ) {
+                                                       special.remove.call( elem, handleObj );
+                                               }
+                                       }
+
+                                       if ( pos != null ) {
+                                               break;
+                                       }
+                               }
+                       }
+
+                       // remove generic event handler if no more handlers exist
+                       if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
+                               if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+                                       removeEvent( elem, type, elemData.handle );
+                               }
+
+                               ret = null;
+                               delete events[ type ];
+                       }
+               }
+
+               // Remove the expando if it's no longer used
+               if ( jQuery.isEmptyObject( events ) ) {
+                       var handle = elemData.handle;
+                       if ( handle ) {
+                               handle.elem = null;
+                       }
+
+                       delete elemData.events;
+                       delete elemData.handle;
+
+                       if ( jQuery.isEmptyObject( elemData ) ) {
+                               jQuery.removeData( elem );
+                       }
+               }
+       },
+
+       // bubbling is internal
+       trigger: function( event, data, elem /*, bubbling */ ) {
+               // Event object or event type
+               var type = event.type || event,
+                       bubbling = arguments[3];
+
+               if ( !bubbling ) {
+                       event = typeof event === "object" ?
+                               // jQuery.Event object
+                               event[expando] ? event :
+                               // Object literal
+                               jQuery.extend( jQuery.Event(type), event ) :
+                               // Just the event type (string)
+                               jQuery.Event(type);
+
+                       if ( type.indexOf("!") >= 0 ) {
+                               event.type = type = type.slice(0, -1);
+                               event.exclusive = true;
+                       }
+
+                       // Handle a global trigger
+                       if ( !elem ) {
+                               // Don't bubble custom events when global (to avoid too much overhead)
+                               event.stopPropagation();
+
+                               // Only trigger if we've ever bound an event for it
+                               if ( jQuery.event.global[ type ] ) {
+                                       jQuery.each( jQuery.cache, function() {
+                                               if ( this.events && this.events[type] ) {
+                                                       jQuery.event.trigger( event, data, this.handle.elem );
+                                               }
+                                       });
+                               }
+                       }
+
+                       // Handle triggering a single element
+
+                       // don't do events on text and comment nodes
+                       if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+                               return undefined;
+                       }
+
+                       // Clean up in case it is reused
+                       event.result = undefined;
+                       event.target = elem;
+
+                       // Clone the incoming data, if any
+                       data = jQuery.makeArray( data );
+                       data.unshift( event );
+               }
+
+               event.currentTarget = elem;
+
+               // Trigger the event, it is assumed that "handle" is a function
+               var handle = jQuery.data( elem, "handle" );
+               if ( handle ) {
+                       handle.apply( elem, data );
+               }
+
+               var parent = elem.parentNode || elem.ownerDocument;
+
+               // Trigger an inline bound script
+               try {
+                       if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
+                               if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
+                                       event.result = false;
+                               }
+                       }
+
+               // prevent IE from throwing an error for some elements with some event types, see #3533
+               } catch (e) {}
+
+               if ( !event.isPropagationStopped() && parent ) {
+                       jQuery.event.trigger( event, data, parent, true );
+
+               } else if ( !event.isDefaultPrevented() ) {
+                       var target = event.target, old,
+                               isClick = jQuery.nodeName(target, "a") && type === "click",
+                               special = jQuery.event.special[ type ] || {};
+
+                       if ( (!special._default || special._default.call( elem, event ) === false) && 
+                               !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
+
+                               try {
+                                       if ( target[ type ] ) {
+                                               // Make sure that we don't accidentally re-trigger the onFOO events
+                                               old = target[ "on" + type ];
+
+                                               if ( old ) {
+                                                       target[ "on" + type ] = null;
+                                               }
+
+                                               jQuery.event.triggered = true;
+                                               target[ type ]();
+                                       }
+
+                               // prevent IE from throwing an error for some elements with some event types, see #3533
+                               } catch (e) {}
+
+                               if ( old ) {
+                                       target[ "on" + type ] = old;
+                               }
+
+                               jQuery.event.triggered = false;
+                       }
+               }
+       },
+
+       handle: function( event ) {
+               var all, handlers, namespaces, namespace, events;
+
+               event = arguments[0] = jQuery.event.fix( event || window.event );
+               event.currentTarget = this;
+
+               // Namespaced event handlers
+               all = event.type.indexOf(".") < 0 && !event.exclusive;
+
+               if ( !all ) {
+                       namespaces = event.type.split(".");
+                       event.type = namespaces.shift();
+                       namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
+               }
+
+               var events = jQuery.data(this, "events"), handlers = events[ event.type ];
+
+               if ( events && handlers ) {
+                       // Clone the handlers to prevent manipulation
+                       handlers = handlers.slice(0);
+
+                       for ( var j = 0, l = handlers.length; j < l; j++ ) {
+                               var handleObj = handlers[ j ];
+
+                               // Filter the functions by class
+                               if ( all || namespace.test( handleObj.namespace ) ) {
+                                       // Pass in a reference to the handler function itself
+                                       // So that we can later remove it
+                                       event.handler = handleObj.handler;
+                                       event.data = handleObj.data;
+                                       event.handleObj = handleObj;
+       
+                                       var ret = handleObj.handler.apply( this, arguments );
+
+                                       if ( ret !== undefined ) {
+                                               event.result = ret;
+                                               if ( ret === false ) {
+                                                       event.preventDefault();
+                                                       event.stopPropagation();
+                                               }
+                                       }
+
+                                       if ( event.isImmediatePropagationStopped() ) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               return event.result;
+       },
+
+       props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+
+       fix: function( event ) {
+               if ( event[ expando ] ) {
+                       return event;
+               }
+
+               // store a copy of the original event object
+               // and "clone" to set read-only properties
+               var originalEvent = event;
+               event = jQuery.Event( originalEvent );
+
+               for ( var i = this.props.length, prop; i; ) {
+                       prop = this.props[ --i ];
+                       event[ prop ] = originalEvent[ prop ];
+               }
+
+               // Fix target property, if necessary
+               if ( !event.target ) {
+                       event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
+               }
+
+               // check if target is a textnode (safari)
+               if ( event.target.nodeType === 3 ) {
+                       event.target = event.target.parentNode;
+               }
+
+               // Add relatedTarget, if necessary
+               if ( !event.relatedTarget && event.fromElement ) {
+                       event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
+               }
+
+               // Calculate pageX/Y if missing and clientX/Y available
+               if ( event.pageX == null && event.clientX != null ) {
+                       var doc = document.documentElement, body = document.body;
+                       event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
+                       event.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
+               }
+
+               // Add which for key events
+               if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
+                       event.which = event.charCode || event.keyCode;
+               }
+
+               // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
+               if ( !event.metaKey && event.ctrlKey ) {
+                       event.metaKey = event.ctrlKey;
+               }
+
+               // Add which for click: 1 === left; 2 === middle; 3 === right
+               // Note: button is not normalized, so don't use it
+               if ( !event.which && event.button !== undefined ) {
+                       event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
+               }
+
+               return event;
+       },
+
+       // Deprecated, use jQuery.guid instead
+       guid: 1E8,
+
+       // Deprecated, use jQuery.proxy instead
+       proxy: jQuery.proxy,
+
+       special: {
+               ready: {
+                       // Make sure the ready event is setup
+                       setup: jQuery.bindReady,
+                       teardown: jQuery.noop
+               },
+
+               live: {
+                       add: function( handleObj ) {
+                               jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); 
+                       },
+
+                       remove: function( handleObj ) {
+                               var remove = true,
+                                       type = handleObj.origType.replace(rnamespaces, "");
+                               
+                               jQuery.each( jQuery.data(this, "events").live || [], function() {
+                                       if ( type === this.origType.replace(rnamespaces, "") ) {
+                                               remove = false;
+                                               return false;
+                                       }
+                               });
+
+                               if ( remove ) {
+                                       jQuery.event.remove( this, handleObj.origType, liveHandler );
+                               }
+                       }
+
+               },
+
+               beforeunload: {
+                       setup: function( data, namespaces, eventHandle ) {
+                               // We only want to do this special case on windows
+                               if ( this.setInterval ) {
+                                       this.onbeforeunload = eventHandle;
+                               }
+
+                               return false;
+                       },
+                       teardown: function( namespaces, eventHandle ) {
+                               if ( this.onbeforeunload === eventHandle ) {
+                                       this.onbeforeunload = null;
+                               }
+                       }
+               }
+       }
+};
+
+var removeEvent = document.removeEventListener ?
+       function( elem, type, handle ) {
+               elem.removeEventListener( type, handle, false );
+       } : 
+       function( elem, type, handle ) {
+               elem.detachEvent( "on" + type, handle );
+       };
+
+jQuery.Event = function( src ) {
+       // Allow instantiation without the 'new' keyword
+       if ( !this.preventDefault ) {
+               return new jQuery.Event( src );
+       }
+
+       // Event object
+       if ( src && src.type ) {
+               this.originalEvent = src;
+               this.type = src.type;
+       // Event type
+       } else {
+               this.type = src;
+       }
+
+       // timeStamp is buggy for some events on Firefox(#3843)
+       // So we won't rely on the native value
+       this.timeStamp = now();
+
+       // Mark it as fixed
+       this[ expando ] = true;
+};
+
+function returnFalse() {
+       return false;
+}
+function returnTrue() {
+       return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+       preventDefault: function() {
+               this.isDefaultPrevented = returnTrue;
+
+               var e = this.originalEvent;
+               if ( !e ) {
+                       return;
+               }
+               
+               // if preventDefault exists run it on the original event
+               if ( e.preventDefault ) {
+                       e.preventDefault();
+               }
+               // otherwise set the returnValue property of the original event to false (IE)
+               e.returnValue = false;
+       },
+       stopPropagation: function() {
+               this.isPropagationStopped = returnTrue;
+
+               var e = this.originalEvent;
+               if ( !e ) {
+                       return;
+               }
+               // if stopPropagation exists run it on the original event
+               if ( e.stopPropagation ) {
+                       e.stopPropagation();
+               }
+               // otherwise set the cancelBubble property of the original event to true (IE)
+               e.cancelBubble = true;
+       },
+       stopImmediatePropagation: function() {
+               this.isImmediatePropagationStopped = returnTrue;
+               this.stopPropagation();
+       },
+       isDefaultPrevented: returnFalse,
+       isPropagationStopped: returnFalse,
+       isImmediatePropagationStopped: returnFalse
+};
+
+// Checks if an event happened on an element within another element
+// Used in jQuery.event.special.mouseenter and mouseleave handlers
+var withinElement = function( event ) {
+       // Check if mouse(over|out) are still within the same parent element
+       var parent = event.relatedTarget;
+
+       // Firefox sometimes assigns relatedTarget a XUL element
+       // which we cannot access the parentNode property of
+       try {
+               // Traverse up the tree
+               while ( parent && parent !== this ) {
+                       parent = parent.parentNode;
+               }
+
+               if ( parent !== this ) {
+                       // set the correct event type
+                       event.type = event.data;
+
+                       // handle event if we actually just moused on to a non sub-element
+                       jQuery.event.handle.apply( this, arguments );
+               }
+
+       // assuming we've left the element since we most likely mousedover a xul element
+       } catch(e) { }
+},
+
+// In case of event delegation, we only need to rename the event.type,
+// liveHandler will take care of the rest.
+delegate = function( event ) {
+       event.type = event.data;
+       jQuery.event.handle.apply( this, arguments );
+};
+
+// Create mouseenter and mouseleave events
+jQuery.each({
+       mouseenter: "mouseover",
+       mouseleave: "mouseout"
+}, function( orig, fix ) {
+       jQuery.event.special[ orig ] = {
+               setup: function( data ) {
+                       jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
+               },
+               teardown: function( data ) {
+                       jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
+               }
+       };
+});
+
+// submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+       jQuery.event.special.submit = {
+               setup: function( data, namespaces ) {
+                       if ( this.nodeName.toLowerCase() !== "form" ) {
+                               jQuery.event.add(this, "click.specialSubmit", function( e ) {
+                                       var elem = e.target, type = elem.type;
+
+                                       if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
+                                               return trigger( "submit", this, arguments );
+                                       }
+                               });
+        
+                               jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
+                                       var elem = e.target, type = elem.type;
+
+                                       if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
+                                               return trigger( "submit", this, arguments );
+                                       }
+                               });
+
+                       } else {
+                               return false;
+                       }
+               },
+
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialSubmit" );
+               }
+       };
+
+}
+
+// change delegation, happens here so we have bind.
+if ( !jQuery.support.changeBubbles ) {
+
+       var formElems = /textarea|input|select/i,
+
+       changeFilters,
+
+       getVal = function( elem ) {
+               var type = elem.type, val = elem.value;
+
+               if ( type === "radio" || type === "checkbox" ) {
+                       val = elem.checked;
+
+               } else if ( type === "select-multiple" ) {
+                       val = elem.selectedIndex > -1 ?
+                               jQuery.map( elem.options, function( elem ) {
+                                       return elem.selected;
+                               }).join("-") :
+                               "";
+
+               } else if ( elem.nodeName.toLowerCase() === "select" ) {
+                       val = elem.selectedIndex;
+               }
+
+               return val;
+       },
+
+       testChange = function testChange( e ) {
+               var elem = e.target, data, val;
+
+               if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
+                       return;
+               }
+
+               data = jQuery.data( elem, "_change_data" );
+               val = getVal(elem);
+
+               // the current data will be also retrieved by beforeactivate
+               if ( e.type !== "focusout" || elem.type !== "radio" ) {
+                       jQuery.data( elem, "_change_data", val );
+               }
+               
+               if ( data === undefined || val === data ) {
+                       return;
+               }
+
+               if ( data != null || val ) {
+                       e.type = "change";
+                       return jQuery.event.trigger( e, arguments[1], elem );
+               }
+       };
+
+       jQuery.event.special.change = {
+               filters: {
+                       focusout: testChange, 
+
+                       click: function( e ) {
+                               var elem = e.target, type = elem.type;
+
+                               if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
+                                       return testChange.call( this, e );
+                               }
+                       },
+
+                       // Change has to be called before submit
+                       // Keydown will be called before keypress, which is used in submit-event delegation
+                       keydown: function( e ) {
+                               var elem = e.target, type = elem.type;
+
+                               if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
+                                       (e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
+                                       type === "select-multiple" ) {
+                                       return testChange.call( this, e );
+                               }
+                       },
+
+                       // Beforeactivate happens also before the previous element is blurred
+                       // with this event you can't trigger a change event, but you can store
+                       // information/focus[in] is not needed anymore
+                       beforeactivate: function( e ) {
+                               var elem = e.target;
+                               jQuery.data( elem, "_change_data", getVal(elem) );
+                       }
+               },
+
+               setup: function( data, namespaces ) {
+                       if ( this.type === "file" ) {
+                               return false;
+                       }
+
+                       for ( var type in changeFilters ) {
+                               jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
+                       }
+
+                       return formElems.test( this.nodeName );
+               },
+
+               teardown: function( namespaces ) {
+                       jQuery.event.remove( this, ".specialChange" );
+
+                       return formElems.test( this.nodeName );
+               }
+       };
+
+       changeFilters = jQuery.event.special.change.filters;
+}
+
+function trigger( type, elem, args ) {
+       args[0].type = type;
+       return jQuery.event.handle.apply( elem, args );
+}
+
+// Create "bubbling" focus and blur events
+if ( document.addEventListener ) {
+       jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+               jQuery.event.special[ fix ] = {
+                       setup: function() {
+                               this.addEventListener( orig, handler, true );
+                       }, 
+                       teardown: function() { 
+                               this.removeEventListener( orig, handler, true );
+                       }
+               };
+
+               function handler( e ) { 
+                       e = jQuery.event.fix( e );
+                       e.type = fix;
+                       return jQuery.event.handle.call( this, e );
+               }
+       });
+}
+
+jQuery.each(["bind", "one"], function( i, name ) {
+       jQuery.fn[ name ] = function( type, data, fn ) {
+               // Handle object literals
+               if ( typeof type === "object" ) {
+                       for ( var key in type ) {
+                               this[ name ](key, data, type[key], fn);
+                       }
+                       return this;
+               }
+               
+               if ( jQuery.isFunction( data ) ) {
+                       fn = data;
+                       data = undefined;
+               }
+
+               var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
+                       jQuery( this ).unbind( event, handler );
+                       return fn.apply( this, arguments );
+               }) : fn;
+
+               if ( type === "unload" && name !== "one" ) {
+                       this.one( type, data, fn );
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.add( this[i], type, handler, data );
+                       }
+               }
+
+               return this;
+       };
+});
+
+jQuery.fn.extend({
+       unbind: function( type, fn ) {
+               // Handle object literals
+               if ( typeof type === "object" && !type.preventDefault ) {
+                       for ( var key in type ) {
+                               this.unbind(key, type[key]);
+                       }
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               jQuery.event.remove( this[i], type, fn );
+                       }
+               }
+
+               return this;
+       },
+       
+       delegate: function( selector, types, data, fn ) {
+               return this.live( types, data, fn, selector );
+       },
+       
+       undelegate: function( selector, types, fn ) {
+               if ( arguments.length === 0 ) {
+                               return this.unbind( "live" );
+               
+               } else {
+                       return this.die( types, null, fn, selector );
+               }
+       },
+       
+       trigger: function( type, data ) {
+               return this.each(function() {
+                       jQuery.event.trigger( type, data, this );
+               });
+       },
+
+       triggerHandler: function( type, data ) {
+               if ( this[0] ) {
+                       var event = jQuery.Event( type );
+                       event.preventDefault();
+                       event.stopPropagation();
+                       jQuery.event.trigger( event, data, this[0] );
+                       return event.result;
+               }
+       },
+
+       toggle: function( fn ) {
+               // Save reference to arguments for access in closure
+               var args = arguments, i = 1;
+
+               // link all the functions, so any of them can unbind this click handler
+               while ( i < args.length ) {
+                       jQuery.proxy( fn, args[ i++ ] );
+               }
+
+               return this.click( jQuery.proxy( fn, function( event ) {
+                       // Figure out which function to execute
+                       var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+                       jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+                       // Make sure that clicks stop
+                       event.preventDefault();
+
+                       // and execute the function
+                       return args[ lastToggle ].apply( this, arguments ) || false;
+               }));
+       },
+
+       hover: function( fnOver, fnOut ) {
+               return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+       }
+});
+
+var liveMap = {
+       focus: "focusin",
+       blur: "focusout",
+       mouseenter: "mouseover",
+       mouseleave: "mouseout"
+};
+
+jQuery.each(["live", "die"], function( i, name ) {
+       jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
+               var type, i = 0, match, namespaces, preType,
+                       selector = origSelector || this.selector,
+                       context = origSelector ? this : jQuery( this.context );
+
+               if ( jQuery.isFunction( data ) ) {
+                       fn = data;
+                       data = undefined;
+               }
+
+               types = (types || "").split(" ");
+
+               while ( (type = types[ i++ ]) != null ) {
+                       match = rnamespaces.exec( type );
+                       namespaces = "";
+
+                       if ( match )  {
+                               namespaces = match[0];
+                               type = type.replace( rnamespaces, "" );
+                       }
+
+                       if ( type === "hover" ) {
+                               types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
+                               continue;
+                       }
+
+                       preType = type;
+
+                       if ( type === "focus" || type === "blur" ) {
+                               types.push( liveMap[ type ] + namespaces );
+                               type = type + namespaces;
+
+                       } else {
+                               type = (liveMap[ type ] || type) + namespaces;
+                       }
+
+                       if ( name === "live" ) {
+                               // bind live handler
+                               context.each(function(){
+                                       jQuery.event.add( this, liveConvert( type, selector ),
+                                               { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
+                               });
+
+                       } else {
+                               // unbind live handler
+                               context.unbind( liveConvert( type, selector ), fn );
+                       }
+               }
+               
+               return this;
+       }
+});
+
+function liveHandler( event ) {
+       var stop, elems = [], selectors = [], args = arguments,
+               related, match, handleObj, elem, j, i, l, data,
+               events = jQuery.data( this, "events" );
+
+       // Make sure we avoid non-left-click bubbling in Firefox (#3861)
+       if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
+               return;
+       }
+
+       event.liveFired = this;
+
+       var live = events.live.slice(0);
+
+       for ( j = 0; j < live.length; j++ ) {
+               handleObj = live[j];
+
+               if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
+                       selectors.push( handleObj.selector );
+
+               } else {
+                       live.splice( j--, 1 );
+               }
+       }
+
+       match = jQuery( event.target ).closest( selectors, event.currentTarget );
+
+       for ( i = 0, l = match.length; i < l; i++ ) {
+               for ( j = 0; j < live.length; j++ ) {
+                       handleObj = live[j];
+
+                       if ( match[i].selector === handleObj.selector ) {
+                               elem = match[i].elem;
+                               related = null;
+
+                               // Those two events require additional checking
+                               if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
+                                       related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
+                               }
+
+                               if ( !related || related !== elem ) {
+                                       elems.push({ elem: elem, handleObj: handleObj });
+                               }
+                       }
+               }
+       }
+
+       for ( i = 0, l = elems.length; i < l; i++ ) {
+               match = elems[i];
+               event.currentTarget = match.elem;
+               event.data = match.handleObj.data;
+               event.handleObj = match.handleObj;
+
+               if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
+                       stop = false;
+                       break;
+               }
+       }
+
+       return stop;
+}
+
+function liveConvert( type, selector ) {
+       return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
+}
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+       "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+       "change select submit keydown keypress keyup error").split(" "), function( i, name ) {
+
+       // Handle event binding
+       jQuery.fn[ name ] = function( fn ) {
+               return fn ? this.bind( name, fn ) : this.trigger( name );
+       };
+
+       if ( jQuery.attrFn ) {
+               jQuery.attrFn[ name ] = true;
+       }
+});
+
+// Prevent memory leaks in IE
+// Window isn't included so as not to unbind existing unload events
+// More info:
+//  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
+if ( window.attachEvent && !window.addEventListener ) {
+       window.attachEvent("onunload", function() {
+               for ( var id in jQuery.cache ) {
+                       if ( jQuery.cache[ id ].handle ) {
+                               // Try/Catch is to handle iframes being unloaded, see #4280
+                               try {
+                                       jQuery.event.remove( jQuery.cache[ id ].handle.elem );
+                               } catch(e) {}
+                       }
+               }
+       });
+}
+/*!
+ * Sizzle CSS Selector Engine - v1.0
+ *  Copyright 2009, The Dojo Foundation
+ *  Released under the MIT, BSD, and GPL Licenses.
+ *  More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+       done = 0,
+       toString = Object.prototype.toString,
+       hasDuplicate = false,
+       baseHasDuplicate = true;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+//   Thus far that includes Google Chrome.
+[0, 0].sort(function(){
+       baseHasDuplicate = false;
+       return 0;
+});
+
+var Sizzle = function(selector, context, results, seed) {
+       results = results || [];
+       var origContext = context = context || document;
+
+       if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+               return [];
+       }
+       
+       if ( !selector || typeof selector !== "string" ) {
+               return results;
+       }
+
+       var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
+               soFar = selector;
+       
+       // Reset the position of the chunker regexp (start from head)
+       while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
+               soFar = m[3];
+               
+               parts.push( m[1] );
+               
+               if ( m[2] ) {
+                       extra = m[3];
+                       break;
+               }
+       }
+
+       if ( parts.length > 1 && origPOS.exec( selector ) ) {
+               if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+                       set = posProcess( parts[0] + parts[1], context );
+               } else {
+                       set = Expr.relative[ parts[0] ] ?
+                               [ context ] :
+                               Sizzle( parts.shift(), context );
+
+                       while ( parts.length ) {
+                               selector = parts.shift();
+
+                               if ( Expr.relative[ selector ] ) {
+                                       selector += parts.shift();
+                               }
+                               
+                               set = posProcess( selector, set );
+                       }
+               }
+       } else {
+               // Take a shortcut and set the context if the root selector is an ID
+               // (but not if it'll be faster if the inner selector is an ID)
+               if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+                               Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+                       var ret = Sizzle.find( parts.shift(), context, contextXML );
+                       context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
+               }
+
+               if ( context ) {
+                       var ret = seed ?
+                               { expr: parts.pop(), set: makeArray(seed) } :
+                               Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+                       set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
+
+                       if ( parts.length > 0 ) {
+                               checkSet = makeArray(set);
+                       } else {
+                               prune = false;
+                       }
+
+                       while ( parts.length ) {
+                               var cur = parts.pop(), pop = cur;
+
+                               if ( !Expr.relative[ cur ] ) {
+                                       cur = "";
+                               } else {
+                                       pop = parts.pop();
+                               }
+
+                               if ( pop == null ) {
+                                       pop = context;
+                               }
+
+                               Expr.relative[ cur ]( checkSet, pop, contextXML );
+                       }
+               } else {
+                       checkSet = parts = [];
+               }
+       }
+
+       if ( !checkSet ) {
+               checkSet = set;
+       }
+
+       if ( !checkSet ) {
+               Sizzle.error( cur || selector );
+       }
+
+       if ( toString.call(checkSet) === "[object Array]" ) {
+               if ( !prune ) {
+                       results.push.apply( results, checkSet );
+               } else if ( context && context.nodeType === 1 ) {
+                       for ( var i = 0; checkSet[i] != null; i++ ) {
+                               if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
+                                       results.push( set[i] );
+                               }
+                       }
+               } else {
+                       for ( var i = 0; checkSet[i] != null; i++ ) {
+                               if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+                                       results.push( set[i] );
+                               }
+                       }
+               }
+       } else {
+               makeArray( checkSet, results );
+       }
+
+       if ( extra ) {
+               Sizzle( extra, origContext, results, seed );
+               Sizzle.uniqueSort( results );
+       }
+
+       return results;
+};
+
+Sizzle.uniqueSort = function(results){
+       if ( sortOrder ) {
+               hasDuplicate = baseHasDuplicate;
+               results.sort(sortOrder);
+
+               if ( hasDuplicate ) {
+                       for ( var i = 1; i < results.length; i++ ) {
+                               if ( results[i] === results[i-1] ) {
+                                       results.splice(i--, 1);
+                               }
+                       }
+               }
+       }
+
+       return results;
+};
+
+Sizzle.matches = function(expr, set){
+       return Sizzle(expr, null, null, set);
+};
+
+Sizzle.find = function(expr, context, isXML){
+       var set, match;
+
+       if ( !expr ) {
+               return [];
+       }
+
+       for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
+               var type = Expr.order[i], match;
+               
+               if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+                       var left = match[1];
+                       match.splice(1,1);
+
+                       if ( left.substr( left.length - 1 ) !== "\\" ) {
+                               match[1] = (match[1] || "").replace(/\\/g, "");
+                               set = Expr.find[ type ]( match, context, isXML );
+                               if ( set != null ) {
+                                       expr = expr.replace( Expr.match[ type ], "" );
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       if ( !set ) {
+               set = context.getElementsByTagName("*");
+       }
+
+       return {set: set, expr: expr};
+};
+
+Sizzle.filter = function(expr, set, inplace, not){
+       var old = expr, result = [], curLoop = set, match, anyFound,
+               isXMLFilter = set && set[0] && isXML(set[0]);
+
+       while ( expr && set.length ) {
+               for ( var type in Expr.filter ) {
+                       if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+                               var filter = Expr.filter[ type ], found, item, left = match[1];
+                               anyFound = false;
+
+                               match.splice(1,1);
+
+                               if ( left.substr( left.length - 1 ) === "\\" ) {
+                                       continue;
+                               }
+
+                               if ( curLoop === result ) {
+                                       result = [];
+                               }
+
+                               if ( Expr.preFilter[ type ] ) {
+                                       match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+                                       if ( !match ) {
+                                               anyFound = found = true;
+                                       } else if ( match === true ) {
+                                               continue;
+                                       }
+                               }
+
+                               if ( match ) {
+                                       for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+                                               if ( item ) {
+                                                       found = filter( item, match, i, curLoop );
+                                                       var pass = not ^ !!found;
+
+                                                       if ( inplace && found != null ) {
+                                                               if ( pass ) {
+                                                                       anyFound = true;
+                                                               } else {
+                                                                       curLoop[i] = false;
+                                                               }
+                                                       } else if ( pass ) {
+                                                               result.push( item );
+                                                               anyFound = true;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               if ( found !== undefined ) {
+                                       if ( !inplace ) {
+                                               curLoop = result;
+                                       }
+
+                                       expr = expr.replace( Expr.match[ type ], "" );
+
+                                       if ( !anyFound ) {
+                                               return [];
+                                       }
+
+                                       break;
+                               }
+                       }
+               }
+
+               // Improper expression
+               if ( expr === old ) {
+                       if ( anyFound == null ) {
+                               Sizzle.error( expr );
+                       } else {
+                               break;
+                       }
+               }
+
+               old = expr;
+       }
+
+       return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+       throw "Syntax error, unrecognized expression: " + msg;
+};
+
+var Expr = Sizzle.selectors = {
+       order: [ "ID", "NAME", "TAG" ],
+       match: {
+               ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+               CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+               NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
+               ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+               TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
+               CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
+               POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
+               PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+       },
+       leftMatch: {},
+       attrMap: {
+               "class": "className",
+               "for": "htmlFor"
+       },
+       attrHandle: {
+               href: function(elem){
+                       return elem.getAttribute("href");
+               }
+       },
+       relative: {
+               "+": function(checkSet, part){
+                       var isPartStr = typeof part === "string",
+                               isTag = isPartStr && !/\W/.test(part),
+                               isPartStrNotTag = isPartStr && !isTag;
+
+                       if ( isTag ) {
+                               part = part.toLowerCase();
+                       }
+
+                       for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+                               if ( (elem = checkSet[i]) ) {
+                                       while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+                                       checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+                                               elem || false :
+                                               elem === part;
+                               }
+                       }
+
+                       if ( isPartStrNotTag ) {
+                               Sizzle.filter( part, checkSet, true );
+                       }
+               },
+               ">": function(checkSet, part){
+                       var isPartStr = typeof part === "string";
+
+                       if ( isPartStr && !/\W/.test(part) ) {
+                               part = part.toLowerCase();
+
+                               for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+                                       var elem = checkSet[i];
+                                       if ( elem ) {
+                                               var parent = elem.parentNode;
+                                               checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+                                       }
+                               }
+                       } else {
+                               for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+                                       var elem = checkSet[i];
+                                       if ( elem ) {
+                                               checkSet[i] = isPartStr ?
+                                                       elem.parentNode :
+                                                       elem.parentNode === part;
+                                       }
+                               }
+
+                               if ( isPartStr ) {
+                                       Sizzle.filter( part, checkSet, true );
+                               }
+                       }
+               },
+               "": function(checkSet, part, isXML){
+                       var doneName = done++, checkFn = dirCheck;
+
+                       if ( typeof part === "string" && !/\W/.test(part) ) {
+                               var nodeCheck = part = part.toLowerCase();
+                               checkFn = dirNodeCheck;
+                       }
+
+                       checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+               },
+               "~": function(checkSet, part, isXML){
+                       var doneName = done++, checkFn = dirCheck;
+
+                       if ( typeof part === "string" && !/\W/.test(part) ) {
+                               var nodeCheck = part = part.toLowerCase();
+                               checkFn = dirNodeCheck;
+                       }
+
+                       checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+               }
+       },
+       find: {
+               ID: function(match, context, isXML){
+                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
+                               var m = context.getElementById(match[1]);
+                               return m ? [m] : [];
+                       }
+               },
+               NAME: function(match, context){
+                       if ( typeof context.getElementsByName !== "undefined" ) {
+                               var ret = [], results = context.getElementsByName(match[1]);
+
+                               for ( var i = 0, l = results.length; i < l; i++ ) {
+                                       if ( results[i].getAttribute("name") === match[1] ) {
+                                               ret.push( results[i] );
+                                       }
+                               }
+
+                               return ret.length === 0 ? null : ret;
+                       }
+               },
+               TAG: function(match, context){
+                       return context.getElementsByTagName(match[1]);
+               }
+       },
+       preFilter: {
+               CLASS: function(match, curLoop, inplace, result, not, isXML){
+                       match = " " + match[1].replace(/\\/g, "") + " ";
+
+                       if ( isXML ) {
+                               return match;
+                       }
+
+                       for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+                               if ( elem ) {
+                                       if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
+                                               if ( !inplace ) {
+                                                       result.push( elem );
+                                               }
+                                       } else if ( inplace ) {
+                                               curLoop[i] = false;
+                                       }
+                               }
+                       }
+
+                       return false;
+               },
+               ID: function(match){
+                       return match[1].replace(/\\/g, "");
+               },
+               TAG: function(match, curLoop){
+                       return match[1].toLowerCase();
+               },
+               CHILD: function(match){
+                       if ( match[1] === "nth" ) {
+                               // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+                               var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+                                       match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+                                       !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+                               // calculate the numbers (first)n+(last) including if they are negative
+                               match[2] = (test[1] + (test[2] || 1)) - 0;
+                               match[3] = test[3] - 0;
+                       }
+
+                       // TODO: Move to normal caching system
+                       match[0] = done++;
+
+                       return match;
+               },
+               ATTR: function(match, curLoop, inplace, result, not, isXML){
+                       var name = match[1].replace(/\\/g, "");
+                       
+                       if ( !isXML && Expr.attrMap[name] ) {
+                               match[1] = Expr.attrMap[name];
+                       }
+
+                       if ( match[2] === "~=" ) {
+                               match[4] = " " + match[4] + " ";
+                       }
+
+                       return match;
+               },
+               PSEUDO: function(match, curLoop, inplace, result, not){
+                       if ( match[1] === "not" ) {
+                               // If we're dealing with a complex expression, or a simple one
+                               if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+                                       match[3] = Sizzle(match[3], null, null, curLoop);
+                               } else {
+                                       var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+                                       if ( !inplace ) {
+                                               result.push.apply( result, ret );
+                                       }
+                                       return false;
+                               }
+                       } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+                               return true;
+                       }
+                       
+                       return match;
+               },
+               POS: function(match){
+                       match.unshift( true );
+                       return match;
+               }
+       },
+       filters: {
+               enabled: function(elem){
+                       return elem.disabled === false && elem.type !== "hidden";
+               },
+               disabled: function(elem){
+                       return elem.disabled === true;
+               },
+               checked: function(elem){
+                       return elem.checked === true;
+               },
+               selected: function(elem){
+                       // Accessing this property makes selected-by-default
+                       // options in Safari work properly
+                       elem.parentNode.selectedIndex;
+                       return elem.selected === true;
+               },
+               parent: function(elem){
+                       return !!elem.firstChild;
+               },
+               empty: function(elem){
+                       return !elem.firstChild;
+               },
+               has: function(elem, i, match){
+                       return !!Sizzle( match[3], elem ).length;
+               },
+               header: function(elem){
+                       return /h\d/i.test( elem.nodeName );
+               },
+               text: function(elem){
+                       return "text" === elem.type;
+               },
+               radio: function(elem){
+                       return "radio" === elem.type;
+               },
+               checkbox: function(elem){
+                       return "checkbox" === elem.type;
+               },
+               file: function(elem){
+                       return "file" === elem.type;
+               },
+               password: function(elem){
+                       return "password" === elem.type;
+               },
+               submit: function(elem){
+                       return "submit" === elem.type;
+               },
+               image: function(elem){
+                       return "image" === elem.type;
+               },
+               reset: function(elem){
+                       return "reset" === elem.type;
+               },
+               button: function(elem){
+                       return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
+               },
+               input: function(elem){
+                       return /input|select|textarea|button/i.test(elem.nodeName);
+               }
+       },
+       setFilters: {
+               first: function(elem, i){
+                       return i === 0;
+               },
+               last: function(elem, i, match, array){
+                       return i === array.length - 1;
+               },
+               even: function(elem, i){
+                       return i % 2 === 0;
+               },
+               odd: function(elem, i){
+                       return i % 2 === 1;
+               },
+               lt: function(elem, i, match){
+                       return i < match[3] - 0;
+               },
+               gt: function(elem, i, match){
+                       return i > match[3] - 0;
+               },
+               nth: function(elem, i, match){
+                       return match[3] - 0 === i;
+               },
+               eq: function(elem, i, match){
+                       return match[3] - 0 === i;
+               }
+       },
+       filter: {
+               PSEUDO: function(elem, match, i, array){
+                       var name = match[1], filter = Expr.filters[ name ];
+
+                       if ( filter ) {
+                               return filter( elem, i, match, array );
+                       } else if ( name === "contains" ) {
+                               return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+                       } else if ( name === "not" ) {
+                               var not = match[3];
+
+                               for ( var i = 0, l = not.length; i < l; i++ ) {
+                                       if ( not[i] === elem ) {
+                                               return false;
+                                       }
+                               }
+
+                               return true;
+                       } else {
+                               Sizzle.error( "Syntax error, unrecognized expression: " + name );
+                       }
+               },
+               CHILD: function(elem, match){
+                       var type = match[1], node = elem;
+                       switch (type) {
+                               case 'only':
+                               case 'first':
+                                       while ( (node = node.previousSibling) )  {
+                                               if ( node.nodeType === 1 ) { 
+                                                       return false; 
+                                               }
+                                       }
+                                       if ( type === "first" ) { 
+                                               return true; 
+                                       }
+                                       node = elem;
+                               case 'last':
+                                       while ( (node = node.nextSibling) )      {
+                                               if ( node.nodeType === 1 ) { 
+                                                       return false; 
+                                               }
+                                       }
+                                       return true;
+                               case 'nth':
+                                       var first = match[2], last = match[3];
+
+                                       if ( first === 1 && last === 0 ) {
+                                               return true;
+                                       }
+                                       
+                                       var doneName = match[0],
+                                               parent = elem.parentNode;
+       
+                                       if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
+                                               var count = 0;
+                                               for ( node = parent.firstChild; node; node = node.nextSibling ) {
+                                                       if ( node.nodeType === 1 ) {
+                                                               node.nodeIndex = ++count;
+                                                       }
+                                               } 
+                                               parent.sizcache = doneName;
+                                       }
+                                       
+                                       var diff = elem.nodeIndex - last;
+                                       if ( first === 0 ) {
+                                               return diff === 0;
+                                       } else {
+                                               return ( diff % first === 0 && diff / first >= 0 );
+                                       }
+                       }
+               },
+               ID: function(elem, match){
+                       return elem.nodeType === 1 && elem.getAttribute("id") === match;
+               },
+               TAG: function(elem, match){
+                       return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+               },
+               CLASS: function(elem, match){
+                       return (" " + (elem.className || elem.getAttribute("class")) + " ")
+                               .indexOf( match ) > -1;
+               },
+               ATTR: function(elem, match){
+                       var name = match[1],
+                               result = Expr.attrHandle[ name ] ?
+                                       Expr.attrHandle[ name ]( elem ) :
+                                       elem[ name ] != null ?
+                                               elem[ name ] :
+                                               elem.getAttribute( name ),
+                               value = result + "",
+                               type = match[2],
+                               check = match[4];
+
+                       return result == null ?
+                               type === "!=" :
+                               type === "=" ?
+                               value === check :
+                               type === "*=" ?
+                               value.indexOf(check) >= 0 :
+                               type === "~=" ?
+                               (" " + value + " ").indexOf(check) >= 0 :
+                               !check ?
+                               value && result !== false :
+                               type === "!=" ?
+                               value !== check :
+                               type === "^=" ?
+                               value.indexOf(check) === 0 :
+                               type === "$=" ?
+                               value.substr(value.length - check.length) === check :
+                               type === "|=" ?
+                               value === check || value.substr(0, check.length + 1) === check + "-" :
+                               false;
+               },
+               POS: function(elem, match, i, array){
+                       var name = match[2], filter = Expr.setFilters[ name ];
+
+                       if ( filter ) {
+                               return filter( elem, i, match, array );
+                       }
+               }
+       }
+};
+
+var origPOS = Expr.match.POS;
+
+for ( var type in Expr.match ) {
+       Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
+       Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){
+               return "\\" + (num - 0 + 1);
+       }));
+}
+
+var makeArray = function(array, results) {
+       array = Array.prototype.slice.call( array, 0 );
+
+       if ( results ) {
+               results.push.apply( results, array );
+               return results;
+       }
+       
+       return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+       Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch(e){
+       makeArray = function(array, results) {
+               var ret = results || [];
+
+               if ( toString.call(array) === "[object Array]" ) {
+                       Array.prototype.push.apply( ret, array );
+               } else {
+                       if ( typeof array.length === "number" ) {
+                               for ( var i = 0, l = array.length; i < l; i++ ) {
+                                       ret.push( array[i] );
+                               }
+                       } else {
+                               for ( var i = 0; array[i]; i++ ) {
+                                       ret.push( array[i] );
+                               }
+                       }
+               }
+
+               return ret;
+       };
+}
+
+var sortOrder;
+
+if ( document.documentElement.compareDocumentPosition ) {
+       sortOrder = function( a, b ) {
+               if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+                       if ( a == b ) {
+                               hasDuplicate = true;
+                       }
+                       return a.compareDocumentPosition ? -1 : 1;
+               }
+
+               var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
+               if ( ret === 0 ) {
+                       hasDuplicate = true;
+               }
+               return ret;
+       };
+} else if ( "sourceIndex" in document.documentElement ) {
+       sortOrder = function( a, b ) {
+               if ( !a.sourceIndex || !b.sourceIndex ) {
+                       if ( a == b ) {
+                               hasDuplicate = true;
+                       }
+                       return a.sourceIndex ? -1 : 1;
+               }
+
+               var ret = a.sourceIndex - b.sourceIndex;
+               if ( ret === 0 ) {
+                       hasDuplicate = true;
+               }
+               return ret;
+       };
+} else if ( document.createRange ) {
+       sortOrder = function( a, b ) {
+               if ( !a.ownerDocument || !b.ownerDocument ) {
+                       if ( a == b ) {
+                               hasDuplicate = true;
+                       }
+                       return a.ownerDocument ? -1 : 1;
+               }
+
+               var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
+               aRange.setStart(a, 0);
+               aRange.setEnd(a, 0);
+               bRange.setStart(b, 0);
+               bRange.setEnd(b, 0);
+               var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
+               if ( ret === 0 ) {
+                       hasDuplicate = true;
+               }
+               return ret;
+       };
+}
+
+// Utility function for retreiving the text value of an array of DOM nodes
+function getText( elems ) {
+       var ret = "", elem;
+
+       for ( var i = 0; elems[i]; i++ ) {
+               elem = elems[i];
+
+               // Get the text from text nodes and CDATA nodes
+               if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
+                       ret += elem.nodeValue;
+
+               // Traverse everything else, except comment nodes
+               } else if ( elem.nodeType !== 8 ) {
+                       ret += getText( elem.childNodes );
+               }
+       }
+
+       return ret;
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+       // We're going to inject a fake input element with a specified name
+       var form = document.createElement("div"),
+               id = "script" + (new Date).getTime();
+       form.innerHTML = "<a name='" + id + "'/>";
+
+       // Inject it into the root element, check its status, and remove it quickly
+       var root = document.documentElement;
+       root.insertBefore( form, root.firstChild );
+
+       // The workaround has to do additional checks after a getElementById
+       // Which slows things down for other browsers (hence the branching)
+       if ( document.getElementById( id ) ) {
+               Expr.find.ID = function(match, context, isXML){
+                       if ( typeof context.getElementById !== "undefined" && !isXML ) {
+                               var m = context.getElementById(match[1]);
+                               return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+                       }
+               };
+
+               Expr.filter.ID = function(elem, match){
+                       var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+                       return elem.nodeType === 1 && node && node.nodeValue === match;
+               };
+       }
+
+       root.removeChild( form );
+       root = form = null; // release memory in IE
+})();
+
+(function(){
+       // Check to see if the browser returns only elements
+       // when doing getElementsByTagName("*")
+
+       // Create a fake element
+       var div = document.createElement("div");
+       div.appendChild( document.createComment("") );
+
+       // Make sure no comments are found
+       if ( div.getElementsByTagName("*").length > 0 ) {
+               Expr.find.TAG = function(match, context){
+                       var results = context.getElementsByTagName(match[1]);
+
+                       // Filter out possible comments
+                       if ( match[1] === "*" ) {
+                               var tmp = [];
+
+                               for ( var i = 0; results[i]; i++ ) {
+                                       if ( results[i].nodeType === 1 ) {
+                                               tmp.push( results[i] );
+                                       }
+                               }
+
+                               results = tmp;
+                       }
+
+                       return results;
+               };
+       }
+
+       // Check to see if an attribute returns normalized href attributes
+       div.innerHTML = "<a href='#'></a>";
+       if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+                       div.firstChild.getAttribute("href") !== "#" ) {
+               Expr.attrHandle.href = function(elem){
+                       return elem.getAttribute("href", 2);
+               };
+       }
+
+       div = null; // release memory in IE
+})();
+
+if ( document.querySelectorAll ) {
+       (function(){
+               var oldSizzle = Sizzle, div = document.createElement("div");
+               div.innerHTML = "<p class='TEST'></p>";
+
+               // Safari can't handle uppercase or unicode characters when
+               // in quirks mode.
+               if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+                       return;
+               }
+       
+               Sizzle = function(query, context, extra, seed){
+                       context = context || document;
+
+                       // Only use querySelectorAll on non-XML documents
+                       // (ID selectors don't work in non-HTML documents)
+                       if ( !seed && context.nodeType === 9 && !isXML(context) ) {
+                               try {
+                                       return makeArray( context.querySelectorAll(query), extra );
+                               } catch(e){}
+                       }
+               
+                       return oldSizzle(query, context, extra, seed);
+               };
+
+               for ( var prop in oldSizzle ) {
+                       Sizzle[ prop ] = oldSizzle[ prop ];
+               }
+
+               div = null; // release memory in IE
+       })();
+}
+
+(function(){
+       var div = document.createElement("div");
+
+       div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+       // Opera can't find a second classname (in 9.6)
+       // Also, make sure that getElementsByClassName actually exists
+       if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+               return;
+       }
+
+       // Safari caches class attributes, doesn't catch changes (in 3.2)
+       div.lastChild.className = "e";
+
+       if ( div.getElementsByClassName("e").length === 1 ) {
+               return;
+       }
+       
+       Expr.order.splice(1, 0, "CLASS");
+       Expr.find.CLASS = function(match, context, isXML) {
+               if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+                       return context.getElementsByClassName(match[1]);
+               }
+       };
+
+       div = null; // release memory in IE
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+               var elem = checkSet[i];
+               if ( elem ) {
+                       elem = elem[dir];
+                       var match = false;
+
+                       while ( elem ) {
+                               if ( elem.sizcache === doneName ) {
+                                       match = checkSet[elem.sizset];
+                                       break;
+                               }
+
+                               if ( elem.nodeType === 1 && !isXML ){
+                                       elem.sizcache = doneName;
+                                       elem.sizset = i;
+                               }
+
+                               if ( elem.nodeName.toLowerCase() === cur ) {
+                                       match = elem;
+                                       break;
+                               }
+
+                               elem = elem[dir];
+                       }
+
+                       checkSet[i] = match;
+               }
+       }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+       for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+               var elem = checkSet[i];
+               if ( elem ) {
+                       elem = elem[dir];
+                       var match = false;
+
+                       while ( elem ) {
+                               if ( elem.sizcache === doneName ) {
+                                       match = checkSet[elem.sizset];
+                                       break;
+                               }
+
+                               if ( elem.nodeType === 1 ) {
+                                       if ( !isXML ) {
+                                               elem.sizcache = doneName;
+                                               elem.sizset = i;
+                                       }
+                                       if ( typeof cur !== "string" ) {
+                                               if ( elem === cur ) {
+                                                       match = true;
+                                                       break;
+                                               }
+
+                                       } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+                                               match = elem;
+                                               break;
+                                       }
+                               }
+
+                               elem = elem[dir];
+                       }
+
+                       checkSet[i] = match;
+               }
+       }
+}
+
+var contains = document.compareDocumentPosition ? function(a, b){
+       return !!(a.compareDocumentPosition(b) & 16);
+} : function(a, b){
+       return a !== b && (a.contains ? a.contains(b) : true);
+};
+
+var isXML = function(elem){
+       // documentElement is verified for cases where it doesn't yet exist
+       // (such as loading iframes in IE - #4833) 
+       var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+       return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function(selector, context){
+       var tmpSet = [], later = "", match,
+               root = context.nodeType ? [context] : context;
+
+       // Position selectors must be done after the filter
+       // And so must :not(positional) so we move all PSEUDOs to the end
+       while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+               later += match[0];
+               selector = selector.replace( Expr.match.PSEUDO, "" );
+       }
+
+       selector = Expr.relative[selector] ? selector + "*" : selector;
+
+       for ( var i = 0, l = root.length; i < l; i++ ) {
+               Sizzle( selector, root[i], tmpSet );
+       }
+
+       return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = getText;
+jQuery.isXMLDoc = isXML;
+jQuery.contains = contains;
+
+return;
+
+window.Sizzle = Sizzle;
+
+})();
+var runtil = /Until$/,
+       rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+       // Note: This RegExp should be improved, or likely pulled from Sizzle
+       rmultiselector = /,/,
+       slice = Array.prototype.slice;
+
+// Implement the identical functionality for filter and not
+var winnow = function( elements, qualifier, keep ) {
+       if ( jQuery.isFunction( qualifier ) ) {
+               return jQuery.grep(elements, function( elem, i ) {
+                       return !!qualifier.call( elem, i, elem ) === keep;
+               });
+
+       } else if ( qualifier.nodeType ) {
+               return jQuery.grep(elements, function( elem, i ) {
+                       return (elem === qualifier) === keep;
+               });
+
+       } else if ( typeof qualifier === "string" ) {
+               var filtered = jQuery.grep(elements, function( elem ) {
+                       return elem.nodeType === 1;
+               });
+
+               if ( isSimple.test( qualifier ) ) {
+                       return jQuery.filter(qualifier, filtered, !keep);
+               } else {
+                       qualifier = jQuery.filter( qualifier, filtered );
+               }
+       }
+
+       return jQuery.grep(elements, function( elem, i ) {
+               return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
+       });
+};
+
+jQuery.fn.extend({
+       find: function( selector ) {
+               var ret = this.pushStack( "", "find", selector ), length = 0;
+
+               for ( var i = 0, l = this.length; i < l; i++ ) {
+                       length = ret.length;
+                       jQuery.find( selector, this[i], ret );
+
+                       if ( i > 0 ) {
+                               // Make sure that the results are unique
+                               for ( var n = length; n < ret.length; n++ ) {
+                                       for ( var r = 0; r < length; r++ ) {
+                                               if ( ret[r] === ret[n] ) {
+                                                       ret.splice(n--, 1);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               return ret;
+       },
+
+       has: function( target ) {
+               var targets = jQuery( target );
+               return this.filter(function() {
+                       for ( var i = 0, l = targets.length; i < l; i++ ) {
+                               if ( jQuery.contains( this, targets[i] ) ) {
+                                       return true;
+                               }
+                       }
+               });
+       },
+
+       not: function( selector ) {
+               return this.pushStack( winnow(this, selector, false), "not", selector);
+       },
+
+       filter: function( selector ) {
+               return this.pushStack( winnow(this, selector, true), "filter", selector );
+       },
+       
+       is: function( selector ) {
+               return !!selector && jQuery.filter( selector, this ).length > 0;
+       },
+
+       closest: function( selectors, context ) {
+               if ( jQuery.isArray( selectors ) ) {
+                       var ret = [], cur = this[0], match, matches = {}, selector;
+
+                       if ( cur && selectors.length ) {
+                               for ( var i = 0, l = selectors.length; i < l; i++ ) {
+                                       selector = selectors[i];
+
+                                       if ( !matches[selector] ) {
+                                               matches[selector] = jQuery.expr.match.POS.test( selector ) ? 
+                                                       jQuery( selector, context || this.context ) :
+                                                       selector;
+                                       }
+                               }
+
+                               while ( cur && cur.ownerDocument && cur !== context ) {
+                                       for ( selector in matches ) {
+                                               match = matches[selector];
+
+                                               if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
+                                                       ret.push({ selector: selector, elem: cur });
+                                                       delete matches[selector];
+                                               }
+                                       }
+                                       cur = cur.parentNode;
+                               }
+                       }
+
+                       return ret;
+               }
+
+               var pos = jQuery.expr.match.POS.test( selectors ) ? 
+                       jQuery( selectors, context || this.context ) : null;
+
+               return this.map(function( i, cur ) {
+                       while ( cur && cur.ownerDocument && cur !== context ) {
+                               if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
+                                       return cur;
+                               }
+                               cur = cur.parentNode;
+                       }
+                       return null;
+               });
+       },
+       
+       // Determine the position of an element within
+       // the matched set of elements
+       index: function( elem ) {
+               if ( !elem || typeof elem === "string" ) {
+                       return jQuery.inArray( this[0],
+                               // If it receives a string, the selector is used
+                               // If it receives nothing, the siblings are used
+                               elem ? jQuery( elem ) : this.parent().children() );
+               }
+               // Locate the position of the desired element
+               return jQuery.inArray(
+                       // If it receives a jQuery object, the first element is used
+                       elem.jquery ? elem[0] : elem, this );
+       },
+
+       add: function( selector, context ) {
+               var set = typeof selector === "string" ?
+                               jQuery( selector, context || this.context ) :
+                               jQuery.makeArray( selector ),
+                       all = jQuery.merge( this.get(), set );
+
+               return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+                       all :
+                       jQuery.unique( all ) );
+       },
+
+       andSelf: function() {
+               return this.add( this.prevObject );
+       }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+       return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+       parent: function( elem ) {
+               var parent = elem.parentNode;
+               return parent && parent.nodeType !== 11 ? parent : null;
+       },
+       parents: function( elem ) {
+               return jQuery.dir( elem, "parentNode" );
+       },
+       parentsUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "parentNode", until );
+       },
+       next: function( elem ) {
+               return jQuery.nth( elem, 2, "nextSibling" );
+       },
+       prev: function( elem ) {
+               return jQuery.nth( elem, 2, "previousSibling" );
+       },
+       nextAll: function( elem ) {
+               return jQuery.dir( elem, "nextSibling" );
+       },
+       prevAll: function( elem ) {
+               return jQuery.dir( elem, "previousSibling" );
+       },
+       nextUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "nextSibling", until );
+       },
+       prevUntil: function( elem, i, until ) {
+               return jQuery.dir( elem, "previousSibling", until );
+       },
+       siblings: function( elem ) {
+               return jQuery.sibling( elem.parentNode.firstChild, elem );
+       },
+       children: function( elem ) {
+               return jQuery.sibling( elem.firstChild );
+       },
+       contents: function( elem ) {
+               return jQuery.nodeName( elem, "iframe" ) ?
+                       elem.contentDocument || elem.contentWindow.document :
+                       jQuery.makeArray( elem.childNodes );
+       }
+}, function( name, fn ) {
+       jQuery.fn[ name ] = function( until, selector ) {
+               var ret = jQuery.map( this, fn, until );
+               
+               if ( !runtil.test( name ) ) {
+                       selector = until;
+               }
+
+               if ( selector && typeof selector === "string" ) {
+                       ret = jQuery.filter( selector, ret );
+               }
+
+               ret = this.length > 1 ? jQuery.unique( ret ) : ret;
+
+               if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+                       ret = ret.reverse();
+               }
+
+               return this.pushStack( ret, name, slice.call(arguments).join(",") );
+       };
+});
+
+jQuery.extend({
+       filter: function( expr, elems, not ) {
+               if ( not ) {
+                       expr = ":not(" + expr + ")";
+               }
+
+               return jQuery.find.matches(expr, elems);
+       },
+       
+       dir: function( elem, dir, until ) {
+               var matched = [], cur = elem[dir];
+               while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+                       if ( cur.nodeType === 1 ) {
+                               matched.push( cur );
+                       }
+                       cur = cur[dir];
+               }
+               return matched;
+       },
+
+       nth: function( cur, result, dir, elem ) {
+               result = result || 1;
+               var num = 0;
+
+               for ( ; cur; cur = cur[dir] ) {
+                       if ( cur.nodeType === 1 && ++num === result ) {
+                               break;
+                       }
+               }
+
+               return cur;
+       },
+
+       sibling: function( n, elem ) {
+               var r = [];
+
+               for ( ; n; n = n.nextSibling ) {
+                       if ( n.nodeType === 1 && n !== elem ) {
+                               r.push( n );
+                       }
+               }
+
+               return r;
+       }
+});
+var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+       rleadingWhitespace = /^\s+/,
+       rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
+       rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
+       rtagName = /<([\w:]+)/,
+       rtbody = /<tbody/i,
+       rhtml = /<|&#?\w+;/,
+       rnocache = /<script|<object|<embed|<option|<style/i,
+       rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,  // checked="checked" or checked (html5)
+       fcloseTag = function( all, front, tag ) {
+               return rselfClosing.test( tag ) ?
+                       all :
+                       front + "></" + tag + ">";
+       },
+       wrapMap = {
+               option: [ 1, "<select multiple='multiple'>", "</select>" ],
+               legend: [ 1, "<fieldset>", "</fieldset>" ],
+               thead: [ 1, "<table>", "</table>" ],
+               tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+               td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+               col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+               area: [ 1, "<map>", "</map>" ],
+               _default: [ 0, "", "" ]
+       };
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+       wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+       text: function( text ) {
+               if ( jQuery.isFunction(text) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               self.text( text.call(this, i, self.text()) );
+                       });
+               }
+
+               if ( typeof text !== "object" && text !== undefined ) {
+                       return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
+               }
+
+               return jQuery.text( this );
+       },
+
+       wrapAll: function( html ) {
+               if ( jQuery.isFunction( html ) ) {
+                       return this.each(function(i) {
+                               jQuery(this).wrapAll( html.call(this, i) );
+                       });
+               }
+
+               if ( this[0] ) {
+                       // The elements to wrap the target around
+                       var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+                       if ( this[0].parentNode ) {
+                               wrap.insertBefore( this[0] );
+                       }
+
+                       wrap.map(function() {
+                               var elem = this;
+
+                               while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+                                       elem = elem.firstChild;
+                               }
+
+                               return elem;
+                       }).append(this);
+               }
+
+               return this;
+       },
+
+       wrapInner: function( html ) {
+               if ( jQuery.isFunction( html ) ) {
+                       return this.each(function(i) {
+                               jQuery(this).wrapInner( html.call(this, i) );
+                       });
+               }
+
+               return this.each(function() {
+                       var self = jQuery( this ), contents = self.contents();
+
+                       if ( contents.length ) {
+                               contents.wrapAll( html );
+
+                       } else {
+                               self.append( html );
+                       }
+               });
+       },
+
+       wrap: function( html ) {
+               return this.each(function() {
+                       jQuery( this ).wrapAll( html );
+               });
+       },
+
+       unwrap: function() {
+               return this.parent().each(function() {
+                       if ( !jQuery.nodeName( this, "body" ) ) {
+                               jQuery( this ).replaceWith( this.childNodes );
+                       }
+               }).end();
+       },
+
+       append: function() {
+               return this.domManip(arguments, true, function( elem ) {
+                       if ( this.nodeType === 1 ) {
+                               this.appendChild( elem );
+                       }
+               });
+       },
+
+       prepend: function() {
+               return this.domManip(arguments, true, function( elem ) {
+                       if ( this.nodeType === 1 ) {
+                               this.insertBefore( elem, this.firstChild );
+                       }
+               });
+       },
+
+       before: function() {
+               if ( this[0] && this[0].parentNode ) {
+                       return this.domManip(arguments, false, function( elem ) {
+                               this.parentNode.insertBefore( elem, this );
+                       });
+               } else if ( arguments.length ) {
+                       var set = jQuery(arguments[0]);
+                       set.push.apply( set, this.toArray() );
+                       return this.pushStack( set, "before", arguments );
+               }
+       },
+
+       after: function() {
+               if ( this[0] && this[0].parentNode ) {
+                       return this.domManip(arguments, false, function( elem ) {
+                               this.parentNode.insertBefore( elem, this.nextSibling );
+                       });
+               } else if ( arguments.length ) {
+                       var set = this.pushStack( this, "after", arguments );
+                       set.push.apply( set, jQuery(arguments[0]).toArray() );
+                       return set;
+               }
+       },
+       
+       // keepData is for internal use only--do not document
+       remove: function( selector, keepData ) {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+                               if ( !keepData && elem.nodeType === 1 ) {
+                                       jQuery.cleanData( elem.getElementsByTagName("*") );
+                                       jQuery.cleanData( [ elem ] );
+                               }
+
+                               if ( elem.parentNode ) {
+                                        elem.parentNode.removeChild( elem );
+                               }
+                       }
+               }
+               
+               return this;
+       },
+
+       empty: function() {
+               for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+                       // Remove element nodes and prevent memory leaks
+                       if ( elem.nodeType === 1 ) {
+                               jQuery.cleanData( elem.getElementsByTagName("*") );
+                       }
+
+                       // Remove any remaining nodes
+                       while ( elem.firstChild ) {
+                               elem.removeChild( elem.firstChild );
+                       }
+               }
+               
+               return this;
+       },
+
+       clone: function( events ) {
+               // Do the clone
+               var ret = this.map(function() {
+                       if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
+                               // IE copies events bound via attachEvent when
+                               // using cloneNode. Calling detachEvent on the
+                               // clone will also remove the events from the orignal
+                               // In order to get around this, we use innerHTML.
+                               // Unfortunately, this means some modifications to
+                               // attributes in IE that are actually only stored
+                               // as properties will not be copied (such as the
+                               // the name attribute on an input).
+                               var html = this.outerHTML, ownerDocument = this.ownerDocument;
+                               if ( !html ) {
+                                       var div = ownerDocument.createElement("div");
+                                       div.appendChild( this.cloneNode(true) );
+                                       html = div.innerHTML;
+                               }
+
+                               return jQuery.clean([html.replace(rinlinejQuery, "")
+                                       // Handle the case in IE 8 where action=/test/> self-closes a tag
+                                       .replace(/=([^="'>\s]+\/)>/g, '="$1">')
+                                       .replace(rleadingWhitespace, "")], ownerDocument)[0];
+                       } else {
+                               return this.cloneNode(true);
+                       }
+               });
+
+               // Copy the events from the original to the clone
+               if ( events === true ) {
+                       cloneCopyEvent( this, ret );
+                       cloneCopyEvent( this.find("*"), ret.find("*") );
+               }
+
+               // Return the cloned set
+               return ret;
+       },
+
+       html: function( value ) {
+               if ( value === undefined ) {
+                       return this[0] && this[0].nodeType === 1 ?
+                               this[0].innerHTML.replace(rinlinejQuery, "") :
+                               null;
+
+               // See if we can take a shortcut and just use innerHTML
+               } else if ( typeof value === "string" && !rnocache.test( value ) &&
+                       (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+                       !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
+
+                       value = value.replace(rxhtmlTag, fcloseTag);
+
+                       try {
+                               for ( var i = 0, l = this.length; i < l; i++ ) {
+                                       // Remove element nodes and prevent memory leaks
+                                       if ( this[i].nodeType === 1 ) {
+                                               jQuery.cleanData( this[i].getElementsByTagName("*") );
+                                               this[i].innerHTML = value;
+                                       }
+                               }
+
+                       // If using innerHTML throws an exception, use the fallback method
+                       } catch(e) {
+                               this.empty().append( value );
+                       }
+
+               } else if ( jQuery.isFunction( value ) ) {
+                       this.each(function(i){
+                               var self = jQuery(this), old = self.html();
+                               self.empty().append(function(){
+                                       return value.call( this, i, old );
+                               });
+                       });
+
+               } else {
+                       this.empty().append( value );
+               }
+
+               return this;
+       },
+
+       replaceWith: function( value ) {
+               if ( this[0] && this[0].parentNode ) {
+                       // Make sure that the elements are removed from the DOM before they are inserted
+                       // this can help fix replacing a parent with child elements
+                       if ( jQuery.isFunction( value ) ) {
+                               return this.each(function(i) {
+                                       var self = jQuery(this), old = self.html();
+                                       self.replaceWith( value.call( this, i, old ) );
+                               });
+                       }
+
+                       if ( typeof value !== "string" ) {
+                               value = jQuery(value).detach();
+                       }
+
+                       return this.each(function() {
+                               var next = this.nextSibling, parent = this.parentNode;
+
+                               jQuery(this).remove();
+
+                               if ( next ) {
+                                       jQuery(next).before( value );
+                               } else {
+                                       jQuery(parent).append( value );
+                               }
+                       });
+               } else {
+                       return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
+               }
+       },
+
+       detach: function( selector ) {
+               return this.remove( selector, true );
+       },
+
+       domManip: function( args, table, callback ) {
+               var results, first, value = args[0], scripts = [], fragment, parent;
+
+               // We can't cloneNode fragments that contain checked, in WebKit
+               if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+                       return this.each(function() {
+                               jQuery(this).domManip( args, table, callback, true );
+                       });
+               }
+
+               if ( jQuery.isFunction(value) ) {
+                       return this.each(function(i) {
+                               var self = jQuery(this);
+                               args[0] = value.call(this, i, table ? self.html() : undefined);
+                               self.domManip( args, table, callback );
+                       });
+               }
+
+               if ( this[0] ) {
+                       parent = value && value.parentNode;
+
+                       // If we're in a fragment, just use that instead of building a new one
+                       if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+                               results = { fragment: parent };
+
+                       } else {
+                               results = buildFragment( args, this, scripts );
+                       }
+                       
+                       fragment = results.fragment;
+                       
+                       if ( fragment.childNodes.length === 1 ) {
+                               first = fragment = fragment.firstChild;
+                       } else {
+                               first = fragment.firstChild;
+                       }
+
+                       if ( first ) {
+                               table = table && jQuery.nodeName( first, "tr" );
+
+                               for ( var i = 0, l = this.length; i < l; i++ ) {
+                                       callback.call(
+                                               table ?
+                                                       root(this[i], first) :
+                                                       this[i],
+                                               i > 0 || results.cacheable || this.length > 1  ?
+                                                       fragment.cloneNode(true) :
+                                                       fragment
+                                       );
+                               }
+                       }
+
+                       if ( scripts.length ) {
+                               jQuery.each( scripts, evalScript );
+                       }
+               }
+
+               return this;
+
+               function root( elem, cur ) {
+                       return jQuery.nodeName(elem, "table") ?
+                               (elem.getElementsByTagName("tbody")[0] ||
+                               elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+                               elem;
+               }
+       }
+});
+
+function cloneCopyEvent(orig, ret) {
+       var i = 0;
+
+       ret.each(function() {
+               if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
+                       return;
+               }
+
+               var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;
+
+               if ( events ) {
+                       delete curData.handle;
+                       curData.events = {};
+
+                       for ( var type in events ) {
+                               for ( var handler in events[ type ] ) {
+                                       jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
+                               }
+                       }
+               }
+       });
+}
+
+function buildFragment( args, nodes, scripts ) {
+       var fragment, cacheable, cacheresults,
+               doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
+
+       // Only cache "small" (1/2 KB) strings that are associated with the main document
+       // Cloning options loses the selected state, so don't cache them
+       // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+       // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+       if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
+               !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
+
+               cacheable = true;
+               cacheresults = jQuery.fragments[ args[0] ];
+               if ( cacheresults ) {
+                       if ( cacheresults !== 1 ) {
+                               fragment = cacheresults;
+                       }
+               }
+       }
+
+       if ( !fragment ) {
+               fragment = doc.createDocumentFragment();
+               jQuery.clean( args, doc, fragment, scripts );
+       }
+
+       if ( cacheable ) {
+               jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
+       }
+
+       return { fragment: fragment, cacheable: cacheable };
+}
+
+jQuery.fragments = {};
+
+jQuery.each({
+       appendTo: "append",
+       prependTo: "prepend",
+       insertBefore: "before",
+       insertAfter: "after",
+       replaceAll: "replaceWith"
+}, function( name, original ) {
+       jQuery.fn[ name ] = function( selector ) {
+               var ret = [], insert = jQuery( selector ),
+                       parent = this.length === 1 && this[0].parentNode;
+               
+               if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+                       insert[ original ]( this[0] );
+                       return this;
+                       
+               } else {
+                       for ( var i = 0, l = insert.length; i < l; i++ ) {
+                               var elems = (i > 0 ? this.clone(true) : this).get();
+                               jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
+                               ret = ret.concat( elems );
+                       }
+               
+                       return this.pushStack( ret, name, insert.selector );
+               }
+       };
+});
+
+jQuery.extend({
+       clean: function( elems, context, fragment, scripts ) {
+               context = context || document;
+
+               // !context.createElement fails in IE with an error but returns typeof 'object'
+               if ( typeof context.createElement === "undefined" ) {
+                       context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+               }
+
+               var ret = [];
+
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       if ( typeof elem === "number" ) {
+                               elem += "";
+                       }
+
+                       if ( !elem ) {
+                               continue;
+                       }
+
+                       // Convert html string into DOM nodes
+                       if ( typeof elem === "string" && !rhtml.test( elem ) ) {
+                               elem = context.createTextNode( elem );
+
+                       } else if ( typeof elem === "string" ) {
+                               // Fix "XHTML"-style tags in all browsers
+                               elem = elem.replace(rxhtmlTag, fcloseTag);
+
+                               // Trim whitespace, otherwise indexOf won't work as expected
+                               var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
+                                       wrap = wrapMap[ tag ] || wrapMap._default,
+                                       depth = wrap[0],
+                                       div = context.createElement("div");
+
+                               // Go to html and back, then peel off extra wrappers
+                               div.innerHTML = wrap[1] + elem + wrap[2];
+
+                               // Move to the right depth
+                               while ( depth-- ) {
+                                       div = div.lastChild;
+                               }
+
+                               // Remove IE's autoinserted <tbody> from table fragments
+                               if ( !jQuery.support.tbody ) {
+
+                                       // String was a <table>, *may* have spurious <tbody>
+                                       var hasBody = rtbody.test(elem),
+                                               tbody = tag === "table" && !hasBody ?
+                                                       div.firstChild && div.firstChild.childNodes :
+
+                                                       // String was a bare <thead> or <tfoot>
+                                                       wrap[1] === "<table>" && !hasBody ?
+                                                               div.childNodes :
+                                                               [];
+
+                                       for ( var j = tbody.length - 1; j >= 0 ; --j ) {
+                                               if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+                                                       tbody[ j ].parentNode.removeChild( tbody[ j ] );
+                                               }
+                                       }
+
+                               }
+
+                               // IE completely kills leading whitespace when innerHTML is used
+                               if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+                                       div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+                               }
+
+                               elem = div.childNodes;
+                       }
+
+                       if ( elem.nodeType ) {
+                               ret.push( elem );
+                       } else {
+                               ret = jQuery.merge( ret, elem );
+                       }
+               }
+
+               if ( fragment ) {
+                       for ( var i = 0; ret[i]; i++ ) {
+                               if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+                                       scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
+                               
+                               } else {
+                                       if ( ret[i].nodeType === 1 ) {
+                                               ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
+                                       }
+                                       fragment.appendChild( ret[i] );
+                               }
+                       }
+               }
+
+               return ret;
+       },
+       
+       cleanData: function( elems ) {
+               var data, id, cache = jQuery.cache,
+                       special = jQuery.event.special,
+                       deleteExpando = jQuery.support.deleteExpando;
+               
+               for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+                       id = elem[ jQuery.expando ];
+                       
+                       if ( id ) {
+                               data = cache[ id ];
+                               
+                               if ( data.events ) {
+                                       for ( var type in data.events ) {
+                                               if ( special[ type ] ) {
+                                                       jQuery.event.remove( elem, type );
+
+                                               } else {
+                                                       removeEvent( elem, type, data.handle );
+                                               }
+                                       }
+                               }
+                               
+                               if ( deleteExpando ) {
+                                       delete elem[ jQuery.expando ];
+
+                               } else if ( elem.removeAttribute ) {
+                                       elem.removeAttribute( jQuery.expando );
+                               }
+                               
+                               delete cache[ id ];
+                       }
+               }
+       }
+});
+// exclude the following css properties to add px
+var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
+       ralpha = /alpha\([^)]*\)/,
+       ropacity = /opacity=([^)]*)/,
+       rfloat = /float/i,
+       rdashAlpha = /-([a-z])/ig,
+       rupper = /([A-Z])/g,
+       rnumpx = /^-?\d+(?:px)?$/i,
+       rnum = /^-?\d/,
+
+       cssShow = { position: "absolute", visibility: "hidden", display:"block" },
+       cssWidth = [ "Left", "Right" ],
+       cssHeight = [ "Top", "Bottom" ],
+
+       // cache check for defaultView.getComputedStyle
+       getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
+       // normalize float css property
+       styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
+       fcamelCase = function( all, letter ) {
+               return letter.toUpperCase();
+       };
+
+jQuery.fn.css = function( name, value ) {
+       return access( this, name, value, true, function( elem, name, value ) {
+               if ( value === undefined ) {
+                       return jQuery.curCSS( elem, name );
+               }
+               
+               if ( typeof value === "number" && !rexclude.test(name) ) {
+                       value += "px";
+               }
+
+               jQuery.style( elem, name, value );
+       });
+};
+
+jQuery.extend({
+       style: function( elem, name, value ) {
+               // don't set styles on text and comment nodes
+               if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
+                       return undefined;
+               }
+
+               // ignore negative width and height values #1599
+               if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
+                       value = undefined;
+               }
+
+               var style = elem.style || elem, set = value !== undefined;
+
+               // IE uses filters for opacity
+               if ( !jQuery.support.opacity && name === "opacity" ) {
+                       if ( set ) {
+                               // IE has trouble with opacity if it does not have layout
+                               // Force it by setting the zoom level
+                               style.zoom = 1;
+
+                               // Set the alpha filter to set the opacity
+                               var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
+                               var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
+                               style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
+                       }
+
+                       return style.filter && style.filter.indexOf("opacity=") >= 0 ?
+                               (parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
+                               "";
+               }
+
+               // Make sure we're using the right name for getting the float value
+               if ( rfloat.test( name ) ) {
+                       name = styleFloat;
+               }
+
+               name = name.replace(rdashAlpha, fcamelCase);
+
+               if ( set ) {
+                       style[ name ] = value;
+               }
+
+               return style[ name ];
+       },
+
+       css: function( elem, name, force, extra ) {
+               if ( name === "width" || name === "height" ) {
+                       var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;
+
+                       function getWH() {
+                               val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
+
+                               if ( extra === "border" ) {
+                                       return;
+                               }
+
+                               jQuery.each( which, function() {
+                                       if ( !extra ) {
+                                               val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
+                                       }
+
+                                       if ( extra === "margin" ) {
+                                               val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
+                                       } else {
+                                               val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
+                                       }
+                               });
+                       }
+
+                       if ( elem.offsetWidth !== 0 ) {
+                               getWH();
+                       } else {
+                               jQuery.swap( elem, props, getWH );
+                       }
+
+                       return Math.max(0, Math.round(val));
+               }
+
+               return jQuery.curCSS( elem, name, force );
+       },
+
+       curCSS: function( elem, name, force ) {
+               var ret, style = elem.style, filter;
+
+               // IE uses filters for opacity
+               if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
+                       ret = ropacity.test(elem.currentStyle.filter || "") ?
+                               (parseFloat(RegExp.$1) / 100) + "" :
+                               "";
+
+                       return ret === "" ?
+                               "1" :
+                               ret;
+               }
+
+               // Make sure we're using the right name for getting the float value
+               if ( rfloat.test( name ) ) {
+                       name = styleFloat;
+               }
+
+               if ( !force && style && style[ name ] ) {
+                       ret = style[ name ];
+
+               } else if ( getComputedStyle ) {
+
+                       // Only "float" is needed here
+                       if ( rfloat.test( name ) ) {
+                               name = "float";
+                       }
+
+                       name = name.replace( rupper, "-$1" ).toLowerCase();
+
+                       var defaultView = elem.ownerDocument.defaultView;
+
+                       if ( !defaultView ) {
+                               return null;
+                       }
+
+                       var computedStyle = defaultView.getComputedStyle( elem, null );
+
+                       if ( computedStyle ) {
+                               ret = computedStyle.getPropertyValue( name );
+                       }
+
+                       // We should always get a number back from opacity
+                       if ( name === "opacity" && ret === "" ) {
+                               ret = "1";
+                       }
+
+               } else if ( elem.currentStyle ) {
+                       var camelCase = name.replace(rdashAlpha, fcamelCase);
+
+                       ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
+
+                       // From the awesome hack by Dean Edwards
+                       // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+                       // If we're not dealing with a regular pixel number
+                       // but a number that has a weird ending, we need to convert it to pixels
+                       if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+                               // Remember the original values
+                               var left = style.left, rsLeft = elem.runtimeStyle.left;
+
+                               // Put in the new values to get a computed value out
+                               elem.runtimeStyle.left = elem.currentStyle.left;
+                               style.left = camelCase === "fontSize" ? "1em" : (ret || 0);
+                               ret = style.pixelLeft + "px";
+
+                               // Revert the changed values
+                               style.left = left;
+                               elem.runtimeStyle.left = rsLeft;
+                       }
+               }
+
+               return ret;
+       },
+
+       // A method for quickly swapping in/out CSS properties to get correct calculations
+       swap: function( elem, options, callback ) {
+               var old = {};
+
+               // Remember the old values, and insert the new ones
+               for ( var name in options ) {
+                       old[ name ] = elem.style[ name ];
+                       elem.style[ name ] = options[ name ];
+               }
+
+               callback.call( elem );
+
+               // Revert the old values
+               for ( var name in options ) {
+                       elem.style[ name ] = old[ name ];
+               }
+       }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+       jQuery.expr.filters.hidden = function( elem ) {
+               var width = elem.offsetWidth, height = elem.offsetHeight,
+                       skip = elem.nodeName.toLowerCase() === "tr";
+
+               return width === 0 && height === 0 && !skip ?
+                       true :
+                       width > 0 && height > 0 && !skip ?
+                               false :
+                               jQuery.curCSS(elem, "display") === "none";
+       };
+
+       jQuery.expr.filters.visible = function( elem ) {
+               return !jQuery.expr.filters.hidden( elem );
+       };
+}
+var jsc = now(),
+       rscript = /<script(.|\s)*?\/script>/gi,
+       rselectTextarea = /select|textarea/i,
+       rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
+       jsre = /=\?(&|$)/,
+       rquery = /\?/,
+       rts = /(\?|&)_=.*?(&|$)/,
+       rurl = /^(\w+:)?\/\/([^\/?#]+)/,
+       r20 = /%20/g,
+
+       // Keep a copy of the old load method
+       _load = jQuery.fn.load;
+
+jQuery.fn.extend({
+       load: function( url, params, callback ) {
+               if ( typeof url !== "string" ) {
+                       return _load.call( this, url );
+
+               // Don't do a request if no elements are being requested
+               } else if ( !this.length ) {
+                       return this;
+               }
+
+               var off = url.indexOf(" ");
+               if ( off >= 0 ) {
+                       var selector = url.slice(off, url.length);
+                       url = url.slice(0, off);
+               }
+
+               // Default to a GET request
+               var type = "GET";
+
+               // If the second parameter was provided
+               if ( params ) {
+                       // If it's a function
+                       if ( jQuery.isFunction( params ) ) {
+                               // We assume that it's the callback
+                               callback = params;
+                               params = null;
+
+                       // Otherwise, build a param string
+                       } else if ( typeof params === "object" ) {
+                               params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+                               type = "POST";
+                       }
+               }
+
+               var self = this;
+
+               // Request the remote document
+               jQuery.ajax({
+                       url: url,
+                       type: type,
+                       dataType: "html",
+                       data: params,
+                       complete: function( res, status ) {
+                               // If successful, inject the HTML into all the matched elements
+                               if ( status === "success" || status === "notmodified" ) {
+                                       // See if a selector was specified
+                                       self.html( selector ?
+                                               // Create a dummy div to hold the results
+                                               jQuery("<div />")
+                                                       // inject the contents of the document in, removing the scripts
+                                                       // to avoid any 'Permission Denied' errors in IE
+                                                       .append(res.responseText.replace(rscript, ""))
+
+                                                       // Locate the specified elements
+                                                       .find(selector) :
+
+                                               // If not, just inject the full result
+                                               res.responseText );
+                               }
+
+                               if ( callback ) {
+                                       self.each( callback, [res.responseText, status, res] );
+                               }
+                       }
+               });
+
+               return this;
+       },
+
+       serialize: function() {
+               return jQuery.param(this.serializeArray());
+       },
+       serializeArray: function() {
+               return this.map(function() {
+                       return this.elements ? jQuery.makeArray(this.elements) : this;
+               })
+               .filter(function() {
+                       return this.name && !this.disabled &&
+                               (this.checked || rselectTextarea.test(this.nodeName) ||
+                                       rinput.test(this.type));
+               })
+               .map(function( i, elem ) {
+                       var val = jQuery(this).val();
+
+                       return val == null ?
+                               null :
+                               jQuery.isArray(val) ?
+                                       jQuery.map( val, function( val, i ) {
+                                               return { name: elem.name, value: val };
+                                       }) :
+                                       { name: elem.name, value: val };
+               }).get();
+       }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
+       jQuery.fn[o] = function( f ) {
+               return this.bind(o, f);
+       };
+});
+
+jQuery.extend({
+
+       get: function( url, data, callback, type ) {
+               // shift arguments if data argument was omited
+               if ( jQuery.isFunction( data ) ) {
+                       type = type || callback;
+                       callback = data;
+                       data = null;
+               }
+
+               return jQuery.ajax({
+                       type: "GET",
+                       url: url,
+                       data: data,
+                       success: callback,
+                       dataType: type
+               });
+       },
+
+       getScript: function( url, callback ) {
+               return jQuery.get(url, null, callback, "script");
+       },
+
+       getJSON: function( url, data, callback ) {
+               return jQuery.get(url, data, callback, "json");
+       },
+
+       post: function( url, data, callback, type ) {
+               // shift arguments if data argument was omited
+               if ( jQuery.isFunction( data ) ) {
+                       type = type || callback;
+                       callback = data;
+                       data = {};
+               }
+
+               return jQuery.ajax({
+                       type: "POST",
+                       url: url,
+                       data: data,
+                       success: callback,
+                       dataType: type
+               });
+       },
+
+       ajaxSetup: function( settings ) {
+               jQuery.extend( jQuery.ajaxSettings, settings );
+       },
+
+       ajaxSettings: {
+               url: location.href,
+               global: true,
+               type: "GET",
+               contentType: "application/x-www-form-urlencoded",
+               processData: true,
+               async: true,
+               /*
+               timeout: 0,
+               data: null,
+               username: null,
+               password: null,
+               traditional: false,
+               */
+               // Create the request object; Microsoft failed to properly
+               // implement the XMLHttpRequest in IE7 (can't request local files),
+               // so we use the ActiveXObject when it is available
+               // This function can be overriden by calling jQuery.ajaxSetup
+               xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
+                       function() {
+                               return new window.XMLHttpRequest();
+                       } :
+                       function() {
+                               try {
+                                       return new window.ActiveXObject("Microsoft.XMLHTTP");
+                               } catch(e) {}
+                       },
+               accepts: {
+                       xml: "application/xml, text/xml",
+                       html: "text/html",
+                       script: "text/javascript, application/javascript",
+                       json: "application/json, text/javascript",
+                       text: "text/plain",
+                       _default: "*/*"
+               }
+       },
+
+       // Last-Modified header cache for next request
+       lastModified: {},
+       etag: {},
+
+       ajax: function( origSettings ) {
+               var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
+               
+               var jsonp, status, data,
+                       callbackContext = origSettings && origSettings.context || s,
+                       type = s.type.toUpperCase();
+
+               // convert data if not already a string
+               if ( s.data && s.processData && typeof s.data !== "string" ) {
+                       s.data = jQuery.param( s.data, s.traditional );
+               }
+
+               // Handle JSONP Parameter Callbacks
+               if ( s.dataType === "jsonp" ) {
+                       if ( type === "GET" ) {
+                               if ( !jsre.test( s.url ) ) {
+                                       s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
+                               }
+                       } else if ( !s.data || !jsre.test(s.data) ) {
+                               s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
+                       }
+                       s.dataType = "json";
+               }
+
+               // Build temporary JSONP function
+               if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
+                       jsonp = s.jsonpCallback || ("jsonp" + jsc++);
+
+                       // Replace the =? sequence both in the query string and the data
+                       if ( s.data ) {
+                               s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+                       }
+
+                       s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+
+                       // We need to make sure
+                       // that a JSONP style response is executed properly
+                       s.dataType = "script";
+
+                       // Handle JSONP-style loading
+                       window[ jsonp ] = window[ jsonp ] || function( tmp ) {
+                               data = tmp;
+                               success();
+                               complete();
+                               // Garbage collect
+                               window[ jsonp ] = undefined;
+
+                               try {
+                                       delete window[ jsonp ];
+                               } catch(e) {}
+
+                               if ( head ) {
+                                       head.removeChild( script );
+                               }
+                       };
+               }
+
+               if ( s.dataType === "script" && s.cache === null ) {
+                       s.cache = false;
+               }
+
+               if ( s.cache === false && type === "GET" ) {
+                       var ts = now();
+
+                       // try replacing _= if it is there
+                       var ret = s.url.replace(rts, "$1_=" + ts + "$2");
+
+                       // if nothing was replaced, add timestamp to the end
+                       s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
+               }
+
+               // If data is available, append data to url for get requests
+               if ( s.data && type === "GET" ) {
+                       s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
+               }
+
+               // Watch for a new set of requests
+               if ( s.global && ! jQuery.active++ ) {
+                       jQuery.event.trigger( "ajaxStart" );
+               }
+
+               // Matches an absolute URL, and saves the domain
+               var parts = rurl.exec( s.url ),
+                       remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
+
+               // If we're requesting a remote document
+               // and trying to load JSON or Script with a GET
+               if ( s.dataType === "script" && type === "GET" && remote ) {
+                       var head = document.getElementsByTagName("head")[0] || document.documentElement;
+                       var script = document.createElement("script");
+                       script.src = s.url;
+                       if ( s.scriptCharset ) {
+                               script.charset = s.scriptCharset;
+                       }
+
+                       // Handle Script loading
+                       if ( !jsonp ) {
+                               var done = false;
+
+                               // Attach handlers for all browsers
+                               script.onload = script.onreadystatechange = function() {
+                                       if ( !done && (!this.readyState ||
+                                                       this.readyState === "loaded" || this.readyState === "complete") ) {
+                                               done = true;
+                                               success();
+                                               complete();
+
+                                               // Handle memory leak in IE
+                                               script.onload = script.onreadystatechange = null;
+                                               if ( head && script.parentNode ) {
+                                                       head.removeChild( script );
+                                               }
+                                       }
+                               };
+                       }
+
+                       // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
+                       // This arises when a base node is used (#2709 and #4378).
+                       head.insertBefore( script, head.firstChild );
+
+                       // We handle everything using the script element injection
+                       return undefined;
+               }
+
+               var requestDone = false;
+
+               // Create the request object
+               var xhr = s.xhr();
+
+               if ( !xhr ) {
+                       return;
+               }
+
+               // Open the socket
+               // Passing null username, generates a login popup on Opera (#2865)
+               if ( s.username ) {
+                       xhr.open(type, s.url, s.async, s.username, s.password);
+               } else {
+                       xhr.open(type, s.url, s.async);
+               }
+
+               // Need an extra try/catch for cross domain requests in Firefox 3
+               try {
+                       // Set the correct header, if data is being sent
+                       if ( s.data || origSettings && origSettings.contentType ) {
+                               xhr.setRequestHeader("Content-Type", s.contentType);
+                       }
+
+                       // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+                       if ( s.ifModified ) {
+                               if ( jQuery.lastModified[s.url] ) {
+                                       xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
+                               }
+
+                               if ( jQuery.etag[s.url] ) {
+                                       xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
+                               }
+                       }
+
+                       // Set header so the called script knows that it's an XMLHttpRequest
+                       // Only send the header if it's not a remote XHR
+                       if ( !remote ) {
+                               xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+                       }
+
+                       // Set the Accepts header for the server, depending on the dataType
+                       xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
+                               s.accepts[ s.dataType ] + ", */*" :
+                               s.accepts._default );
+               } catch(e) {}
+
+               // Allow custom headers/mimetypes and early abort
+               if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {
+                       // Handle the global AJAX counter
+                       if ( s.global && ! --jQuery.active ) {
+                               jQuery.event.trigger( "ajaxStop" );
+                       }
+
+                       // close opended socket
+                       xhr.abort();
+                       return false;
+               }
+
+               if ( s.global ) {
+                       trigger("ajaxSend", [xhr, s]);
+               }
+
+               // Wait for a response to come back
+               var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
+                       // The request was aborted
+                       if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
+                               // Opera doesn't call onreadystatechange before this point
+                               // so we simulate the call
+                               if ( !requestDone ) {
+                                       complete();
+                               }
+
+                               requestDone = true;
+                               if ( xhr ) {
+                                       xhr.onreadystatechange = jQuery.noop;
+                               }
+
+                       // The transfer is complete and the data is available, or the request timed out
+                       } else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
+                               requestDone = true;
+                               xhr.onreadystatechange = jQuery.noop;
+
+                               status = isTimeout === "timeout" ?
+                                       "timeout" :
+                                       !jQuery.httpSuccess( xhr ) ?
+                                               "error" :
+                                               s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
+                                                       "notmodified" :
+                                                       "success";
+
+                               var errMsg;
+
+                               if ( status === "success" ) {
+                                       // Watch for, and catch, XML document parse errors
+                                       try {
+                                               // process the data (runs the xml through httpData regardless of callback)
+                                               data = jQuery.httpData( xhr, s.dataType, s );
+                                       } catch(err) {
+                                               status = "parsererror";
+                                               errMsg = err;
+                                       }
+                               }
+
+                               // Make sure that the request was successful or notmodified
+                               if ( status === "success" || status === "notmodified" ) {
+                                       // JSONP handles its own success callback
+                                       if ( !jsonp ) {
+                                               success();
+                                       }
+                               } else {
+                                       jQuery.handleError(s, xhr, status, errMsg);
+                               }
+
+                               // Fire the complete handlers
+                               complete();
+
+                               if ( isTimeout === "timeout" ) {
+                                       xhr.abort();
+                               }
+
+                               // Stop memory leaks
+                               if ( s.async ) {
+                                       xhr = null;
+                               }
+                       }
+               };
+
+               // Override the abort handler, if we can (IE doesn't allow it, but that's OK)
+               // Opera doesn't fire onreadystatechange at all on abort
+               try {
+                       var oldAbort = xhr.abort;
+                       xhr.abort = function() {
+                               if ( xhr ) {
+                                       oldAbort.call( xhr );
+                               }
+
+                               onreadystatechange( "abort" );
+                       };
+               } catch(e) { }
+
+               // Timeout checker
+               if ( s.async && s.timeout > 0 ) {
+                       setTimeout(function() {
+                               // Check to see if the request is still happening
+                               if ( xhr && !requestDone ) {
+                                       onreadystatechange( "timeout" );
+                               }
+                       }, s.timeout);
+               }
+
+               // Send the data
+               try {
+                       xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
+               } catch(e) {
+                       jQuery.handleError(s, xhr, null, e);
+                       // Fire the complete handlers
+                       complete();
+               }
+
+               // firefox 1.5 doesn't fire statechange for sync requests
+               if ( !s.async ) {
+                       onreadystatechange();
+               }
+
+               function success() {
+                       // If a local callback was specified, fire it and pass it the data
+                       if ( s.success ) {
+                               s.success.call( callbackContext, data, status, xhr );
+                       }
+
+                       // Fire the global callback
+                       if ( s.global ) {
+                               trigger( "ajaxSuccess", [xhr, s] );
+                       }
+               }
+
+               function complete() {
+                       // Process result
+                       if ( s.complete ) {
+                               s.complete.call( callbackContext, xhr, status);
+                       }
+
+                       // The request was completed
+                       if ( s.global ) {
+                               trigger( "ajaxComplete", [xhr, s] );
+                       }
+
+                       // Handle the global AJAX counter
+                       if ( s.global && ! --jQuery.active ) {
+                               jQuery.event.trigger( "ajaxStop" );
+                       }
+               }
+               
+               function trigger(type, args) {
+                       (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
+               }
+
+               // return XMLHttpRequest to allow aborting the request etc.
+               return xhr;
+       },
+
+       handleError: function( s, xhr, status, e ) {
+               // If a local callback was specified, fire it
+               if ( s.error ) {
+                       s.error.call( s.context || s, xhr, status, e );
+               }
+
+               // Fire the global callback
+               if ( s.global ) {
+                       (s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
+               }
+       },
+
+       // Counter for holding the number of active queries
+       active: 0,
+
+       // Determines if an XMLHttpRequest was successful or not
+       httpSuccess: function( xhr ) {
+               try {
+                       // IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
+                       return !xhr.status && location.protocol === "file:" ||
+                               // Opera returns 0 when status is 304
+                               ( xhr.status >= 200 && xhr.status < 300 ) ||
+                               xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
+               } catch(e) {}
+
+               return false;
+       },
+
+       // Determines if an XMLHttpRequest returns NotModified
+       httpNotModified: function( xhr, url ) {
+               var lastModified = xhr.getResponseHeader("Last-Modified"),
+                       etag = xhr.getResponseHeader("Etag");
+
+               if ( lastModified ) {
+                       jQuery.lastModified[url] = lastModified;
+               }
+
+               if ( etag ) {
+                       jQuery.etag[url] = etag;
+               }
+
+               // Opera returns 0 when status is 304
+               return xhr.status === 304 || xhr.status === 0;
+       },
+
+       httpData: function( xhr, type, s ) {
+               var ct = xhr.getResponseHeader("content-type") || "",
+                       xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
+                       data = xml ? xhr.responseXML : xhr.responseText;
+
+               if ( xml && data.documentElement.nodeName === "parsererror" ) {
+                       jQuery.error( "parsererror" );
+               }
+
+               // Allow a pre-filtering function to sanitize the response
+               // s is checked to keep backwards compatibility
+               if ( s && s.dataFilter ) {
+                       data = s.dataFilter( data, type );
+               }
+
+               // The filter can actually parse the response
+               if ( typeof data === "string" ) {
+                       // Get the JavaScript object, if JSON is used.
+                       if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
+                               data = jQuery.parseJSON( data );
+
+                       // If the type is "script", eval it in global context
+                       } else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
+                               jQuery.globalEval( data );
+                       }
+               }
+
+               return data;
+       },
+
+       // Serialize an array of form elements or a set of
+       // key/values into a query string
+       param: function( a, traditional ) {
+               var s = [];
+               
+               // Set traditional to true for jQuery <= 1.3.2 behavior.
+               if ( traditional === undefined ) {
+                       traditional = jQuery.ajaxSettings.traditional;
+               }
+               
+               // If an array was passed in, assume that it is an array of form elements.
+               if ( jQuery.isArray(a) || a.jquery ) {
+                       // Serialize the form elements
+                       jQuery.each( a, function() {
+                               add( this.name, this.value );
+                       });
+                       
+               } else {
+                       // If traditional, encode the "old" way (the way 1.3.2 or older
+                       // did it), otherwise encode params recursively.
+                       for ( var prefix in a ) {
+                               buildParams( prefix, a[prefix] );
+                       }
+               }
+
+               // Return the resulting serialization
+               return s.join("&").replace(r20, "+");
+
+               function buildParams( prefix, obj ) {
+                       if ( jQuery.isArray(obj) ) {
+                               // Serialize array item.
+                               jQuery.each( obj, function( i, v ) {
+                                       if ( traditional || /\[\]$/.test( prefix ) ) {
+                                               // Treat each array item as a scalar.
+                                               add( prefix, v );
+                                       } else {
+                                               // If array item is non-scalar (array or object), encode its
+                                               // numeric index to resolve deserialization ambiguity issues.
+                                               // Note that rack (as of 1.0.0) can't currently deserialize
+                                               // nested arrays properly, and attempting to do so may cause
+                                               // a server error. Possible fixes are to modify rack's
+                                               // deserialization algorithm or to provide an option or flag
+                                               // to force array serialization to be shallow.
+                                               buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v );
+                                       }
+                               });
+                                       
+                       } else if ( !traditional && obj != null && typeof obj === "object" ) {
+                               // Serialize object item.
+                               jQuery.each( obj, function( k, v ) {
+                                       buildParams( prefix + "[" + k + "]", v );
+                               });
+                                       
+                       } else {
+                               // Serialize scalar item.
+                               add( prefix, obj );
+                       }
+               }
+
+               function add( key, value ) {
+                       // If value is a function, invoke it and return its value
+                       value = jQuery.isFunction(value) ? value() : value;
+                       s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
+               }
+       }
+});
+var elemdisplay = {},
+       rfxtypes = /toggle|show|hide/,
+       rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
+       timerId,
+       fxAttrs = [
+               // height animations
+               [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+               // width animations
+               [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+               // opacity animations
+               [ "opacity" ]
+       ];
+
+jQuery.fn.extend({
+       show: function( speed, callback ) {
+               if ( speed || speed === 0) {
+                       return this.animate( genFx("show", 3), speed, callback);
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               var old = jQuery.data(this[i], "olddisplay");
+
+                               this[i].style.display = old || "";
+
+                               if ( jQuery.css(this[i], "display") === "none" ) {
+                                       var nodeName = this[i].nodeName, display;
+
+                                       if ( elemdisplay[ nodeName ] ) {
+                                               display = elemdisplay[ nodeName ];
+
+                                       } else {
+                                               var elem = jQuery("<" + nodeName + " />").appendTo("body");
+
+                                               display = elem.css("display");
+
+                                               if ( display === "none" ) {
+                                                       display = "block";
+                                               }
+
+                                               elem.remove();
+
+                                               elemdisplay[ nodeName ] = display;
+                                       }
+
+                                       jQuery.data(this[i], "olddisplay", display);
+                               }
+                       }
+
+                       // Set the display of the elements in a second loop
+                       // to avoid the constant reflow
+                       for ( var j = 0, k = this.length; j < k; j++ ) {
+                               this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
+                       }
+
+                       return this;
+               }
+       },
+
+       hide: function( speed, callback ) {
+               if ( speed || speed === 0 ) {
+                       return this.animate( genFx("hide", 3), speed, callback);
+
+               } else {
+                       for ( var i = 0, l = this.length; i < l; i++ ) {
+                               var old = jQuery.data(this[i], "olddisplay");
+                               if ( !old && old !== "none" ) {
+                                       jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
+                               }
+                       }
+
+                       // Set the display of the elements in a second loop
+                       // to avoid the constant reflow
+                       for ( var j = 0, k = this.length; j < k; j++ ) {
+                               this[j].style.display = "none";
+                       }
+
+                       return this;
+               }
+       },
+
+       // Save the old toggle function
+       _toggle: jQuery.fn.toggle,
+
+       toggle: function( fn, fn2 ) {
+               var bool = typeof fn === "boolean";
+
+               if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+                       this._toggle.apply( this, arguments );
+
+               } else if ( fn == null || bool ) {
+                       this.each(function() {
+                               var state = bool ? fn : jQuery(this).is(":hidden");
+                               jQuery(this)[ state ? "show" : "hide" ]();
+                       });
+
+               } else {
+                       this.animate(genFx("toggle", 3), fn, fn2);
+               }
+
+               return this;
+       },
+
+       fadeTo: function( speed, to, callback ) {
+               return this.filter(":hidden").css("opacity", 0).show().end()
+                                       .animate({opacity: to}, speed, callback);
+       },
+
+       animate: function( prop, speed, easing, callback ) {
+               var optall = jQuery.speed(speed, easing, callback);
+
+               if ( jQuery.isEmptyObject( prop ) ) {
+                       return this.each( optall.complete );
+               }
+
+               return this[ optall.queue === false ? "each" : "queue" ](function() {
+                       var opt = jQuery.extend({}, optall), p,
+                               hidden = this.nodeType === 1 && jQuery(this).is(":hidden"),
+                               self = this;
+
+                       for ( p in prop ) {
+                               var name = p.replace(rdashAlpha, fcamelCase);
+
+                               if ( p !== name ) {
+                                       prop[ name ] = prop[ p ];
+                                       delete prop[ p ];
+                                       p = name;
+                               }
+
+                               if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
+                                       return opt.complete.call(this);
+                               }
+
+                               if ( ( p === "height" || p === "width" ) && this.style ) {
+                                       // Store display property
+                                       opt.display = jQuery.css(this, "display");
+
+                                       // Make sure that nothing sneaks out
+                                       opt.overflow = this.style.overflow;
+                               }
+
+                               if ( jQuery.isArray( prop[p] ) ) {
+                                       // Create (if needed) and add to specialEasing
+                                       (opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
+                                       prop[p] = prop[p][0];
+                               }
+                       }
+
+                       if ( opt.overflow != null ) {
+                               this.style.overflow = "hidden";
+                       }
+
+                       opt.curAnim = jQuery.extend({}, prop);
+
+                       jQuery.each( prop, function( name, val ) {
+                               var e = new jQuery.fx( self, opt, name );
+
+                               if ( rfxtypes.test(val) ) {
+                                       e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
+
+                               } else {
+                                       var parts = rfxnum.exec(val),
+                                               start = e.cur(true) || 0;
+
+                                       if ( parts ) {
+                                               var end = parseFloat( parts[2] ),
+                                                       unit = parts[3] || "px";
+
+                                               // We need to compute starting value
+                                               if ( unit !== "px" ) {
+                                                       self.style[ name ] = (end || 1) + unit;
+                                                       start = ((end || 1) / e.cur(true)) * start;
+                                                       self.style[ name ] = start + unit;
+                                               }
+
+                                               // If a +=/-= token was provided, we're doing a relative animation
+                                               if ( parts[1] ) {
+                                                       end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
+                                               }
+
+                                               e.custom( start, end, unit );
+
+                                       } else {
+                                               e.custom( start, val, "" );
+                                       }
+                               }
+                       });
+
+                       // For JS strict compliance
+                       return true;
+               });
+       },
+
+       stop: function( clearQueue, gotoEnd ) {
+               var timers = jQuery.timers;
+
+               if ( clearQueue ) {
+                       this.queue([]);
+               }
+
+               this.each(function() {
+                       // go in reverse order so anything added to the queue during the loop is ignored
+                       for ( var i = timers.length - 1; i >= 0; i-- ) {
+                               if ( timers[i].elem === this ) {
+                                       if (gotoEnd) {
+                                               // force the next step to be the last
+                                               timers[i](true);
+                                       }
+
+                                       timers.splice(i, 1);
+                               }
+                       }
+               });
+
+               // start the next in the queue if the last step wasn't forced
+               if ( !gotoEnd ) {
+                       this.dequeue();
+               }
+
+               return this;
+       }
+
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+       slideDown: genFx("show", 1),
+       slideUp: genFx("hide", 1),
+       slideToggle: genFx("toggle", 1),
+       fadeIn: { opacity: "show" },
+       fadeOut: { opacity: "hide" }
+}, function( name, props ) {
+       jQuery.fn[ name ] = function( speed, callback ) {
+               return this.animate( props, speed, callback );
+       };
+});
+
+jQuery.extend({
+       speed: function( speed, easing, fn ) {
+               var opt = speed && typeof speed === "object" ? speed : {
+                       complete: fn || !fn && easing ||
+                               jQuery.isFunction( speed ) && speed,
+                       duration: speed,
+                       easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
+               };
+
+               opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+                       jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
+
+               // Queueing
+               opt.old = opt.complete;
+               opt.complete = function() {
+                       if ( opt.queue !== false ) {
+                               jQuery(this).dequeue();
+                       }
+                       if ( jQuery.isFunction( opt.old ) ) {
+                               opt.old.call( this );
+                       }
+               };
+
+               return opt;
+       },
+
+       easing: {
+               linear: function( p, n, firstNum, diff ) {
+                       return firstNum + diff * p;
+               },
+               swing: function( p, n, firstNum, diff ) {
+                       return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
+               }
+       },
+
+       timers: [],
+
+       fx: function( elem, options, prop ) {
+               this.options = options;
+               this.elem = elem;
+               this.prop = prop;
+
+               if ( !options.orig ) {
+                       options.orig = {};
+               }
+       }
+
+});
+
+jQuery.fx.prototype = {
+       // Simple function for setting a style value
+       update: function() {
+               if ( this.options.step ) {
+                       this.options.step.call( this.elem, this.now, this );
+               }
+
+               (jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
+
+               // Set display property to block for height/width animations
+               if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) {
+                       this.elem.style.display = "block";
+               }
+       },
+
+       // Get the current size
+       cur: function( force ) {
+               if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
+                       return this.elem[ this.prop ];
+               }
+
+               var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+               return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+       },
+
+       // Start an animation from one number to another
+       custom: function( from, to, unit ) {
+               this.startTime = now();
+               this.start = from;
+               this.end = to;
+               this.unit = unit || this.unit || "px";
+               this.now = this.start;
+               this.pos = this.state = 0;
+
+               var self = this;
+               function t( gotoEnd ) {
+                       return self.step(gotoEnd);
+               }
+
+               t.elem = this.elem;
+
+               if ( t() && jQuery.timers.push(t) && !timerId ) {
+                       timerId = setInterval(jQuery.fx.tick, 13);
+               }
+       },
+
+       // Simple 'show' function
+       show: function() {
+               // Remember where we started, so that we can go back to it later
+               this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+               this.options.show = true;
+
+               // Begin the animation
+               // Make sure that we start at a small width/height to avoid any
+               // flash of content
+               this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
+
+               // Start by showing the element
+               jQuery( this.elem ).show();
+       },
+
+       // Simple 'hide' function
+       hide: function() {
+               // Remember where we started, so that we can go back to it later
+               this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
+               this.options.hide = true;
+
+               // Begin the animation
+               this.custom(this.cur(), 0);
+       },
+
+       // Each step of an animation
+       step: function( gotoEnd ) {
+               var t = now(), done = true;
+
+               if ( gotoEnd || t >= this.options.duration + this.startTime ) {
+                       this.now = this.end;
+                       this.pos = this.state = 1;
+                       this.update();
+
+                       this.options.curAnim[ this.prop ] = true;
+
+                       for ( var i in this.options.curAnim ) {
+                               if ( this.options.curAnim[i] !== true ) {
+                                       done = false;
+                               }
+                       }
+
+                       if ( done ) {
+                               if ( this.options.display != null ) {
+                                       // Reset the overflow
+                                       this.elem.style.overflow = this.options.overflow;
+
+                                       // Reset the display
+                                       var old = jQuery.data(this.elem, "olddisplay");
+                                       this.elem.style.display = old ? old : this.options.display;
+
+                                       if ( jQuery.css(this.elem, "display") === "none" ) {
+                                               this.elem.style.display = "block";
+                                       }
+                               }
+
+                               // Hide the element if the "hide" operation was done
+                               if ( this.options.hide ) {
+                                       jQuery(this.elem).hide();
+                               }
+
+                               // Reset the properties, if the item has been hidden or shown
+                               if ( this.options.hide || this.options.show ) {
+                                       for ( var p in this.options.curAnim ) {
+                                               jQuery.style(this.elem, p, this.options.orig[p]);
+                                       }
+                               }
+
+                               // Execute the complete function
+                               this.options.complete.call( this.elem );
+                       }
+
+                       return false;
+
+               } else {
+                       var n = t - this.startTime;
+                       this.state = n / this.options.duration;
+
+                       // Perform the easing function, defaults to swing
+                       var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
+                       var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
+                       this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
+                       this.now = this.start + ((this.end - this.start) * this.pos);
+
+                       // Perform the next step of the animation
+                       this.update();
+               }
+
+               return true;
+       }
+};
+
+jQuery.extend( jQuery.fx, {
+       tick: function() {
+               var timers = jQuery.timers;
+
+               for ( var i = 0; i < timers.length; i++ ) {
+                       if ( !timers[i]() ) {
+                               timers.splice(i--, 1);
+                       }
+               }
+
+               if ( !timers.length ) {
+                       jQuery.fx.stop();
+               }
+       },
+               
+       stop: function() {
+               clearInterval( timerId );
+               timerId = null;
+       },
+       
+       speeds: {
+               slow: 600,
+               fast: 200,
+               // Default speed
+               _default: 400
+       },
+
+       step: {
+               opacity: function( fx ) {
+                       jQuery.style(fx.elem, "opacity", fx.now);
+               },
+
+               _default: function( fx ) {
+                       if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+                               fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
+                       } else {
+                               fx.elem[ fx.prop ] = fx.now;
+                       }
+               }
+       }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+       jQuery.expr.filters.animated = function( elem ) {
+               return jQuery.grep(jQuery.timers, function( fn ) {
+                       return elem === fn.elem;
+               }).length;
+       };
+}
+
+function genFx( type, num ) {
+       var obj = {};
+
+       jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
+               obj[ this ] = type;
+       });
+
+       return obj;
+}
+if ( "getBoundingClientRect" in document.documentElement ) {
+       jQuery.fn.offset = function( options ) {
+               var elem = this[0];
+
+               if ( options ) { 
+                       return this.each(function( i ) {
+                               jQuery.offset.setOffset( this, options, i );
+                       });
+               }
+
+               if ( !elem || !elem.ownerDocument ) {
+                       return null;
+               }
+
+               if ( elem === elem.ownerDocument.body ) {
+                       return jQuery.offset.bodyOffset( elem );
+               }
+
+               var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,
+                       clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+                       top  = box.top  + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,
+                       left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
+
+               return { top: top, left: left };
+       };
+
+} else {
+       jQuery.fn.offset = function( options ) {
+               var elem = this[0];
+
+               if ( options ) { 
+                       return this.each(function( i ) {
+                               jQuery.offset.setOffset( this, options, i );
+                       });
+               }
+
+               if ( !elem || !elem.ownerDocument ) {
+                       return null;
+               }
+
+               if ( elem === elem.ownerDocument.body ) {
+                       return jQuery.offset.bodyOffset( elem );
+               }
+
+               jQuery.offset.initialize();
+
+               var offsetParent = elem.offsetParent, prevOffsetParent = elem,
+                       doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
+                       body = doc.body, defaultView = doc.defaultView,
+                       prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+                       top = elem.offsetTop, left = elem.offsetLeft;
+
+               while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+                       if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+                               break;
+                       }
+
+                       computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+                       top  -= elem.scrollTop;
+                       left -= elem.scrollLeft;
+
+                       if ( elem === offsetParent ) {
+                               top  += elem.offsetTop;
+                               left += elem.offsetLeft;
+
+                               if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
+                                       top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
+                                       left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+                               }
+
+                               prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
+                       }
+
+                       if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+                               top  += parseFloat( computedStyle.borderTopWidth  ) || 0;
+                               left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+                       }
+
+                       prevComputedStyle = computedStyle;
+               }
+
+               if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+                       top  += body.offsetTop;
+                       left += body.offsetLeft;
+               }
+
+               if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
+                       top  += Math.max( docElem.scrollTop, body.scrollTop );
+                       left += Math.max( docElem.scrollLeft, body.scrollLeft );
+               }
+
+               return { top: top, left: left };
+       };
+}
+
+jQuery.offset = {
+       initialize: function() {
+               var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0,
+                       html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+
+               jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
+
+               container.innerHTML = html;
+               body.insertBefore( container, body.firstChild );
+               innerDiv = container.firstChild;
+               checkDiv = innerDiv.firstChild;
+               td = innerDiv.nextSibling.firstChild.firstChild;
+
+               this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
+               this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
+
+               checkDiv.style.position = "fixed", checkDiv.style.top = "20px";
+               // safari subtracts parent border width here which is 5px
+               this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
+               checkDiv.style.position = checkDiv.style.top = "";
+
+               innerDiv.style.overflow = "hidden", innerDiv.style.position = "relative";
+               this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
+
+               this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
+
+               body.removeChild( container );
+               body = container = innerDiv = checkDiv = table = td = null;
+               jQuery.offset.initialize = jQuery.noop;
+       },
+
+       bodyOffset: function( body ) {
+               var top = body.offsetTop, left = body.offsetLeft;
+
+               jQuery.offset.initialize();
+
+               if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
+                       top  += parseFloat( jQuery.curCSS(body, "marginTop",  true) ) || 0;
+                       left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
+               }
+
+               return { top: top, left: left };
+       },
+       
+       setOffset: function( elem, options, i ) {
+               // set position first, in-case top/left are set even on static elem
+               if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
+                       elem.style.position = "relative";
+               }
+               var curElem   = jQuery( elem ),
+                       curOffset = curElem.offset(),
+                       curTop    = parseInt( jQuery.curCSS( elem, "top",  true ), 10 ) || 0,
+                       curLeft   = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;
+
+               if ( jQuery.isFunction( options ) ) {
+                       options = options.call( elem, i, curOffset );
+               }
+
+               var props = {
+                       top:  (options.top  - curOffset.top)  + curTop,
+                       left: (options.left - curOffset.left) + curLeft
+               };
+               
+               if ( "using" in options ) {
+                       options.using.call( elem, props );
+               } else {
+                       curElem.css( props );
+               }
+       }
+};
+
+
+jQuery.fn.extend({
+       position: function() {
+               if ( !this[0] ) {
+                       return null;
+               }
+
+               var elem = this[0],
+
+               // Get *real* offsetParent
+               offsetParent = this.offsetParent(),
+
+               // Get correct offsets
+               offset       = this.offset(),
+               parentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+               // Subtract element margins
+               // note: when an element has margin: auto the offsetLeft and marginLeft
+               // are the same in Safari causing offset.left to incorrectly be 0
+               offset.top  -= parseFloat( jQuery.curCSS(elem, "marginTop",  true) ) || 0;
+               offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;
+
+               // Add offsetParent borders
+               parentOffset.top  += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth",  true) ) || 0;
+               parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0;
+
+               // Subtract the two offsets
+               return {
+                       top:  offset.top  - parentOffset.top,
+                       left: offset.left - parentOffset.left
+               };
+       },
+
+       offsetParent: function() {
+               return this.map(function() {
+                       var offsetParent = this.offsetParent || document.body;
+                       while ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+                               offsetParent = offsetParent.offsetParent;
+                       }
+                       return offsetParent;
+               });
+       }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+       var method = "scroll" + name;
+
+       jQuery.fn[ method ] = function(val) {
+               var elem = this[0], win;
+               
+               if ( !elem ) {
+                       return null;
+               }
+
+               if ( val !== undefined ) {
+                       // Set the scroll offset
+                       return this.each(function() {
+                               win = getWindow( this );
+
+                               if ( win ) {
+                                       win.scrollTo(
+                                               !i ? val : jQuery(win).scrollLeft(),
+                                                i ? val : jQuery(win).scrollTop()
+                                       );
+
+                               } else {
+                                       this[ method ] = val;
+                               }
+                       });
+               } else {
+                       win = getWindow( elem );
+
+                       // Return the scroll offset
+                       return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
+                               jQuery.support.boxModel && win.document.documentElement[ method ] ||
+                                       win.document.body[ method ] :
+                               elem[ method ];
+               }
+       };
+});
+
+function getWindow( elem ) {
+       return ("scrollTo" in elem && elem.document) ?
+               elem :
+               elem.nodeType === 9 ?
+                       elem.defaultView || elem.parentWindow :
+                       false;
+}
+// Create innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+       var type = name.toLowerCase();
+
+       // innerHeight and innerWidth
+       jQuery.fn["inner" + name] = function() {
+               return this[0] ?
+                       jQuery.css( this[0], type, false, "padding" ) :
+                       null;
+       };
+
+       // outerHeight and outerWidth
+       jQuery.fn["outer" + name] = function( margin ) {
+               return this[0] ?
+                       jQuery.css( this[0], type, false, margin ? "margin" : "border" ) :
+                       null;
+       };
+
+       jQuery.fn[ type ] = function( size ) {
+               // Get window width or height
+               var elem = this[0];
+               if ( !elem ) {
+                       return size == null ? null : this;
+               }
+               
+               if ( jQuery.isFunction( size ) ) {
+                       return this.each(function( i ) {
+                               var self = jQuery( this );
+                               self[ type ]( size.call( this, i, self[ type ]() ) );
+                       });
+               }
+
+               return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
+                       // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
+                       elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
+                       elem.document.body[ "client" + name ] :
+
+                       // Get document width or height
+                       (elem.nodeType === 9) ? // is it a document
+                               // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+                               Math.max(
+                                       elem.documentElement["client" + name],
+                                       elem.body["scroll" + name], elem.documentElement["scroll" + name],
+                                       elem.body["offset" + name], elem.documentElement["offset" + name]
+                               ) :
+
+                               // Get or set width or height on the element
+                               size === undefined ?
+                                       // Get width or height on the element
+                                       jQuery.css( elem, type ) :
+
+                                       // Set the width or height on the element (default to pixels if value is unitless)
+                                       this.css( type, typeof size === "string" ? size : size + "px" );
+       };
+
+});
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+})(window);
diff --git a/docs/jscripts/infusion/lib/jquery/plugins/tooltip/css/jquery.tooltip.css b/docs/jscripts/infusion/lib/jquery/plugins/tooltip/css/jquery.tooltip.css
new file mode 100644 (file)
index 0000000..6af88fd
--- /dev/null
@@ -0,0 +1,16 @@
+/* Tooltip
+----------------------------------*/
+.ui-tooltip {
+    padding:8px;
+    position:absolute;
+    z-index:9999;
+    -o-box-shadow: 0 0 5px #aaa;
+    -moz-box-shadow: 0 0 5px #aaa;
+    -webkit-box-shadow: 0 0 5px #aaa;
+    box-shadow: 0 0 5px #aaa;
+}
+/* Fades and background-images don't work well together in IE6, drop the image */
+* html .ui-tooltip {
+    background-image: none;
+}
+body .ui-tooltip { border-width:2px; }
diff --git a/docs/jscripts/infusion/lib/jquery/plugins/tooltip/js/jquery.ui.tooltip.js b/docs/jscripts/infusion/lib/jquery/plugins/tooltip/js/jquery.ui.tooltip.js
new file mode 100644 (file)
index 0000000..122652b
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * jQuery UI Tooltip @VERSION
+ *
+ * Copyright 2010, AUTHORS.txt
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tooltip
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.widget.js
+ *     jquery.ui.position.js
+ */
+(function($) {
+
+var increments = 0;
+
+$.widget("ui.tooltip", {
+       options: {
+               items: "[title]",
+               content: function() {
+                       return $(this).attr("title");
+               },
+               position: {
+                       my: "left center",
+                       at: "right center",
+                       offset: "15 0"
+               }
+       },
+       _create: function() {
+               var self = this;
+               this.tooltip = $("<div></div>")
+                       .attr("id", "ui-tooltip-" + increments++)
+                       .attr("role", "tooltip")
+                       .attr("aria-hidden", "true")
+                       .addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content")
+                       .appendTo(document.body)
+                       .hide();
+               this.tooltipContent = $("<div></div>")
+                       .addClass("ui-tooltip-content")
+                       .appendTo(this.tooltip);
+               this.opacity = this.tooltip.css("opacity");
+               this.element
+                       .bind("focus.tooltip mouseover.tooltip", function(event) {
+                               self.open( event );
+                       })
+                       .bind("blur.tooltip mouseout.tooltip", function(event) {
+                               self.close( event );
+                       });
+       },
+       
+       enable: function() {
+               this.options.disabled = false;
+       },
+       
+       disable: function() {
+               this.options.disabled = true;
+       },
+       
+       destroy: function() {
+               this.tooltip.remove();
+               $.Widget.prototype.destroy.apply(this, arguments);
+       },
+       
+       widget: function() {
+               return this.element.pushStack(this.tooltip.get());
+       },
+       
+       open: function(event) {
+               var target = $(event && event.target || this.element).closest(this.options.items);
+               // already visible? possible when both focus and mouseover events occur
+               if (this.current && this.current[0] == target[0])
+                       return;
+               var self = this;
+               this.current = target;
+               this.currentTitle = target.attr("title");
+               var content = this.options.content.call(target[0], function(response) {
+                       // IE may instantly serve a cached response, need to give it a chance to finish with _show before that
+                       setTimeout(function() {
+                               // ignore async responses that come in after the tooltip is already hidden
+                               if (self.current == target)
+                                       self._show(event, target, response);
+                       }, 13);
+               });
+               if (content) {
+                       self._show(event, target, content);
+               }
+       },
+       
+       _show: function(event, target, content) {
+               if (!content)
+                       return;
+               
+               target.attr("title", "");
+               
+               if (this.options.disabled)
+                       return;
+                       
+               this.tooltipContent.html(content);
+               this.tooltip.css({
+                       top: 0,
+                       left: 0
+               }).show().position( $.extend({
+                       of: target
+               }, this.options.position )).hide();
+               
+               this.tooltip.attr("aria-hidden", "false");
+               target.attr("aria-describedby", this.tooltip.attr("id"));
+
+               this.tooltip.stop(false, true).fadeIn();
+
+               this._trigger( "open", event );
+       },
+       
+       close: function(event) {
+               if (!this.current)
+                       return;
+               
+               var current = this.current.attr("title", this.currentTitle);
+               this.current = null;
+               
+               if (this.options.disabled)
+                       return;
+               
+               current.removeAttr("aria-describedby");
+               this.tooltip.attr("aria-hidden", "true");
+               
+               this.tooltip.stop(false, true).fadeOut();
+               
+               this._trigger( "close", event );
+       }
+       
+});
+
+})(jQuery);
\ No newline at end of file
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/coal.css b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/coal.css
new file mode 100644 (file)
index 0000000..90d60a1
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* Layout helpers
+----------------------------------*/
+.fl-theme-coal .ui-helper-hidden { display: none; }
+.fl-theme-coal .ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
+.fl-theme-coal .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.fl-theme-coal .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.fl-theme-coal .ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.fl-theme-coal .ui-helper-clearfix { display:block; }
+/* end clearfix */
+.fl-theme-coal .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-coal .ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-coal .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.fl-theme-coal .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=0&bgColorHeader=575757&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=25&borderColorHeader=00000&fcHeader=cccccc&iconColorHeader=cccccc&bgColorContent=cccccc&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=cccccc&fcContent=222222&iconColorContent=222222&bgColorDefault=333333&bgTextureDefault=03_highlight_soft.png&bgImgOpacityDefault=75&borderColorDefault=000000&fcDefault=a3a3a3&iconColorDefault=a3a3a3&bgColorHover=666666&bgTextureHover=03_highlight_soft.png&bgImgOpacityHover=75&borderColorHover=000000&fcHover=ffffff&iconColorHover=ffffff&bgColorActive=000000&bgTextureActive=03_highlight_soft.png&bgImgOpacityActive=65&borderColorActive=000000&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=cccccc&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=55&borderColorHighlight=666666&fcHighlight=000000&iconColorHighlight=333333&bgColorError=fef1ec&bgTextureError=05_inset_soft.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=000000&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=40&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=50&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
+*/
+
+
+/* Component containers
+----------------------------------*/
+.fl-theme-coal .ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-coal .ui-widget input, .fl-theme-coal .ui-widget select, .fl-theme-coal .ui-widget textarea, .fl-theme-coal .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-coal .ui-widget-content { border: 1px solid #cccccc; background: #cccccc url(images/ui-bg_flat_75_cccccc_40x100.png) 50% 50% repeat-x; color: #222222; }
+.fl-theme-coal .ui-widget-content a { color: #222222; }
+.fl-theme-coal .ui-widget-header { border: 1px solid #00000; background: #575757 url(images/ui-bg_highlight-soft_25_575757_1x100.png) 50% 50% repeat-x; color: #cccccc; font-weight: bold; }
+.fl-theme-coal .ui-widget-header a { color: #cccccc; }
+
+/* Interaction states
+----------------------------------*/
+.fl-theme-coal .ui-state-default, .fl-theme-coal .ui-widget-content .ui-state-default { border: 1px solid #000000; background: #333333 url(images/ui-bg_highlight-soft_75_333333_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #a3a3a3; outline: none; }
+.fl-theme-coal .ui-state-default a, .fl-theme-coal .ui-state-default a:link, .fl-theme-coal .ui-state-default a:visited { color: #a3a3a3; text-decoration: none; outline: none; }
+.fl-theme-coal .ui-state-hover, .fl-theme-coal .ui-widget-content .ui-state-hover, .fl-theme-coal .ui-state-focus, .fl-theme-coal .ui-widget-content .ui-state-focus { border: 1px solid #000000; background: #666666 url(images/ui-bg_highlight-soft_75_666666_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; outline: none; }
+.fl-theme-coal .ui-state-hover a, .fl-theme-coal .ui-state-hover a:hover { color: #ffffff; text-decoration: none; outline: none; }
+.fl-theme-coal .ui-state-active, .fl-theme-coal .ui-widget-content .ui-state-active { border: 1px solid #000000; background: #000000 url(images/ui-bg_highlight-soft_65_000000_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; outline: none; }
+.fl-theme-coal .ui-state-active a, .fl-theme-coal .ui-state-active a:link, .fl-theme-coal .ui-state-active a:visited { color: #ffffff; outline: none; text-decoration: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-coal .ui-state-highlight, .fl-theme-coal .ui-widget-content .ui-state-highlight {border: 1px solid #666666; background: #cccccc url(images/ui-bg_highlight-soft_55_cccccc_1x100.png) 50% top repeat-x; color: #000000; }
+.fl-theme-coal .ui-state-highlight a, .fl-theme-coal .ui-widget-content .ui-state-highlight a { color: #000000; }
+.fl-theme-coal .ui-state-error, .fl-theme-coal .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_inset-soft_95_fef1ec_1x100.png) 50% bottom repeat-x; color: #cd0a0a; }
+.fl-theme-coal .ui-state-error a, .fl-theme-coal .ui-widget-content .ui-state-error a { color: #cd0a0a; }
+.fl-theme-coal .ui-state-error-text, .fl-theme-coal .ui-widget-content .ui-state-error-text { color: #cd0a0a; }
+.fl-theme-coal .ui-state-disabled, .fl-theme-coal .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+.fl-theme-coal .ui-priority-primary, .fl-theme-coal .ui-widget-content .ui-priority-primary { font-weight: bold; }
+.fl-theme-coal .ui-priority-secondary, .fl-theme-coal .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-coal .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
+.fl-theme-coal .ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.fl-theme-coal .ui-widget-header .ui-icon {background-image: url(images/ui-icons_cccccc_256x240.png); }
+.fl-theme-coal .ui-state-default .ui-icon { background-image: url(images/ui-icons_a3a3a3_256x240.png); }
+.fl-theme-coal .ui-state-hover .ui-icon, .fl-theme-coal .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.fl-theme-coal .ui-state-active .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.fl-theme-coal .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_333333_256x240.png); }
+.fl-theme-coal .ui-state-error .ui-icon, .fl-theme-coal .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+
+/* positioning */
+.fl-theme-coal .ui-icon-carat-1-n { background-position: 0 0; }
+.fl-theme-coal .ui-icon-carat-1-ne { background-position: -16px 0; }
+.fl-theme-coal .ui-icon-carat-1-e { background-position: -32px 0; }
+.fl-theme-coal .ui-icon-carat-1-se { background-position: -48px 0; }
+.fl-theme-coal .ui-icon-carat-1-s { background-position: -64px 0; }
+.fl-theme-coal .ui-icon-carat-1-sw { background-position: -80px 0; }
+.fl-theme-coal .ui-icon-carat-1-w { background-position: -96px 0; }
+.fl-theme-coal .ui-icon-carat-1-nw { background-position: -112px 0; }
+.fl-theme-coal .ui-icon-carat-2-n-s { background-position: -128px 0; }
+.fl-theme-coal .ui-icon-carat-2-e-w { background-position: -144px 0; }
+.fl-theme-coal .ui-icon-triangle-1-n { background-position: 0 -16px; }
+.fl-theme-coal .ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.fl-theme-coal .ui-icon-triangle-1-e { background-position: -32px -16px; }
+.fl-theme-coal .ui-icon-triangle-1-se { background-position: -48px -16px; }
+.fl-theme-coal .ui-icon-triangle-1-s { background-position: -64px -16px; }
+.fl-theme-coal .ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.fl-theme-coal .ui-icon-triangle-1-w { background-position: -96px -16px; }
+.fl-theme-coal .ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.fl-theme-coal .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.fl-theme-coal .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.fl-theme-coal .ui-icon-arrow-1-n { background-position: 0 -32px; }
+.fl-theme-coal .ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.fl-theme-coal .ui-icon-arrow-1-e { background-position: -32px -32px; }
+.fl-theme-coal .ui-icon-arrow-1-se { background-position: -48px -32px; }
+.fl-theme-coal .ui-icon-arrow-1-s { background-position: -64px -32px; }
+.fl-theme-coal .ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.fl-theme-coal .ui-icon-arrow-1-w { background-position: -96px -32px; }
+.fl-theme-coal .ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.fl-theme-coal .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.fl-theme-coal .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.fl-theme-coal .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.fl-theme-coal .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.fl-theme-coal .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.fl-theme-coal .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.fl-theme-coal .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.fl-theme-coal .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.fl-theme-coal .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.fl-theme-coal .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.fl-theme-coal .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.fl-theme-coal .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.fl-theme-coal .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.fl-theme-coal .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.fl-theme-coal .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.fl-theme-coal .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.fl-theme-coal .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.fl-theme-coal .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.fl-theme-coal .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.fl-theme-coal .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.fl-theme-coal .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.fl-theme-coal .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.fl-theme-coal .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.fl-theme-coal .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.fl-theme-coal .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.fl-theme-coal .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.fl-theme-coal .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.fl-theme-coal .ui-icon-arrow-4 { background-position: 0 -80px; }
+.fl-theme-coal .ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.fl-theme-coal .ui-icon-extlink { background-position: -32px -80px; }
+.fl-theme-coal .ui-icon-newwin { background-position: -48px -80px; }
+.fl-theme-coal .ui-icon-refresh { background-position: -64px -80px; }
+.fl-theme-coal .ui-icon-shuffle { background-position: -80px -80px; }
+.fl-theme-coal .ui-icon-transfer-e-w { background-position: -96px -80px; }
+.fl-theme-coal .ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.fl-theme-coal .ui-icon-folder-collapsed { background-position: 0 -96px; }
+.fl-theme-coal .ui-icon-folder-open { background-position: -16px -96px; }
+.fl-theme-coal .ui-icon-document { background-position: -32px -96px; }
+.fl-theme-coal .ui-icon-document-b { background-position: -48px -96px; }
+.fl-theme-coal .ui-icon-note { background-position: -64px -96px; }
+.fl-theme-coal .ui-icon-mail-closed { background-position: -80px -96px; }
+.fl-theme-coal .ui-icon-mail-open { background-position: -96px -96px; }
+.fl-theme-coal .ui-icon-suitcase { background-position: -112px -96px; }
+.fl-theme-coal .ui-icon-comment { background-position: -128px -96px; }
+.fl-theme-coal .ui-icon-person { background-position: -144px -96px; }
+.fl-theme-coal .ui-icon-print { background-position: -160px -96px; }
+.fl-theme-coal .ui-icon-trash { background-position: -176px -96px; }
+.fl-theme-coal .ui-icon-locked { background-position: -192px -96px; }
+.fl-theme-coal .ui-icon-unlocked { background-position: -208px -96px; }
+.fl-theme-coal .ui-icon-bookmark { background-position: -224px -96px; }
+.fl-theme-coal .ui-icon-tag { background-position: -240px -96px; }
+.fl-theme-coal .ui-icon-home { background-position: 0 -112px; }
+.fl-theme-coal .ui-icon-flag { background-position: -16px -112px; }
+.fl-theme-coal .ui-icon-calendar { background-position: -32px -112px; }
+.fl-theme-coal .ui-icon-cart { background-position: -48px -112px; }
+.fl-theme-coal .ui-icon-pencil { background-position: -64px -112px; }
+.fl-theme-coal .ui-icon-clock { background-position: -80px -112px; }
+.fl-theme-coal .ui-icon-disk { background-position: -96px -112px; }
+.fl-theme-coal .ui-icon-calculator { background-position: -112px -112px; }
+.fl-theme-coal .ui-icon-zoomin { background-position: -128px -112px; }
+.fl-theme-coal .ui-icon-zoomout { background-position: -144px -112px; }
+.fl-theme-coal .ui-icon-search { background-position: -160px -112px; }
+.fl-theme-coal .ui-icon-wrench { background-position: -176px -112px; }
+.fl-theme-coal .ui-icon-gear { background-position: -192px -112px; }
+.fl-theme-coal .ui-icon-heart { background-position: -208px -112px; }
+.fl-theme-coal .ui-icon-star { background-position: -224px -112px; }
+.fl-theme-coal .ui-icon-link { background-position: -240px -112px; }
+.fl-theme-coal .ui-icon-cancel { background-position: 0 -128px; }
+.fl-theme-coal .ui-icon-plus { background-position: -16px -128px; }
+.fl-theme-coal .ui-icon-plusthick { background-position: -32px -128px; }
+.fl-theme-coal .ui-icon-minus { background-position: -48px -128px; }
+.fl-theme-coal .ui-icon-minusthick { background-position: -64px -128px; }
+.fl-theme-coal .ui-icon-close { background-position: -80px -128px; }
+.fl-theme-coal .ui-icon-closethick { background-position: -96px -128px; }
+.fl-theme-coal .ui-icon-key { background-position: -112px -128px; }
+.fl-theme-coal .ui-icon-lightbulb { background-position: -128px -128px; }
+.fl-theme-coal .ui-icon-scissors { background-position: -144px -128px; }
+.fl-theme-coal .ui-icon-clipboard { background-position: -160px -128px; }
+.fl-theme-coal .ui-icon-copy { background-position: -176px -128px; }
+.fl-theme-coal .ui-icon-contact { background-position: -192px -128px; }
+.fl-theme-coal .ui-icon-image { background-position: -208px -128px; }
+.fl-theme-coal .ui-icon-video { background-position: -224px -128px; }
+.fl-theme-coal .ui-icon-script { background-position: -240px -128px; }
+.fl-theme-coal .ui-icon-alert { background-position: 0 -144px; }
+.fl-theme-coal .ui-icon-info { background-position: -16px -144px; }
+.fl-theme-coal .ui-icon-notice { background-position: -32px -144px; }
+.fl-theme-coal .ui-icon-help { background-position: -48px -144px; }
+.fl-theme-coal .ui-icon-check { background-position: -64px -144px; }
+.fl-theme-coal .ui-icon-bullet { background-position: -80px -144px; }
+.fl-theme-coal .ui-icon-radio-off { background-position: -96px -144px; }
+.fl-theme-coal .ui-icon-radio-on { background-position: -112px -144px; }
+.fl-theme-coal .ui-icon-pin-w { background-position: -128px -144px; }
+.fl-theme-coal .ui-icon-pin-s { background-position: -144px -144px; }
+.fl-theme-coal .ui-icon-play { background-position: 0 -160px; }
+.fl-theme-coal .ui-icon-pause { background-position: -16px -160px; }
+.fl-theme-coal .ui-icon-seek-next { background-position: -32px -160px; }
+.fl-theme-coal .ui-icon-seek-prev { background-position: -48px -160px; }
+.fl-theme-coal .ui-icon-seek-end { background-position: -64px -160px; }
+.fl-theme-coal .ui-icon-seek-first { background-position: -80px -160px; }
+.fl-theme-coal .ui-icon-stop { background-position: -96px -160px; }
+.fl-theme-coal .ui-icon-eject { background-position: -112px -160px; }
+.fl-theme-coal .ui-icon-volume-off { background-position: -128px -160px; }
+.fl-theme-coal .ui-icon-volume-on { background-position: -144px -160px; }
+.fl-theme-coal .ui-icon-power { background-position: 0 -176px; }
+.fl-theme-coal .ui-icon-signal-diag { background-position: -16px -176px; }
+.fl-theme-coal .ui-icon-signal { background-position: -32px -176px; }
+.fl-theme-coal .ui-icon-battery-0 { background-position: -48px -176px; }
+.fl-theme-coal .ui-icon-battery-1 { background-position: -64px -176px; }
+.fl-theme-coal .ui-icon-battery-2 { background-position: -80px -176px; }
+.fl-theme-coal .ui-icon-battery-3 { background-position: -96px -176px; }
+.fl-theme-coal .ui-icon-circle-plus { background-position: 0 -192px; }
+.fl-theme-coal .ui-icon-circle-minus { background-position: -16px -192px; }
+.fl-theme-coal .ui-icon-circle-close { background-position: -32px -192px; }
+.fl-theme-coal .ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.fl-theme-coal .ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.fl-theme-coal .ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.fl-theme-coal .ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.fl-theme-coal .ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.fl-theme-coal .ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.fl-theme-coal .ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.fl-theme-coal .ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.fl-theme-coal .ui-icon-circle-zoomin { background-position: -176px -192px; }
+.fl-theme-coal .ui-icon-circle-zoomout { background-position: -192px -192px; }
+.fl-theme-coal .ui-icon-circle-check { background-position: -208px -192px; }
+.fl-theme-coal .ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.fl-theme-coal .ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.fl-theme-coal .ui-icon-circlesmall-close { background-position: -32px -208px; }
+.fl-theme-coal .ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.fl-theme-coal .ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.fl-theme-coal .ui-icon-squaresmall-close { background-position: -80px -208px; }
+.fl-theme-coal .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.fl-theme-coal .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.fl-theme-coal .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.fl-theme-coal .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.fl-theme-coal .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.fl-theme-coal .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.fl-theme-coal .ui-corner-tl { -moz-border-radius-topleft: 0; -webkit-border-top-left-radius: 0; }
+.fl-theme-coal .ui-corner-tr { -moz-border-radius-topright: 0; -webkit-border-top-right-radius: 0; }
+.fl-theme-coal .ui-corner-bl { -moz-border-radius-bottomleft: 0; -webkit-border-bottom-left-radius: 0; }
+.fl-theme-coal .ui-corner-br { -moz-border-radius-bottomright: 0; -webkit-border-bottom-right-radius: 0; }
+.fl-theme-coal .ui-corner-top { -moz-border-radius-topleft: 0; -webkit-border-top-left-radius: 0; -moz-border-radius-topright: 0; -webkit-border-top-right-radius: 0; }
+.fl-theme-coal .ui-corner-bottom { -moz-border-radius-bottomleft: 0; -webkit-border-bottom-left-radius: 0; -moz-border-radius-bottomright: 0; -webkit-border-bottom-right-radius: 0; }
+.fl-theme-coal .ui-corner-right {  -moz-border-radius-topright: 0; -webkit-border-top-right-radius: 0; -moz-border-radius-bottomright: 0; -webkit-border-bottom-right-radius: 0; }
+.fl-theme-coal .ui-corner-left { -moz-border-radius-topleft: 0; -webkit-border-top-left-radius: 0; -moz-border-radius-bottomleft: 0; -webkit-border-bottom-left-radius: 0; }
+.fl-theme-coal .ui-corner-all { -moz-border-radius: 0; -webkit-border-radius: 0; }
+
+/* Overlays */
+.fl-theme-coal .ui-widget-overlay { background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; opacity: .40;filter:Alpha(Opacity=40); }
+.fl-theme-coal .ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; opacity: .50;filter:Alpha(Opacity=50); -moz-border-radius: 5px; -webkit-border-radius: 5px; }/* Accordion
+----------------------------------*/
+.fl-theme-coal .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.fl-theme-coal .ui-accordion .ui-accordion-li-fix { display: inline; }
+.fl-theme-coal .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.fl-theme-coal .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
+.fl-theme-coal .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.fl-theme-coal .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
+.fl-theme-coal .ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker
+----------------------------------*/
+.fl-theme-coal .ui-datepicker { width: 17em; padding: .2em .2em 0; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-prev, .fl-theme-coal .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-prev-hover, .fl-theme-coal .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-prev { left:2px; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-next { right:2px; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-prev span, .fl-theme-coal .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }
+.fl-theme-coal .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
+.fl-theme-coal .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.fl-theme-coal .ui-datepicker select.ui-datepicker-month, 
+.fl-theme-coal .ui-datepicker select.ui-datepicker-year { width: 49%;}
+.fl-theme-coal .ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
+.fl-theme-coal .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.fl-theme-coal .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
+.fl-theme-coal .ui-datepicker td { border: 0; padding: 1px; }
+.fl-theme-coal .ui-datepicker td span, .fl-theme-coal .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.fl-theme-coal .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.fl-theme-coal .ui-datepicker.ui-datepicker-multi { width:auto; }
+.fl-theme-coal .ui-datepicker-multi .ui-datepicker-group { float:left; }
+.fl-theme-coal .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.fl-theme-coal .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.fl-theme-coal .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.fl-theme-coal .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.fl-theme-coal .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.fl-theme-coal .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.fl-theme-coal .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.fl-theme-coal .ui-datepicker-row-break { clear:both; width:100%; }
+
+/* RTL support */
+.fl-theme-coal .ui-datepicker-rtl { direction: rtl; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.fl-theme-coal .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.fl-theme-coal .ui-datepicker-cover {
+    display: none; /*sorry for IE5*/
+    display/**/: block; /*sorry for IE5*/
+    position: absolute; /*must have*/
+    z-index: -1; /*must have*/
+    filter: mask(); /*must have*/
+    top: -4px; /*must have*/
+    left: -4px; /*must have*/
+    width: 200px; /*must have*/
+    height: 200px; /*must have*/
+}/* Dialog
+----------------------------------*/
+.fl-theme-coal .ui-dialog { position: relative; padding: .2em; width: 300px; }
+.fl-theme-coal .ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative;  }
+.fl-theme-coal .ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; } 
+.fl-theme-coal .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.fl-theme-coal .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.fl-theme-coal .ui-dialog .ui-dialog-titlebar-close:hover, .fl-theme-coal .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.fl-theme-coal .ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.fl-theme-coal .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.fl-theme-coal .ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
+.fl-theme-coal .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.fl-theme-coal .ui-draggable .ui-dialog-titlebar { cursor: move; }
+/* Progressbar
+----------------------------------*/
+.fl-theme-coal .ui-progressbar { height:2em; text-align: left; }
+.fl-theme-coal .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
+----------------------------------*/
+.fl-theme-coal .ui-resizable { position: relative;}
+.fl-theme-coal .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.fl-theme-coal .ui-resizable-disabled .ui-resizable-handle, .fl-theme-coal .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.fl-theme-coal .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
+.fl-theme-coal .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
+.fl-theme-coal .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
+.fl-theme-coal .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
+.fl-theme-coal .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.fl-theme-coal .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.fl-theme-coal .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.fl-theme-coal .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
+----------------------------------*/
+.fl-theme-coal .ui-slider { position: relative; text-align: left; }
+.fl-theme-coal .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.fl-theme-coal .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; }
+
+.fl-theme-coal .ui-slider-horizontal { height: .8em; }
+.fl-theme-coal .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.fl-theme-coal .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.fl-theme-coal .ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.fl-theme-coal .ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.fl-theme-coal .ui-slider-vertical { width: .8em; height: 100px; }
+.fl-theme-coal .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.fl-theme-coal .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.fl-theme-coal .ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.fl-theme-coal .ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
+----------------------------------*/
+.fl-theme-coal .ui-tabs { padding: .2em; zoom: 1; }
+.fl-theme-coal .ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; }
+.fl-theme-coal .ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; }
+.fl-theme-coal .ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; }
+.fl-theme-coal .ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; }
+.fl-theme-coal .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .fl-theme-coal .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .fl-theme-coal .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.fl-theme-coal .ui-tabs .ui-tabs-nav li a, .fl-theme-coal .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.fl-theme-coal .ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; }
+.fl-theme-coal .ui-tabs .ui-tabs-hide { display: none !important; }
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_0_000000_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_0_000000_40x100.png
new file mode 100644 (file)
index 0000000..abdc010
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_0_000000_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_75_cccccc_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_75_cccccc_40x100.png
new file mode 100644 (file)
index 0000000..5473aff
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_flat_75_cccccc_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_25_575757_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_25_575757_1x100.png
new file mode 100644 (file)
index 0000000..3a8f50c
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_25_575757_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_55_cccccc_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_55_cccccc_1x100.png
new file mode 100644 (file)
index 0000000..13e421b
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_55_cccccc_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_65_000000_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_65_000000_1x100.png
new file mode 100644 (file)
index 0000000..f07ec5f
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_65_000000_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_333333_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_333333_1x100.png
new file mode 100644 (file)
index 0000000..b932638
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_333333_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_666666_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_666666_1x100.png
new file mode 100644 (file)
index 0000000..053c7aa
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_highlight-soft_75_666666_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_inset-soft_95_fef1ec_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_inset-soft_95_fef1ec_1x100.png
new file mode 100644 (file)
index 0000000..0e05810
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-bg_inset-soft_95_fef1ec_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_222222_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_222222_256x240.png
new file mode 100644 (file)
index 0000000..67560da
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_222222_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_333333_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_333333_256x240.png
new file mode 100644 (file)
index 0000000..4d44b9b
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_333333_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_a3a3a3_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_a3a3a3_256x240.png
new file mode 100644 (file)
index 0000000..6ef54f5
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_a3a3a3_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cccccc_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cccccc_256x240.png
new file mode 100644 (file)
index 0000000..bdb3cf2
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cccccc_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cd0a0a_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cd0a0a_256x240.png
new file mode 100644 (file)
index 0000000..2db88b7
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_cd0a0a_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_ffffff_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_ffffff_256x240.png
new file mode 100644 (file)
index 0000000..746e6fa
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-coal/images/ui-icons_ffffff_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/hc.css b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/hc.css
new file mode 100644 (file)
index 0000000..c4f1b1b
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* Layout helpers
+----------------------------------*/
+.fl-theme-hc .ui-helper-hidden { display: none; }
+.fl-theme-hc .ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
+.fl-theme-hc .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.fl-theme-hc .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.fl-theme-hc .ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.fl-theme-hc .ui-helper-clearfix { display:block; }
+/* end clearfix */
+.fl-theme-hc .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-hc .ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-hc .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.fl-theme-hc .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.2em&cornerRadius=5px&bgColorHeader=ffffff&bgTextureHeader=01_flat.png&bgImgOpacityHeader=0&borderColorHeader=000000&fcHeader=000000&iconColorHeader=000000&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=0&borderColorContent=000000&fcContent=000000&iconColorContent=000000&bgColorDefault=000000&bgTextureDefault=01_flat.png&bgImgOpacityDefault=0&borderColorDefault=000000&fcDefault=ffffff&iconColorDefault=ffffff&bgColorHover=ffffff&bgTextureHover=01_flat.png&bgImgOpacityHover=0&borderColorHover=000000&fcHover=000000&iconColorHover=000000&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=0&borderColorActive=000000&fcActive=000000&iconColorActive=000000&bgColorHighlight=ffffff&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=0&borderColorHighlight=000000&fcHighlight=000000&iconColorHighlight=000000&bgColorError=000000&bgTextureError=01_flat.png&bgImgOpacityError=0&borderColorError=ffffff&fcError=ffffff&iconColorError=ffffff&bgColorOverlay=ffffff&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=75&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=100&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
+*/
+
+
+/* Component containers
+----------------------------------*/
+.fl-theme-hc .ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-hc .ui-widget input, .fl-theme-hc .ui-widget select, .fl-theme-hc .ui-widget textarea, .fl-theme-hc .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-hc .ui-widget-content { border: 1px solid #000000; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; color: #000000; }
+.fl-theme-hc .ui-widget-content a { color: #000000; }
+.fl-theme-hc .ui-widget-header { border: 1px solid #000000; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; color: #000000; font-weight: bold; }
+.fl-theme-hc .ui-widget-header a { color: #000000; }
+
+/* Interaction states
+----------------------------------*/
+.fl-theme-hc .ui-state-default, .fl-theme-hc .ui-widget-content .ui-state-default { border: 1px solid #000000; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; outline: none; }
+.fl-theme-hc .ui-state-default a, .fl-theme-hc .ui-state-default a:link, .fl-theme-hc .ui-state-default a:visited { color: #ffffff; text-decoration: none; outline: none; }
+.fl-theme-hc .ui-state-hover, .fl-theme-hc .ui-widget-content .ui-state-hover, .fl-theme-hc .ui-state-focus, .fl-theme-hc .ui-widget-content .ui-state-focus { border: 1px solid #000000; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #000000; outline: none; }
+.fl-theme-hc .ui-state-hover a, .fl-theme-hc .ui-state-hover a:hover { color: #000000; text-decoration: none; outline: none; }
+.fl-theme-hc .ui-state-active, .fl-theme-hc .ui-widget-content .ui-state-active { border: 1px solid #000000; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #000000; outline: none; }
+.fl-theme-hc .ui-state-active a, .fl-theme-hc .ui-state-active a:link, .fl-theme-hc .ui-state-active a:visited { color: #000000; outline: none; text-decoration: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-hc .ui-state-highlight, .fl-theme-hc .ui-widget-content .ui-state-highlight {border: 1px solid #000000; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; color: #000000; }
+.fl-theme-hc .ui-state-highlight a, .fl-theme-hc .ui-widget-content .ui-state-highlight a { color: #000000; }
+.fl-theme-hc .ui-state-error, .fl-theme-hc .ui-widget-content .ui-state-error {border: 1px solid #ffffff; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; color: #ffffff; }
+.fl-theme-hc .ui-state-error a, .fl-theme-hc .ui-widget-content .ui-state-error a { color: #ffffff; }
+.fl-theme-hc .ui-state-error-text, .fl-theme-hc .ui-widget-content .ui-state-error-text { color: #ffffff; }
+.fl-theme-hc .ui-state-disabled, .fl-theme-hc .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+.fl-theme-hc .ui-priority-primary, .fl-theme-hc .ui-widget-content .ui-priority-primary { font-weight: bold; }
+.fl-theme-hc .ui-priority-secondary, .fl-theme-hc .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-hc .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-hc .ui-widget-content .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-hc .ui-widget-header .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-hc .ui-state-default .ui-icon { background-image: url(images/ui-icons_ffffff_256x240.png); }
+.fl-theme-hc .ui-state-hover .ui-icon, .fl-theme-hc .ui-state-focus .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-hc .ui-state-active .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-hc .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-hc .ui-state-error .ui-icon, .fl-theme-hc .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+
+/* positioning */
+.fl-theme-hc .ui-icon-carat-1-n { background-position: 0 0; }
+.fl-theme-hc .ui-icon-carat-1-ne { background-position: -16px 0; }
+.fl-theme-hc .ui-icon-carat-1-e { background-position: -32px 0; }
+.fl-theme-hc .ui-icon-carat-1-se { background-position: -48px 0; }
+.fl-theme-hc .ui-icon-carat-1-s { background-position: -64px 0; }
+.fl-theme-hc .ui-icon-carat-1-sw { background-position: -80px 0; }
+.fl-theme-hc .ui-icon-carat-1-w { background-position: -96px 0; }
+.fl-theme-hc .ui-icon-carat-1-nw { background-position: -112px 0; }
+.fl-theme-hc .ui-icon-carat-2-n-s { background-position: -128px 0; }
+.fl-theme-hc .ui-icon-carat-2-e-w { background-position: -144px 0; }
+.fl-theme-hc .ui-icon-triangle-1-n { background-position: 0 -16px; }
+.fl-theme-hc .ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.fl-theme-hc .ui-icon-triangle-1-e { background-position: -32px -16px; }
+.fl-theme-hc .ui-icon-triangle-1-se { background-position: -48px -16px; }
+.fl-theme-hc .ui-icon-triangle-1-s { background-position: -64px -16px; }
+.fl-theme-hc .ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.fl-theme-hc .ui-icon-triangle-1-w { background-position: -96px -16px; }
+.fl-theme-hc .ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.fl-theme-hc .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.fl-theme-hc .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.fl-theme-hc .ui-icon-arrow-1-n { background-position: 0 -32px; }
+.fl-theme-hc .ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.fl-theme-hc .ui-icon-arrow-1-e { background-position: -32px -32px; }
+.fl-theme-hc .ui-icon-arrow-1-se { background-position: -48px -32px; }
+.fl-theme-hc .ui-icon-arrow-1-s { background-position: -64px -32px; }
+.fl-theme-hc .ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.fl-theme-hc .ui-icon-arrow-1-w { background-position: -96px -32px; }
+.fl-theme-hc .ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.fl-theme-hc .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.fl-theme-hc .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.fl-theme-hc .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.fl-theme-hc .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.fl-theme-hc .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.fl-theme-hc .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.fl-theme-hc .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.fl-theme-hc .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.fl-theme-hc .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.fl-theme-hc .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.fl-theme-hc .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.fl-theme-hc .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.fl-theme-hc .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.fl-theme-hc .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.fl-theme-hc .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.fl-theme-hc .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.fl-theme-hc .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.fl-theme-hc .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.fl-theme-hc .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.fl-theme-hc .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.fl-theme-hc .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.fl-theme-hc .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.fl-theme-hc .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.fl-theme-hc .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.fl-theme-hc .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.fl-theme-hc .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.fl-theme-hc .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.fl-theme-hc .ui-icon-arrow-4 { background-position: 0 -80px; }
+.fl-theme-hc .ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.fl-theme-hc .ui-icon-extlink { background-position: -32px -80px; }
+.fl-theme-hc .ui-icon-newwin { background-position: -48px -80px; }
+.fl-theme-hc .ui-icon-refresh { background-position: -64px -80px; }
+.fl-theme-hc .ui-icon-shuffle { background-position: -80px -80px; }
+.fl-theme-hc .ui-icon-transfer-e-w { background-position: -96px -80px; }
+.fl-theme-hc .ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.fl-theme-hc .ui-icon-folder-collapsed { background-position: 0 -96px; }
+.fl-theme-hc .ui-icon-folder-open { background-position: -16px -96px; }
+.fl-theme-hc .ui-icon-document { background-position: -32px -96px; }
+.fl-theme-hc .ui-icon-document-b { background-position: -48px -96px; }
+.fl-theme-hc .ui-icon-note { background-position: -64px -96px; }
+.fl-theme-hc .ui-icon-mail-closed { background-position: -80px -96px; }
+.fl-theme-hc .ui-icon-mail-open { background-position: -96px -96px; }
+.fl-theme-hc .ui-icon-suitcase { background-position: -112px -96px; }
+.fl-theme-hc .ui-icon-comment { background-position: -128px -96px; }
+.fl-theme-hc .ui-icon-person { background-position: -144px -96px; }
+.fl-theme-hc .ui-icon-print { background-position: -160px -96px; }
+.fl-theme-hc .ui-icon-trash { background-position: -176px -96px; }
+.fl-theme-hc .ui-icon-locked { background-position: -192px -96px; }
+.fl-theme-hc .ui-icon-unlocked { background-position: -208px -96px; }
+.fl-theme-hc .ui-icon-bookmark { background-position: -224px -96px; }
+.fl-theme-hc .ui-icon-tag { background-position: -240px -96px; }
+.fl-theme-hc .ui-icon-home { background-position: 0 -112px; }
+.fl-theme-hc .ui-icon-flag { background-position: -16px -112px; }
+.fl-theme-hc .ui-icon-calendar { background-position: -32px -112px; }
+.fl-theme-hc .ui-icon-cart { background-position: -48px -112px; }
+.fl-theme-hc .ui-icon-pencil { background-position: -64px -112px; }
+.fl-theme-hc .ui-icon-clock { background-position: -80px -112px; }
+.fl-theme-hc .ui-icon-disk { background-position: -96px -112px; }
+.fl-theme-hc .ui-icon-calculator { background-position: -112px -112px; }
+.fl-theme-hc .ui-icon-zoomin { background-position: -128px -112px; }
+.fl-theme-hc .ui-icon-zoomout { background-position: -144px -112px; }
+.fl-theme-hc .ui-icon-search { background-position: -160px -112px; }
+.fl-theme-hc .ui-icon-wrench { background-position: -176px -112px; }
+.fl-theme-hc .ui-icon-gear { background-position: -192px -112px; }
+.fl-theme-hc .ui-icon-heart { background-position: -208px -112px; }
+.fl-theme-hc .ui-icon-star { background-position: -224px -112px; }
+.fl-theme-hc .ui-icon-link { background-position: -240px -112px; }
+.fl-theme-hc .ui-icon-cancel { background-position: 0 -128px; }
+.fl-theme-hc .ui-icon-plus { background-position: -16px -128px; }
+.fl-theme-hc .ui-icon-plusthick { background-position: -32px -128px; }
+.fl-theme-hc .ui-icon-minus { background-position: -48px -128px; }
+.fl-theme-hc .ui-icon-minusthick { background-position: -64px -128px; }
+.fl-theme-hc .ui-icon-close { background-position: -80px -128px; }
+.fl-theme-hc .ui-icon-closethick { background-position: -96px -128px; }
+.fl-theme-hc .ui-icon-key { background-position: -112px -128px; }
+.fl-theme-hc .ui-icon-lightbulb { background-position: -128px -128px; }
+.fl-theme-hc .ui-icon-scissors { background-position: -144px -128px; }
+.fl-theme-hc .ui-icon-clipboard { background-position: -160px -128px; }
+.fl-theme-hc .ui-icon-copy { background-position: -176px -128px; }
+.fl-theme-hc .ui-icon-contact { background-position: -192px -128px; }
+.fl-theme-hc .ui-icon-image { background-position: -208px -128px; }
+.fl-theme-hc .ui-icon-video { background-position: -224px -128px; }
+.fl-theme-hc .ui-icon-script { background-position: -240px -128px; }
+.fl-theme-hc .ui-icon-alert { background-position: 0 -144px; }
+.fl-theme-hc .ui-icon-info { background-position: -16px -144px; }
+.fl-theme-hc .ui-icon-notice { background-position: -32px -144px; }
+.fl-theme-hc .ui-icon-help { background-position: -48px -144px; }
+.fl-theme-hc .ui-icon-check { background-position: -64px -144px; }
+.fl-theme-hc .ui-icon-bullet { background-position: -80px -144px; }
+.fl-theme-hc .ui-icon-radio-off { background-position: -96px -144px; }
+.fl-theme-hc .ui-icon-radio-on { background-position: -112px -144px; }
+.fl-theme-hc .ui-icon-pin-w { background-position: -128px -144px; }
+.fl-theme-hc .ui-icon-pin-s { background-position: -144px -144px; }
+.fl-theme-hc .ui-icon-play { background-position: 0 -160px; }
+.fl-theme-hc .ui-icon-pause { background-position: -16px -160px; }
+.fl-theme-hc .ui-icon-seek-next { background-position: -32px -160px; }
+.fl-theme-hc .ui-icon-seek-prev { background-position: -48px -160px; }
+.fl-theme-hc .ui-icon-seek-end { background-position: -64px -160px; }
+.fl-theme-hc .ui-icon-seek-first { background-position: -80px -160px; }
+.fl-theme-hc .ui-icon-stop { background-position: -96px -160px; }
+.fl-theme-hc .ui-icon-eject { background-position: -112px -160px; }
+.fl-theme-hc .ui-icon-volume-off { background-position: -128px -160px; }
+.fl-theme-hc .ui-icon-volume-on { background-position: -144px -160px; }
+.fl-theme-hc .ui-icon-power { background-position: 0 -176px; }
+.fl-theme-hc .ui-icon-signal-diag { background-position: -16px -176px; }
+.fl-theme-hc .ui-icon-signal { background-position: -32px -176px; }
+.fl-theme-hc .ui-icon-battery-0 { background-position: -48px -176px; }
+.fl-theme-hc .ui-icon-battery-1 { background-position: -64px -176px; }
+.fl-theme-hc .ui-icon-battery-2 { background-position: -80px -176px; }
+.fl-theme-hc .ui-icon-battery-3 { background-position: -96px -176px; }
+.fl-theme-hc .ui-icon-circle-plus { background-position: 0 -192px; }
+.fl-theme-hc .ui-icon-circle-minus { background-position: -16px -192px; }
+.fl-theme-hc .ui-icon-circle-close { background-position: -32px -192px; }
+.fl-theme-hc .ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.fl-theme-hc .ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.fl-theme-hc .ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.fl-theme-hc .ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.fl-theme-hc .ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.fl-theme-hc .ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.fl-theme-hc .ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.fl-theme-hc .ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.fl-theme-hc .ui-icon-circle-zoomin { background-position: -176px -192px; }
+.fl-theme-hc .ui-icon-circle-zoomout { background-position: -192px -192px; }
+.fl-theme-hc .ui-icon-circle-check { background-position: -208px -192px; }
+.fl-theme-hc .ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.fl-theme-hc .ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.fl-theme-hc .ui-icon-circlesmall-close { background-position: -32px -208px; }
+.fl-theme-hc .ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.fl-theme-hc .ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.fl-theme-hc .ui-icon-squaresmall-close { background-position: -80px -208px; }
+.fl-theme-hc .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.fl-theme-hc .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.fl-theme-hc .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.fl-theme-hc .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.fl-theme-hc .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.fl-theme-hc .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.fl-theme-hc .ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; }
+.fl-theme-hc .ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
+.fl-theme-hc .ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
+.fl-theme-hc .ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
+.fl-theme-hc .ui-corner-top { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
+.fl-theme-hc .ui-corner-bottom { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
+.fl-theme-hc .ui-corner-right {  -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
+.fl-theme-hc .ui-corner-left { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
+.fl-theme-hc .ui-corner-all { -moz-border-radius: 5px; -webkit-border-radius: 5px; }
+
+/* Overlays */
+.fl-theme-hc .ui-widget-overlay { background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; opacity: .75;filter:Alpha(Opacity=75); }
+.fl-theme-hc .ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; opacity: 100;filter:Alpha(Opacity=100); -moz-border-radius: 5px; -webkit-border-radius: 5px; }/* Accordion
+----------------------------------*/
+.fl-theme-hc .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.fl-theme-hc .ui-accordion .ui-accordion-li-fix { display: inline; }
+.fl-theme-hc .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.fl-theme-hc .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
+.fl-theme-hc .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.fl-theme-hc .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
+.fl-theme-hc .ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker
+----------------------------------*/
+.fl-theme-hc .ui-datepicker { width: 17em; padding: .2em .2em 0; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-prev, .fl-theme-hc .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-prev-hover, .fl-theme-hc .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-prev { left:2px; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-next { right:2px; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-prev span, .fl-theme-hc .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }
+.fl-theme-hc .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
+.fl-theme-hc .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.fl-theme-hc .ui-datepicker select.ui-datepicker-month, 
+.fl-theme-hc .ui-datepicker select.ui-datepicker-year { width: 49%;}
+.fl-theme-hc .ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
+.fl-theme-hc .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.fl-theme-hc .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
+.fl-theme-hc .ui-datepicker td { border: 0; padding: 1px; }
+.fl-theme-hc .ui-datepicker td span, .fl-theme-hc .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.fl-theme-hc .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.fl-theme-hc .ui-datepicker.ui-datepicker-multi { width:auto; }
+.fl-theme-hc .ui-datepicker-multi .ui-datepicker-group { float:left; }
+.fl-theme-hc .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.fl-theme-hc .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.fl-theme-hc .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.fl-theme-hc .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.fl-theme-hc .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.fl-theme-hc .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.fl-theme-hc .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.fl-theme-hc .ui-datepicker-row-break { clear:both; width:100%; }
+
+/* RTL support */
+.fl-theme-hc .ui-datepicker-rtl { direction: rtl; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.fl-theme-hc .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.fl-theme-hc .ui-datepicker-cover {
+    display: none; /*sorry for IE5*/
+    display/**/: block; /*sorry for IE5*/
+    position: absolute; /*must have*/
+    z-index: -1; /*must have*/
+    filter: mask(); /*must have*/
+    top: -4px; /*must have*/
+    left: -4px; /*must have*/
+    width: 200px; /*must have*/
+    height: 200px; /*must have*/
+}/* Dialog
+----------------------------------*/
+.fl-theme-hc .ui-dialog { position: relative; padding: .2em; width: 300px; }
+.fl-theme-hc .ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative;  }
+.fl-theme-hc .ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; } 
+.fl-theme-hc .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.fl-theme-hc .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.fl-theme-hc .ui-dialog .ui-dialog-titlebar-close:hover, .fl-theme-hc .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.fl-theme-hc .ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.fl-theme-hc .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.fl-theme-hc .ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
+.fl-theme-hc .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.fl-theme-hc .ui-draggable .ui-dialog-titlebar { cursor: move; }
+/* Progressbar
+----------------------------------*/
+.fl-theme-hc .ui-progressbar { height:2em; text-align: left; }
+.fl-theme-hc .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
+----------------------------------*/
+.fl-theme-hc .ui-resizable { position: relative;}
+.fl-theme-hc .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.fl-theme-hc .ui-resizable-disabled .ui-resizable-handle, .fl-theme-hc .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.fl-theme-hc .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
+.fl-theme-hc .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
+.fl-theme-hc .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
+.fl-theme-hc .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
+.fl-theme-hc .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.fl-theme-hc .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.fl-theme-hc .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.fl-theme-hc .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
+----------------------------------*/
+.fl-theme-hc .ui-slider { position: relative; text-align: left; }
+.fl-theme-hc .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.fl-theme-hc .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; }
+
+.fl-theme-hc .ui-slider-horizontal { height: .8em; }
+.fl-theme-hc .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.fl-theme-hc .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.fl-theme-hc .ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.fl-theme-hc .ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.fl-theme-hc .ui-slider-vertical { width: .8em; height: 100px; }
+.fl-theme-hc .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.fl-theme-hc .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.fl-theme-hc .ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.fl-theme-hc .ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
+----------------------------------*/
+.fl-theme-hc .ui-tabs { padding: .2em; zoom: 1; }
+.fl-theme-hc .ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; }
+.fl-theme-hc .ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; }
+.fl-theme-hc .ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; }
+.fl-theme-hc .ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; }
+.fl-theme-hc .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .fl-theme-hc .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .fl-theme-hc .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.fl-theme-hc .ui-tabs .ui-tabs-nav li a, .fl-theme-hc .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.fl-theme-hc .ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; }
+.fl-theme-hc .ui-tabs .ui-tabs-hide { display: none !important; }
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_000000_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_000000_40x100.png
new file mode 100644 (file)
index 0000000..abdc010
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_000000_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_ffffff_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_ffffff_40x100.png
new file mode 100644 (file)
index 0000000..ac8b229
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-bg_flat_0_ffffff_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_000000_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_000000_256x240.png
new file mode 100644 (file)
index 0000000..842156d
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_000000_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_ffffff_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_ffffff_256x240.png
new file mode 100644 (file)
index 0000000..746e6fa
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hc/images/ui-icons_ffffff_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/hci.css b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/hci.css
new file mode 100644 (file)
index 0000000..f26f101
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* Layout helpers
+----------------------------------*/
+.fl-theme-hci .ui-helper-hidden { display: none; }
+.fl-theme-hci .ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
+.fl-theme-hci .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.fl-theme-hci .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.fl-theme-hci .ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.fl-theme-hci .ui-helper-clearfix { display:block; }
+/* end clearfix */
+.fl-theme-hci .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-hci .ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-hci .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.fl-theme-hci .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1em&cornerRadius=5px&bgColorHeader=999999&bgTextureHeader=01_flat.png&bgImgOpacityHeader=0&borderColorHeader=ffffff&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=000000&bgTextureContent=01_flat.png&bgImgOpacityContent=0&borderColorContent=ffffff&fcContent=ffffff&iconColorContent=ffffff&bgColorDefault=ffffff&bgTextureDefault=01_flat.png&bgImgOpacityDefault=0&borderColorDefault=000000&fcDefault=000000&iconColorDefault=000000&bgColorHover=000000&bgTextureHover=01_flat.png&bgImgOpacityHover=0&borderColorHover=ffffff&fcHover=ffffff&iconColorHover=fffff&bgColorActive=000000&bgTextureActive=01_flat.png&bgImgOpacityActive=0&borderColorActive=ffffff&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=000000&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=0&borderColorHighlight=ffffff&fcHighlight=ffffff&iconColorHighlight=ffffff&bgColorError=ffffff&bgTextureError=01_flat.png&bgImgOpacityError=0&borderColorError=000000&fcError=000000&iconColorError=000000&bgColorOverlay=000000&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=75&bgColorShadow=ffffff&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=100&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
+*/
+
+
+/* Component containers
+----------------------------------*/
+.fl-theme-hci .ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-hci .ui-widget input, .fl-theme-hci .ui-widget select, .fl-theme-hci .ui-widget textarea, .fl-theme-hci .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-hci .ui-widget-content { border: 1px solid #ffffff; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; color: #ffffff; }
+.fl-theme-hci .ui-widget-content a { color: #ffffff; }
+.fl-theme-hci .ui-widget-header { border: 1px solid #ffffff; background: #999999 url(images/ui-bg_flat_0_999999_40x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
+.fl-theme-hci .ui-widget-header a { color: #ffffff; }
+
+/* Interaction states
+----------------------------------*/
+.fl-theme-hci .ui-state-default, .fl-theme-hci .ui-widget-content .ui-state-default { border: 1px solid #000000; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #000000; outline: none; }
+.fl-theme-hci .ui-state-default a, .fl-theme-hci .ui-state-default a:link, .fl-theme-hci .ui-state-default a:visited { color: #000000; text-decoration: none; outline: none; }
+.fl-theme-hci .ui-state-hover, .fl-theme-hci .ui-widget-content .ui-state-hover, .fl-theme-hci .ui-state-focus, .fl-theme-hci .ui-widget-content .ui-state-focus { border: 1px solid #ffffff; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; outline: none; }
+.fl-theme-hci .ui-state-hover a, .fl-theme-hci .ui-state-hover a:hover { color: #ffffff; text-decoration: none; outline: none; }
+.fl-theme-hci .ui-state-active, .fl-theme-hci .ui-widget-content .ui-state-active { border: 1px solid #ffffff; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; outline: none; }
+.fl-theme-hci .ui-state-active a, .fl-theme-hci .ui-state-active a:link, .fl-theme-hci .ui-state-active a:visited { color: #ffffff; outline: none; text-decoration: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-hci .ui-state-highlight, .fl-theme-hci .ui-widget-content .ui-state-highlight {border: 1px solid #ffffff; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; color: #ffffff; }
+.fl-theme-hci .ui-state-highlight a, .fl-theme-hci .ui-widget-content .ui-state-highlight a { color: #ffffff; }
+.fl-theme-hci .ui-state-error, .fl-theme-hci .ui-widget-content .ui-state-error {border: 1px solid #000000; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; color: #000000; }
+.fl-theme-hci .ui-state-error a, .fl-theme-hci .ui-widget-content .ui-state-error a { color: #000000; }
+.fl-theme-hci .ui-state-error-text, .fl-theme-hci .ui-widget-content .ui-state-error-text { color: #000000; }
+.fl-theme-hci .ui-state-disabled, .fl-theme-hci .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+.fl-theme-hci .ui-priority-primary, .fl-theme-hci .ui-widget-content .ui-priority-primary { font-weight: bold; }
+.fl-theme-hci .ui-priority-secondary, .fl-theme-hci .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-hci .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_ffffff_256x240.png); }
+.fl-theme-hci .ui-widget-content .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.fl-theme-hci .ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.fl-theme-hci .ui-state-default .ui-icon { background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-hci .ui-state-hover .ui-icon, .fl-theme-hci .ui-state-focus .ui-icon {background-image: url(images/ui-icons_fffff_256x240.png); }
+.fl-theme-hci .ui-state-active .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.fl-theme-hci .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.fl-theme-hci .ui-state-error .ui-icon, .fl-theme-hci .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+
+/* positioning */
+.fl-theme-hci .ui-icon-carat-1-n { background-position: 0 0; }
+.fl-theme-hci .ui-icon-carat-1-ne { background-position: -16px 0; }
+.fl-theme-hci .ui-icon-carat-1-e { background-position: -32px 0; }
+.fl-theme-hci .ui-icon-carat-1-se { background-position: -48px 0; }
+.fl-theme-hci .ui-icon-carat-1-s { background-position: -64px 0; }
+.fl-theme-hci .ui-icon-carat-1-sw { background-position: -80px 0; }
+.fl-theme-hci .ui-icon-carat-1-w { background-position: -96px 0; }
+.fl-theme-hci .ui-icon-carat-1-nw { background-position: -112px 0; }
+.fl-theme-hci .ui-icon-carat-2-n-s { background-position: -128px 0; }
+.fl-theme-hci .ui-icon-carat-2-e-w { background-position: -144px 0; }
+.fl-theme-hci .ui-icon-triangle-1-n { background-position: 0 -16px; }
+.fl-theme-hci .ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.fl-theme-hci .ui-icon-triangle-1-e { background-position: -32px -16px; }
+.fl-theme-hci .ui-icon-triangle-1-se { background-position: -48px -16px; }
+.fl-theme-hci .ui-icon-triangle-1-s { background-position: -64px -16px; }
+.fl-theme-hci .ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.fl-theme-hci .ui-icon-triangle-1-w { background-position: -96px -16px; }
+.fl-theme-hci .ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.fl-theme-hci .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.fl-theme-hci .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.fl-theme-hci .ui-icon-arrow-1-n { background-position: 0 -32px; }
+.fl-theme-hci .ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.fl-theme-hci .ui-icon-arrow-1-e { background-position: -32px -32px; }
+.fl-theme-hci .ui-icon-arrow-1-se { background-position: -48px -32px; }
+.fl-theme-hci .ui-icon-arrow-1-s { background-position: -64px -32px; }
+.fl-theme-hci .ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.fl-theme-hci .ui-icon-arrow-1-w { background-position: -96px -32px; }
+.fl-theme-hci .ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.fl-theme-hci .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.fl-theme-hci .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.fl-theme-hci .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.fl-theme-hci .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.fl-theme-hci .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.fl-theme-hci .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.fl-theme-hci .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.fl-theme-hci .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.fl-theme-hci .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.fl-theme-hci .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.fl-theme-hci .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.fl-theme-hci .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.fl-theme-hci .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.fl-theme-hci .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.fl-theme-hci .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.fl-theme-hci .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.fl-theme-hci .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.fl-theme-hci .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.fl-theme-hci .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.fl-theme-hci .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.fl-theme-hci .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.fl-theme-hci .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.fl-theme-hci .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.fl-theme-hci .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.fl-theme-hci .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.fl-theme-hci .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.fl-theme-hci .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.fl-theme-hci .ui-icon-arrow-4 { background-position: 0 -80px; }
+.fl-theme-hci .ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.fl-theme-hci .ui-icon-extlink { background-position: -32px -80px; }
+.fl-theme-hci .ui-icon-newwin { background-position: -48px -80px; }
+.fl-theme-hci .ui-icon-refresh { background-position: -64px -80px; }
+.fl-theme-hci .ui-icon-shuffle { background-position: -80px -80px; }
+.fl-theme-hci .ui-icon-transfer-e-w { background-position: -96px -80px; }
+.fl-theme-hci .ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.fl-theme-hci .ui-icon-folder-collapsed { background-position: 0 -96px; }
+.fl-theme-hci .ui-icon-folder-open { background-position: -16px -96px; }
+.fl-theme-hci .ui-icon-document { background-position: -32px -96px; }
+.fl-theme-hci .ui-icon-document-b { background-position: -48px -96px; }
+.fl-theme-hci .ui-icon-note { background-position: -64px -96px; }
+.fl-theme-hci .ui-icon-mail-closed { background-position: -80px -96px; }
+.fl-theme-hci .ui-icon-mail-open { background-position: -96px -96px; }
+.fl-theme-hci .ui-icon-suitcase { background-position: -112px -96px; }
+.fl-theme-hci .ui-icon-comment { background-position: -128px -96px; }
+.fl-theme-hci .ui-icon-person { background-position: -144px -96px; }
+.fl-theme-hci .ui-icon-print { background-position: -160px -96px; }
+.fl-theme-hci .ui-icon-trash { background-position: -176px -96px; }
+.fl-theme-hci .ui-icon-locked { background-position: -192px -96px; }
+.fl-theme-hci .ui-icon-unlocked { background-position: -208px -96px; }
+.fl-theme-hci .ui-icon-bookmark { background-position: -224px -96px; }
+.fl-theme-hci .ui-icon-tag { background-position: -240px -96px; }
+.fl-theme-hci .ui-icon-home { background-position: 0 -112px; }
+.fl-theme-hci .ui-icon-flag { background-position: -16px -112px; }
+.fl-theme-hci .ui-icon-calendar { background-position: -32px -112px; }
+.fl-theme-hci .ui-icon-cart { background-position: -48px -112px; }
+.fl-theme-hci .ui-icon-pencil { background-position: -64px -112px; }
+.fl-theme-hci .ui-icon-clock { background-position: -80px -112px; }
+.fl-theme-hci .ui-icon-disk { background-position: -96px -112px; }
+.fl-theme-hci .ui-icon-calculator { background-position: -112px -112px; }
+.fl-theme-hci .ui-icon-zoomin { background-position: -128px -112px; }
+.fl-theme-hci .ui-icon-zoomout { background-position: -144px -112px; }
+.fl-theme-hci .ui-icon-search { background-position: -160px -112px; }
+.fl-theme-hci .ui-icon-wrench { background-position: -176px -112px; }
+.fl-theme-hci .ui-icon-gear { background-position: -192px -112px; }
+.fl-theme-hci .ui-icon-heart { background-position: -208px -112px; }
+.fl-theme-hci .ui-icon-star { background-position: -224px -112px; }
+.fl-theme-hci .ui-icon-link { background-position: -240px -112px; }
+.fl-theme-hci .ui-icon-cancel { background-position: 0 -128px; }
+.fl-theme-hci .ui-icon-plus { background-position: -16px -128px; }
+.fl-theme-hci .ui-icon-plusthick { background-position: -32px -128px; }
+.fl-theme-hci .ui-icon-minus { background-position: -48px -128px; }
+.fl-theme-hci .ui-icon-minusthick { background-position: -64px -128px; }
+.fl-theme-hci .ui-icon-close { background-position: -80px -128px; }
+.fl-theme-hci .ui-icon-closethick { background-position: -96px -128px; }
+.fl-theme-hci .ui-icon-key { background-position: -112px -128px; }
+.fl-theme-hci .ui-icon-lightbulb { background-position: -128px -128px; }
+.fl-theme-hci .ui-icon-scissors { background-position: -144px -128px; }
+.fl-theme-hci .ui-icon-clipboard { background-position: -160px -128px; }
+.fl-theme-hci .ui-icon-copy { background-position: -176px -128px; }
+.fl-theme-hci .ui-icon-contact { background-position: -192px -128px; }
+.fl-theme-hci .ui-icon-image { background-position: -208px -128px; }
+.fl-theme-hci .ui-icon-video { background-position: -224px -128px; }
+.fl-theme-hci .ui-icon-script { background-position: -240px -128px; }
+.fl-theme-hci .ui-icon-alert { background-position: 0 -144px; }
+.fl-theme-hci .ui-icon-info { background-position: -16px -144px; }
+.fl-theme-hci .ui-icon-notice { background-position: -32px -144px; }
+.fl-theme-hci .ui-icon-help { background-position: -48px -144px; }
+.fl-theme-hci .ui-icon-check { background-position: -64px -144px; }
+.fl-theme-hci .ui-icon-bullet { background-position: -80px -144px; }
+.fl-theme-hci .ui-icon-radio-off { background-position: -96px -144px; }
+.fl-theme-hci .ui-icon-radio-on { background-position: -112px -144px; }
+.fl-theme-hci .ui-icon-pin-w { background-position: -128px -144px; }
+.fl-theme-hci .ui-icon-pin-s { background-position: -144px -144px; }
+.fl-theme-hci .ui-icon-play { background-position: 0 -160px; }
+.fl-theme-hci .ui-icon-pause { background-position: -16px -160px; }
+.fl-theme-hci .ui-icon-seek-next { background-position: -32px -160px; }
+.fl-theme-hci .ui-icon-seek-prev { background-position: -48px -160px; }
+.fl-theme-hci .ui-icon-seek-end { background-position: -64px -160px; }
+.fl-theme-hci .ui-icon-seek-first { background-position: -80px -160px; }
+.fl-theme-hci .ui-icon-stop { background-position: -96px -160px; }
+.fl-theme-hci .ui-icon-eject { background-position: -112px -160px; }
+.fl-theme-hci .ui-icon-volume-off { background-position: -128px -160px; }
+.fl-theme-hci .ui-icon-volume-on { background-position: -144px -160px; }
+.fl-theme-hci .ui-icon-power { background-position: 0 -176px; }
+.fl-theme-hci .ui-icon-signal-diag { background-position: -16px -176px; }
+.fl-theme-hci .ui-icon-signal { background-position: -32px -176px; }
+.fl-theme-hci .ui-icon-battery-0 { background-position: -48px -176px; }
+.fl-theme-hci .ui-icon-battery-1 { background-position: -64px -176px; }
+.fl-theme-hci .ui-icon-battery-2 { background-position: -80px -176px; }
+.fl-theme-hci .ui-icon-battery-3 { background-position: -96px -176px; }
+.fl-theme-hci .ui-icon-circle-plus { background-position: 0 -192px; }
+.fl-theme-hci .ui-icon-circle-minus { background-position: -16px -192px; }
+.fl-theme-hci .ui-icon-circle-close { background-position: -32px -192px; }
+.fl-theme-hci .ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.fl-theme-hci .ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.fl-theme-hci .ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.fl-theme-hci .ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.fl-theme-hci .ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.fl-theme-hci .ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.fl-theme-hci .ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.fl-theme-hci .ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.fl-theme-hci .ui-icon-circle-zoomin { background-position: -176px -192px; }
+.fl-theme-hci .ui-icon-circle-zoomout { background-position: -192px -192px; }
+.fl-theme-hci .ui-icon-circle-check { background-position: -208px -192px; }
+.fl-theme-hci .ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.fl-theme-hci .ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.fl-theme-hci .ui-icon-circlesmall-close { background-position: -32px -208px; }
+.fl-theme-hci .ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.fl-theme-hci .ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.fl-theme-hci .ui-icon-squaresmall-close { background-position: -80px -208px; }
+.fl-theme-hci .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.fl-theme-hci .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.fl-theme-hci .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.fl-theme-hci .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.fl-theme-hci .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.fl-theme-hci .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.fl-theme-hci .ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; }
+.fl-theme-hci .ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
+.fl-theme-hci .ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
+.fl-theme-hci .ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
+.fl-theme-hci .ui-corner-top { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
+.fl-theme-hci .ui-corner-bottom { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
+.fl-theme-hci .ui-corner-right {  -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
+.fl-theme-hci .ui-corner-left { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
+.fl-theme-hci .ui-corner-all { -moz-border-radius: 5px; -webkit-border-radius: 5px; }
+
+/* Overlays */
+.fl-theme-hci .ui-widget-overlay { background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; opacity: .75;filter:Alpha(Opacity=75); }
+.fl-theme-hci .ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; opacity: 100;filter:Alpha(Opacity=100); -moz-border-radius: 5px; -webkit-border-radius: 5px; }/* Accordion
+----------------------------------*/
+.fl-theme-hci .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.fl-theme-hci .ui-accordion .ui-accordion-li-fix { display: inline; }
+.fl-theme-hci .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.fl-theme-hci .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
+.fl-theme-hci .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.fl-theme-hci .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
+.fl-theme-hci .ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker
+----------------------------------*/
+.fl-theme-hci .ui-datepicker { width: 17em; padding: .2em .2em 0; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-prev, .fl-theme-hci .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-prev-hover, .fl-theme-hci .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-prev { left:2px; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-next { right:2px; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-prev span, .fl-theme-hci .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }
+.fl-theme-hci .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
+.fl-theme-hci .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.fl-theme-hci .ui-datepicker select.ui-datepicker-month, 
+.fl-theme-hci .ui-datepicker select.ui-datepicker-year { width: 49%;}
+.fl-theme-hci .ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
+.fl-theme-hci .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.fl-theme-hci .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
+.fl-theme-hci .ui-datepicker td { border: 0; padding: 1px; }
+.fl-theme-hci .ui-datepicker td span, .fl-theme-hci .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.fl-theme-hci .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.fl-theme-hci .ui-datepicker.ui-datepicker-multi { width:auto; }
+.fl-theme-hci .ui-datepicker-multi .ui-datepicker-group { float:left; }
+.fl-theme-hci .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.fl-theme-hci .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.fl-theme-hci .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.fl-theme-hci .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.fl-theme-hci .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.fl-theme-hci .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.fl-theme-hci .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.fl-theme-hci .ui-datepicker-row-break { clear:both; width:100%; }
+
+/* RTL support */
+.fl-theme-hci .ui-datepicker-rtl { direction: rtl; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.fl-theme-hci .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.fl-theme-hci .ui-datepicker-cover {
+    display: none; /*sorry for IE5*/
+    display/**/: block; /*sorry for IE5*/
+    position: absolute; /*must have*/
+    z-index: -1; /*must have*/
+    filter: mask(); /*must have*/
+    top: -4px; /*must have*/
+    left: -4px; /*must have*/
+    width: 200px; /*must have*/
+    height: 200px; /*must have*/
+}/* Dialog
+----------------------------------*/
+.fl-theme-hci .ui-dialog { position: relative; padding: .2em; width: 300px; }
+.fl-theme-hci .ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative;  }
+.fl-theme-hci .ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; } 
+.fl-theme-hci .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.fl-theme-hci .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.fl-theme-hci .ui-dialog .ui-dialog-titlebar-close:hover, .fl-theme-hci .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.fl-theme-hci .ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.fl-theme-hci .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.fl-theme-hci .ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
+.fl-theme-hci .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.fl-theme-hci .ui-draggable .ui-dialog-titlebar { cursor: move; }
+/* Progressbar
+----------------------------------*/
+.fl-theme-hci .ui-progressbar { height:2em; text-align: left; }
+.fl-theme-hci .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
+----------------------------------*/
+.fl-theme-hci .ui-resizable { position: relative;}
+.fl-theme-hci .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.fl-theme-hci .ui-resizable-disabled .ui-resizable-handle, .fl-theme-hci .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.fl-theme-hci .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
+.fl-theme-hci .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
+.fl-theme-hci .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
+.fl-theme-hci .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
+.fl-theme-hci .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.fl-theme-hci .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.fl-theme-hci .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.fl-theme-hci .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
+----------------------------------*/
+.fl-theme-hci .ui-slider { position: relative; text-align: left; }
+.fl-theme-hci .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.fl-theme-hci .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; }
+
+.fl-theme-hci .ui-slider-horizontal { height: .8em; }
+.fl-theme-hci .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.fl-theme-hci .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.fl-theme-hci .ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.fl-theme-hci .ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.fl-theme-hci .ui-slider-vertical { width: .8em; height: 100px; }
+.fl-theme-hci .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.fl-theme-hci .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.fl-theme-hci .ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.fl-theme-hci .ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
+----------------------------------*/
+.fl-theme-hci .ui-tabs { padding: .2em; zoom: 1; }
+.fl-theme-hci .ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; }
+.fl-theme-hci .ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; }
+.fl-theme-hci .ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; }
+.fl-theme-hci .ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; }
+.fl-theme-hci .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .fl-theme-hci .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .fl-theme-hci .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.fl-theme-hci .ui-tabs .ui-tabs-nav li a, .fl-theme-hci .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.fl-theme-hci .ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; }
+.fl-theme-hci .ui-tabs .ui-tabs-hide { display: none !important; }
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_000000_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_000000_40x100.png
new file mode 100644 (file)
index 0000000..abdc010
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_000000_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_999999_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_999999_40x100.png
new file mode 100644 (file)
index 0000000..6b6de7d
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_999999_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_ffffff_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_ffffff_40x100.png
new file mode 100644 (file)
index 0000000..ac8b229
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-bg_flat_0_ffffff_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_000000_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_000000_256x240.png
new file mode 100644 (file)
index 0000000..842156d
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_000000_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_fffff_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_fffff_256x240.png
new file mode 100644 (file)
index 0000000..746e6fa
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_fffff_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_ffffff_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_ffffff_256x240.png
new file mode 100644 (file)
index 0000000..746e6fa
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-hci/images/ui-icons_ffffff_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_000000_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_000000_40x100.png
new file mode 100644 (file)
index 0000000..abdc010
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_000000_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_2e83ff_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_2e83ff_40x100.png
new file mode 100644 (file)
index 0000000..54b690f
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_0_2e83ff_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_75_ffffff_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_75_ffffff_40x100.png
new file mode 100644 (file)
index 0000000..ac8b229
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_flat_75_ffffff_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_65_ffffff_1x400.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_65_ffffff_1x400.png
new file mode 100644 (file)
index 0000000..42ccba2
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_65_ffffff_1x400.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_9dcaf6_1x400.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_9dcaf6_1x400.png
new file mode 100644 (file)
index 0000000..9ac3e31
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_9dcaf6_1x400.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_d9e8f7_1x400.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_d9e8f7_1x400.png
new file mode 100644 (file)
index 0000000..c7db55a
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_glass_75_d9e8f7_1x400.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_highlight-soft_55_9dcaf6_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_highlight-soft_55_9dcaf6_1x100.png
new file mode 100644 (file)
index 0000000..370050f
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_highlight-soft_55_9dcaf6_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_inset-soft_95_fef1ec_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_inset-soft_95_fef1ec_1x100.png
new file mode 100644 (file)
index 0000000..0e05810
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-bg_inset-soft_95_fef1ec_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_000000_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_000000_256x240.png
new file mode 100644 (file)
index 0000000..842156d
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_000000_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_222222_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_222222_256x240.png
new file mode 100644 (file)
index 0000000..67560da
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_222222_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_2e83ff_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_2e83ff_256x240.png
new file mode 100644 (file)
index 0000000..b425c44
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_2e83ff_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_454545_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_454545_256x240.png
new file mode 100644 (file)
index 0000000..0cd64a2
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_454545_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_888888_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_888888_256x240.png
new file mode 100644 (file)
index 0000000..2e5180e
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_888888_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_cd0a0a_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_cd0a0a_256x240.png
new file mode 100644 (file)
index 0000000..2db88b7
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/images/ui-icons_cd0a0a_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/mist.css b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-mist/mist.css
new file mode 100644 (file)
index 0000000..f0c1869
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* Layout helpers
+----------------------------------*/
+.fl-theme-mist .ui-helper-hidden { display: none; }
+.fl-theme-mist .ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
+.fl-theme-mist .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.fl-theme-mist .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.fl-theme-mist .ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.fl-theme-mist .ui-helper-clearfix { display:block; }
+/* end clearfix */
+.fl-theme-mist .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-mist .ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-mist .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.fl-theme-mist .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=9dcaf6&bgTextureHeader=02_glass.png&bgImgOpacityHeader=75&borderColorHeader=5A95CF&fcHeader=222222&iconColorHeader=000000&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=cccccc&fcContent=222222&iconColorContent=222222&bgColorDefault=d9e8f7&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=9dcaf6&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=5A95CF&fcActive=000000&iconColorActive=454545&bgColorHighlight=9dcaf6&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=55&borderColorHighlight=2e83ff&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=05_inset_soft.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=2e83ff&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=20&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
+*/
+
+
+/* Component containers
+----------------------------------*/
+.fl-theme-mist .ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-mist .ui-widget input, .fl-theme-mist .ui-widget select, .fl-theme-mist .ui-widget textarea, .fl-theme-mist .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-mist .ui-widget-content { border: 1px solid #cccccc; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
+.fl-theme-mist .ui-widget-content a { color: #222222; }
+.fl-theme-mist .ui-widget-header { border: 1px solid #5A95CF; background: #9dcaf6 url(images/ui-bg_glass_75_9dcaf6_1x400.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
+.fl-theme-mist .ui-widget-header a { color: #222222; }
+
+/* Interaction states
+----------------------------------*/
+.fl-theme-mist .ui-state-default, .fl-theme-mist .ui-widget-content .ui-state-default { border: 1px solid #d3d3d3; background: #d9e8f7 url(images/ui-bg_glass_75_d9e8f7_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; outline: none; }
+.fl-theme-mist .ui-state-default a, .fl-theme-mist .ui-state-default a:link, .fl-theme-mist .ui-state-default a:visited { color: #555555; text-decoration: none; outline: none; }
+.fl-theme-mist .ui-state-hover, .fl-theme-mist .ui-widget-content .ui-state-hover, .fl-theme-mist .ui-state-focus, .fl-theme-mist .ui-widget-content .ui-state-focus { border: 1px solid #999999; background: #9dcaf6 url(images/ui-bg_glass_75_9dcaf6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; outline: none; }
+.fl-theme-mist .ui-state-hover a, .fl-theme-mist .ui-state-hover a:hover { color: #212121; text-decoration: none; outline: none; }
+.fl-theme-mist .ui-state-active, .fl-theme-mist .ui-widget-content .ui-state-active { border: 1px solid #5A95CF; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #000000; outline: none; }
+.fl-theme-mist .ui-state-active a, .fl-theme-mist .ui-state-active a:link, .fl-theme-mist .ui-state-active a:visited { color: #000000; outline: none; text-decoration: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-mist .ui-state-highlight, .fl-theme-mist .ui-widget-content .ui-state-highlight {border: 1px solid #2e83ff; background: #9dcaf6 url(images/ui-bg_highlight-soft_55_9dcaf6_1x100.png) 50% top repeat-x; color: #363636; }
+.fl-theme-mist .ui-state-highlight a, .fl-theme-mist .ui-widget-content .ui-state-highlight a { color: #363636; }
+.fl-theme-mist .ui-state-error, .fl-theme-mist .ui-widget-content .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_inset-soft_95_fef1ec_1x100.png) 50% bottom repeat-x; color: #cd0a0a; }
+.fl-theme-mist .ui-state-error a, .fl-theme-mist .ui-widget-content .ui-state-error a { color: #cd0a0a; }
+.fl-theme-mist .ui-state-error-text, .fl-theme-mist .ui-widget-content .ui-state-error-text { color: #cd0a0a; }
+.fl-theme-mist .ui-state-disabled, .fl-theme-mist .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+.fl-theme-mist .ui-priority-primary, .fl-theme-mist .ui-widget-content .ui-priority-primary { font-weight: bold; }
+.fl-theme-mist .ui-priority-secondary, .fl-theme-mist .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-mist .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
+.fl-theme-mist .ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.fl-theme-mist .ui-widget-header .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-mist .ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
+.fl-theme-mist .ui-state-hover .ui-icon, .fl-theme-mist .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.fl-theme-mist .ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.fl-theme-mist .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
+.fl-theme-mist .ui-state-error .ui-icon, .fl-theme-mist .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+
+/* positioning */
+.fl-theme-mist .ui-icon-carat-1-n { background-position: 0 0; }
+.fl-theme-mist .ui-icon-carat-1-ne { background-position: -16px 0; }
+.fl-theme-mist .ui-icon-carat-1-e { background-position: -32px 0; }
+.fl-theme-mist .ui-icon-carat-1-se { background-position: -48px 0; }
+.fl-theme-mist .ui-icon-carat-1-s { background-position: -64px 0; }
+.fl-theme-mist .ui-icon-carat-1-sw { background-position: -80px 0; }
+.fl-theme-mist .ui-icon-carat-1-w { background-position: -96px 0; }
+.fl-theme-mist .ui-icon-carat-1-nw { background-position: -112px 0; }
+.fl-theme-mist .ui-icon-carat-2-n-s { background-position: -128px 0; }
+.fl-theme-mist .ui-icon-carat-2-e-w { background-position: -144px 0; }
+.fl-theme-mist .ui-icon-triangle-1-n { background-position: 0 -16px; }
+.fl-theme-mist .ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.fl-theme-mist .ui-icon-triangle-1-e { background-position: -32px -16px; }
+.fl-theme-mist .ui-icon-triangle-1-se { background-position: -48px -16px; }
+.fl-theme-mist .ui-icon-triangle-1-s { background-position: -64px -16px; }
+.fl-theme-mist .ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.fl-theme-mist .ui-icon-triangle-1-w { background-position: -96px -16px; }
+.fl-theme-mist .ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.fl-theme-mist .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.fl-theme-mist .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.fl-theme-mist .ui-icon-arrow-1-n { background-position: 0 -32px; }
+.fl-theme-mist .ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.fl-theme-mist .ui-icon-arrow-1-e { background-position: -32px -32px; }
+.fl-theme-mist .ui-icon-arrow-1-se { background-position: -48px -32px; }
+.fl-theme-mist .ui-icon-arrow-1-s { background-position: -64px -32px; }
+.fl-theme-mist .ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.fl-theme-mist .ui-icon-arrow-1-w { background-position: -96px -32px; }
+.fl-theme-mist .ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.fl-theme-mist .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.fl-theme-mist .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.fl-theme-mist .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.fl-theme-mist .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.fl-theme-mist .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.fl-theme-mist .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.fl-theme-mist .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.fl-theme-mist .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.fl-theme-mist .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.fl-theme-mist .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.fl-theme-mist .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.fl-theme-mist .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.fl-theme-mist .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.fl-theme-mist .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.fl-theme-mist .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.fl-theme-mist .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.fl-theme-mist .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.fl-theme-mist .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.fl-theme-mist .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.fl-theme-mist .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.fl-theme-mist .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.fl-theme-mist .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.fl-theme-mist .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.fl-theme-mist .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.fl-theme-mist .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.fl-theme-mist .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.fl-theme-mist .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.fl-theme-mist .ui-icon-arrow-4 { background-position: 0 -80px; }
+.fl-theme-mist .ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.fl-theme-mist .ui-icon-extlink { background-position: -32px -80px; }
+.fl-theme-mist .ui-icon-newwin { background-position: -48px -80px; }
+.fl-theme-mist .ui-icon-refresh { background-position: -64px -80px; }
+.fl-theme-mist .ui-icon-shuffle { background-position: -80px -80px; }
+.fl-theme-mist .ui-icon-transfer-e-w { background-position: -96px -80px; }
+.fl-theme-mist .ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.fl-theme-mist .ui-icon-folder-collapsed { background-position: 0 -96px; }
+.fl-theme-mist .ui-icon-folder-open { background-position: -16px -96px; }
+.fl-theme-mist .ui-icon-document { background-position: -32px -96px; }
+.fl-theme-mist .ui-icon-document-b { background-position: -48px -96px; }
+.fl-theme-mist .ui-icon-note { background-position: -64px -96px; }
+.fl-theme-mist .ui-icon-mail-closed { background-position: -80px -96px; }
+.fl-theme-mist .ui-icon-mail-open { background-position: -96px -96px; }
+.fl-theme-mist .ui-icon-suitcase { background-position: -112px -96px; }
+.fl-theme-mist .ui-icon-comment { background-position: -128px -96px; }
+.fl-theme-mist .ui-icon-person { background-position: -144px -96px; }
+.fl-theme-mist .ui-icon-print { background-position: -160px -96px; }
+.fl-theme-mist .ui-icon-trash { background-position: -176px -96px; }
+.fl-theme-mist .ui-icon-locked { background-position: -192px -96px; }
+.fl-theme-mist .ui-icon-unlocked { background-position: -208px -96px; }
+.fl-theme-mist .ui-icon-bookmark { background-position: -224px -96px; }
+.fl-theme-mist .ui-icon-tag { background-position: -240px -96px; }
+.fl-theme-mist .ui-icon-home { background-position: 0 -112px; }
+.fl-theme-mist .ui-icon-flag { background-position: -16px -112px; }
+.fl-theme-mist .ui-icon-calendar { background-position: -32px -112px; }
+.fl-theme-mist .ui-icon-cart { background-position: -48px -112px; }
+.fl-theme-mist .ui-icon-pencil { background-position: -64px -112px; }
+.fl-theme-mist .ui-icon-clock { background-position: -80px -112px; }
+.fl-theme-mist .ui-icon-disk { background-position: -96px -112px; }
+.fl-theme-mist .ui-icon-calculator { background-position: -112px -112px; }
+.fl-theme-mist .ui-icon-zoomin { background-position: -128px -112px; }
+.fl-theme-mist .ui-icon-zoomout { background-position: -144px -112px; }
+.fl-theme-mist .ui-icon-search { background-position: -160px -112px; }
+.fl-theme-mist .ui-icon-wrench { background-position: -176px -112px; }
+.fl-theme-mist .ui-icon-gear { background-position: -192px -112px; }
+.fl-theme-mist .ui-icon-heart { background-position: -208px -112px; }
+.fl-theme-mist .ui-icon-star { background-position: -224px -112px; }
+.fl-theme-mist .ui-icon-link { background-position: -240px -112px; }
+.fl-theme-mist .ui-icon-cancel { background-position: 0 -128px; }
+.fl-theme-mist .ui-icon-plus { background-position: -16px -128px; }
+.fl-theme-mist .ui-icon-plusthick { background-position: -32px -128px; }
+.fl-theme-mist .ui-icon-minus { background-position: -48px -128px; }
+.fl-theme-mist .ui-icon-minusthick { background-position: -64px -128px; }
+.fl-theme-mist .ui-icon-close { background-position: -80px -128px; }
+.fl-theme-mist .ui-icon-closethick { background-position: -96px -128px; }
+.fl-theme-mist .ui-icon-key { background-position: -112px -128px; }
+.fl-theme-mist .ui-icon-lightbulb { background-position: -128px -128px; }
+.fl-theme-mist .ui-icon-scissors { background-position: -144px -128px; }
+.fl-theme-mist .ui-icon-clipboard { background-position: -160px -128px; }
+.fl-theme-mist .ui-icon-copy { background-position: -176px -128px; }
+.fl-theme-mist .ui-icon-contact { background-position: -192px -128px; }
+.fl-theme-mist .ui-icon-image { background-position: -208px -128px; }
+.fl-theme-mist .ui-icon-video { background-position: -224px -128px; }
+.fl-theme-mist .ui-icon-script { background-position: -240px -128px; }
+.fl-theme-mist .ui-icon-alert { background-position: 0 -144px; }
+.fl-theme-mist .ui-icon-info { background-position: -16px -144px; }
+.fl-theme-mist .ui-icon-notice { background-position: -32px -144px; }
+.fl-theme-mist .ui-icon-help { background-position: -48px -144px; }
+.fl-theme-mist .ui-icon-check { background-position: -64px -144px; }
+.fl-theme-mist .ui-icon-bullet { background-position: -80px -144px; }
+.fl-theme-mist .ui-icon-radio-off { background-position: -96px -144px; }
+.fl-theme-mist .ui-icon-radio-on { background-position: -112px -144px; }
+.fl-theme-mist .ui-icon-pin-w { background-position: -128px -144px; }
+.fl-theme-mist .ui-icon-pin-s { background-position: -144px -144px; }
+.fl-theme-mist .ui-icon-play { background-position: 0 -160px; }
+.fl-theme-mist .ui-icon-pause { background-position: -16px -160px; }
+.fl-theme-mist .ui-icon-seek-next { background-position: -32px -160px; }
+.fl-theme-mist .ui-icon-seek-prev { background-position: -48px -160px; }
+.fl-theme-mist .ui-icon-seek-end { background-position: -64px -160px; }
+.fl-theme-mist .ui-icon-seek-first { background-position: -80px -160px; }
+.fl-theme-mist .ui-icon-stop { background-position: -96px -160px; }
+.fl-theme-mist .ui-icon-eject { background-position: -112px -160px; }
+.fl-theme-mist .ui-icon-volume-off { background-position: -128px -160px; }
+.fl-theme-mist .ui-icon-volume-on { background-position: -144px -160px; }
+.fl-theme-mist .ui-icon-power { background-position: 0 -176px; }
+.fl-theme-mist .ui-icon-signal-diag { background-position: -16px -176px; }
+.fl-theme-mist .ui-icon-signal { background-position: -32px -176px; }
+.fl-theme-mist .ui-icon-battery-0 { background-position: -48px -176px; }
+.fl-theme-mist .ui-icon-battery-1 { background-position: -64px -176px; }
+.fl-theme-mist .ui-icon-battery-2 { background-position: -80px -176px; }
+.fl-theme-mist .ui-icon-battery-3 { background-position: -96px -176px; }
+.fl-theme-mist .ui-icon-circle-plus { background-position: 0 -192px; }
+.fl-theme-mist .ui-icon-circle-minus { background-position: -16px -192px; }
+.fl-theme-mist .ui-icon-circle-close { background-position: -32px -192px; }
+.fl-theme-mist .ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.fl-theme-mist .ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.fl-theme-mist .ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.fl-theme-mist .ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.fl-theme-mist .ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.fl-theme-mist .ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.fl-theme-mist .ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.fl-theme-mist .ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.fl-theme-mist .ui-icon-circle-zoomin { background-position: -176px -192px; }
+.fl-theme-mist .ui-icon-circle-zoomout { background-position: -192px -192px; }
+.fl-theme-mist .ui-icon-circle-check { background-position: -208px -192px; }
+.fl-theme-mist .ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.fl-theme-mist .ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.fl-theme-mist .ui-icon-circlesmall-close { background-position: -32px -208px; }
+.fl-theme-mist .ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.fl-theme-mist .ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.fl-theme-mist .ui-icon-squaresmall-close { background-position: -80px -208px; }
+.fl-theme-mist .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.fl-theme-mist .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.fl-theme-mist .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.fl-theme-mist .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.fl-theme-mist .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.fl-theme-mist .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.fl-theme-mist .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; }
+.fl-theme-mist .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; }
+.fl-theme-mist .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; }
+.fl-theme-mist .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; }
+.fl-theme-mist .ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; }
+.fl-theme-mist .ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; }
+.fl-theme-mist .ui-corner-right {  -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; }
+.fl-theme-mist .ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; }
+.fl-theme-mist .ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; }
+
+/* Overlays */
+.fl-theme-mist .ui-widget-overlay { background: #2e83ff url(images/ui-bg_flat_0_2e83ff_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); }
+.fl-theme-mist .ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_0_000000_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 5px; -webkit-border-radius: 5px; }/* Accordion
+----------------------------------*/
+.fl-theme-mist .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.fl-theme-mist .ui-accordion .ui-accordion-li-fix { display: inline; }
+.fl-theme-mist .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.fl-theme-mist .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
+.fl-theme-mist .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.fl-theme-mist .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
+.fl-theme-mist .ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker
+----------------------------------*/
+.fl-theme-mist .ui-datepicker { width: 17em; padding: .2em .2em 0; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-prev, .fl-theme-mist .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-prev-hover, .fl-theme-mist .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-prev { left:2px; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-next { right:2px; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-prev span, .fl-theme-mist .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }
+.fl-theme-mist .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
+.fl-theme-mist .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.fl-theme-mist .ui-datepicker select.ui-datepicker-month, 
+.fl-theme-mist .ui-datepicker select.ui-datepicker-year { width: 49%;}
+.fl-theme-mist .ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
+.fl-theme-mist .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.fl-theme-mist .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
+.fl-theme-mist .ui-datepicker td { border: 0; padding: 1px; }
+.fl-theme-mist .ui-datepicker td span, .fl-theme-mist .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.fl-theme-mist .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.fl-theme-mist .ui-datepicker.ui-datepicker-multi { width:auto; }
+.fl-theme-mist .ui-datepicker-multi .ui-datepicker-group { float:left; }
+.fl-theme-mist .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.fl-theme-mist .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.fl-theme-mist .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.fl-theme-mist .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.fl-theme-mist .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.fl-theme-mist .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.fl-theme-mist .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.fl-theme-mist .ui-datepicker-row-break { clear:both; width:100%; }
+
+/* RTL support */
+.fl-theme-mist .ui-datepicker-rtl { direction: rtl; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.fl-theme-mist .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.fl-theme-mist .ui-datepicker-cover {
+    display: none; /*sorry for IE5*/
+    display/**/: block; /*sorry for IE5*/
+    position: absolute; /*must have*/
+    z-index: -1; /*must have*/
+    filter: mask(); /*must have*/
+    top: -4px; /*must have*/
+    left: -4px; /*must have*/
+    width: 200px; /*must have*/
+    height: 200px; /*must have*/
+}/* Dialog
+----------------------------------*/
+.fl-theme-mist .ui-dialog { position: relative; padding: .2em; width: 300px; }
+.fl-theme-mist .ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative;  }
+.fl-theme-mist .ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; } 
+.fl-theme-mist .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.fl-theme-mist .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.fl-theme-mist .ui-dialog .ui-dialog-titlebar-close:hover, .fl-theme-mist .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.fl-theme-mist .ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.fl-theme-mist .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.fl-theme-mist .ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
+.fl-theme-mist .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.fl-theme-mist .ui-draggable .ui-dialog-titlebar { cursor: move; }
+/* Progressbar
+----------------------------------*/
+.fl-theme-mist .ui-progressbar { height:2em; text-align: left; }
+.fl-theme-mist .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
+----------------------------------*/
+.fl-theme-mist .ui-resizable { position: relative;}
+.fl-theme-mist .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.fl-theme-mist .ui-resizable-disabled .ui-resizable-handle, .fl-theme-mist .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.fl-theme-mist .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
+.fl-theme-mist .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
+.fl-theme-mist .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
+.fl-theme-mist .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
+.fl-theme-mist .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.fl-theme-mist .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.fl-theme-mist .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.fl-theme-mist .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
+----------------------------------*/
+.fl-theme-mist .ui-slider { position: relative; text-align: left; }
+.fl-theme-mist .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.fl-theme-mist .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; }
+
+.fl-theme-mist .ui-slider-horizontal { height: .8em; }
+.fl-theme-mist .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.fl-theme-mist .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.fl-theme-mist .ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.fl-theme-mist .ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.fl-theme-mist .ui-slider-vertical { width: .8em; height: 100px; }
+.fl-theme-mist .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.fl-theme-mist .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.fl-theme-mist .ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.fl-theme-mist .ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
+----------------------------------*/
+.fl-theme-mist .ui-tabs { padding: .2em; zoom: 1; }
+.fl-theme-mist .ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; }
+.fl-theme-mist .ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; }
+.fl-theme-mist .ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; }
+.fl-theme-mist .ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; }
+.fl-theme-mist .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .fl-theme-mist .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .fl-theme-mist .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.fl-theme-mist .ui-tabs .ui-tabs-nav li a, .fl-theme-mist .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.fl-theme-mist .ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; }
+.fl-theme-mist .ui-tabs .ui-tabs-hide { display: none !important; }
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_666666_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_666666_40x100.png
new file mode 100644 (file)
index 0000000..b3dc88e
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_666666_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_999999_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_999999_40x100.png
new file mode 100644 (file)
index 0000000..6b6de7d
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_999999_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_cccccc_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_cccccc_40x100.png
new file mode 100644 (file)
index 0000000..5473aff
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_cccccc_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ebebeb_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ebebeb_40x100.png
new file mode 100644 (file)
index 0000000..1d773f9
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ebebeb_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ffffff_40x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ffffff_40x100.png
new file mode 100644 (file)
index 0000000..ac8b229
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_flat_0_ffffff_40x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_glass_75_666666_1x400.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_glass_75_666666_1x400.png
new file mode 100644 (file)
index 0000000..f7b1655
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_glass_75_666666_1x400.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-hard_100_ebebeb_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-hard_100_ebebeb_1x100.png
new file mode 100644 (file)
index 0000000..f182c8b
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-hard_100_ebebeb_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-soft_75_999999_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-soft_75_999999_1x100.png
new file mode 100644 (file)
index 0000000..0721365
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_highlight-soft_75_999999_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_inset-hard_100_ebebeb_1x100.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_inset-hard_100_ebebeb_1x100.png
new file mode 100644 (file)
index 0000000..9926860
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-bg_inset-hard_100_ebebeb_1x100.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_000000_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_000000_256x240.png
new file mode 100644 (file)
index 0000000..842156d
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_000000_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_666666_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_666666_256x240.png
new file mode 100644 (file)
index 0000000..ba04b08
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_666666_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ebebeb_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ebebeb_256x240.png
new file mode 100644 (file)
index 0000000..c3a2a91
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ebebeb_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ffffff_256x240.png b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ffffff_256x240.png
new file mode 100644 (file)
index 0000000..746e6fa
Binary files /dev/null and b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/images/ui-icons_ffffff_256x240.png differ
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/slate.css b/docs/jscripts/infusion/lib/jquery/ui/css/fl-theme-slate/slate.css
new file mode 100644 (file)
index 0000000..5017e3b
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+*/
+
+/* Layout helpers
+----------------------------------*/
+.fl-theme-slate .ui-helper-hidden { display: none; }
+.fl-theme-slate .ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
+.fl-theme-slate .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.fl-theme-slate .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.fl-theme-slate .ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.fl-theme-slate .ui-helper-clearfix { display:block; }
+/* end clearfix */
+.fl-theme-slate .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-slate .ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-slate .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.fl-theme-slate .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+/*
+* jQuery UI CSS Framework
+* Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.2em&cornerRadius=5px&bgColorHeader=666666&bgTextureHeader=02_glass.png&bgImgOpacityHeader=75&borderColorHeader=666666&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=ebebeb&bgTextureContent=01_flat.png&bgImgOpacityContent=0&borderColorContent=999999&fcContent=000000&iconColorContent=000000&bgColorDefault=ebebeb&bgTextureDefault=06_inset_hard.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=666666&iconColorDefault=666666&bgColorHover=999999&bgTextureHover=03_highlight_soft.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=ebebeb&iconColorHover=ebebeb&bgColorActive=ebebeb&bgTextureActive=04_highlight_hard.png&bgImgOpacityActive=100&borderColorActive=cccccc&fcActive=000000&iconColorActive=000000&bgColorHighlight=ffffff&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=0&borderColorHighlight=cccccc&fcHighlight=666666&iconColorHighlight=666666&bgColorError=666666&bgTextureError=01_flat.png&bgImgOpacityError=0&borderColorError=000000&fcError=ffffff&iconColorError=ffffff&bgColorOverlay=cccccc&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=50&bgColorShadow=999999&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=50&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
+*/
+
+
+/* Component containers
+----------------------------------*/
+.fl-theme-slate .ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-slate .ui-widget input, .fl-theme-slate .ui-widget select, .fl-theme-slate .ui-widget textarea, .fl-theme-slate .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.fl-theme-slate .ui-widget-content { border: 1px solid #999999; background: #ebebeb url(images/ui-bg_flat_0_ebebeb_40x100.png) 50% 50% repeat-x; color: #000000; }
+.fl-theme-slate .ui-widget-content a { color: #000000; }
+.fl-theme-slate .ui-widget-header { border: 1px solid #666666; background: #666666 url(images/ui-bg_glass_75_666666_1x400.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
+.fl-theme-slate .ui-widget-header a { color: #ffffff; }
+
+/* Interaction states
+----------------------------------*/
+.fl-theme-slate .ui-state-default, .fl-theme-slate .ui-widget-content .ui-state-default { border: 1px solid #cccccc; background: #ebebeb url(images/ui-bg_inset-hard_100_ebebeb_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #666666; outline: none; }
+.fl-theme-slate .ui-state-default a, .fl-theme-slate .ui-state-default a:link, .fl-theme-slate .ui-state-default a:visited { color: #666666; text-decoration: none; outline: none; }
+.fl-theme-slate .ui-state-hover, .fl-theme-slate .ui-widget-content .ui-state-hover, .fl-theme-slate .ui-state-focus, .fl-theme-slate .ui-widget-content .ui-state-focus { border: 1px solid #999999; background: #999999 url(images/ui-bg_highlight-soft_75_999999_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #ebebeb; outline: none; }
+.fl-theme-slate .ui-state-hover a, .fl-theme-slate .ui-state-hover a:hover { color: #ebebeb; text-decoration: none; outline: none; }
+.fl-theme-slate .ui-state-active, .fl-theme-slate .ui-widget-content .ui-state-active { border: 1px solid #cccccc; background: #ebebeb url(images/ui-bg_highlight-hard_100_ebebeb_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #000000; outline: none; }
+.fl-theme-slate .ui-state-active a, .fl-theme-slate .ui-state-active a:link, .fl-theme-slate .ui-state-active a:visited { color: #000000; outline: none; text-decoration: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.fl-theme-slate .ui-state-highlight, .fl-theme-slate .ui-widget-content .ui-state-highlight {border: 1px solid #cccccc; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; color: #666666; }
+.fl-theme-slate .ui-state-highlight a, .fl-theme-slate .ui-widget-content .ui-state-highlight a { color: #666666; }
+.fl-theme-slate .ui-state-error, .fl-theme-slate .ui-widget-content .ui-state-error {border: 1px solid #000000; background: #666666 url(images/ui-bg_flat_0_666666_40x100.png) 50% 50% repeat-x; color: #ffffff; }
+.fl-theme-slate .ui-state-error a, .fl-theme-slate .ui-widget-content .ui-state-error a { color: #ffffff; }
+.fl-theme-slate .ui-state-error-text, .fl-theme-slate .ui-widget-content .ui-state-error-text { color: #ffffff; }
+.fl-theme-slate .ui-state-disabled, .fl-theme-slate .ui-widget-content .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+.fl-theme-slate .ui-priority-primary, .fl-theme-slate .ui-widget-content .ui-priority-primary { font-weight: bold; }
+.fl-theme-slate .ui-priority-secondary, .fl-theme-slate .ui-widget-content .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.fl-theme-slate .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-slate .ui-widget-content .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-slate .ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.fl-theme-slate .ui-state-default .ui-icon { background-image: url(images/ui-icons_666666_256x240.png); }
+.fl-theme-slate .ui-state-hover .ui-icon, .fl-theme-slate .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ebebeb_256x240.png); }
+.fl-theme-slate .ui-state-active .ui-icon {background-image: url(images/ui-icons_000000_256x240.png); }
+.fl-theme-slate .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_666666_256x240.png); }
+.fl-theme-slate .ui-state-error .ui-icon, .fl-theme-slate .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+
+/* positioning */
+.fl-theme-slate .ui-icon-carat-1-n { background-position: 0 0; }
+.fl-theme-slate .ui-icon-carat-1-ne { background-position: -16px 0; }
+.fl-theme-slate .ui-icon-carat-1-e { background-position: -32px 0; }
+.fl-theme-slate .ui-icon-carat-1-se { background-position: -48px 0; }
+.fl-theme-slate .ui-icon-carat-1-s { background-position: -64px 0; }
+.fl-theme-slate .ui-icon-carat-1-sw { background-position: -80px 0; }
+.fl-theme-slate .ui-icon-carat-1-w { background-position: -96px 0; }
+.fl-theme-slate .ui-icon-carat-1-nw { background-position: -112px 0; }
+.fl-theme-slate .ui-icon-carat-2-n-s { background-position: -128px 0; }
+.fl-theme-slate .ui-icon-carat-2-e-w { background-position: -144px 0; }
+.fl-theme-slate .ui-icon-triangle-1-n { background-position: 0 -16px; }
+.fl-theme-slate .ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.fl-theme-slate .ui-icon-triangle-1-e { background-position: -32px -16px; }
+.fl-theme-slate .ui-icon-triangle-1-se { background-position: -48px -16px; }
+.fl-theme-slate .ui-icon-triangle-1-s { background-position: -64px -16px; }
+.fl-theme-slate .ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.fl-theme-slate .ui-icon-triangle-1-w { background-position: -96px -16px; }
+.fl-theme-slate .ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.fl-theme-slate .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.fl-theme-slate .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.fl-theme-slate .ui-icon-arrow-1-n { background-position: 0 -32px; }
+.fl-theme-slate .ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.fl-theme-slate .ui-icon-arrow-1-e { background-position: -32px -32px; }
+.fl-theme-slate .ui-icon-arrow-1-se { background-position: -48px -32px; }
+.fl-theme-slate .ui-icon-arrow-1-s { background-position: -64px -32px; }
+.fl-theme-slate .ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.fl-theme-slate .ui-icon-arrow-1-w { background-position: -96px -32px; }
+.fl-theme-slate .ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.fl-theme-slate .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.fl-theme-slate .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.fl-theme-slate .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.fl-theme-slate .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.fl-theme-slate .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.fl-theme-slate .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.fl-theme-slate .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.fl-theme-slate .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.fl-theme-slate .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.fl-theme-slate .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.fl-theme-slate .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.fl-theme-slate .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.fl-theme-slate .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.fl-theme-slate .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.fl-theme-slate .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.fl-theme-slate .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.fl-theme-slate .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.fl-theme-slate .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.fl-theme-slate .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.fl-theme-slate .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.fl-theme-slate .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.fl-theme-slate .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.fl-theme-slate .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.fl-theme-slate .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.fl-theme-slate .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.fl-theme-slate .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.fl-theme-slate .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.fl-theme-slate .ui-icon-arrow-4 { background-position: 0 -80px; }
+.fl-theme-slate .ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.fl-theme-slate .ui-icon-extlink { background-position: -32px -80px; }
+.fl-theme-slate .ui-icon-newwin { background-position: -48px -80px; }
+.fl-theme-slate .ui-icon-refresh { background-position: -64px -80px; }
+.fl-theme-slate .ui-icon-shuffle { background-position: -80px -80px; }
+.fl-theme-slate .ui-icon-transfer-e-w { background-position: -96px -80px; }
+.fl-theme-slate .ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.fl-theme-slate .ui-icon-folder-collapsed { background-position: 0 -96px; }
+.fl-theme-slate .ui-icon-folder-open { background-position: -16px -96px; }
+.fl-theme-slate .ui-icon-document { background-position: -32px -96px; }
+.fl-theme-slate .ui-icon-document-b { background-position: -48px -96px; }
+.fl-theme-slate .ui-icon-note { background-position: -64px -96px; }
+.fl-theme-slate .ui-icon-mail-closed { background-position: -80px -96px; }
+.fl-theme-slate .ui-icon-mail-open { background-position: -96px -96px; }
+.fl-theme-slate .ui-icon-suitcase { background-position: -112px -96px; }
+.fl-theme-slate .ui-icon-comment { background-position: -128px -96px; }
+.fl-theme-slate .ui-icon-person { background-position: -144px -96px; }
+.fl-theme-slate .ui-icon-print { background-position: -160px -96px; }
+.fl-theme-slate .ui-icon-trash { background-position: -176px -96px; }
+.fl-theme-slate .ui-icon-locked { background-position: -192px -96px; }
+.fl-theme-slate .ui-icon-unlocked { background-position: -208px -96px; }
+.fl-theme-slate .ui-icon-bookmark { background-position: -224px -96px; }
+.fl-theme-slate .ui-icon-tag { background-position: -240px -96px; }
+.fl-theme-slate .ui-icon-home { background-position: 0 -112px; }
+.fl-theme-slate .ui-icon-flag { background-position: -16px -112px; }
+.fl-theme-slate .ui-icon-calendar { background-position: -32px -112px; }
+.fl-theme-slate .ui-icon-cart { background-position: -48px -112px; }
+.fl-theme-slate .ui-icon-pencil { background-position: -64px -112px; }
+.fl-theme-slate .ui-icon-clock { background-position: -80px -112px; }
+.fl-theme-slate .ui-icon-disk { background-position: -96px -112px; }
+.fl-theme-slate .ui-icon-calculator { background-position: -112px -112px; }
+.fl-theme-slate .ui-icon-zoomin { background-position: -128px -112px; }
+.fl-theme-slate .ui-icon-zoomout { background-position: -144px -112px; }
+.fl-theme-slate .ui-icon-search { background-position: -160px -112px; }
+.fl-theme-slate .ui-icon-wrench { background-position: -176px -112px; }
+.fl-theme-slate .ui-icon-gear { background-position: -192px -112px; }
+.fl-theme-slate .ui-icon-heart { background-position: -208px -112px; }
+.fl-theme-slate .ui-icon-star { background-position: -224px -112px; }
+.fl-theme-slate .ui-icon-link { background-position: -240px -112px; }
+.fl-theme-slate .ui-icon-cancel { background-position: 0 -128px; }
+.fl-theme-slate .ui-icon-plus { background-position: -16px -128px; }
+.fl-theme-slate .ui-icon-plusthick { background-position: -32px -128px; }
+.fl-theme-slate .ui-icon-minus { background-position: -48px -128px; }
+.fl-theme-slate .ui-icon-minusthick { background-position: -64px -128px; }
+.fl-theme-slate .ui-icon-close { background-position: -80px -128px; }
+.fl-theme-slate .ui-icon-closethick { background-position: -96px -128px; }
+.fl-theme-slate .ui-icon-key { background-position: -112px -128px; }
+.fl-theme-slate .ui-icon-lightbulb { background-position: -128px -128px; }
+.fl-theme-slate .ui-icon-scissors { background-position: -144px -128px; }
+.fl-theme-slate .ui-icon-clipboard { background-position: -160px -128px; }
+.fl-theme-slate .ui-icon-copy { background-position: -176px -128px; }
+.fl-theme-slate .ui-icon-contact { background-position: -192px -128px; }
+.fl-theme-slate .ui-icon-image { background-position: -208px -128px; }
+.fl-theme-slate .ui-icon-video { background-position: -224px -128px; }
+.fl-theme-slate .ui-icon-script { background-position: -240px -128px; }
+.fl-theme-slate .ui-icon-alert { background-position: 0 -144px; }
+.fl-theme-slate .ui-icon-info { background-position: -16px -144px; }
+.fl-theme-slate .ui-icon-notice { background-position: -32px -144px; }
+.fl-theme-slate .ui-icon-help { background-position: -48px -144px; }
+.fl-theme-slate .ui-icon-check { background-position: -64px -144px; }
+.fl-theme-slate .ui-icon-bullet { background-position: -80px -144px; }
+.fl-theme-slate .ui-icon-radio-off { background-position: -96px -144px; }
+.fl-theme-slate .ui-icon-radio-on { background-position: -112px -144px; }
+.fl-theme-slate .ui-icon-pin-w { background-position: -128px -144px; }
+.fl-theme-slate .ui-icon-pin-s { background-position: -144px -144px; }
+.fl-theme-slate .ui-icon-play { background-position: 0 -160px; }
+.fl-theme-slate .ui-icon-pause { background-position: -16px -160px; }
+.fl-theme-slate .ui-icon-seek-next { background-position: -32px -160px; }
+.fl-theme-slate .ui-icon-seek-prev { background-position: -48px -160px; }
+.fl-theme-slate .ui-icon-seek-end { background-position: -64px -160px; }
+.fl-theme-slate .ui-icon-seek-first { background-position: -80px -160px; }
+.fl-theme-slate .ui-icon-stop { background-position: -96px -160px; }
+.fl-theme-slate .ui-icon-eject { background-position: -112px -160px; }
+.fl-theme-slate .ui-icon-volume-off { background-position: -128px -160px; }
+.fl-theme-slate .ui-icon-volume-on { background-position: -144px -160px; }
+.fl-theme-slate .ui-icon-power { background-position: 0 -176px; }
+.fl-theme-slate .ui-icon-signal-diag { background-position: -16px -176px; }
+.fl-theme-slate .ui-icon-signal { background-position: -32px -176px; }
+.fl-theme-slate .ui-icon-battery-0 { background-position: -48px -176px; }
+.fl-theme-slate .ui-icon-battery-1 { background-position: -64px -176px; }
+.fl-theme-slate .ui-icon-battery-2 { background-position: -80px -176px; }
+.fl-theme-slate .ui-icon-battery-3 { background-position: -96px -176px; }
+.fl-theme-slate .ui-icon-circle-plus { background-position: 0 -192px; }
+.fl-theme-slate .ui-icon-circle-minus { background-position: -16px -192px; }
+.fl-theme-slate .ui-icon-circle-close { background-position: -32px -192px; }
+.fl-theme-slate .ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.fl-theme-slate .ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.fl-theme-slate .ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.fl-theme-slate .ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.fl-theme-slate .ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.fl-theme-slate .ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.fl-theme-slate .ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.fl-theme-slate .ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.fl-theme-slate .ui-icon-circle-zoomin { background-position: -176px -192px; }
+.fl-theme-slate .ui-icon-circle-zoomout { background-position: -192px -192px; }
+.fl-theme-slate .ui-icon-circle-check { background-position: -208px -192px; }
+.fl-theme-slate .ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.fl-theme-slate .ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.fl-theme-slate .ui-icon-circlesmall-close { background-position: -32px -208px; }
+.fl-theme-slate .ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.fl-theme-slate .ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.fl-theme-slate .ui-icon-squaresmall-close { background-position: -80px -208px; }
+.fl-theme-slate .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.fl-theme-slate .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.fl-theme-slate .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.fl-theme-slate .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.fl-theme-slate .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.fl-theme-slate .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.fl-theme-slate .ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; }
+.fl-theme-slate .ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
+.fl-theme-slate .ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
+.fl-theme-slate .ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
+.fl-theme-slate .ui-corner-top { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; }
+.fl-theme-slate .ui-corner-bottom { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
+.fl-theme-slate .ui-corner-right {  -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; }
+.fl-theme-slate .ui-corner-left { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; }
+.fl-theme-slate .ui-corner-all { -moz-border-radius: 5px; -webkit-border-radius: 5px; }
+
+/* Overlays */
+.fl-theme-slate .ui-widget-overlay { background: #cccccc url(images/ui-bg_flat_0_cccccc_40x100.png) 50% 50% repeat-x; opacity: .50;filter:Alpha(Opacity=50); }
+.fl-theme-slate .ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #999999 url(images/ui-bg_flat_0_999999_40x100.png) 50% 50% repeat-x; opacity: .50;filter:Alpha(Opacity=50); -moz-border-radius: 5px; -webkit-border-radius: 5px; }/* Accordion
+----------------------------------*/
+.fl-theme-slate .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.fl-theme-slate .ui-accordion .ui-accordion-li-fix { display: inline; }
+.fl-theme-slate .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.fl-theme-slate .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em 2.2em; }
+.fl-theme-slate .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.fl-theme-slate .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; }
+.fl-theme-slate .ui-accordion .ui-accordion-content-active { display: block; }/* Datepicker
+----------------------------------*/
+.fl-theme-slate .ui-datepicker { width: 17em; padding: .2em .2em 0; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-prev, .fl-theme-slate .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-prev-hover, .fl-theme-slate .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-prev { left:2px; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-next { right:2px; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-prev span, .fl-theme-slate .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }
+.fl-theme-slate .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-title select { float:left; font-size:1em; margin:1px 0; }
+.fl-theme-slate .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.fl-theme-slate .ui-datepicker select.ui-datepicker-month, 
+.fl-theme-slate .ui-datepicker select.ui-datepicker-year { width: 49%;}
+.fl-theme-slate .ui-datepicker .ui-datepicker-title select.ui-datepicker-year { float: right; }
+.fl-theme-slate .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.fl-theme-slate .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
+.fl-theme-slate .ui-datepicker td { border: 0; padding: 1px; }
+.fl-theme-slate .ui-datepicker td span, .fl-theme-slate .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.fl-theme-slate .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.fl-theme-slate .ui-datepicker.ui-datepicker-multi { width:auto; }
+.fl-theme-slate .ui-datepicker-multi .ui-datepicker-group { float:left; }
+.fl-theme-slate .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.fl-theme-slate .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.fl-theme-slate .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.fl-theme-slate .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.fl-theme-slate .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.fl-theme-slate .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.fl-theme-slate .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.fl-theme-slate .ui-datepicker-row-break { clear:both; width:100%; }
+
+/* RTL support */
+.fl-theme-slate .ui-datepicker-rtl { direction: rtl; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.fl-theme-slate .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.fl-theme-slate .ui-datepicker-cover {
+    display: none; /*sorry for IE5*/
+    display/**/: block; /*sorry for IE5*/
+    position: absolute; /*must have*/
+    z-index: -1; /*must have*/
+    filter: mask(); /*must have*/
+    top: -4px; /*must have*/
+    left: -4px; /*must have*/
+    width: 200px; /*must have*/
+    height: 200px; /*must have*/
+}/* Dialog
+----------------------------------*/
+.fl-theme-slate .ui-dialog { position: relative; padding: .2em; width: 300px; }
+.fl-theme-slate .ui-dialog .ui-dialog-titlebar { padding: .5em .3em .3em 1em; position: relative;  }
+.fl-theme-slate .ui-dialog .ui-dialog-title { float: left; margin: .1em 0 .2em; } 
+.fl-theme-slate .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.fl-theme-slate .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.fl-theme-slate .ui-dialog .ui-dialog-titlebar-close:hover, .fl-theme-slate .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.fl-theme-slate .ui-dialog .ui-dialog-content { border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.fl-theme-slate .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.fl-theme-slate .ui-dialog .ui-dialog-buttonpane button { float: right; margin: .5em .4em .5em 0; cursor: pointer; padding: .2em .6em .3em .6em; line-height: 1.4em; width:auto; overflow:visible; }
+.fl-theme-slate .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.fl-theme-slate .ui-draggable .ui-dialog-titlebar { cursor: move; }
+/* Progressbar
+----------------------------------*/
+.fl-theme-slate .ui-progressbar { height:2em; text-align: left; }
+.fl-theme-slate .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }/* Resizable
+----------------------------------*/
+.fl-theme-slate .ui-resizable { position: relative;}
+.fl-theme-slate .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}
+.fl-theme-slate .ui-resizable-disabled .ui-resizable-handle, .fl-theme-slate .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.fl-theme-slate .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0px; }
+.fl-theme-slate .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0px; }
+.fl-theme-slate .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0px; height: 100%; }
+.fl-theme-slate .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0px; height: 100%; }
+.fl-theme-slate .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.fl-theme-slate .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.fl-theme-slate .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.fl-theme-slate .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* Slider
+----------------------------------*/
+.fl-theme-slate .ui-slider { position: relative; text-align: left; }
+.fl-theme-slate .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.fl-theme-slate .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; }
+
+.fl-theme-slate .ui-slider-horizontal { height: .8em; }
+.fl-theme-slate .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.fl-theme-slate .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.fl-theme-slate .ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.fl-theme-slate .ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.fl-theme-slate .ui-slider-vertical { width: .8em; height: 100px; }
+.fl-theme-slate .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.fl-theme-slate .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.fl-theme-slate .ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.fl-theme-slate .ui-slider-vertical .ui-slider-range-max { top: 0; }/* Tabs
+----------------------------------*/
+.fl-theme-slate .ui-tabs { padding: .2em; zoom: 1; }
+.fl-theme-slate .ui-tabs .ui-tabs-nav { list-style: none; position: relative; padding: .2em .2em 0; }
+.fl-theme-slate .ui-tabs .ui-tabs-nav li { position: relative; float: left; border-bottom-width: 0 !important; margin: 0 .2em -1px 0; padding: 0; }
+.fl-theme-slate .ui-tabs .ui-tabs-nav li a { float: left; text-decoration: none; padding: .5em 1em; }
+.fl-theme-slate .ui-tabs .ui-tabs-nav li.ui-tabs-selected { padding-bottom: 1px; border-bottom-width: 0; }
+.fl-theme-slate .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .fl-theme-slate .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .fl-theme-slate .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.fl-theme-slate .ui-tabs .ui-tabs-nav li a, .fl-theme-slate .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.fl-theme-slate .ui-tabs .ui-tabs-panel { padding: 1em 1.4em; display: block; border-width: 0; background: none; }
+.fl-theme-slate .ui-tabs .ui-tabs-hide { display: none !important; }
diff --git a/docs/jscripts/infusion/lib/jquery/ui/css/jquery.ui.theme.css b/docs/jscripts/infusion/lib/jquery/ui/css/jquery.ui.theme.css
new file mode 100644 (file)
index 0000000..f9e42a1
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * jQuery UI CSS Framework 1.8.6
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/
+ */
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1.1em/*{fsDefault}*/; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif/*{ffDefault}*/; font-size: 1em; }
+.ui-widget-content { border: 1px solid #aaaaaa/*{borderColorContent}*/; background: #ffffff/*{bgColorContent}*/ url(images/ui-bg_flat_75_ffffff_40x100.png)/*{bgImgUrlContent}*/ 50%/*{bgContentXPos}*/ 50%/*{bgContentYPos}*/ repeat-x/*{bgContentRepeat}*/; color: #222222/*{fcContent}*/; }
+.ui-widget-content a { color: #222222/*{fcContent}*/; }
+.ui-widget-header { border: 1px solid #aaaaaa/*{borderColorHeader}*/; background: #cccccc/*{bgColorHeader}*/ url(images/ui-bg_highlight-soft_75_cccccc_1x100.png)/*{bgImgUrlHeader}*/ 50%/*{bgHeaderXPos}*/ 50%/*{bgHeaderYPos}*/ repeat-x/*{bgHeaderRepeat}*/; color: #222222/*{fcHeader}*/; font-weight: bold; }
+.ui-widget-header a { color: #222222/*{fcHeader}*/; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3/*{borderColorDefault}*/; background: #e6e6e6/*{bgColorDefault}*/ url(images/ui-bg_glass_75_e6e6e6_1x400.png)/*{bgImgUrlDefault}*/ 50%/*{bgDefaultXPos}*/ 50%/*{bgDefaultYPos}*/ repeat-x/*{bgDefaultRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #555555/*{fcDefault}*/; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555/*{fcDefault}*/; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999/*{borderColorHover}*/; background: #dadada/*{bgColorHover}*/ url(images/ui-bg_glass_75_dadada_1x400.png)/*{bgImgUrlHover}*/ 50%/*{bgHoverXPos}*/ 50%/*{bgHoverYPos}*/ repeat-x/*{bgHoverRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcHover}*/; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #212121/*{fcHover}*/; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa/*{borderColorActive}*/; background: #ffffff/*{bgColorActive}*/ url(images/ui-bg_glass_65_ffffff_1x400.png)/*{bgImgUrlActive}*/ 50%/*{bgActiveXPos}*/ 50%/*{bgActiveYPos}*/ repeat-x/*{bgActiveRepeat}*/; font-weight: normal/*{fwDefault}*/; color: #212121/*{fcActive}*/; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121/*{fcActive}*/; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight  {border: 1px solid #fcefa1/*{borderColorHighlight}*/; background: #fbf9ee/*{bgColorHighlight}*/ url(images/ui-bg_glass_55_fbf9ee_1x400.png)/*{bgImgUrlHighlight}*/ 50%/*{bgHighlightXPos}*/ 50%/*{bgHighlightYPos}*/ repeat-x/*{bgHighlightRepeat}*/; color: #363636/*{fcHighlight}*/; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636/*{fcHighlight}*/; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a/*{borderColorError}*/; background: #fef1ec/*{bgColorError}*/ url(images/ui-bg_glass_95_fef1ec_1x400.png)/*{bgImgUrlError}*/ 50%/*{bgErrorXPos}*/ 50%/*{bgErrorYPos}*/ repeat-x/*{bgErrorRepeat}*/; color: #cd0a0a/*{fcError}*/; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a/*{fcError}*/; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a/*{fcError}*/; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary,  .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsContent}*/; }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png)/*{iconsHeader}*/; }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png)/*{iconsDefault}*/; }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsHover}*/; }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png)/*{iconsActive}*/; }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png)/*{iconsHighlight}*/; }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png)/*{iconsError}*/; }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-tl { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-tr { -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-bl { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-br { -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-top { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-bottom { -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-right {  -moz-border-radius-topright: 4px/*{cornerRadius}*/; -webkit-border-top-right-radius: 4px/*{cornerRadius}*/; border-top-right-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomright: 4px/*{cornerRadius}*/; -webkit-border-bottom-right-radius: 4px/*{cornerRadius}*/; border-bottom-right-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-left { -moz-border-radius-topleft: 4px/*{cornerRadius}*/; -webkit-border-top-left-radius: 4px/*{cornerRadius}*/; border-top-left-radius: 4px/*{cornerRadius}*/; -moz-border-radius-bottomleft: 4px/*{cornerRadius}*/; -webkit-border-bottom-left-radius: 4px/*{cornerRadius}*/; border-bottom-left-radius: 4px/*{cornerRadius}*/; }
+.ui-corner-all { -moz-border-radius: 4px/*{cornerRadius}*/; -webkit-border-radius: 4px/*{cornerRadius}*/; border-radius: 4px/*{cornerRadius}*/; }
+
+/* Overlays */
+.ui-widget-overlay { background: #aaaaaa/*{bgColorOverlay}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlOverlay}*/ 50%/*{bgOverlayXPos}*/ 50%/*{bgOverlayYPos}*/ repeat-x/*{bgOverlayRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityOverlay}*/; }
+.ui-widget-shadow { margin: -8px/*{offsetTopShadow}*/ 0 0 -8px/*{offsetLeftShadow}*/; padding: 8px/*{thicknessShadow}*/; background: #aaaaaa/*{bgColorShadow}*/ url(images/ui-bg_flat_0_aaaaaa_40x100.png)/*{bgImgUrlShadow}*/ 50%/*{bgShadowXPos}*/ 50%/*{bgShadowYPos}*/ repeat-x/*{bgShadowRepeat}*/; opacity: .3;filter:Alpha(Opacity=30)/*{opacityShadow}*/; -moz-border-radius: 8px/*{cornerRadiusShadow}*/; -webkit-border-radius: 8px/*{cornerRadiusShadow}*/; border-radius: 8px/*{cornerRadiusShadow}*/; }
\ No newline at end of file
diff --git a/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.accordion.js b/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.accordion.js
new file mode 100644 (file)
index 0000000..8ad255f
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * jQuery UI Accordion 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.accordion", {
+       options: {
+               active: 0,
+               animated: 'slide',
+               autoHeight: true,
+               clearStyle: false,
+               collapsible: false,
+               event: "click",
+               fillSpace: false,
+               header: "> li > :first-child,> :not(li):even",
+               icons: {
+                       header: "ui-icon-triangle-1-e",
+                       headerSelected: "ui-icon-triangle-1-s"
+               },
+               navigation: false,
+               navigationFilter: function() {
+                       return this.href.toLowerCase() == location.href.toLowerCase();
+               }
+       },
+       _create: function() {
+
+               var o = this.options, self = this;
+               this.running = 0;
+
+               this.element.addClass("ui-accordion ui-widget ui-helper-reset");
+               
+               // in lack of child-selectors in CSS we need to mark top-LIs in a UL-accordion for some IE-fix
+               if (this.element[0].nodeName == "UL") {
+                       this.element.children("li").addClass("ui-accordion-li-fix");
+               }
+
+               this.headers = this.element.find(o.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all")
+                       .bind("mouseenter.accordion", function(){ $(this).addClass('ui-state-hover'); })
+                       .bind("mouseleave.accordion", function(){ $(this).removeClass('ui-state-hover'); })
+                       .bind("focus.accordion", function(){ $(this).addClass('ui-state-focus'); })
+                       .bind("blur.accordion", function(){ $(this).removeClass('ui-state-focus'); });
+
+               this.headers
+                       .next()
+                               .addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
+
+               if ( o.navigation ) {
+                       var current = this.element.find("a").filter(o.navigationFilter);
+                       if ( current.length ) {
+                               var header = current.closest(".ui-accordion-header");
+                               if ( header.length ) {
+                                       // anchor within header
+                                       this.active = header;
+                               } else {
+                                       // anchor within content
+                                       this.active = current.closest(".ui-accordion-content").prev();
+                               }
+                       }
+               }
+
+               this.active = this._findActive(this.active || o.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");
+               this.active.next().addClass('ui-accordion-content-active');
+
+               //Append icon elements
+               this._createIcons();
+
+               // IE7-/Win - Extra vertical space in lists fixed
+               if ($.browser.msie) {
+                       this.element.find('a').css('zoom', '1');
+               }
+
+               this.resize();
+
+               //ARIA
+               this.element.attr('role','tablist');
+
+               this.headers
+                       .attr('role','tab')
+                       .bind('keydown', function(event) { return self._keydown(event); })
+                       .next()
+                       .attr('role','tabpanel');
+
+               this.headers
+                       .not(this.active || "")
+                       .attr('aria-expanded','false')
+                       .attr("tabIndex", "-1")
+                       .next()
+                       .hide();
+
+               // make sure at least one header is in the tab order
+               if (!this.active.length) {
+                       this.headers.eq(0).attr('tabIndex','0');
+               } else {
+                       this.active
+                               .attr('aria-expanded','true')
+                               .attr('tabIndex', '0');
+               }
+
+               // only need links in taborder for Safari
+               if (!$.browser.safari)
+                       this.headers.find('a').attr('tabIndex','-1');
+
+               if (o.event) {
+                       this.headers.bind((o.event) + ".accordion", function(event) {
+                               self._clickHandler.call(self, event, this);
+                               event.preventDefault();
+                       });
+               }
+
+       },
+       
+       _createIcons: function() {
+               var o = this.options;
+               if (o.icons) {
+                       $("<span/>").addClass("ui-icon " + o.icons.header).prependTo(this.headers);
+                       this.active.find(".ui-icon").toggleClass(o.icons.header).toggleClass(o.icons.headerSelected);
+                       this.element.addClass("ui-accordion-icons");
+               }
+       },
+       
+       _destroyIcons: function() {
+               this.headers.children(".ui-icon").remove();
+               this.element.removeClass("ui-accordion-icons");
+       },
+
+       destroy: function() {
+               var o = this.options;
+
+               this.element
+                       .removeClass("ui-accordion ui-widget ui-helper-reset")
+                       .removeAttr("role")
+                       .unbind('.accordion')
+                       .removeData('accordion');
+
+               this.headers
+                       .unbind(".accordion")
+                       .removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top")
+                       .removeAttr("role").removeAttr("aria-expanded").removeAttr("tabindex");
+
+               this.headers.find("a").removeAttr("tabindex");
+               this._destroyIcons();
+               var contents = this.headers.next().css("display", "").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active");
+               if (o.autoHeight || o.fillHeight) {
+                       contents.css("height", "");
+               }
+
+               return this;
+       },
+       
+       _setOption: function(key, value) {
+               $.Widget.prototype._setOption.apply(this, arguments);
+                       
+               if (key == "active") {
+                       this.activate(value);
+               }
+               if (key == "icons") {
+                       this._destroyIcons();
+                       if (value) {
+                               this._createIcons();
+                       }
+               }
+               
+       },
+
+       _keydown: function(event) {
+
+               var o = this.options, keyCode = $.ui.keyCode;
+
+               if (o.disabled || event.altKey || event.ctrlKey)
+                       return;
+
+               var length = this.headers.length;
+               var currentIndex = this.headers.index(event.target);
+               var toFocus = false;
+
+               switch(event.keyCode) {
+                       case keyCode.RIGHT:
+                       case keyCode.DOWN:
+                               toFocus = this.headers[(currentIndex + 1) % length];
+                               break;
+                       case keyCode.LEFT:
+                       case keyCode.UP:
+                               toFocus = this.headers[(currentIndex - 1 + length) % length];
+                               break;
+                       case keyCode.SPACE:
+                       case keyCode.ENTER:
+                               this._clickHandler({ target: event.target }, event.target);
+                               event.preventDefault();
+               }
+
+               if (toFocus) {
+                       $(event.target).attr('tabIndex','-1');
+                       $(toFocus).attr('tabIndex','0');
+                       toFocus.focus();
+                       return false;
+               }
+
+               return true;
+
+       },
+
+       resize: function() {
+
+               var o = this.options, maxHeight;
+
+               if (o.fillSpace) {
+                       
+                       if($.browser.msie) { var defOverflow = this.element.parent().css('overflow'); this.element.parent().css('overflow', 'hidden'); }
+                       maxHeight = this.element.parent().height();
+                       if($.browser.msie) { this.element.parent().css('overflow', defOverflow); }
+       
+                       this.headers.each(function() {
+                               maxHeight -= $(this).outerHeight(true);
+                       });
+
+                       this.headers.next().each(function() {
+                  $(this).height(Math.max(0, maxHeight - $(this).innerHeight() + $(this).height()));
+                       }).css('overflow', 'auto');
+
+               } else if ( o.autoHeight ) {
+                       maxHeight = 0;
+                       this.headers.next().each(function() {
+                               maxHeight = Math.max(maxHeight, $(this).height());
+                       }).height(maxHeight);
+               }
+
+               return this;
+       },
+
+       activate: function(index) {
+               // TODO this gets called on init, changing the option without an explicit call for that
+               this.options.active = index;
+               // call clickHandler with custom event
+               var active = this._findActive(index)[0];
+               this._clickHandler({ target: active }, active);
+
+               return this;
+       },
+
+       _findActive: function(selector) {
+               return selector
+                       ? typeof selector == "number"
+                               ? this.headers.filter(":eq(" + selector + ")")
+                               : this.headers.not(this.headers.not(selector))
+                       : selector === false
+                               ? $([])
+                               : this.headers.filter(":eq(0)");
+       },
+
+       // TODO isn't event.target enough? why the seperate target argument?
+       _clickHandler: function(event, target) {
+
+               var o = this.options;
+               if (o.disabled)
+                       return;
+
+               // called only when using activate(false) to close all parts programmatically
+               if (!event.target) {
+                       if (!o.collapsible)
+                               return;
+                       this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
+                               .find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
+                       this.active.next().addClass('ui-accordion-content-active');
+                       var toHide = this.active.next(),
+                               data = {
+                                       options: o,
+                                       newHeader: $([]),
+                                       oldHeader: o.active,
+                                       newContent: $([]),
+                                       oldContent: toHide
+                               },
+                               toShow = (this.active = $([]));
+                       this._toggle(toShow, toHide, data);
+                       return;
+               }
+
+               // get the click target
+               var clicked = $(event.currentTarget || target);
+               var clickedIsActive = clicked[0] == this.active[0];
+               
+               // TODO the option is changed, is that correct?
+               // TODO if it is correct, shouldn't that happen after determining that the click is valid?
+               o.active = o.collapsible && clickedIsActive ? false : $('.ui-accordion-header', this.element).index(clicked);
+
+               // if animations are still active, or the active header is the target, ignore click
+               if (this.running || (!o.collapsible && clickedIsActive)) {
+                       return;
+               }
+
+               // switch classes
+               this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all")
+                       .find(".ui-icon").removeClass(o.icons.headerSelected).addClass(o.icons.header);
+               if (!clickedIsActive) {
+                       clicked.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top")
+                               .find(".ui-icon").removeClass(o.icons.header).addClass(o.icons.headerSelected);
+                       clicked.next().addClass('ui-accordion-content-active');
+               }
+
+               // find elements to show and hide
+               var toShow = clicked.next(),
+                       toHide = this.active.next(),
+                       data = {
+                               options: o,
+                               newHeader: clickedIsActive && o.collapsible ? $([]) : clicked,
+                               oldHeader: this.active,
+                               newContent: clickedIsActive && o.collapsible ? $([]) : toShow,
+                               oldContent: toHide
+                       },
+                       down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
+
+               this.active = clickedIsActive ? $([]) : clicked;
+               this._toggle(toShow, toHide, data, clickedIsActive, down);
+
+               return;
+
+       },
+
+       _toggle: function(toShow, toHide, data, clickedIsActive, down) {
+
+               var o = this.options, self = this;
+
+               this.toShow = toShow;
+               this.toHide = toHide;
+               this.data = data;
+
+               var complete = function() { if(!self) return; return self._completed.apply(self, arguments); };
+
+               // trigger changestart event
+               this._trigger("changestart", null, this.data);
+
+               // count elements to animate
+               this.running = toHide.size() === 0 ? toShow.size() : toHide.size();
+
+               if (o.animated) {
+
+                       var animOptions = {};
+
+                       if ( o.collapsible && clickedIsActive ) {
+                               animOptions = {
+                                       toShow: $([]),
+                                       toHide: toHide,
+                                       complete: complete,
+                                       down: down,
+                                       autoHeight: o.autoHeight || o.fillSpace
+                               };
+                       } else {
+                               animOptions = {
+                                       toShow: toShow,
+                                       toHide: toHide,
+                                       complete: complete,
+                                       down: down,
+                                       autoHeight: o.autoHeight || o.fillSpace
+                               };
+                       }
+
+                       if (!o.proxied) {
+                               o.proxied = o.animated;
+                       }
+
+                       if (!o.proxiedDuration) {
+                               o.proxiedDuration = o.duration;
+                       }
+
+                       o.animated = $.isFunction(o.proxied) ?
+                               o.proxied(animOptions) : o.proxied;
+
+                       o.duration = $.isFunction(o.proxiedDuration) ?
+                               o.proxiedDuration(animOptions) : o.proxiedDuration;
+
+                       var animations = $.ui.accordion.animations,
+                               duration = o.duration,
+                               easing = o.animated;
+
+                       if (easing && !animations[easing] && !$.easing[easing]) {
+                               easing = 'slide';
+                       }
+                       if (!animations[easing]) {
+                               animations[easing] = function(options) {
+                                       this.slide(options, {
+                                               easing: easing,
+                                               duration: duration || 700
+                                       });
+                               };
+                       }
+
+                       animations[easing](animOptions);
+
+               } else {
+
+                       if (o.collapsible && clickedIsActive) {
+                               toShow.toggle();
+                       } else {
+                               toHide.hide();
+                               toShow.show();
+                       }
+
+                       complete(true);
+
+               }
+
+               // TODO assert that the blur and focus triggers are really necessary, remove otherwise
+               toHide.prev().attr('aria-expanded','false').attr("tabIndex", "-1").blur();
+               toShow.prev().attr('aria-expanded','true').attr("tabIndex", "0").focus();
+
+       },
+
+       _completed: function(cancel) {
+
+               var o = this.options;
+
+               this.running = cancel ? 0 : --this.running;
+               if (this.running) return;
+
+               if (o.clearStyle) {
+                       this.toShow.add(this.toHide).css({
+                               height: "",
+                               overflow: ""
+                       });
+               }
+               
+               // other classes are removed before the animation; this one needs to stay until completed
+               this.toHide.removeClass("ui-accordion-content-active");
+
+               this._trigger('change', null, this.data);
+       }
+
+});
+
+
+$.extend($.ui.accordion, {
+       version: "1.8",
+       animations: {
+               slide: function(options, additions) {
+                       options = $.extend({
+                               easing: "swing",
+                               duration: 300
+                       }, options, additions);
+                       if ( !options.toHide.size() ) {
+                               options.toShow.animate({height: "show"}, options);
+                               return;
+                       }
+                       if ( !options.toShow.size() ) {
+                               options.toHide.animate({height: "hide"}, options);
+                               return;
+                       }
+                       var overflow = options.toShow.css('overflow'),
+                               percentDone = 0,
+                               showProps = {},
+                               hideProps = {},
+                               fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
+                               originalWidth;
+                       // fix width before calculating height of hidden element
+                       var s = options.toShow;
+                       originalWidth = s[0].style.width;
+                       s.width( parseInt(s.parent().width(),10) - parseInt(s.css("paddingLeft"),10) - parseInt(s.css("paddingRight"),10) - (parseInt(s.css("borderLeftWidth"),10) || 0) - (parseInt(s.css("borderRightWidth"),10) || 0) );
+                       
+                       $.each(fxAttrs, function(i, prop) {
+                               hideProps[prop] = 'hide';
+                               
+                               var parts = ('' + $.css(options.toShow[0], prop)).match(/^([\d+-.]+)(.*)$/);
+                               showProps[prop] = {
+                                       value: parts[1],
+                                       unit: parts[2] || 'px'
+                               };
+                       });
+                       options.toShow.css({ height: 0, overflow: 'hidden' }).show();
+                       options.toHide.filter(":hidden").each(options.complete).end().filter(":visible").animate(hideProps,{
+                               step: function(now, settings) {
+                                       // only calculate the percent when animating height
+                                       // IE gets very inconsistent results when animating elements
+                                       // with small values, which is common for padding
+                                       if (settings.prop == 'height') {
+                                               percentDone = ( settings.end - settings.start === 0 ) ? 0 :
+                                                       (settings.now - settings.start) / (settings.end - settings.start);
+                                       }
+                                       
+                                       options.toShow[0].style[settings.prop] =
+                                               (percentDone * showProps[settings.prop].value) + showProps[settings.prop].unit;
+                               },
+                               duration: options.duration,
+                               easing: options.easing,
+                               complete: function() {
+                                       if ( !options.autoHeight ) {
+                                               options.toShow.css("height", "");
+                                       }
+                                       options.toShow.css("width", originalWidth);
+                                       options.toShow.css({overflow: overflow});
+                                       options.complete();
+                               }
+                       });
+               },
+               bounceslide: function(options) {
+                       this.slide(options, {
+                               easing: options.down ? "easeOutBounce" : "swing",
+                               duration: options.down ? 1000 : 200
+                       });
+               }
+       }
+});
+
+})(jQuery);
diff --git a/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.core.js b/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.core.js
new file mode 100644 (file)
index 0000000..e81699e
--- /dev/null
@@ -0,0 +1,203 @@
+/*!
+ * jQuery UI 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI
+ */
+;jQuery.ui || (function($) {
+
+//Helper functions and ui object
+$.ui = {
+       version: "1.8",
+
+       // $.ui.plugin is deprecated.  Use the proxy pattern instead.
+       plugin: {
+               add: function(module, option, set) {
+                       var proto = $.ui[module].prototype;
+                       for(var i in set) {
+                               proto.plugins[i] = proto.plugins[i] || [];
+                               proto.plugins[i].push([option, set[i]]);
+                       }
+               },
+               call: function(instance, name, args) {
+                       var set = instance.plugins[name];
+                       if(!set || !instance.element[0].parentNode) { return; }
+
+                       for (var i = 0; i < set.length; i++) {
+                               if (instance.options[set[i][0]]) {
+                                       set[i][1].apply(instance.element, args);
+                               }
+                       }
+               }
+       },
+
+       contains: function(a, b) {
+               return document.compareDocumentPosition
+                       ? a.compareDocumentPosition(b) & 16
+                       : a !== b && a.contains(b);
+       },
+
+       hasScroll: function(el, a) {
+
+               //If overflow is hidden, the element might have extra content, but the user wants to hide it
+               if ($(el).css('overflow') == 'hidden') { return false; }
+
+               var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop',
+                       has = false;
+
+               if (el[scroll] > 0) { return true; }
+
+               // TODO: determine which cases actually cause this to happen
+               // if the element doesn't have the scroll set, see if it's possible to
+               // set the scroll
+               el[scroll] = 1;
+               has = (el[scroll] > 0);
+               el[scroll] = 0;
+               return has;
+       },
+
+       isOverAxis: function(x, reference, size) {
+               //Determines when x coordinate is over "b" element axis
+               return (x > reference) && (x < (reference + size));
+       },
+
+       isOver: function(y, x, top, left, height, width) {
+               //Determines when x, y coordinates is over "b" element
+               return $.ui.isOverAxis(y, top, height) && $.ui.isOverAxis(x, left, width);
+       },
+
+       keyCode: {
+               BACKSPACE: 8,
+               CAPS_LOCK: 20,
+               COMMA: 188,
+               CONTROL: 17,
+               DELETE: 46,
+               DOWN: 40,
+               END: 35,
+               ENTER: 13,
+               ESCAPE: 27,
+               HOME: 36,
+               INSERT: 45,
+               LEFT: 37,
+               NUMPAD_ADD: 107,
+               NUMPAD_DECIMAL: 110,
+               NUMPAD_DIVIDE: 111,
+               NUMPAD_ENTER: 108,
+               NUMPAD_MULTIPLY: 106,
+               NUMPAD_SUBTRACT: 109,
+               PAGE_DOWN: 34,
+               PAGE_UP: 33,
+               PERIOD: 190,
+               RIGHT: 39,
+               SHIFT: 16,
+               SPACE: 32,
+               TAB: 9,
+               UP: 38
+       }
+};
+
+//jQuery plugins
+$.fn.extend({
+       _focus: $.fn.focus,
+       focus: function(delay, fn) {
+               return typeof delay === 'number'
+                       ? this.each(function() {
+                               var elem = this;
+                               setTimeout(function() {
+                                       $(elem).focus();
+                                       (fn && fn.call(elem));
+                               }, delay);
+                       })
+                       : this._focus.apply(this, arguments);
+       },
+       
+       enableSelection: function() {
+               return this
+                       .attr('unselectable', 'off')
+                       .css('MozUserSelect', '')
+                       .unbind('selectstart.ui');
+       },
+
+       disableSelection: function() {
+               return this
+                       .attr('unselectable', 'on')
+                       .css('MozUserSelect', 'none')
+                       .bind('selectstart.ui', function() { return false; });
+       },
+
+       scrollParent: function() {
+               var scrollParent;
+               if(($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
+                       scrollParent = this.parents().filter(function() {
+                               return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+                       }).eq(0);
+               } else {
+                       scrollParent = this.parents().filter(function() {
+                               return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+                       }).eq(0);
+               }
+
+               return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
+       },
+
+       zIndex: function(zIndex) {
+               if (zIndex !== undefined) {
+                       return this.css('zIndex', zIndex);
+               }
+               
+               if (this.length) {
+                       var elem = $(this[0]), position, value;
+                       while (elem.length && elem[0] !== document) {
+                               // Ignore z-index if position is set to a value where z-index is ignored by the browser
+                               // This makes behavior of this function consistent across browsers
+                               // WebKit always returns auto if the element is positioned
+                               position = elem.css('position');
+                               if (position == 'absolute' || position == 'relative' || position == 'fixed')
+                               {
+                                       // IE returns 0 when zIndex is not specified
+                                       // other browsers return a string
+                                       // we ignore the case of nested elements with an explicit value of 0
+                                       // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
+                                       value = parseInt(elem.css('zIndex'));
+                                       if (!isNaN(value) && value != 0) {
+                                               return value;
+                                       }
+                               }
+                               elem = elem.parent();
+                       }
+               }
+
+               return 0;
+       }
+});
+
+
+//Additional selectors
+$.extend($.expr[':'], {
+       data: function(elem, i, match) {
+               return !!$.data(elem, match[3]);
+       },
+
+       focusable: function(element) {
+               var nodeName = element.nodeName.toLowerCase(),
+                       tabIndex = $.attr(element, 'tabindex');
+               return (/input|select|textarea|button|object/.test(nodeName)
+                       ? !element.disabled
+                       : 'a' == nodeName || 'area' == nodeName
+                               ? element.href || !isNaN(tabIndex)
+                               : !isNaN(tabIndex))
+                       // the element and all of its ancestors must be visible
+                       // the browser may report that the area is hidden
+                       && !$(element)['area' == nodeName ? 'parents' : 'closest'](':hidden').length;
+       },
+
+       tabbable: function(element) {
+               var tabIndex = $.attr(element, 'tabindex');
+               return (isNaN(tabIndex) || tabIndex >= 0) && $(element).is(':focusable');
+       }
+});
+
+})(jQuery);
diff --git a/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.dialog.js b/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.dialog.js
new file mode 100644 (file)
index 0000000..8f924f6
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ * jQuery UI Dialog 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.widget.js
+ *  jquery.ui.button.js
+ *     jquery.ui.draggable.js
+ *     jquery.ui.mouse.js
+ *     jquery.ui.position.js
+ *     jquery.ui.resizable.js
+ */
+(function($) {
+
+var uiDialogClasses =
+       'ui-dialog ' +
+       'ui-widget ' +
+       'ui-widget-content ' +
+       'ui-corner-all ';
+
+$.widget("ui.dialog", {
+       options: {
+               autoOpen: true,
+               buttons: {},
+               closeOnEscape: true,
+               closeText: 'close',
+               dialogClass: '',
+               draggable: true,
+               hide: null,
+               height: 'auto',
+               maxHeight: false,
+               maxWidth: false,
+               minHeight: 150,
+               minWidth: 150,
+               modal: false,
+               position: 'center',
+               resizable: true,
+               show: null,
+               stack: true,
+               title: '',
+               width: 300,
+               zIndex: 1000
+       },
+       _create: function() {
+               this.originalTitle = this.element.attr('title');
+
+               var self = this,
+                       options = self.options,
+
+                       title = options.title || self.originalTitle || '&#160;',
+                       titleId = $.ui.dialog.getTitleId(self.element),
+
+                       uiDialog = (self.uiDialog = $('<div></div>'))
+                               .appendTo(document.body)
+                               .hide()
+                               .addClass(uiDialogClasses + options.dialogClass)
+                               .css({
+                                       zIndex: options.zIndex
+                               })
+                               // setting tabIndex makes the div focusable
+                               // setting outline to 0 prevents a border on focus in Mozilla
+                               .attr('tabIndex', -1).css('outline', 0).keydown(function(event) {
+                                       if (options.closeOnEscape && event.keyCode &&
+                                               event.keyCode === $.ui.keyCode.ESCAPE) {
+                                               
+                                               self.close(event);
+                                               event.preventDefault();
+                                       }
+                               })
+                               .attr({
+                                       role: 'dialog',
+                                       'aria-labelledby': titleId
+                               })
+                               .mousedown(function(event) {
+                                       self.moveToTop(false, event);
+                               }),
+
+                       uiDialogContent = self.element
+                               .show()
+                               .removeAttr('title')
+                               .addClass(
+                                       'ui-dialog-content ' +
+                                       'ui-widget-content')
+                               .appendTo(uiDialog),
+
+                       uiDialogTitlebar = (self.uiDialogTitlebar = $('<div></div>'))
+                               .addClass(
+                                       'ui-dialog-titlebar ' +
+                                       'ui-widget-header ' +
+                                       'ui-corner-all ' +
+                                       'ui-helper-clearfix'
+                               )
+                               .prependTo(uiDialog),
+
+                       uiDialogTitlebarClose = $('<a href="#"></a>')
+                               .addClass(
+                                       'ui-dialog-titlebar-close ' +
+                                       'ui-corner-all'
+                               )
+                               .attr('role', 'button')
+                               .hover(
+                                       function() {
+                                               uiDialogTitlebarClose.addClass('ui-state-hover');
+                                       },
+                                       function() {
+                                               uiDialogTitlebarClose.removeClass('ui-state-hover');
+                                       }
+                               )
+                               .focus(function() {
+                                       uiDialogTitlebarClose.addClass('ui-state-focus');
+                               })
+                               .blur(function() {
+                                       uiDialogTitlebarClose.removeClass('ui-state-focus');
+                               })
+                               .click(function(event) {
+                                       self.close(event);
+                                       return false;
+                               })
+                               .appendTo(uiDialogTitlebar),
+
+                       uiDialogTitlebarCloseText = (self.uiDialogTitlebarCloseText = $('<span></span>'))
+                               .addClass(
+                                       'ui-icon ' +
+                                       'ui-icon-closethick'
+                               )
+                               .text(options.closeText)
+                               .appendTo(uiDialogTitlebarClose),
+
+                       uiDialogTitle = $('<span></span>')
+                               .addClass('ui-dialog-title')
+                               .attr('id', titleId)
+                               .html(title)
+                               .prependTo(uiDialogTitlebar);
+
+               //handling of deprecated beforeclose (vs beforeClose) option
+               //Ticket #4669 http://dev.jqueryui.com/ticket/4669
+               //TODO: remove in 1.9pre
+               if ($.isFunction(options.beforeclose) && !$.isFunction(options.beforeClose)) {
+                       options.beforeClose = options.beforeclose;
+               }
+
+               uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection();
+
+               if (options.draggable && $.fn.draggable) {
+                       self._makeDraggable();
+               }
+               if (options.resizable && $.fn.resizable) {
+                       self._makeResizable();
+               }
+
+               self._createButtons(options.buttons);
+               self._isOpen = false;
+
+               if ($.fn.bgiframe) {
+                       uiDialog.bgiframe();
+               }
+       },
+       _init: function() {
+               if ( this.options.autoOpen ) {
+                       this.open();
+               }
+       },
+
+       destroy: function() {
+               var self = this;
+               
+               if (self.overlay) {
+                       self.overlay.destroy();
+               }
+               self.uiDialog.hide();
+               self.element
+                       .unbind('.dialog')
+                       .removeData('dialog')
+                       .removeClass('ui-dialog-content ui-widget-content')
+                       .hide().appendTo('body');
+               self.uiDialog.remove();
+
+               if (self.originalTitle) {
+                       self.element.attr('title', self.originalTitle);
+               }
+
+               return self;
+       },
+       
+       widget: function() {
+               return this.uiDialog;
+       },
+
+       close: function(event) {
+               var self = this,
+                       maxZ;
+               
+               if (false === self._trigger('beforeClose', event)) {
+                       return;
+               }
+
+               if (self.overlay) {
+                       self.overlay.destroy();
+               }
+               self.uiDialog.unbind('keypress.ui-dialog');
+
+               self._isOpen = false;
+
+               if (self.options.hide) {
+                       self.uiDialog.hide(self.options.hide, function() {
+                               self._trigger('close', event);
+                       });
+               } else {
+                       self.uiDialog.hide();
+                       self._trigger('close', event);
+               }
+
+               $.ui.dialog.overlay.resize();
+
+               // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
+               if (self.options.modal) {
+                       maxZ = 0;
+                       $('.ui-dialog').each(function() {
+                               if (this !== self.uiDialog[0]) {
+                                       maxZ = Math.max(maxZ, $(this).css('z-index'));
+                               }
+                       });
+                       $.ui.dialog.maxZ = maxZ;
+               }
+
+               return self;
+       },
+
+       isOpen: function() {
+               return this._isOpen;
+       },
+
+       // the force parameter allows us to move modal dialogs to their correct
+       // position on open
+       moveToTop: function(force, event) {
+               var self = this,
+                       options = self.options,
+                       saveScroll;
+               
+               if ((options.modal && !force) ||
+                       (!options.stack && !options.modal)) {
+                       return self._trigger('focus', event);
+               }
+               
+               if (options.zIndex > $.ui.dialog.maxZ) {
+                       $.ui.dialog.maxZ = options.zIndex;
+               }
+               if (self.overlay) {
+                       $.ui.dialog.maxZ += 1;
+                       self.overlay.$el.css('z-index', $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ);
+               }
+
+               //Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed.
+               //  http://ui.jquery.com/bugs/ticket/3193
+               saveScroll = { scrollTop: self.element.attr('scrollTop'), scrollLeft: self.element.attr('scrollLeft') };
+               $.ui.dialog.maxZ += 1;
+               self.uiDialog.css('z-index', $.ui.dialog.maxZ);
+               self.element.attr(saveScroll);
+               self._trigger('focus', event);
+
+               return self;
+       },
+
+       open: function() {
+               if (this._isOpen) { return; }
+
+               var self = this,
+                       options = self.options,
+                       uiDialog = self.uiDialog;
+
+               self.overlay = options.modal ? new $.ui.dialog.overlay(self) : null;
+               if (uiDialog.next().length) {
+                       uiDialog.appendTo('body');
+               }
+               self._size();
+               self._position(options.position);
+               uiDialog.show(options.show);
+               self.moveToTop(true);
+
+               // prevent tabbing out of modal dialogs
+               if (options.modal) {
+                       uiDialog.bind('keypress.ui-dialog', function(event) {
+                               if (event.keyCode !== $.ui.keyCode.TAB) {
+                                       return;
+                               }
+       
+                               var tabbables = $(':tabbable', this),
+                                       first = tabbables.filter(':first'),
+                                       last  = tabbables.filter(':last');
+       
+                               if (event.target === last[0] && !event.shiftKey) {
+                                       first.focus(1);
+                                       return false;
+                               } else if (event.target === first[0] && event.shiftKey) {
+                                       last.focus(1);
+                                       return false;
+                               }
+                       });
+               }
+
+               // set focus to the first tabbable element in the content area or the first button
+               // if there are no tabbable elements, set focus on the dialog itself
+               $([])
+                       .add(uiDialog.find('.ui-dialog-content :tabbable:first'))
+                       .add(uiDialog.find('.ui-dialog-buttonpane :tabbable:first'))
+                       .add(uiDialog)
+                       .filter(':first')
+                       .focus();
+
+               self._trigger('open');
+               self._isOpen = true;
+
+               return self;
+       },
+
+       _createButtons: function(buttons) {
+               var self = this,
+                       hasButtons = false,
+                       uiDialogButtonPane = $('<div></div>')
+                               .addClass(
+                                       'ui-dialog-buttonpane ' +
+                                       'ui-widget-content ' +
+                                       'ui-helper-clearfix'
+                               );
+
+               // if we already have a button pane, remove it
+               self.uiDialog.find('.ui-dialog-buttonpane').remove();
+
+               if (typeof buttons === 'object' && buttons !== null) {
+                       $.each(buttons, function() {
+                               return !(hasButtons = true);
+                       });
+               }
+               if (hasButtons) {
+                       $.each(buttons, function(name, fn) {
+                               var button = $('<button type="button"></button>')
+                                       .text(name)
+                                       .click(function() { fn.apply(self.element[0], arguments); })
+                                       .appendTo(uiDialogButtonPane);
+                               if ($.fn.button) {
+                                       button.button();
+                               }
+                       });
+                       uiDialogButtonPane.appendTo(self.uiDialog);
+               }
+       },
+
+       _makeDraggable: function() {
+               var self = this,
+                       options = self.options,
+                       doc = $(document),
+                       heightBeforeDrag;
+
+               function filteredUi(ui) {
+                       return {
+                               position: ui.position,
+                               offset: ui.offset
+                       };
+               }
+
+               self.uiDialog.draggable({
+                       cancel: '.ui-dialog-content, .ui-dialog-titlebar-close',
+                       handle: '.ui-dialog-titlebar',
+                       containment: 'document',
+                       start: function(event, ui) {
+                               heightBeforeDrag = options.height === "auto" ? "auto" : $(this).height();
+                               $(this).height($(this).height()).addClass("ui-dialog-dragging");
+                               self._trigger('dragStart', event, filteredUi(ui));
+                       },
+                       drag: function(event, ui) {
+                               self._trigger('drag', event, filteredUi(ui));
+                       },
+                       stop: function(event, ui) {
+                               options.position = [ui.position.left - doc.scrollLeft(),
+                                       ui.position.top - doc.scrollTop()];
+                               $(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag);
+                               self._trigger('dragStop', event, filteredUi(ui));
+                               $.ui.dialog.overlay.resize();
+                       }
+               });
+       },
+
+       _makeResizable: function(handles) {
+               handles = (handles === undefined ? this.options.resizable : handles);
+               var self = this,
+                       options = self.options,
+                       // .ui-resizable has position: relative defined in the stylesheet
+                       // but dialogs have to use absolute or fixed positioning
+                       position = self.uiDialog.css('position'),
+                       resizeHandles = (typeof handles === 'string' ?
+                               handles :
+                               'n,e,s,w,se,sw,ne,nw'
+                       );
+
+               function filteredUi(ui) {
+                       return {
+                               originalPosition: ui.originalPosition,
+                               originalSize: ui.originalSize,
+                               position: ui.position,
+                               size: ui.size
+                       };
+               }
+
+               self.uiDialog.resizable({
+                       cancel: '.ui-dialog-content',
+                       containment: 'document',
+                       alsoResize: self.element,
+                       maxWidth: options.maxWidth,
+                       maxHeight: options.maxHeight,
+                       minWidth: options.minWidth,
+                       minHeight: self._minHeight(),
+                       handles: resizeHandles,
+                       start: function(event, ui) {
+                               $(this).addClass("ui-dialog-resizing");
+                               self._trigger('resizeStart', event, filteredUi(ui));
+                       },
+                       resize: function(event, ui) {
+                               self._trigger('resize', event, filteredUi(ui));
+                       },
+                       stop: function(event, ui) {
+                               $(this).removeClass("ui-dialog-resizing");
+                               options.height = $(this).height();
+                               options.width = $(this).width();
+                               self._trigger('resizeStop', event, filteredUi(ui));
+                               $.ui.dialog.overlay.resize();
+                       }
+               })
+               .css('position', position)
+               .find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se');
+       },
+
+       _minHeight: function() {
+               var options = this.options;
+
+               if (options.height === 'auto') {
+                       return options.minHeight;
+               } else {
+                       return Math.min(options.minHeight, options.height);
+               }
+       },
+
+       _position: function(position) {
+               var myAt = [],
+                       offset = [0, 0],
+                       isVisible;
+
+               position = position || $.ui.dialog.prototype.options.position;
+
+               // deep extending converts arrays to objects in jQuery <= 1.3.2 :-(
+//             if (typeof position == 'string' || $.isArray(position)) {
+//                     myAt = $.isArray(position) ? position : position.split(' ');
+
+               if (typeof position === 'string' || (typeof position === 'object' && '0' in position)) {
+                       myAt = position.split ? position.split(' ') : [position[0], position[1]];
+                       if (myAt.length === 1) {
+                               myAt[1] = myAt[0];
+                       }
+
+                       $.each(['left', 'top'], function(i, offsetPosition) {
+                               if (+myAt[i] === myAt[i]) {
+                                       offset[i] = myAt[i];
+                                       myAt[i] = offsetPosition;
+                               }
+                       });
+               } else if (typeof position === 'object') {
+                       if ('left' in position) {
+                               myAt[0] = 'left';
+                               offset[0] = position.left;
+                       } else if ('right' in position) {
+                               myAt[0] = 'right';
+                               offset[0] = -position.right;
+                       }
+
+                       if ('top' in position) {
+                               myAt[1] = 'top';
+                               offset[1] = position.top;
+                       } else if ('bottom' in position) {
+                               myAt[1] = 'bottom';
+                               offset[1] = -position.bottom;
+                       }
+               }
+
+               // need to show the dialog to get the actual offset in the position plugin
+               isVisible = this.uiDialog.is(':visible');
+               if (!isVisible) {
+                       this.uiDialog.show();
+               }
+               this.uiDialog
+                       // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
+                       .css({ top: 0, left: 0 })
+                       .position({
+                               my: myAt.join(' '),
+                               at: myAt.join(' '),
+                               offset: offset.join(' '),
+                               of: window,
+                               collision: 'fit',
+                               // ensure that the titlebar is never outside the document
+                               using: function(pos) {
+                                       var topOffset = $(this).css(pos).offset().top;
+                                       if (topOffset < 0) {
+                                               $(this).css('top', pos.top - topOffset);
+                                       }
+                               }
+                       });
+               if (!isVisible) {
+                       this.uiDialog.hide();
+               }
+       },
+
+       _setOption: function(key, value){
+               var self = this,
+                       uiDialog = self.uiDialog,
+                       isResizable = uiDialog.is(':data(resizable)'),
+                       resize = false;
+               
+               switch (key) {
+                       //handling of deprecated beforeclose (vs beforeClose) option
+                       //Ticket #4669 http://dev.jqueryui.com/ticket/4669
+                       //TODO: remove in 1.9pre
+                       case "beforeclose":
+                               key = "beforeClose";
+                               break;
+                       case "buttons":
+                               self._createButtons(value);
+                               break;
+                       case "closeText":
+                               // convert whatever was passed in to a string, for text() to not throw up
+                               self.uiDialogTitlebarCloseText.text("" + value);
+                               break;
+                       case "dialogClass":
+                               uiDialog
+                                       .removeClass(self.options.dialogClass)
+                                       .addClass(uiDialogClasses + value);
+                               break;
+                       case "disabled":
+                               if (value) {
+                                       uiDialog.addClass('ui-dialog-disabled');
+                               } else {
+                                       uiDialog.removeClass('ui-dialog-disabled');
+                               }
+                               break;
+                       case "draggable":
+                               if (value) {
+                                       self._makeDraggable();
+                               } else {
+                                       uiDialog.draggable('destroy');
+                               }
+                               break;
+                       case "height":
+                               resize = true;
+                               break;
+                       case "maxHeight":
+                               if (isResizable) {
+                                       uiDialog.resizable('option', 'maxHeight', value);
+                               }
+                               resize = true;
+                               break;
+                       case "maxWidth":
+                               if (isResizable) {
+                                       uiDialog.resizable('option', 'maxWidth', value);
+                               }
+                               resize = true;
+                               break;
+                       case "minHeight":
+                               if (isResizable) {
+                                       uiDialog.resizable('option', 'minHeight', value);
+                               }
+                               resize = true;
+                               break;
+                       case "minWidth":
+                               if (isResizable) {
+                                       uiDialog.resizable('option', 'minWidth', value);
+                               }
+                               resize = true;
+                               break;
+                       case "position":
+                               self._position(value);
+                               break;
+                       case "resizable":
+                               // currently resizable, becoming non-resizable
+                               if (isResizable && !value) {
+                                       uiDialog.resizable('destroy');
+                               }
+
+                               // currently resizable, changing handles
+                               if (isResizable && typeof value === 'string') {
+                                       uiDialog.resizable('option', 'handles', value);
+                               }
+
+                               // currently non-resizable, becoming resizable
+                               if (!isResizable && value !== false) {
+                                       self._makeResizable(value);
+                               }
+                               break;
+                       case "title":
+                               // convert whatever was passed in o a string, for html() to not throw up
+                               $(".ui-dialog-title", self.uiDialogTitlebar).html("" + (value || '&#160;'));
+                               break;
+                       case "width":
+                               resize = true;
+                               break;
+               }
+
+               $.Widget.prototype._setOption.apply(self, arguments);
+               if (resize) {
+                       self._size();
+               }
+       },
+
+       _size: function() {
+               /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
+                * divs will both have width and height set, so we need to reset them
+                */
+               var options = this.options,
+                       nonContentHeight;
+
+               // reset content sizing
+               // hide for non content measurement because height: 0 doesn't work in IE quirks mode (see #4350)
+               this.element.css('width', 'auto')
+                       .hide();
+
+               // reset wrapper sizing
+               // determine the height of all the non-content elements
+               nonContentHeight = this.uiDialog.css({
+                               height: 'auto',
+                               width: options.width
+                       })
+                       .height();
+
+               this.element
+                       .css(options.height === 'auto' ? {
+                                       minHeight: Math.max(options.minHeight - nonContentHeight, 0),
+                                       height: 'auto'
+                               } : {
+                                       minHeight: 0,
+                                       height: Math.max(options.height - nonContentHeight, 0)                          
+                       })
+                       .show();
+
+               if (this.uiDialog.is(':data(resizable)')) {
+                       this.uiDialog.resizable('option', 'minHeight', this._minHeight());
+               }
+       }
+});
+
+$.extend($.ui.dialog, {
+       version: "1.8",
+
+       uuid: 0,
+       maxZ: 0,
+
+       getTitleId: function($el) {
+               var id = $el.attr('id');
+               if (!id) {
+                       this.uuid += 1;
+                       id = this.uuid;
+               }
+               return 'ui-dialog-title-' + id;
+       },
+
+       overlay: function(dialog) {
+               this.$el = $.ui.dialog.overlay.create(dialog);
+       }
+});
+
+$.extend($.ui.dialog.overlay, {
+       instances: [],
+       // reuse old instances due to IE memory leak with alpha transparency (see #5185)
+       oldInstances: [],
+       maxZ: 0,
+       events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
+               function(event) { return event + '.dialog-overlay'; }).join(' '),
+       create: function(dialog) {
+               if (this.instances.length === 0) {
+                       // prevent use of anchors and inputs
+                       // we use a setTimeout in case the overlay is created from an
+                       // event that we're going to be cancelling (see #2804)
+                       setTimeout(function() {
+                               // handle $(el).dialog().dialog('close') (see #4065)
+                               if ($.ui.dialog.overlay.instances.length) {
+                                       $(document).bind($.ui.dialog.overlay.events, function(event) {
+                                               // stop events if the z-index of the target is < the z-index of the overlay
+                                               return ($(event.target).zIndex() >= $.ui.dialog.overlay.maxZ);
+                                       });
+                               }
+                       }, 1);
+
+                       // allow closing by pressing the escape key
+                       $(document).bind('keydown.dialog-overlay', function(event) {
+                               if (dialog.options.closeOnEscape && event.keyCode &&
+                                       event.keyCode === $.ui.keyCode.ESCAPE) {
+                                       
+                                       dialog.close(event);
+                                       event.preventDefault();
+                               }
+                       });
+
+                       // handle window resize
+                       $(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
+               }
+
+               var $el = (this.oldInstances.pop() || $('<div></div>').addClass('ui-widget-overlay'))
+                       .appendTo(document.body)
+                       .css({
+                               width: this.width(),
+                               height: this.height()
+                       });
+
+               if ($.fn.bgiframe) {
+                       $el.bgiframe();
+               }
+
+               this.instances.push($el);
+               return $el;
+       },
+
+       destroy: function($el) {
+               this.oldInstances.push(this.instances.splice($.inArray($el, this.instances), 1)[0]);
+
+               if (this.instances.length === 0) {
+                       $([document, window]).unbind('.dialog-overlay');
+               }
+
+               $el.remove();
+               
+               // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
+               var maxZ = 0;
+               $.each(this.instances, function() {
+                       maxZ = Math.max(maxZ, this.css('z-index'));
+               });
+               this.maxZ = maxZ;
+       },
+
+       height: function() {
+               var scrollHeight,
+                       offsetHeight;
+               // handle IE 6
+               if ($.browser.msie && $.browser.version < 7) {
+                       scrollHeight = Math.max(
+                               document.documentElement.scrollHeight,
+                               document.body.scrollHeight
+                       );
+                       offsetHeight = Math.max(
+                               document.documentElement.offsetHeight,
+                               document.body.offsetHeight
+                       );
+
+                       if (scrollHeight < offsetHeight) {
+                               return $(window).height() + 'px';
+                       } else {
+                               return scrollHeight + 'px';
+                       }
+               // handle "good" browsers
+               } else {
+                       return $(document).height() + 'px';
+               }
+       },
+
+       width: function() {
+               var scrollWidth,
+                       offsetWidth;
+               // handle IE 6
+               if ($.browser.msie && $.browser.version < 7) {
+                       scrollWidth = Math.max(
+                               document.documentElement.scrollWidth,
+                               document.body.scrollWidth
+                       );
+                       offsetWidth = Math.max(
+                               document.documentElement.offsetWidth,
+                               document.body.offsetWidth
+                       );
+
+                       if (scrollWidth < offsetWidth) {
+                               return $(window).width() + 'px';
+                       } else {
+                               return scrollWidth + 'px';
+                       }
+               // handle "good" browsers
+               } else {
+                       return $(document).width() + 'px';
+               }
+       },
+
+       resize: function() {
+               /* If the dialog is draggable and the user drags it past the
+                * right edge of the window, the document becomes wider so we
+                * need to stretch the overlay. If the user then drags the
+                * dialog back to the left, the document will become narrower,
+                * so we need to shrink the overlay to the appropriate size.
+                * This is handled by shrinking the overlay before setting it
+                * to the full document size.
+                */
+               var $overlays = $([]);
+               $.each($.ui.dialog.overlay.instances, function() {
+                       $overlays = $overlays.add(this);
+               });
+
+               $overlays.css({
+                       width: 0,
+                       height: 0
+               }).css({
+                       width: $.ui.dialog.overlay.width(),
+                       height: $.ui.dialog.overlay.height()
+               });
+       }
+});
+
+$.extend($.ui.dialog.overlay.prototype, {
+       destroy: function() {
+               $.ui.dialog.overlay.destroy(this.$el);
+       }
+});
+
+}(jQuery));
diff --git a/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.draggable.js b/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.draggable.js
new file mode 100644 (file)
index 0000000..81ed1d2
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * jQuery UI Draggable 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.mouse.js
+ *     jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.draggable", $.ui.mouse, {
+       widgetEventPrefix: "drag",
+       options: {
+               addClasses: true,
+               appendTo: "parent",
+               axis: false,
+               connectToSortable: false,
+               containment: false,
+               cursor: "auto",
+               cursorAt: false,
+               grid: false,
+               handle: false,
+               helper: "original",
+               iframeFix: false,
+               opacity: false,
+               refreshPositions: false,
+               revert: false,
+               revertDuration: 500,
+               scope: "default",
+               scroll: true,
+               scrollSensitivity: 20,
+               scrollSpeed: 20,
+               snap: false,
+               snapMode: "both",
+               snapTolerance: 20,
+               stack: false,
+               zIndex: false
+       },
+       _create: function() {
+
+               if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
+                       this.element[0].style.position = 'relative';
+
+               (this.options.addClasses && this.element.addClass("ui-draggable"));
+               (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
+
+               this._mouseInit();
+
+       },
+
+       destroy: function() {
+               if(!this.element.data('draggable')) return;
+               this.element
+                       .removeData("draggable")
+                       .unbind(".draggable")
+                       .removeClass("ui-draggable"
+                               + " ui-draggable-dragging"
+                               + " ui-draggable-disabled");
+               this._mouseDestroy();
+
+               return this;
+       },
+
+       _mouseCapture: function(event) {
+
+               var o = this.options;
+
+               // among others, prevent a drag on a resizable-handle
+               if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
+                       return false;
+
+               //Quit if we're not on a valid handle
+               this.handle = this._getHandle(event);
+               if (!this.handle)
+                       return false;
+
+               return true;
+
+       },
+
+       _mouseStart: function(event) {
+
+               var o = this.options;
+
+               //Create and append the visible helper
+               this.helper = this._createHelper(event);
+
+               //Cache the helper size
+               this._cacheHelperProportions();
+
+               //If ddmanager is used for droppables, set the global draggable
+               if($.ui.ddmanager)
+                       $.ui.ddmanager.current = this;
+
+               /*
+                * - Position generation -
+                * This block generates everything position related - it's the core of draggables.
+                */
+
+               //Cache the margins of the original element
+               this._cacheMargins();
+
+               //Store the helper's css position
+               this.cssPosition = this.helper.css("position");
+               this.scrollParent = this.helper.scrollParent();
+
+               //The element's absolute position on the page minus margins
+               this.offset = this.positionAbs = this.element.offset();
+               this.offset = {
+                       top: this.offset.top - this.margins.top,
+                       left: this.offset.left - this.margins.left
+               };
+
+               $.extend(this.offset, {
+                       click: { //Where the click happened, relative to the element
+                               left: event.pageX - this.offset.left,
+                               top: event.pageY - this.offset.top
+                       },
+                       parent: this._getParentOffset(),
+                       relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
+               });
+
+               //Generate the original position
+               this.originalPosition = this.position = this._generatePosition(event);
+               this.originalPageX = event.pageX;
+               this.originalPageY = event.pageY;
+
+               //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
+               (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
+
+               //Set a containment if given in the options
+               if(o.containment)
+                       this._setContainment();
+
+               //Trigger event + callbacks
+               if(this._trigger("start", event) === false) {
+                       this._clear();
+                       return false;
+               }
+
+               //Recache the helper size
+               this._cacheHelperProportions();
+
+               //Prepare the droppable offsets
+               if ($.ui.ddmanager && !o.dropBehaviour)
+                       $.ui.ddmanager.prepareOffsets(this, event);
+
+               this.helper.addClass("ui-draggable-dragging");
+               this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
+               return true;
+       },
+
+       _mouseDrag: function(event, noPropagation) {
+
+               //Compute the helpers position
+               this.position = this._generatePosition(event);
+               this.positionAbs = this._convertPositionTo("absolute");
+
+               //Call plugins and callbacks and use the resulting position if something is returned
+               if (!noPropagation) {
+                       var ui = this._uiHash();
+                       if(this._trigger('drag', event, ui) === false) {
+                               this._mouseUp({});
+                               return false;
+                       }
+                       this.position = ui.position;
+               }
+
+               if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
+               if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
+               if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
+
+               return false;
+       },
+
+       _mouseStop: function(event) {
+
+               //If we are using droppables, inform the manager about the drop
+               var dropped = false;
+               if ($.ui.ddmanager && !this.options.dropBehaviour)
+                       dropped = $.ui.ddmanager.drop(this, event);
+
+               //if a drop comes from outside (a sortable)
+               if(this.dropped) {
+                       dropped = this.dropped;
+                       this.dropped = false;
+               }
+               
+               //if the original element is removed, don't bother to continue
+               if(!this.element[0] || !this.element[0].parentNode)
+                       return false;
+
+               if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
+                       var self = this;
+                       $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
+                               if(self._trigger("stop", event) !== false) {
+                                       self._clear();
+                               }
+                       });
+               } else {
+                       if(this._trigger("stop", event) !== false) {
+                               this._clear();
+                       }
+               }
+
+               return false;
+       },
+       
+       cancel: function() {
+               
+               if(this.helper.is(".ui-draggable-dragging")) {
+                       this._mouseUp({});
+               } else {
+                       this._clear();
+               }
+               
+               return this;
+               
+       },
+
+       _getHandle: function(event) {
+
+               var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
+               $(this.options.handle, this.element)
+                       .find("*")
+                       .andSelf()
+                       .each(function() {
+                               if(this == event.target) handle = true;
+                       });
+
+               return handle;
+
+       },
+
+       _createHelper: function(event) {
+
+               var o = this.options;
+               var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element);
+
+               if(!helper.parents('body').length)
+                       helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
+
+               if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
+                       helper.css("position", "absolute");
+
+               return helper;
+
+       },
+
+       _adjustOffsetFromHelper: function(obj) {
+               if (typeof obj == 'string') {
+                       obj = obj.split(' ');
+               }
+               if ($.isArray(obj)) {
+                       obj = {left: +obj[0], top: +obj[1] || 0};
+               }
+               if ('left' in obj) {
+                       this.offset.click.left = obj.left + this.margins.left;
+               }
+               if ('right' in obj) {
+                       this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
+               }
+               if ('top' in obj) {
+                       this.offset.click.top = obj.top + this.margins.top;
+               }
+               if ('bottom' in obj) {
+                       this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
+               }
+       },
+
+       _getParentOffset: function() {
+
+               //Get the offsetParent and cache its position
+               this.offsetParent = this.helper.offsetParent();
+               var po = this.offsetParent.offset();
+
+               // This is a special case where we need to modify a offset calculated on start, since the following happened:
+               // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
+               // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
+               //    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
+               if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
+                       po.left += this.scrollParent.scrollLeft();
+                       po.top += this.scrollParent.scrollTop();
+               }
+
+               if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
+               || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
+                       po = { top: 0, left: 0 };
+
+               return {
+                       top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
+                       left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
+               };
+
+       },
+
+       _getRelativeOffset: function() {
+
+               if(this.cssPosition == "relative") {
+                       var p = this.element.position();
+                       return {
+                               top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
+                               left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
+                       };
+               } else {
+                       return { top: 0, left: 0 };
+               }
+
+       },
+
+       _cacheMargins: function() {
+               this.margins = {
+                       left: (parseInt(this.element.css("marginLeft"),10) || 0),
+                       top: (parseInt(this.element.css("marginTop"),10) || 0)
+               };
+       },
+
+       _cacheHelperProportions: function() {
+               this.helperProportions = {
+                       width: this.helper.outerWidth(),
+                       height: this.helper.outerHeight()
+               };
+       },
+
+       _setContainment: function() {
+
+               var o = this.options;
+               if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
+               if(o.containment == 'document' || o.containment == 'window') this.containment = [
+                       0 - this.offset.relative.left - this.offset.parent.left,
+                       0 - this.offset.relative.top - this.offset.parent.top,
+                       $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
+                       ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
+               ];
+
+               if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
+                       var ce = $(o.containment)[0]; if(!ce) return;
+                       var co = $(o.containment).offset();
+                       var over = ($(ce).css("overflow") != 'hidden');
+
+                       this.containment = [
+                               co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
+                               co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
+                               co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
+                               co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
+                       ];
+               } else if(o.containment.constructor == Array) {
+                       this.containment = o.containment;
+               }
+
+       },
+
+       _convertPositionTo: function(d, pos) {
+
+               if(!pos) pos = this.position;
+               var mod = d == "absolute" ? 1 : -1;
+               var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+               return {
+                       top: (
+                               pos.top                                                                                                                                 // The absolute mouse position
+                               + this.offset.relative.top * mod                                                                                // Only for relative positioned nodes: Relative offset from element to offset parent
+                               + this.offset.parent.top * mod                                                                                  // The offsetParent's offset without borders (offset + border)
+                               - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
+                       ),
+                       left: (
+                               pos.left                                                                                                                                // The absolute mouse position
+                               + this.offset.relative.left * mod                                                                               // Only for relative positioned nodes: Relative offset from element to offset parent
+                               + this.offset.parent.left * mod                                                                                 // The offsetParent's offset without borders (offset + border)
+                               - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
+                       )
+               };
+
+       },
+
+       _generatePosition: function(event) {
+
+               var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+               var pageX = event.pageX;
+               var pageY = event.pageY;
+
+               /*
+                * - Position constraining -
+                * Constrain the position to a mix of grid, containment.
+                */
+
+               if(this.originalPosition) { //If we are not dragging yet, we won't check for options
+
+                       if(this.containment) {
+                               if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
+                               if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
+                               if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
+                               if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
+                       }
+
+                       if(o.grid) {
+                               var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
+                               pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
+
+                               var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
+                               pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
+                       }
+
+               }
+
+               return {
+                       top: (
+                               pageY                                                                                                                           // The absolute mouse position
+                               - this.offset.click.top                                                                                                 // Click offset (relative to the element)
+                               - this.offset.relative.top                                                                                              // Only for relative positioned nodes: Relative offset from element to offset parent
+                               - this.offset.parent.top                                                                                                // The offsetParent's offset without borders (offset + border)
+                               + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
+                       ),
+                       left: (
+                               pageX                                                                                                                           // The absolute mouse position
+                               - this.offset.click.left                                                                                                // Click offset (relative to the element)
+                               - this.offset.relative.left                                                                                             // Only for relative positioned nodes: Relative offset from element to offset parent
+                               - this.offset.parent.left                                                                                               // The offsetParent's offset without borders (offset + border)
+                               + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
+                       )
+               };
+
+       },
+
+       _clear: function() {
+               this.helper.removeClass("ui-draggable-dragging");
+               if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
+               //if($.ui.ddmanager) $.ui.ddmanager.current = null;
+               this.helper = null;
+               this.cancelHelperRemoval = false;
+       },
+
+       // From now on bulk stuff - mainly helpers
+
+       _trigger: function(type, event, ui) {
+               ui = ui || this._uiHash();
+               $.ui.plugin.call(this, type, [event, ui]);
+               if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
+               return $.Widget.prototype._trigger.call(this, type, event, ui);
+       },
+
+       plugins: {},
+
+       _uiHash: function(event) {
+               return {
+                       helper: this.helper,
+                       position: this.position,
+                       originalPosition: this.originalPosition,
+                       offset: this.positionAbs
+               };
+       }
+
+});
+
+$.extend($.ui.draggable, {
+       version: "1.8"
+});
+
+$.ui.plugin.add("draggable", "connectToSortable", {
+       start: function(event, ui) {
+
+               var inst = $(this).data("draggable"), o = inst.options,
+                       uiSortable = $.extend({}, ui, { item: inst.element });
+               inst.sortables = [];
+               $(o.connectToSortable).each(function() {
+                       var sortable = $.data(this, 'sortable');
+                       if (sortable && !sortable.options.disabled) {
+                               inst.sortables.push({
+                                       instance: sortable,
+                                       shouldRevert: sortable.options.revert
+                               });
+                               sortable._refreshItems();       //Do a one-time refresh at start to refresh the containerCache
+                               sortable._trigger("activate", event, uiSortable);
+                       }
+               });
+
+       },
+       stop: function(event, ui) {
+
+               //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
+               var inst = $(this).data("draggable"),
+                       uiSortable = $.extend({}, ui, { item: inst.element });
+
+               $.each(inst.sortables, function() {
+                       if(this.instance.isOver) {
+
+                               this.instance.isOver = 0;
+
+                               inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
+                               this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
+
+                               //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
+                               if(this.shouldRevert) this.instance.options.revert = true;
+
+                               //Trigger the stop of the sortable
+                               this.instance._mouseStop(event);
+
+                               this.instance.options.helper = this.instance.options._helper;
+
+                               //If the helper has been the original item, restore properties in the sortable
+                               if(inst.options.helper == 'original')
+                                       this.instance.currentItem.css({ top: 'auto', left: 'auto' });
+
+                       } else {
+                               this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
+                               this.instance._trigger("deactivate", event, uiSortable);
+                       }
+
+               });
+
+       },
+       drag: function(event, ui) {
+
+               var inst = $(this).data("draggable"), self = this;
+
+               var checkPos = function(o) {
+                       var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
+                       var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
+                       var itemHeight = o.height, itemWidth = o.width;
+                       var itemTop = o.top, itemLeft = o.left;
+
+                       return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
+               };
+
+               $.each(inst.sortables, function(i) {
+                       
+                       //Copy over some variables to allow calling the sortable's native _intersectsWith
+                       this.instance.positionAbs = inst.positionAbs;
+                       this.instance.helperProportions = inst.helperProportions;
+                       this.instance.offset.click = inst.offset.click;
+                       
+                       if(this.instance._intersectsWith(this.instance.containerCache)) {
+
+                               //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
+                               if(!this.instance.isOver) {
+
+                                       this.instance.isOver = 1;
+                                       //Now we fake the start of dragging for the sortable instance,
+                                       //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
+                                       //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
+                                       this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true);
+                                       this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
+                                       this.instance.options.helper = function() { return ui.helper[0]; };
+
+                                       event.target = this.instance.currentItem[0];
+                                       this.instance._mouseCapture(event, true);
+                                       this.instance._mouseStart(event, true, true);
+
+                                       //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
+                                       this.instance.offset.click.top = inst.offset.click.top;
+                                       this.instance.offset.click.left = inst.offset.click.left;
+                                       this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
+                                       this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
+
+                                       inst._trigger("toSortable", event);
+                                       inst.dropped = this.instance.element; //draggable revert needs that
+                                       //hack so receive/update callbacks work (mostly)
+                                       inst.currentItem = inst.element;
+                                       this.instance.fromOutside = inst;
+
+                               }
+
+                               //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
+                               if(this.instance.currentItem) this.instance._mouseDrag(event);
+
+                       } else {
+
+                               //If it doesn't intersect with the sortable, and it intersected before,
+                               //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
+                               if(this.instance.isOver) {
+
+                                       this.instance.isOver = 0;
+                                       this.instance.cancelHelperRemoval = true;
+                                       
+                                       //Prevent reverting on this forced stop
+                                       this.instance.options.revert = false;
+                                       
+                                       // The out event needs to be triggered independently
+                                       this.instance._trigger('out', event, this.instance._uiHash(this.instance));
+                                       
+                                       this.instance._mouseStop(event, true);
+                                       this.instance.options.helper = this.instance.options._helper;
+
+                                       //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
+                                       this.instance.currentItem.remove();
+                                       if(this.instance.placeholder) this.instance.placeholder.remove();
+
+                                       inst._trigger("fromSortable", event);
+                                       inst.dropped = false; //draggable revert needs that
+                               }
+
+                       };
+
+               });
+
+       }
+});
+
+$.ui.plugin.add("draggable", "cursor", {
+       start: function(event, ui) {
+               var t = $('body'), o = $(this).data('draggable').options;
+               if (t.css("cursor")) o._cursor = t.css("cursor");
+               t.css("cursor", o.cursor);
+       },
+       stop: function(event, ui) {
+               var o = $(this).data('draggable').options;
+               if (o._cursor) $('body').css("cursor", o._cursor);
+       }
+});
+
+$.ui.plugin.add("draggable", "iframeFix", {
+       start: function(event, ui) {
+               var o = $(this).data('draggable').options;
+               $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
+                       $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
+                       .css({
+                               width: this.offsetWidth+"px", height: this.offsetHeight+"px",
+                               position: "absolute", opacity: "0.001", zIndex: 1000
+                       })
+                       .css($(this).offset())
+                       .appendTo("body");
+               });
+       },
+       stop: function(event, ui) {
+               $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers
+       }
+});
+
+$.ui.plugin.add("draggable", "opacity", {
+       start: function(event, ui) {
+               var t = $(ui.helper), o = $(this).data('draggable').options;
+               if(t.css("opacity")) o._opacity = t.css("opacity");
+               t.css('opacity', o.opacity);
+       },
+       stop: function(event, ui) {
+               var o = $(this).data('draggable').options;
+               if(o._opacity) $(ui.helper).css('opacity', o._opacity);
+       }
+});
+
+$.ui.plugin.add("draggable", "scroll", {
+       start: function(event, ui) {
+               var i = $(this).data("draggable");
+               if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
+       },
+       drag: function(event, ui) {
+
+               var i = $(this).data("draggable"), o = i.options, scrolled = false;
+
+               if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
+
+                       if(!o.axis || o.axis != 'x') {
+                               if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
+                                       i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
+                               else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
+                                       i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
+                       }
+
+                       if(!o.axis || o.axis != 'y') {
+                               if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
+                                       i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
+                               else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
+                                       i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
+                       }
+
+               } else {
+
+                       if(!o.axis || o.axis != 'x') {
+                               if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
+                                       scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
+                               else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
+                                       scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
+                       }
+
+                       if(!o.axis || o.axis != 'y') {
+                               if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
+                                       scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
+                               else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
+                                       scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
+                       }
+
+               }
+
+               if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
+                       $.ui.ddmanager.prepareOffsets(i, event);
+
+       }
+});
+
+$.ui.plugin.add("draggable", "snap", {
+       start: function(event, ui) {
+
+               var i = $(this).data("draggable"), o = i.options;
+               i.snapElements = [];
+
+               $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
+                       var $t = $(this); var $o = $t.offset();
+                       if(this != i.element[0]) i.snapElements.push({
+                               item: this,
+                               width: $t.outerWidth(), height: $t.outerHeight(),
+                               top: $o.top, left: $o.left
+                       });
+               });
+
+       },
+       drag: function(event, ui) {
+
+               var inst = $(this).data("draggable"), o = inst.options;
+               var d = o.snapTolerance;
+
+               var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
+                       y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
+
+               for (var i = inst.snapElements.length - 1; i >= 0; i--){
+
+                       var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
+                               t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
+
+                       //Yes, I know, this is insane ;)
+                       if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
+                               if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+                               inst.snapElements[i].snapping = false;
+                               continue;
+                       }
+
+                       if(o.snapMode != 'inner') {
+                               var ts = Math.abs(t - y2) <= d;
+                               var bs = Math.abs(b - y1) <= d;
+                               var ls = Math.abs(l - x2) <= d;
+                               var rs = Math.abs(r - x1) <= d;
+                               if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+                               if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
+                               if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
+                               if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
+                       }
+
+                       var first = (ts || bs || ls || rs);
+
+                       if(o.snapMode != 'outer') {
+                               var ts = Math.abs(t - y1) <= d;
+                               var bs = Math.abs(b - y2) <= d;
+                               var ls = Math.abs(l - x1) <= d;
+                               var rs = Math.abs(r - x2) <= d;
+                               if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
+                               if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+                               if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
+                               if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
+                       }
+
+                       if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
+                               (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+                       inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
+
+               };
+
+       }
+});
+
+$.ui.plugin.add("draggable", "stack", {
+       start: function(event, ui) {
+
+               var o = $(this).data("draggable").options;
+
+               var group = $.makeArray($(o.stack)).sort(function(a,b) {
+                       return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
+               });
+               if (!group.length) { return; }
+               
+               var min = parseInt(group[0].style.zIndex) || 0;
+               $(group).each(function(i) {
+                       this.style.zIndex = min + i;
+               });
+
+               this[0].style.zIndex = min + group.length;
+
+       }
+});
+
+$.ui.plugin.add("draggable", "zIndex", {
+       start: function(event, ui) {
+               var t = $(ui.helper), o = $(this).data("draggable").options;
+               if(t.css("zIndex")) o._zIndex = t.css("zIndex");
+               t.css('zIndex', o.zIndex);
+       },
+       stop: function(event, ui) {
+               var o = $(this).data("draggable").options;
+               if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
+       }
+});
+
+})(jQuery);
diff --git a/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.mouse.js b/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.mouse.js
new file mode 100644 (file)
index 0000000..46aeb81
--- /dev/null
@@ -0,0 +1,151 @@
+/*!
+ * jQuery UI Mouse 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ *     jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.mouse", {
+       options: {
+               cancel: ':input,option',
+               distance: 1,
+               delay: 0
+       },
+       _mouseInit: function() {
+               var self = this;
+
+               this.element
+                       .bind('mousedown.'+this.widgetName, function(event) {
+                               return self._mouseDown(event);
+                       })
+                       .bind('click.'+this.widgetName, function(event) {
+                               if(self._preventClickEvent) {
+                                       self._preventClickEvent = false;
+                                       event.stopImmediatePropagation();
+                                       return false;
+                               }
+                       });
+
+               this.started = false;
+       },
+
+       // TODO: make sure destroying one instance of mouse doesn't mess with
+       // other instances of mouse
+       _mouseDestroy: function() {
+               this.element.unbind('.'+this.widgetName);
+       },
+
+       _mouseDown: function(event) {
+               // don't let more than one widget handle mouseStart
+               // TODO: figure out why we have to use originalEvent
+               event.originalEvent = event.originalEvent || {};
+               if (event.originalEvent.mouseHandled) { return; }
+
+               // we may have missed mouseup (out of window)
+               (this._mouseStarted && this._mouseUp(event));
+
+               this._mouseDownEvent = event;
+
+               var self = this,
+                       btnIsLeft = (event.which == 1),
+                       elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
+               if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
+                       return true;
+               }
+
+               this.mouseDelayMet = !this.options.delay;
+               if (!this.mouseDelayMet) {
+                       this._mouseDelayTimer = setTimeout(function() {
+                               self.mouseDelayMet = true;
+                       }, this.options.delay);
+               }
+
+               if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+                       this._mouseStarted = (this._mouseStart(event) !== false);
+                       if (!this._mouseStarted) {
+                               event.preventDefault();
+                               return true;
+                       }
+               }
+
+               // these delegates are required to keep context
+               this._mouseMoveDelegate = function(event) {
+                       return self._mouseMove(event);
+               };
+               this._mouseUpDelegate = function(event) {
+                       return self._mouseUp(event);
+               };
+               $(document)
+                       .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+                       .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+               // preventDefault() is used to prevent the selection of text here -
+               // however, in Safari, this causes select boxes not to be selectable
+               // anymore, so this fix is needed
+               ($.browser.safari || event.preventDefault());
+
+               event.originalEvent.mouseHandled = true;
+               return true;
+       },
+
+       _mouseMove: function(event) {
+               // IE mouseup check - mouseup happened when mouse was out of window
+               if ($.browser.msie && !event.button) {
+                       return this._mouseUp(event);
+               }
+
+               if (this._mouseStarted) {
+                       this._mouseDrag(event);
+                       return event.preventDefault();
+               }
+
+               if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+                       this._mouseStarted =
+                               (this._mouseStart(this._mouseDownEvent, event) !== false);
+                       (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
+               }
+
+               return !this._mouseStarted;
+       },
+
+       _mouseUp: function(event) {
+               $(document)
+                       .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+                       .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+               if (this._mouseStarted) {
+                       this._mouseStarted = false;
+                       this._preventClickEvent = (event.target == this._mouseDownEvent.target);
+                       this._mouseStop(event);
+               }
+
+               return false;
+       },
+
+       _mouseDistanceMet: function(event) {
+               return (Math.max(
+                               Math.abs(this._mouseDownEvent.pageX - event.pageX),
+                               Math.abs(this._mouseDownEvent.pageY - event.pageY)
+                       ) >= this.options.distance
+               );
+       },
+
+       _mouseDelayMet: function(event) {
+               return this.mouseDelayMet;
+       },
+
+       // These are placeholder methods, to be overriden by extending plugin
+       _mouseStart: function(event) {},
+       _mouseDrag: function(event) {},
+       _mouseStop: function(event) {},
+       _mouseCapture: function(event) { return true; }
+});
+
+})(jQuery);
diff --git a/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.position.js b/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.position.js
new file mode 100644 (file)
index 0000000..cfb727e
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * jQuery UI Position 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function( $ ) {
+
+$.ui = $.ui || {};
+
+var horizontalPositions = /left|center|right/,
+       horizontalDefault = "center",
+       verticalPositions = /top|center|bottom/,
+       verticalDefault = "center",
+       _position = $.fn.position,
+       _offset = $.fn.offset;
+
+$.fn.position = function( options ) {
+       if ( !options || !options.of ) {
+               return _position.apply( this, arguments );
+       }
+
+       // make a copy, we don't want to modify arguments
+       options = $.extend( {}, options );
+
+       var target = $( options.of ),
+               collision = ( options.collision || "flip" ).split( " " ),
+               offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
+               targetWidth,
+               targetHeight,
+               basePosition;
+
+       if ( options.of.nodeType === 9 ) {
+               targetWidth = target.width();
+               targetHeight = target.height();
+               basePosition = { top: 0, left: 0 };
+       } else if ( options.of.scrollTo && options.of.document ) {
+               targetWidth = target.width();
+               targetHeight = target.height();
+               basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
+       } else if ( options.of.preventDefault ) {
+               // force left top to allow flipping
+               options.at = "left top";
+               targetWidth = targetHeight = 0;
+               basePosition = { top: options.of.pageY, left: options.of.pageX };
+       } else {
+               targetWidth = target.outerWidth();
+               targetHeight = target.outerHeight();
+               basePosition = target.offset();
+       }
+
+       // force my and at to have valid horizontal and veritcal positions
+       // if a value is missing or invalid, it will be converted to center 
+       $.each( [ "my", "at" ], function() {
+               var pos = ( options[this] || "" ).split( " " );
+               if ( pos.length === 1) {
+                       pos = horizontalPositions.test( pos[0] ) ?
+                               pos.concat( [verticalDefault] ) :
+                               verticalPositions.test( pos[0] ) ?
+                                       [ horizontalDefault ].concat( pos ) :
+                                       [ horizontalDefault, verticalDefault ];
+               }
+               pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : horizontalDefault;
+               pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : verticalDefault;
+               options[ this ] = pos;
+       });
+
+       // normalize collision option
+       if ( collision.length === 1 ) {
+               collision[ 1 ] = collision[ 0 ];
+       }
+
+       // normalize offset option
+       offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
+       if ( offset.length === 1 ) {
+               offset[ 1 ] = offset[ 0 ];
+       }
+       offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
+
+       if ( options.at[0] === "right" ) {
+               basePosition.left += targetWidth;
+       } else if (options.at[0] === horizontalDefault ) {
+               basePosition.left += targetWidth / 2;
+       }
+
+       if ( options.at[1] === "bottom" ) {
+               basePosition.top += targetHeight;
+       } else if ( options.at[1] === verticalDefault ) {
+               basePosition.top += targetHeight / 2;
+       }
+
+       basePosition.left += offset[ 0 ];
+       basePosition.top += offset[ 1 ];
+
+       return this.each(function() {
+               var elem = $( this ),
+                       elemWidth = elem.outerWidth(),
+                       elemHeight = elem.outerHeight(),
+                       position = $.extend( {}, basePosition );
+
+               if ( options.my[0] === "right" ) {
+                       position.left -= elemWidth;
+               } else if ( options.my[0] === horizontalDefault ) {
+                       position.left -= elemWidth / 2;
+               }
+
+               if ( options.my[1] === "bottom" ) {
+                       position.top -= elemHeight;
+               } else if ( options.my[1] === verticalDefault ) {
+                       position.top -= elemHeight / 2;
+               }
+
+               $.each( [ "left", "top" ], function( i, dir ) {
+                       if ( $.ui.position[ collision[i] ] ) {
+                               $.ui.position[ collision[i] ][ dir ]( position, {
+                                       targetWidth: targetWidth,
+                                       targetHeight: targetHeight,
+                                       elemWidth: elemWidth,
+                                       elemHeight: elemHeight,
+                                       offset: offset,
+                                       my: options.my,
+                                       at: options.at
+                               });
+                       }
+               });
+
+               if ( $.fn.bgiframe ) {
+                       elem.bgiframe();
+               }
+               elem.offset( $.extend( position, { using: options.using } ) );
+       });
+};
+
+$.ui.position = {
+       fit: {
+               left: function( position, data ) {
+                       var win = $( window ),
+                               over = position.left + data.elemWidth - win.width() - win.scrollLeft();
+                       position.left = over > 0 ? position.left - over : Math.max( 0, position.left );
+               },
+               top: function( position, data ) {
+                       var win = $( window ),
+                               over = position.top + data.elemHeight - win.height() - win.scrollTop();
+                       position.top = over > 0 ? position.top - over : Math.max( 0, position.top );
+               }
+       },
+
+       flip: {
+               left: function( position, data ) {
+                       if ( data.at[0] === "center" ) {
+                               return;
+                       }
+                       var win = $( window ),
+                               over = position.left + data.elemWidth - win.width() - win.scrollLeft(),
+                               myOffset = data.my[ 0 ] === "left" ?
+                                       -data.elemWidth :
+                                       data.my[ 0 ] === "right" ?
+                                               data.elemWidth :
+                                               0,
+                               offset = -2 * data.offset[ 0 ];
+                       position.left += position.left < 0 ?
+                               myOffset + data.targetWidth + offset :
+                               over > 0 ?
+                                       myOffset - data.targetWidth + offset :
+                                       0;
+               },
+               top: function( position, data ) {
+                       if ( data.at[1] === "center" ) {
+                               return;
+                       }
+                       var win = $( window ),
+                               over = position.top + data.elemHeight - win.height() - win.scrollTop(),
+                               myOffset = data.my[ 1 ] === "top" ?
+                                       -data.elemHeight :
+                                       data.my[ 1 ] === "bottom" ?
+                                               data.elemHeight :
+                                               0,
+                               atOffset = data.at[ 1 ] === "top" ?
+                                       data.targetHeight :
+                                       -data.targetHeight,
+                               offset = -2 * data.offset[ 1 ];
+                       position.top += position.top < 0 ?
+                               myOffset + data.targetHeight + offset :
+                               over > 0 ?
+                                       myOffset + atOffset + offset :
+                                       0;
+               }
+       }
+};
+
+// offset setter from jQuery 1.4
+if ( !$.offset.setOffset ) {
+       $.offset.setOffset = function( elem, options ) {
+               // set position first, in-case top/left are set even on static elem
+               if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
+                       elem.style.position = "relative";
+               }
+               var curElem   = $( elem ),
+                       curOffset = curElem.offset(),
+                       curTop    = parseInt( $.curCSS( elem, "top",  true ), 10 ) || 0,
+                       curLeft   = parseInt( $.curCSS( elem, "left", true ), 10)  || 0,
+                       props     = {
+                               top:  (options.top  - curOffset.top)  + curTop,
+                               left: (options.left - curOffset.left) + curLeft
+                       };
+               
+               if ( 'using' in options ) {
+                       options.using.call( elem, props );
+               } else {
+                       curElem.css( props );
+               }
+       };
+
+       $.fn.offset = function( options ) {
+               var elem = this[ 0 ];
+               if ( !elem || !elem.ownerDocument ) { return null; }
+               if ( options ) { 
+                       return this.each(function() {
+                               $.offset.setOffset( this, options );
+                       });
+               }
+               return _offset.call( this );
+       };
+}
+
+}( jQuery ));
diff --git a/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.slider.js b/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.slider.js
new file mode 100644 (file)
index 0000000..e1e3e82
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ * jQuery UI Slider 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.mouse.js
+ *     jquery.ui.widget.js
+ */
+
+(function($) {
+
+// number of pages in a slider
+// (how many times can you page up/down to go through the whole range)
+var numPages = 5;
+
+$.widget("ui.slider", $.ui.mouse, {
+       widgetEventPrefix: "slide",
+       options: {
+               animate: false,
+               distance: 0,
+               max: 100,
+               min: 0,
+               orientation: 'horizontal',
+               range: false,
+               step: 1,
+               value: 0,
+               values: null
+       },
+       _create: function() {
+
+               var self = this, o = this.options;
+               this._keySliding = false;
+               this._mouseSliding = false;
+               this._animateOff = true;
+               this._handleIndex = null;
+               this._detectOrientation();
+               this._mouseInit();
+
+               this.element
+                       .addClass("ui-slider"
+                               + " ui-slider-" + this.orientation
+                               + " ui-widget"
+                               + " ui-widget-content"
+                               + " ui-corner-all");
+               
+               if (o.disabled) {
+                       this.element.addClass('ui-slider-disabled ui-disabled');
+               }
+
+               this.range = $([]);
+
+               if (o.range) {
+
+                       if (o.range === true) {
+                               this.range = $('<div></div>');
+                               if (!o.values) o.values = [this._valueMin(), this._valueMin()];
+                               if (o.values.length && o.values.length != 2) {
+                                       o.values = [o.values[0], o.values[0]];
+                               }
+                       } else {
+                               this.range = $('<div></div>');
+                       }
+
+                       this.range
+                               .appendTo(this.element)
+                               .addClass("ui-slider-range");
+
+                       if (o.range == "min" || o.range == "max") {
+                               this.range.addClass("ui-slider-range-" + o.range);
+                       }
+
+                       // note: this isn't the most fittingly semantic framework class for this element,
+                       // but worked best visually with a variety of themes
+                       this.range.addClass("ui-widget-header");
+
+               }
+
+               if ($(".ui-slider-handle", this.element).length == 0)
+                       $('<a href="#"></a>')
+                               .appendTo(this.element)
+                               .addClass("ui-slider-handle");
+
+               if (o.values && o.values.length) {
+                       while ($(".ui-slider-handle", this.element).length < o.values.length)
+                               $('<a href="#"></a>')
+                                       .appendTo(this.element)
+                                       .addClass("ui-slider-handle");
+               }
+
+               this.handles = $(".ui-slider-handle", this.element)
+                       .addClass("ui-state-default"
+                               + " ui-corner-all");
+
+               this.handle = this.handles.eq(0);
+
+               this.handles.add(this.range).filter("a")
+                       .click(function(event) {
+                               event.preventDefault();
+                       })
+                       .hover(function() {
+                               if (!o.disabled) {
+                                       $(this).addClass('ui-state-hover');
+                               }
+                       }, function() {
+                               $(this).removeClass('ui-state-hover');
+                       })
+                       .focus(function() {
+                               if (!o.disabled) {
+                                       $(".ui-slider .ui-state-focus").removeClass('ui-state-focus'); $(this).addClass('ui-state-focus');
+                               } else {
+                                       $(this).blur();
+                               }
+                       })
+                       .blur(function() {
+                               $(this).removeClass('ui-state-focus');
+                       });
+
+               this.handles.each(function(i) {
+                       $(this).data("index.ui-slider-handle", i);
+               });
+
+               this.handles.keydown(function(event) {
+
+                       var ret = true;
+
+                       var index = $(this).data("index.ui-slider-handle");
+
+                       if (self.options.disabled)
+                               return;
+
+                       switch (event.keyCode) {
+                               case $.ui.keyCode.HOME:
+                               case $.ui.keyCode.END:
+                               case $.ui.keyCode.PAGE_UP:
+                               case $.ui.keyCode.PAGE_DOWN:
+                               case $.ui.keyCode.UP:
+                               case $.ui.keyCode.RIGHT:
+                               case $.ui.keyCode.DOWN:
+                               case $.ui.keyCode.LEFT:
+                                       ret = false;
+                                       if (!self._keySliding) {
+                                               self._keySliding = true;
+                                               $(this).addClass("ui-state-active");
+                                               self._start(event, index);
+                                       }
+                                       break;
+                       }
+
+                       var curVal, newVal, step = self._step();
+                       if (self.options.values && self.options.values.length) {
+                               curVal = newVal = self.values(index);
+                       } else {
+                               curVal = newVal = self.value();
+                       }
+
+                       switch (event.keyCode) {
+                               case $.ui.keyCode.HOME:
+                                       newVal = self._valueMin();
+                                       break;
+                               case $.ui.keyCode.END:
+                                       newVal = self._valueMax();
+                                       break;
+                               case $.ui.keyCode.PAGE_UP:
+                                       newVal = curVal + ((self._valueMax() - self._valueMin()) / numPages);
+                                       break;
+                               case $.ui.keyCode.PAGE_DOWN:
+                                       newVal = curVal - ((self._valueMax() - self._valueMin()) / numPages);
+                                       break;
+                               case $.ui.keyCode.UP:
+                               case $.ui.keyCode.RIGHT:
+                                       if(curVal == self._valueMax()) return;
+                                       newVal = curVal + step;
+                                       break;
+                               case $.ui.keyCode.DOWN:
+                               case $.ui.keyCode.LEFT:
+                                       if(curVal == self._valueMin()) return;
+                                       newVal = curVal - step;
+                                       break;
+                       }
+
+                       self._slide(event, index, newVal);
+
+                       return ret;
+
+               }).keyup(function(event) {
+
+                       var index = $(this).data("index.ui-slider-handle");
+
+                       if (self._keySliding) {
+                               self._keySliding = false;
+                               self._stop(event, index);
+                               self._change(event, index);
+                               $(this).removeClass("ui-state-active");
+                       }
+
+               });
+
+               this._refreshValue();
+
+               this._animateOff = false;
+
+       },
+
+       destroy: function() {
+
+               this.handles.remove();
+               this.range.remove();
+
+               this.element
+                       .removeClass("ui-slider"
+                               + " ui-slider-horizontal"
+                               + " ui-slider-vertical"
+                               + " ui-slider-disabled"
+                               + " ui-widget"
+                               + " ui-widget-content"
+                               + " ui-corner-all")
+                       .removeData("slider")
+                       .unbind(".slider");
+
+               this._mouseDestroy();
+
+               return this;
+       },
+
+       _mouseCapture: function(event) {
+
+               var o = this.options;
+
+               if (o.disabled)
+                       return false;
+
+               this.elementSize = {
+                       width: this.element.outerWidth(),
+                       height: this.element.outerHeight()
+               };
+               this.elementOffset = this.element.offset();
+
+               var position = { x: event.pageX, y: event.pageY };
+               var normValue = this._normValueFromMouse(position);
+
+               var distance = this._valueMax() - this._valueMin() + 1, closestHandle;
+               var self = this, index;
+               this.handles.each(function(i) {
+                       var thisDistance = Math.abs(normValue - self.values(i));
+                       if (distance > thisDistance) {
+                               distance = thisDistance;
+                               closestHandle = $(this);
+                               index = i;
+                       }
+               });
+
+               // workaround for bug #3736 (if both handles of a range are at 0,
+               // the first is always used as the one with least distance,
+               // and moving it is obviously prevented by preventing negative ranges)
+               if(o.range == true && this.values(1) == o.min) {
+                       closestHandle = $(this.handles[++index]);
+               }
+
+               this._start(event, index);
+               this._mouseSliding = true;
+
+               self._handleIndex = index;
+
+               closestHandle
+                       .addClass("ui-state-active")
+                       .focus();
+               
+               var offset = closestHandle.offset();
+               var mouseOverHandle = !$(event.target).parents().andSelf().is('.ui-slider-handle');
+               this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
+                       left: event.pageX - offset.left - (closestHandle.width() / 2),
+                       top: event.pageY - offset.top
+                               - (closestHandle.height() / 2)
+                               - (parseInt(closestHandle.css('borderTopWidth'),10) || 0)
+                               - (parseInt(closestHandle.css('borderBottomWidth'),10) || 0)
+                               + (parseInt(closestHandle.css('marginTop'),10) || 0)
+               };
+
+               normValue = this._normValueFromMouse(position);
+               this._slide(event, index, normValue);
+               this._animateOff = true;
+               return true;
+
+       },
+
+       _mouseStart: function(event) {
+               return true;
+       },
+
+       _mouseDrag: function(event) {
+
+               var position = { x: event.pageX, y: event.pageY };
+               var normValue = this._normValueFromMouse(position);
+               
+               this._slide(event, this._handleIndex, normValue);
+
+               return false;
+
+       },
+
+       _mouseStop: function(event) {
+
+               this.handles.removeClass("ui-state-active");
+               this._mouseSliding = false;
+               this._stop(event, this._handleIndex);
+               this._change(event, this._handleIndex);
+               this._handleIndex = null;
+               this._clickOffset = null;
+
+               this._animateOff = false;
+               return false;
+
+       },
+       
+       _detectOrientation: function() {
+               this.orientation = this.options.orientation == 'vertical' ? 'vertical' : 'horizontal';
+       },
+
+       _normValueFromMouse: function(position) {
+
+               var pixelTotal, pixelMouse;
+               if ('horizontal' == this.orientation) {
+                       pixelTotal = this.elementSize.width;
+                       pixelMouse = position.x - this.elementOffset.left - (this._clickOffset ? this._clickOffset.left : 0);
+               } else {
+                       pixelTotal = this.elementSize.height;
+                       pixelMouse = position.y - this.elementOffset.top - (this._clickOffset ? this._clickOffset.top : 0);
+               }
+
+               var percentMouse = (pixelMouse / pixelTotal);
+               if (percentMouse > 1) percentMouse = 1;
+               if (percentMouse < 0) percentMouse = 0;
+               if ('vertical' == this.orientation)
+                       percentMouse = 1 - percentMouse;
+
+               var valueTotal = this._valueMax() - this._valueMin(),
+                       valueMouse = percentMouse * valueTotal,
+                       valueMouseModStep = valueMouse % this.options.step,
+                       normValue = this._valueMin() + valueMouse - valueMouseModStep;
+
+               if (valueMouseModStep > (this.options.step / 2))
+                       normValue += this.options.step;
+
+               // Since JavaScript has problems with large floats, round
+               // the final value to 5 digits after the decimal point (see #4124)
+               return parseFloat(normValue.toFixed(5));
+
+       },
+
+       _start: function(event, index) {
+               var uiHash = {
+                       handle: this.handles[index],
+                       value: this.value()
+               };
+               if (this.options.values && this.options.values.length) {
+                       uiHash.value = this.values(index);
+                       uiHash.values = this.values();
+               }
+               this._trigger("start", event, uiHash);
+       },
+
+       _slide: function(event, index, newVal) {
+
+               var handle = this.handles[index];
+
+               if (this.options.values && this.options.values.length) {
+
+                       var otherVal = this.values(index ? 0 : 1);
+
+                       if ((this.options.values.length == 2 && this.options.range === true) && 
+                               ((index == 0 && newVal > otherVal) || (index == 1 && newVal < otherVal))){
+                               newVal = otherVal;
+                       }
+
+                       if (newVal != this.values(index)) {
+                               var newValues = this.values();
+                               newValues[index] = newVal;
+                               // A slide can be canceled by returning false from the slide callback
+                               var allowed = this._trigger("slide", event, {
+                                       handle: this.handles[index],
+                                       value: newVal,
+                                       values: newValues
+                               });
+                               var otherVal = this.values(index ? 0 : 1);
+                               if (allowed !== false) {
+                                       this.values(index, newVal, true);
+                               }
+                       }
+
+               } else {
+
+                       if (newVal != this.value()) {
+                               // A slide can be canceled by returning false from the slide callback
+                               var allowed = this._trigger("slide", event, {
+                                       handle: this.handles[index],
+                                       value: newVal
+                               });
+                               if (allowed !== false) {
+                                       this.value(newVal);
+                               }
+                                       
+                       }
+
+               }
+
+       },
+
+       _stop: function(event, index) {
+               var uiHash = {
+                       handle: this.handles[index],
+                       value: this.value()
+               };
+               if (this.options.values && this.options.values.length) {
+                       uiHash.value = this.values(index);
+                       uiHash.values = this.values();
+               }
+               this._trigger("stop", event, uiHash);
+       },
+
+       _change: function(event, index) {
+               if (!this._keySliding && !this._mouseSliding) {
+                       var uiHash = {
+                               handle: this.handles[index],
+                               value: this.value()
+                       };
+                       if (this.options.values && this.options.values.length) {
+                               uiHash.value = this.values(index);
+                               uiHash.values = this.values();
+                       }
+                       this._trigger("change", event, uiHash);
+               }
+       },
+
+       value: function(newValue) {
+
+               if (arguments.length) {
+                       this.options.value = this._trimValue(newValue);
+                       this._refreshValue();
+                       this._change(null, 0);
+               }
+
+               return this._value();
+
+       },
+
+       values: function(index, newValue) {
+
+               if (arguments.length > 1) {
+                       this.options.values[index] = this._trimValue(newValue);
+                       this._refreshValue();
+                       this._change(null, index);
+               }
+
+               if (arguments.length) {
+                       if ($.isArray(arguments[0])) {
+                               var vals = this.options.values, newValues = arguments[0];
+                               for (var i = 0, l = vals.length; i < l; i++) {
+                                       vals[i] = this._trimValue(newValues[i]);
+                                       this._change(null, i);
+                               }
+                               this._refreshValue();
+                       } else {
+                               if (this.options.values && this.options.values.length) {
+                                       return this._values(index);
+                               } else {
+                                       return this.value();
+                               }
+                       }
+               } else {
+                       return this._values();
+               }
+
+       },
+
+       _setOption: function(key, value) {
+               
+               var i,
+                       valsLength = 0;
+               if ( jQuery.isArray(this.options.values) ) {
+                       valsLength = this.options.values.length;
+               };
+
+               $.Widget.prototype._setOption.apply(this, arguments);
+
+               switch (key) {
+                       case 'disabled':
+                               if (value) {
+                                       this.handles.filter(".ui-state-focus").blur();
+                                       this.handles.removeClass("ui-state-hover");
+                                       this.handles.attr("disabled", "disabled");
+                                       this.element.addClass("ui-disabled");
+                               } else {
+                                       this.handles.removeAttr("disabled");
+                                       this.element.removeClass("ui-disabled");
+                               }
+                       case 'orientation':
+
+                               this._detectOrientation();
+                               
+                               this.element
+                                       .removeClass("ui-slider-horizontal ui-slider-vertical")
+                                       .addClass("ui-slider-" + this.orientation);
+                               this._refreshValue();
+                               break;
+                       case 'value':
+                               this._animateOff = true;
+                               this._refreshValue();
+                               this._change(null, 0);
+                               this._animateOff = false;
+                               break;
+                       case 'values':
+                               this._animateOff = true;
+                               this._refreshValue();
+                               for (i = 0; i < valsLength; i++) {
+                                       this._change(null, i);
+                               }
+                               this._animateOff = false;
+                               break;
+               }
+
+       },
+
+       _step: function() {
+               var step = this.options.step;
+               return step;
+       },
+
+       _value: function() {
+               //internal value getter
+               // _value() returns value trimmed by min and max
+               var val = this.options.value;
+               val = this._trimValue(val);
+
+               return val;
+       },
+
+       _values: function(index) {
+               //internal values getter
+               // _values() returns array of values trimmed by min and max
+               // _values(index) returns single value trimmed by min and max
+
+               if (arguments.length) {
+                       var val = this.options.values[index];
+                       val = this._trimValue(val);
+
+                       return val;
+               } else {
+                       // .slice() creates a copy of the array
+                       // this copy gets trimmed by min and max and then returned
+                       var vals = this.options.values.slice();
+                       for (var i = 0, l = vals.length; i < l; i++) {
+                               vals[i] = this._trimValue(vals[i]);
+                       }
+
+                       return vals;
+               }
+
+       },
+       
+       _trimValue: function(val) {
+               if (val < this._valueMin()) val = this._valueMin();
+               if (val > this._valueMax()) val = this._valueMax();
+
+               return val;
+       },
+
+       _valueMin: function() {
+               var valueMin = this.options.min;
+               return valueMin;
+       },
+
+       _valueMax: function() {
+               var valueMax = this.options.max;
+               return valueMax;
+       },
+       
+       _refreshValue: function() {
+
+               var oRange = this.options.range, o = this.options, self = this;
+               var animate = (!this._animateOff) ? o.animate : false;
+
+               if (this.options.values && this.options.values.length) {
+                       var vp0, vp1;
+                       this.handles.each(function(i, j) {
+                               var valPercent = (self.values(i) - self._valueMin()) / (self._valueMax() - self._valueMin()) * 100;
+                               var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
+                               $(this).stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
+                               if (self.options.range === true) {
+                                       if (self.orientation == 'horizontal') {
+                                               (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ left: valPercent + '%' }, o.animate);
+                                               (i == 1) && self.range[animate ? 'animate' : 'css']({ width: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
+                                       } else {
+                                               (i == 0) && self.range.stop(1,1)[animate ? 'animate' : 'css']({ bottom: (valPercent) + '%' }, o.animate);
+                                               (i == 1) && self.range[animate ? 'animate' : 'css']({ height: (valPercent - lastValPercent) + '%' }, { queue: false, duration: o.animate });
+                                       }
+                               }
+                               lastValPercent = valPercent;
+                       });
+               } else {
+                       var value = this.value(),
+                               valueMin = this._valueMin(),
+                               valueMax = this._valueMax(),
+                               valPercent = valueMax != valueMin
+                                       ? (value - valueMin) / (valueMax - valueMin) * 100
+                                       : 0;
+                       var _set = {}; _set[self.orientation == 'horizontal' ? 'left' : 'bottom'] = valPercent + '%';
+                       this.handle.stop(1,1)[animate ? 'animate' : 'css'](_set, o.animate);
+
+                       (oRange == "min") && (this.orientation == "horizontal") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ width: valPercent + '%' }, o.animate);
+                       (oRange == "max") && (this.orientation == "horizontal") && this.range[animate ? 'animate' : 'css']({ width: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
+                       (oRange == "min") && (this.orientation == "vertical") && this.range.stop(1,1)[animate ? 'animate' : 'css']({ height: valPercent + '%' }, o.animate);
+                       (oRange == "max") && (this.orientation == "vertical") && this.range[animate ? 'animate' : 'css']({ height: (100 - valPercent) + '%' }, { queue: false, duration: o.animate });
+               }
+
+       }
+       
+});
+
+$.extend($.ui.slider, {
+       version: "1.8"
+});
+
+})(jQuery);
diff --git a/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.widget.js b/docs/jscripts/infusion/lib/jquery/ui/js/jquery.ui.widget.js
new file mode 100644 (file)
index 0000000..682999d
--- /dev/null
@@ -0,0 +1,236 @@
+/*!
+ * jQuery UI Widget 1.8
+ *
+ * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function( $ ) {
+
+var _remove = $.fn.remove;
+
+$.fn.remove = function( selector, keepData ) {
+       return this.each(function() {
+               if ( !keepData ) {
+                       if ( !selector || $.filter( selector, [ this ] ).length ) {
+                               $( "*", this ).add( this ).each(function() {
+                                       $( this ).triggerHandler( "remove" );
+                               });
+                       }
+               }
+               return _remove.call( $(this), selector, keepData );
+       });
+};
+
+$.widget = function( name, base, prototype ) {
+       var namespace = name.split( "." )[ 0 ],
+               fullName;
+       name = name.split( "." )[ 1 ];
+       fullName = namespace + "-" + name;
+
+       if ( !prototype ) {
+               prototype = base;
+               base = $.Widget;
+       }
+
+       // create selector for plugin
+       $.expr[ ":" ][ fullName ] = function( elem ) {
+               return !!$.data( elem, name );
+       };
+
+       $[ namespace ] = $[ namespace ] || {};
+       $[ namespace ][ name ] = function( options, element ) {
+               // allow instantiation without initializing for simple inheritance
+               if ( arguments.length ) {
+                       this._createWidget( options, element );
+               }
+       };
+
+       var basePrototype = new base();
+       // we need to make the options hash a property directly on the new instance
+       // otherwise we'll modify the options hash on the prototype that we're
+       // inheriting from
+//     $.each( basePrototype, function( key, val ) {
+//             if ( $.isPlainObject(val) ) {
+//                     basePrototype[ key ] = $.extend( {}, val );
+//             }
+//     });
+       basePrototype.options = $.extend( {}, basePrototype.options );
+       $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+               namespace: namespace,
+               widgetName: name,
+               widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+               widgetBaseClass: fullName
+       }, prototype );
+
+       $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+       $.fn[ name ] = function( options ) {
+               var isMethodCall = typeof options === "string",
+                       args = Array.prototype.slice.call( arguments, 1 ),
+                       returnValue = this;
+
+               // allow multiple hashes to be passed on init
+               options = !isMethodCall && args.length ?
+                       $.extend.apply( null, [ true, options ].concat(args) ) :
+                       options;
+
+               // prevent calls to internal methods
+               if ( isMethodCall && options.substring( 0, 1 ) === "_" ) {
+                       return returnValue;
+               }
+
+               if ( isMethodCall ) {
+                       this.each(function() {
+                               var instance = $.data( this, name ),
+                                       methodValue = instance && $.isFunction( instance[options] ) ?
+                                               instance[ options ].apply( instance, args ) :
+                                               instance;
+                               if ( methodValue !== instance && methodValue !== undefined ) {
+                                       returnValue = methodValue;
+                                       return false;
+                               }
+                       });
+               } else {
+                       this.each(function() {
+                               var instance = $.data( this, name );
+                               if ( instance ) {
+                                       if ( options ) {
+                                               instance.option( options );
+                                       }
+                                       instance._init();
+                               } else {
+                                       $.data( this, name, new object( options, this ) );
+                               }
+                       });
+               }
+
+               return returnValue;
+       };
+};
+
+$.Widget = function( options, element ) {
+       // allow instantiation without initializing for simple inheritance
+       if ( arguments.length ) {
+               this._createWidget( options, element );
+       }
+};
+
+$.Widget.prototype = {
+       widgetName: "widget",
+       widgetEventPrefix: "",
+       options: {
+               disabled: false
+       },
+       _createWidget: function( options, element ) {
+               // $.widget.bridge stores the plugin instance, but we do it anyway
+               // so that it's stored even before the _create function runs
+               this.element = $( element ).data( this.widgetName, this );
+               this.options = $.extend( true, {},
+                       this.options,
+                       $.metadata && $.metadata.get( element )[ this.widgetName ],
+                       options );
+
+               var self = this;
+               this.element.bind( "remove." + this.widgetName, function() {
+                       self.destroy();
+               });
+
+               this._create();
+               this._init();
+       },
+       _create: function() {},
+       _init: function() {},
+
+       destroy: function() {
+               this.element
+                       .unbind( "." + this.widgetName )
+                       .removeData( this.widgetName );
+               this.widget()
+                       .unbind( "." + this.widgetName )
+                       .removeAttr( "aria-disabled" )
+                       .removeClass(
+                               this.widgetBaseClass + "-disabled " +
+                               this.namespace + "-state-disabled" );
+       },
+
+       widget: function() {
+               return this.element;
+       },
+
+       option: function( key, value ) {
+               var options = key,
+                       self = this;
+
+               if ( arguments.length === 0 ) {
+                       // don't return a reference to the internal hash
+                       return $.extend( {}, self.options );
+               }
+
+               if  (typeof key === "string" ) {
+                       if ( value === undefined ) {
+                               return this.options[ key ];
+                       }
+                       options = {};
+                       options[ key ] = value;
+               }
+
+               $.each( options, function( key, value ) {
+                       self._setOption( key, value );
+               });
+
+               return self;
+       },
+       _setOption: function( key, value ) {
+               this.options[ key ] = value;
+
+               if ( key === "disabled" ) {
+                       this.widget()
+                               [ value ? "addClass" : "removeClass"](
+                                       this.widgetBaseClass + "-disabled" + " " +
+                                       this.namespace + "-state-disabled" )
+                               .attr( "aria-disabled", value );
+               }
+
+               return this;
+       },
+
+       enable: function() {
+               return this._setOption( "disabled", false );
+       },
+       disable: function() {
+               return this._setOption( "disabled", true );
+       },
+
+       _trigger: function( type, event, data ) {
+               var callback = this.options[ type ];
+
+               event = $.Event( event );
+               event.type = ( type === this.widgetEventPrefix ?
+                       type :
+                       this.widgetEventPrefix + type ).toLowerCase();
+               data = data || {};
+
+               // copy original event properties over to the new event
+               // this would happen if we could call $.event.fix instead of $.Event
+               // but we don't have a way to force an event to be fixed multiple times
+               if ( event.originalEvent ) {
+                       for ( var i = $.event.props.length, prop; i; ) {
+                               prop = $.event.props[ --i ];
+                               event[ prop ] = event.originalEvent[ prop ];
+                       }
+               }
+
+               this.element.trigger( event, data );
+
+               return !( $.isFunction(callback) &&
+                       callback.call( this.element[0], event, data ) === false ||
+                       event.isDefaultPrevented() );
+       }
+};
+
+})( jQuery );
diff --git a/docs/jscripts/infusion/lib/jquery/ui/js/ui-FLUID-readme.txt b/docs/jscripts/infusion/lib/jquery/ui/js/ui-FLUID-readme.txt
new file mode 100644 (file)
index 0000000..c2c8bc1
--- /dev/null
@@ -0,0 +1 @@
+The jquery.ui.*.js files in this folder were taken from the 1.8 bundle, downloaded Mar. 29, 2010
diff --git a/docs/jscripts/infusion/lib/json/js/json2.js b/docs/jscripts/infusion/lib/json/js/json2.js
new file mode 100644 (file)
index 0000000..12669c7
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+    json2.js
+    2007-11-06
+
+    Public Domain
+
+    No warranty expressed or implied. Use at your own risk.
+
+    See http://www.JSON.org/js.html
+
+    This file creates a global JSON object containing two methods:
+
+        JSON.stringify(value, whitelist)
+            value       any JavaScript value, usually an object or array.
+
+            whitelist   an optional that determines how object values are
+                        stringified.
+
+            This method produces a JSON text from a JavaScript value.
+            There are three possible ways to stringify an object, depending
+            on the optional whitelist parameter.
+
+            If an object has a toJSON method, then the toJSON() method will be
+            called. The value returned from the toJSON method will be
+            stringified.
+
+            Otherwise, if the optional whitelist parameter is an array, then
+            the elements of the array will be used to select members of the
+            object for stringification.
+
+            Otherwise, if there is no whitelist parameter, then all of the
+            members of the object will be stringified.
+
+            Values that do not have JSON representaions, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped, in arrays will be replaced with null. JSON.stringify()
+            returns undefined. Dates will be stringified as quoted ISO dates.
+
+            Example:
+
+            var text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+        JSON.parse(text, filter)
+            This method parses a JSON text to produce an object or
+            array. It can throw a SyntaxError exception.
+
+            The optional filter parameter is a function that can filter and
+            transform the results. It receives each of the keys and values, and
+            its return value is used instead of the original value. If it
+            returns what it received, then structure is not modified. If it
+            returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. If a key contains the string 'date' then
+            // convert the value to a date.
+
+            myData = JSON.parse(text, function (key, value) {
+                return key.indexOf('date') >= 0 ? new Date(value) : value;
+            });
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+
+    Use your own copy. It is extremely unwise to load third party
+    code into your pages.
+*/
+
+/*jslint evil: true */
+/*extern JSON */
+
+if (!this.JSON) {
+
+    JSON = function () {
+
+        function f(n) {    // Format integers to have at least two digits.
+            return n < 10 ? '0' + n : n;
+        }
+
+        Date.prototype.toJSON = function () {
+
+// Eventually, this method will be based on the date.toISOString method.
+
+            return this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z';
+        };
+
+
+        var m = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        };
+
+        function stringify(value, whitelist) {
+            var a,          // The array holding the partial texts.
+                i,          // The loop counter.
+                k,          // The member key.
+                l,          // Length.
+                r = /["\\\x00-\x1f\x7f-\x9f]/g,
+                v;          // The member value.
+
+            switch (typeof value) {
+            case 'string':
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe sequences.
+
+                return r.test(value) ?
+                    '"' + value.replace(r, function (a) {
+                        var c = m[a];
+                        if (c) {
+                            return c;
+                        }
+                        c = a.charCodeAt();
+                        return '\\u00' + Math.floor(c / 16).toString(16) +
+                                                   (c % 16).toString(16);
+                    }) + '"' :
+                    '"' + value + '"';
+
+            case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+                return isFinite(value) ? String(value) : 'null';
+
+            case 'boolean':
+            case 'null':
+                return String(value);
+
+            case 'object':
+
+// Due to a specification blunder in ECMAScript,
+// typeof null is 'object', so watch out for that case.
+
+                if (!value) {
+                    return 'null';
+                }
+
+// If the object has a toJSON method, call it, and stringify the result.
+
+                if (typeof value.toJSON === 'function') {
+                    return stringify(value.toJSON());
+                }
+                a = [];
+                if (typeof value.length === 'number' &&
+                        !(value.propertyIsEnumerable('length'))) {
+
+// The object is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                    l = value.length;
+                    for (i = 0; i < l; i += 1) {
+                        a.push(stringify(value[i], whitelist) || 'null');
+                    }
+
+// Join all of the elements together and wrap them in brackets.
+
+                    return '[' + a.join(',') + ']';
+                }
+                if (whitelist) {
+
+// If a whitelist (array of keys) is provided, use it to select the components
+// of the object.
+
+                    l = whitelist.length;
+                    for (i = 0; i < l; i += 1) {
+                        k = whitelist[i];
+                        if (typeof k === 'string') {
+                            v = stringify(value[k], whitelist);
+                            if (v) {
+                                a.push(stringify(k) + ':' + v);
+                            }
+                        }
+                    }
+                } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                    for (k in value) {
+                        if (typeof k === 'string') {
+                            v = stringify(value[k], whitelist);
+                            if (v) {
+                                a.push(stringify(k) + ':' + v);
+                            }
+                        }
+                    }
+                }
+
+// Join all of the member texts together and wrap them in braces.
+
+                return '{' + a.join(',') + '}';
+            }
+        }
+
+        return {
+            stringify: stringify,
+            parse: function (text, filter) {
+                var j;
+
+                function walk(k, v) {
+                    var i, n;
+                    if (v && typeof v === 'object') {
+                        for (i in v) {
+                            if (Object.prototype.hasOwnProperty.apply(v, [i])) {
+                                n = walk(i, v[i]);
+                                if (n !== undefined) {
+                                    v[i] = n;
+                                }
+                            }
+                        }
+                    }
+                    return filter(k, v);
+                }
+
+
+// Parsing happens in three stages. In the first stage, we run the text against
+// regular expressions that look for non-JSON patterns. We are especially
+// concerned with '()' and 'new' because they can cause invocation, and '='
+// because it can cause mutation. But just to be safe, we want to reject all
+// unexpected forms.
+
+// We split the first stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace all backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+                if (/^[\],:{}\s]*$/.test(text.replace(/\\./g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the second stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                    j = eval('(' + text + ')');
+
+// In the optional third stage, we recursively walk the new structure, passing
+// each name/value pair to a filter function for possible transformation.
+
+                    return typeof filter === 'function' ? walk('', j) : j;
+                }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+                throw new SyntaxError('parseJSON');
+            }
+        };
+    }();
+}
diff --git a/docs/jscripts/infusion/lib/swfobject/js/swfobject.js b/docs/jscripts/infusion/lib/swfobject/js/swfobject.js
new file mode 100644 (file)
index 0000000..9378c8f
--- /dev/null
@@ -0,0 +1,777 @@
+/*!    SWFObject v2.2 <http://code.google.com/p/swfobject/> 
+       is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
+*/
+
+var swfobject = function() {
+       
+       var UNDEF = "undefined",
+               OBJECT = "object",
+               SHOCKWAVE_FLASH = "Shockwave Flash",
+               SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
+               FLASH_MIME_TYPE = "application/x-shockwave-flash",
+               EXPRESS_INSTALL_ID = "SWFObjectExprInst",
+               ON_READY_STATE_CHANGE = "onreadystatechange",
+               
+               win = window,
+               doc = document,
+               nav = navigator,
+               
+               plugin = false,
+               domLoadFnArr = [main],
+               regObjArr = [],
+               objIdArr = [],
+               listenersArr = [],
+               storedAltContent,
+               storedAltContentId,
+               storedCallbackFn,
+               storedCallbackObj,
+               isDomLoaded = false,
+               isExpressInstallActive = false,
+               dynamicStylesheet,
+               dynamicStylesheetMedia,
+               autoHideShow = true,
+       
+       /* Centralized function for browser feature detection
+               - User agent string detection is only used when no good alternative is possible
+               - Is executed directly for optimal performance
+       */      
+       ua = function() {
+               var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
+                       u = nav.userAgent.toLowerCase(),
+                       p = nav.platform.toLowerCase(),
+                       windows = p ? /win/.test(p) : /win/.test(u),
+                       mac = p ? /mac/.test(p) : /mac/.test(u),
+                       webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
+                       ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
+                       playerVersion = [0,0,0],
+                       d = null;
+               if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
+                       d = nav.plugins[SHOCKWAVE_FLASH].description;
+                       if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
+                               plugin = true;
+                               ie = false; // cascaded feature detection for Internet Explorer
+                               d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
+                               playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
+                               playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
+                               playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
+                       }
+               }
+               else if (typeof win.ActiveXObject != UNDEF) {
+                       try {
+                               var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
+                               if (a) { // a will return null when ActiveX is disabled
+                                       d = a.GetVariable("$version");
+                                       if (d) {
+                                               ie = true; // cascaded feature detection for Internet Explorer
+                                               d = d.split(" ")[1].split(",");
+                                               playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
+                                       }
+                               }
+                       }
+                       catch(e) {}
+               }
+               return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
+       }(),
+       
+       /* Cross-browser onDomLoad
+               - Will fire an event as soon as the DOM of a web page is loaded
+               - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
+               - Regular onload serves as fallback
+       */ 
+       onDomLoad = function() {
+               if (!ua.w3) { return; }
+               if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically 
+                       callDomLoadFunctions();
+               }
+               if (!isDomLoaded) {
+                       if (typeof doc.addEventListener != UNDEF) {
+                               doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
+                       }               
+                       if (ua.ie && ua.win) {
+                               doc.attachEvent(ON_READY_STATE_CHANGE, function() {
+                                       if (doc.readyState == "complete") {
+                                               doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
+                                               callDomLoadFunctions();
+                                       }
+                               });
+                               if (win == top) { // if not inside an iframe
+                                       (function(){
+                                               if (isDomLoaded) { return; }
+                                               try {
+                                                       doc.documentElement.doScroll("left");
+                                               }
+                                               catch(e) {
+                                                       setTimeout(arguments.callee, 0);
+                                                       return;
+                                               }
+                                               callDomLoadFunctions();
+                                       })();
+                               }
+                       }
+                       if (ua.wk) {
+                               (function(){
+                                       if (isDomLoaded) { return; }
+                                       if (!/loaded|complete/.test(doc.readyState)) {
+                                               setTimeout(arguments.callee, 0);
+                                               return;
+                                       }
+                                       callDomLoadFunctions();
+                               })();
+                       }
+                       addLoadEvent(callDomLoadFunctions);
+               }
+       }();
+       
+       function callDomLoadFunctions() {
+               if (isDomLoaded) { return; }
+               try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
+                       var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
+                       t.parentNode.removeChild(t);
+               }
+               catch (e) { return; }
+               isDomLoaded = true;
+               var dl = domLoadFnArr.length;
+               for (var i = 0; i < dl; i++) {
+                       domLoadFnArr[i]();
+               }
+       }
+       
+       function addDomLoadEvent(fn) {
+               if (isDomLoaded) {
+                       fn();
+               }
+               else { 
+                       domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
+               }
+       }
+       
+       /* Cross-browser onload
+               - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
+               - Will fire an event as soon as a web page including all of its assets are loaded 
+        */
+       function addLoadEvent(fn) {
+               if (typeof win.addEventListener != UNDEF) {
+                       win.addEventListener("load", fn, false);
+               }
+               else if (typeof doc.addEventListener != UNDEF) {
+                       doc.addEventListener("load", fn, false);
+               }
+               else if (typeof win.attachEvent != UNDEF) {
+                       addListener(win, "onload", fn);
+               }
+               else if (typeof win.onload == "function") {
+                       var fnOld = win.onload;
+                       win.onload = function() {
+                               fnOld();
+                               fn();
+                       };
+               }
+               else {
+                       win.onload = fn;
+               }
+       }
+       
+       /* Main function
+               - Will preferably execute onDomLoad, otherwise onload (as a fallback)
+       */
+       function main() { 
+               if (plugin) {
+                       testPlayerVersion();
+               }
+               else {
+                       matchVersions();
+               }
+       }
+       
+       /* Detect the Flash Player version for non-Internet Explorer browsers
+               - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
+                 a. Both release and build numbers can be detected
+                 b. Avoid wrong descriptions by corrupt installers provided by Adobe
+                 c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
+               - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
+       */
+       function testPlayerVersion() {
+               var b = doc.getElementsByTagName("body")[0];
+               var o = createElement(OBJECT);
+               o.setAttribute("type", FLASH_MIME_TYPE);
+               var t = b.appendChild(o);
+               if (t) {
+                       var counter = 0;
+                       (function(){
+                               if (typeof t.GetVariable != UNDEF) {
+                                       var d = t.GetVariable("$version");
+                                       if (d) {
+                                               d = d.split(" ")[1].split(",");
+                                               ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
+                                       }
+                               }
+                               else if (counter < 10) {
+                                       counter++;
+                                       setTimeout(arguments.callee, 10);
+                                       return;
+                               }
+                               b.removeChild(o);
+                               t = null;
+                               matchVersions();
+                       })();
+               }
+               else {
+                       matchVersions();
+               }
+       }
+       
+       /* Perform Flash Player and SWF version matching; static publishing only
+       */
+       function matchVersions() {
+               var rl = regObjArr.length;
+               if (rl > 0) {
+                       for (var i = 0; i < rl; i++) { // for each registered object element
+                               var id = regObjArr[i].id;
+                               var cb = regObjArr[i].callbackFn;
+                               var cbObj = {success:false, id:id};
+                               if (ua.pv[0] > 0) {
+                                       var obj = getElementById(id);
+                                       if (obj) {
+                                               if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
+                                                       setVisibility(id, true);
+                                                       if (cb) {
+                                                               cbObj.success = true;
+                                                               cbObj.ref = getObjectById(id);
+                                                               cb(cbObj);
+                                                       }
+                                               }
+                                               else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
+                                                       var att = {};
+                                                       att.data = regObjArr[i].expressInstall;
+                                                       att.width = obj.getAttribute("width") || "0";
+                                                       att.height = obj.getAttribute("height") || "0";
+                                                       if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
+                                                       if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
+                                                       // parse HTML object param element's name-value pairs
+                                                       var par = {};
+                                                       var p = obj.getElementsByTagName("param");
+                                                       var pl = p.length;
+                                                       for (var j = 0; j < pl; j++) {
+                                                               if (p[j].getAttribute("name").toLowerCase() != "movie") {
+                                                                       par[p[j].getAttribute("name")] = p[j].getAttribute("value");
+                                                               }
+                                                       }
+                                                       showExpressInstall(att, par, id, cb);
+                                               }
+                                               else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
+                                                       displayAltContent(obj);
+                                                       if (cb) { cb(cbObj); }
+                                               }
+                                       }
+                               }
+                               else {  // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
+                                       setVisibility(id, true);
+                                       if (cb) {
+                                               var o = getObjectById(id); // test whether there is an HTML object element or not
+                                               if (o && typeof o.SetVariable != UNDEF) { 
+                                                       cbObj.success = true;
+                                                       cbObj.ref = o;
+                                               }
+                                               cb(cbObj);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       function getObjectById(objectIdStr) {
+               var r = null;
+               var o = getElementById(objectIdStr);
+               if (o && o.nodeName == "OBJECT") {
+                       if (typeof o.SetVariable != UNDEF) {
+                               r = o;
+                       }
+                       else {
+                               var n = o.getElementsByTagName(OBJECT)[0];
+                               if (n) {
+                                       r = n;
+                               }
+                       }
+               }
+               return r;
+       }
+       
+       /* Requirements for Adobe Express Install
+               - only one instance can be active at a time
+               - fp 6.0.65 or higher
+               - Win/Mac OS only
+               - no Webkit engines older than version 312
+       */
+       function canExpressInstall() {
+               return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
+       }
+       
+       /* Show the Adobe Express Install dialog
+               - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
+       */
+       function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
+               isExpressInstallActive = true;
+               storedCallbackFn = callbackFn || null;
+               storedCallbackObj = {success:false, id:replaceElemIdStr};
+               var obj = getElementById(replaceElemIdStr);
+               if (obj) {
+                       if (obj.nodeName == "OBJECT") { // static publishing
+                               storedAltContent = abstractAltContent(obj);
+                               storedAltContentId = null;
+                       }
+                       else { // dynamic publishing
+                               storedAltContent = obj;
+                               storedAltContentId = replaceElemIdStr;
+                       }
+                       att.id = EXPRESS_INSTALL_ID;
+                       if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
+                       if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
+                       doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
+                       var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
+                               fv = "MMredirectURL=" + win.location.toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
+                       if (typeof par.flashvars != UNDEF) {
+                               par.flashvars += "&" + fv;
+                       }
+                       else {
+                               par.flashvars = fv;
+                       }
+                       // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
+                       // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
+                       if (ua.ie && ua.win && obj.readyState != 4) {
+                               var newObj = createElement("div");
+                               replaceElemIdStr += "SWFObjectNew";
+                               newObj.setAttribute("id", replaceElemIdStr);
+                               obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
+                               obj.style.display = "none";
+                               (function(){
+                                       if (obj.readyState == 4) {
+                                               obj.parentNode.removeChild(obj);
+                                       }
+                                       else {
+                                               setTimeout(arguments.callee, 10);
+                                       }
+                               })();
+                       }
+                       createSWF(att, par, replaceElemIdStr);
+               }
+       }
+       
+       /* Functions to abstract and display alternative content
+       */
+       function displayAltContent(obj) {
+               if (ua.ie && ua.win && obj.readyState != 4) {
+                       // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
+                       // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
+                       var el = createElement("div");
+                       obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
+                       el.parentNode.replaceChild(abstractAltContent(obj), el);
+                       obj.style.display = "none";
+                       (function(){
+                               if (obj.readyState == 4) {
+                                       obj.parentNode.removeChild(obj);
+                               }
+                               else {
+                                       setTimeout(arguments.callee, 10);
+                               }
+                       })();
+               }
+               else {
+                       obj.parentNode.replaceChild(abstractAltContent(obj), obj);
+               }
+       } 
+
+       function abstractAltContent(obj) {
+               var ac = createElement("div");
+               if (ua.win && ua.ie) {
+                       ac.innerHTML = obj.innerHTML;
+               }
+               else {
+                       var nestedObj = obj.getElementsByTagName(OBJECT)[0];
+                       if (nestedObj) {
+                               var c = nestedObj.childNodes;
+                               if (c) {
+                                       var cl = c.length;
+                                       for (var i = 0; i < cl; i++) {
+                                               if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
+                                                       ac.appendChild(c[i].cloneNode(true));
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return ac;
+       }
+       
+       /* Cross-browser dynamic SWF creation
+       */
+       function createSWF(attObj, parObj, id) {
+               var r, el = getElementById(id);
+               if (ua.wk && ua.wk < 312) { return r; }
+               if (el) {
+                       if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
+                               attObj.id = id;
+                       }
+                       if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
+                               var att = "";
+                               for (var i in attObj) {
+                                       if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
+                                               if (i.toLowerCase() == "data") {
+                                                       parObj.movie = attObj[i];
+                                               }
+                                               else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
+                                                       att += ' class="' + attObj[i] + '"';
+                                               }
+                                               else if (i.toLowerCase() != "classid") {
+                                                       att += ' ' + i + '="' + attObj[i] + '"';
+                                               }
+                                       }
+                               }
+                               var par = "";
+                               for (var j in parObj) {
+                                       if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
+                                               par += '<param name="' + j + '" value="' + parObj[j] + '" />';
+                                       }
+                               }
+                               el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
+                               objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
+                               r = getElementById(attObj.id);  
+                       }
+                       else { // well-behaving browsers
+                               var o = createElement(OBJECT);
+                               o.setAttribute("type", FLASH_MIME_TYPE);
+                               for (var m in attObj) {
+                                       if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
+                                               if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
+                                                       o.setAttribute("class", attObj[m]);
+                                               }
+                                               else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
+                                                       o.setAttribute(m, attObj[m]);
+                                               }
+                                       }
+                               }
+                               for (var n in parObj) {
+                                       if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
+                                               createObjParam(o, n, parObj[n]);
+                                       }
+                               }
+                               el.parentNode.replaceChild(o, el);
+                               r = o;
+                       }
+               }
+               return r;
+       }
+       
+       function createObjParam(el, pName, pValue) {
+               var p = createElement("param");
+               p.setAttribute("name", pName);  
+               p.setAttribute("value", pValue);
+               el.appendChild(p);
+       }
+       
+       /* Cross-browser SWF removal
+               - Especially needed to safely and completely remove a SWF in Internet Explorer
+       */
+       function removeSWF(id) {
+               var obj = getElementById(id);
+               if (obj && obj.nodeName == "OBJECT") {
+                       if (ua.ie && ua.win) {
+                               obj.style.display = "none";
+                               (function(){
+                                       if (obj.readyState == 4) {
+                                               removeObjectInIE(id);
+                                       }
+                                       else {
+                                               setTimeout(arguments.callee, 10);
+                                       }
+                               })();
+                       }
+                       else {
+                               obj.parentNode.removeChild(obj);
+                       }
+               }
+       }
+       
+       function removeObjectInIE(id) {
+               var obj = getElementById(id);
+               if (obj) {
+                       for (var i in obj) {
+                               if (typeof obj[i] == "function") {
+                                       obj[i] = null;
+                               }
+                       }
+                       obj.parentNode.removeChild(obj);
+               }
+       }
+       
+       /* Functions to optimize JavaScript compression
+       */
+       function getElementById(id) {
+               var el = null;
+               try {
+                       el = doc.getElementById(id);
+               }
+               catch (e) {}
+               return el;
+       }
+       
+       function createElement(el) {
+               return doc.createElement(el);
+       }
+       
+       /* Updated attachEvent function for Internet Explorer
+               - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
+       */      
+       function addListener(target, eventType, fn) {
+               target.attachEvent(eventType, fn);
+               listenersArr[listenersArr.length] = [target, eventType, fn];
+       }
+       
+       /* Flash Player and SWF content version matching
+       */
+       function hasPlayerVersion(rv) {
+               var pv = ua.pv, v = rv.split(".");
+               v[0] = parseInt(v[0], 10);
+               v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
+               v[2] = parseInt(v[2], 10) || 0;
+               return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
+       }
+       
+       /* Cross-browser dynamic CSS creation
+               - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
+       */      
+       function createCSS(sel, decl, media, newStyle) {
+               if (ua.ie && ua.mac) { return; }
+               var h = doc.getElementsByTagName("head")[0];
+               if (!h) { return; } // to also support badly authored HTML pages that lack a head element
+               var m = (media && typeof media == "string") ? media : "screen";
+               if (newStyle) {
+                       dynamicStylesheet = null;
+                       dynamicStylesheetMedia = null;
+               }
+               if (!dynamicStylesheet || dynamicStylesheetMedia != m) { 
+                       // create dynamic stylesheet + get a global reference to it
+                       var s = createElement("style");
+                       s.setAttribute("type", "text/css");
+                       s.setAttribute("media", m);
+                       dynamicStylesheet = h.appendChild(s);
+                       if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
+                               dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
+                       }
+                       dynamicStylesheetMedia = m;
+               }
+               // add style rule
+               if (ua.ie && ua.win) {
+                       if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
+                               dynamicStylesheet.addRule(sel, decl);
+                       }
+               }
+               else {
+                       if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
+                               dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
+                       }
+               }
+       }
+       
+       function setVisibility(id, isVisible) {
+               if (!autoHideShow) { return; }
+               var v = isVisible ? "visible" : "hidden";
+               if (isDomLoaded && getElementById(id)) {
+                       getElementById(id).style.visibility = v;
+               }
+               else {
+                       createCSS("#" + id, "visibility:" + v);
+               }
+       }
+
+       /* Filter to avoid XSS attacks
+       */
+       function urlEncodeIfNecessary(s) {
+               var regex = /[\\\"<>\.;]/;
+               var hasBadChars = regex.exec(s) != null;
+               return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
+       }
+       
+       /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
+       */
+       var cleanup = function() {
+               if (ua.ie && ua.win) {
+                       window.attachEvent("onunload", function() {
+                               // remove listeners to avoid memory leaks
+                               var ll = listenersArr.length;
+                               for (var i = 0; i < ll; i++) {
+                                       listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
+                               }
+                               // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
+                               var il = objIdArr.length;
+                               for (var j = 0; j < il; j++) {
+                                       removeSWF(objIdArr[j]);
+                               }
+                               // cleanup library's main closures to avoid memory leaks
+                               for (var k in ua) {
+                                       ua[k] = null;
+                               }
+                               ua = null;
+                               for (var l in swfobject) {
+                                       swfobject[l] = null;
+                               }
+                               swfobject = null;
+                       });
+               }
+       }();
+       
+       return {
+               /* Public API
+                       - Reference: http://code.google.com/p/swfobject/wiki/documentation
+               */ 
+               registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
+                       if (ua.w3 && objectIdStr && swfVersionStr) {
+                               var regObj = {};
+                               regObj.id = objectIdStr;
+                               regObj.swfVersion = swfVersionStr;
+                               regObj.expressInstall = xiSwfUrlStr;
+                               regObj.callbackFn = callbackFn;
+                               regObjArr[regObjArr.length] = regObj;
+                               setVisibility(objectIdStr, false);
+                       }
+                       else if (callbackFn) {
+                               callbackFn({success:false, id:objectIdStr});
+                       }
+               },
+               
+               getObjectById: function(objectIdStr) {
+                       if (ua.w3) {
+                               return getObjectById(objectIdStr);
+                       }
+               },
+               
+               embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
+                       var callbackObj = {success:false, id:replaceElemIdStr};
+                       if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
+                               setVisibility(replaceElemIdStr, false);
+                               addDomLoadEvent(function() {
+                                       widthStr += ""; // auto-convert to string
+                                       heightStr += "";
+                                       var att = {};
+                                       if (attObj && typeof attObj === OBJECT) {
+                                               for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
+                                                       att[i] = attObj[i];
+                                               }
+                                       }
+                                       att.data = swfUrlStr;
+                                       att.width = widthStr;
+                                       att.height = heightStr;
+                                       var par = {}; 
+                                       if (parObj && typeof parObj === OBJECT) {
+                                               for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
+                                                       par[j] = parObj[j];
+                                               }
+                                       }
+                                       if (flashvarsObj && typeof flashvarsObj === OBJECT) {
+                                               for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
+                                                       if (typeof par.flashvars != UNDEF) {
+                                                               par.flashvars += "&" + k + "=" + flashvarsObj[k];
+                                                       }
+                                                       else {
+                                                               par.flashvars = k + "=" + flashvarsObj[k];
+                                                       }
+                                               }
+                                       }
+                                       if (hasPlayerVersion(swfVersionStr)) { // create SWF
+                                               var obj = createSWF(att, par, replaceElemIdStr);
+                                               if (att.id == replaceElemIdStr) {
+                                                       setVisibility(replaceElemIdStr, true);
+                                               }
+                                               callbackObj.success = true;
+                                               callbackObj.ref = obj;
+                                       }
+                                       else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
+                                               att.data = xiSwfUrlStr;
+                                               showExpressInstall(att, par, replaceElemIdStr, callbackFn);
+                                               return;
+                                       }
+                                       else { // show alternative content
+                                               setVisibility(replaceElemIdStr, true);
+                                       }
+                                       if (callbackFn) { callbackFn(callbackObj); }
+                               });
+                       }
+                       else if (callbackFn) { callbackFn(callbackObj); }
+               },
+               
+               switchOffAutoHideShow: function() {
+                       autoHideShow = false;
+               },
+               
+               ua: ua,
+               
+               getFlashPlayerVersion: function() {
+                       return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
+               },
+               
+               hasFlashPlayerVersion: hasPlayerVersion,
+               
+               createSWF: function(attObj, parObj, replaceElemIdStr) {
+                       if (ua.w3) {
+                               return createSWF(attObj, parObj, replaceElemIdStr);
+                       }
+                       else {
+                               return undefined;
+                       }
+               },
+               
+               showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
+                       if (ua.w3 && canExpressInstall()) {
+                               showExpressInstall(att, par, replaceElemIdStr, callbackFn);
+                       }
+               },
+               
+               removeSWF: function(objElemIdStr) {
+                       if (ua.w3) {
+                               removeSWF(objElemIdStr);
+                       }
+               },
+               
+               createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
+                       if (ua.w3) {
+                               createCSS(selStr, declStr, mediaStr, newStyleBoolean);
+                       }
+               },
+               
+               addDomLoadEvent: addDomLoadEvent,
+               
+               addLoadEvent: addLoadEvent,
+               
+               getQueryParamValue: function(param) {
+                       var q = doc.location.search || doc.location.hash;
+                       if (q) {
+                               if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
+                               if (param == null) {
+                                       return urlEncodeIfNecessary(q);
+                               }
+                               var pairs = q.split("&");
+                               for (var i = 0; i < pairs.length; i++) {
+                                       if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
+                                               return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
+                                       }
+                               }
+                       }
+                       return "";
+               },
+               
+               // For internal usage only
+               expressInstallCallback: function() {
+                       if (isExpressInstallActive) {
+                               var obj = getElementById(EXPRESS_INSTALL_ID);
+                               if (obj && storedAltContent) {
+                                       obj.parentNode.replaceChild(storedAltContent, obj);
+                                       if (storedAltContentId) {
+                                               setVisibility(storedAltContentId, true);
+                                               if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
+                                       }
+                                       if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
+                               }
+                               isExpressInstallActive = false;
+                       } 
+               }
+       };
+}();
diff --git a/docs/jscripts/infusion/lib/swfupload/flash/swfupload.swf b/docs/jscripts/infusion/lib/swfupload/flash/swfupload.swf
new file mode 100644 (file)
index 0000000..e3f7670
Binary files /dev/null and b/docs/jscripts/infusion/lib/swfupload/flash/swfupload.swf differ
diff --git a/docs/jscripts/infusion/lib/swfupload/js/swfupload.js b/docs/jscripts/infusion/lib/swfupload/js/swfupload.js
new file mode 100644 (file)
index 0000000..e65b19c
--- /dev/null
@@ -0,0 +1,980 @@
+/**
+ * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com
+ *
+ * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/,  http://www.vinterwebb.se/
+ *
+ * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ */
+
+
+/* ******************* */
+/* Constructor & Init  */
+/* ******************* */
+var SWFUpload;
+
+if (SWFUpload == undefined) {
+       SWFUpload = function (settings) {
+               this.initSWFUpload(settings);
+       };
+}
+
+SWFUpload.prototype.initSWFUpload = function (settings) {
+       try {
+               this.customSettings = {};       // A container where developers can place their own settings associated with this instance.
+               this.settings = settings;
+               this.eventQueue = [];
+               this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
+               this.movieElement = null;
+
+
+               // Setup global control tracking
+               SWFUpload.instances[this.movieName] = this;
+
+               // Load the settings.  Load the Flash movie.
+               this.initSettings();
+               this.loadFlash();
+               this.displayDebugInfo();
+       } catch (ex) {
+               delete SWFUpload.instances[this.movieName];
+               throw ex;
+       }
+};
+
+/* *************** */
+/* Static Members  */
+/* *************** */
+SWFUpload.instances = {};
+SWFUpload.movieCount = 0;
+SWFUpload.version = "2.2.0 2009-03-25";
+SWFUpload.QUEUE_ERROR = {
+       QUEUE_LIMIT_EXCEEDED                    : -100,
+       FILE_EXCEEDS_SIZE_LIMIT                 : -110,
+       ZERO_BYTE_FILE                                  : -120,
+       INVALID_FILETYPE                                : -130
+};
+SWFUpload.UPLOAD_ERROR = {
+       HTTP_ERROR                                              : -200,
+       MISSING_UPLOAD_URL                      : -210,
+       IO_ERROR                                                : -220,
+       SECURITY_ERROR                                  : -230,
+       UPLOAD_LIMIT_EXCEEDED                   : -240,
+       UPLOAD_FAILED                                   : -250,
+       SPECIFIED_FILE_ID_NOT_FOUND             : -260,
+       FILE_VALIDATION_FAILED                  : -270,
+       FILE_CANCELLED                                  : -280,
+       UPLOAD_STOPPED                                  : -290
+};
+SWFUpload.FILE_STATUS = {
+       QUEUED           : -1,
+       IN_PROGRESS      : -2,
+       ERROR            : -3,
+       COMPLETE         : -4,
+       CANCELLED        : -5
+};
+SWFUpload.BUTTON_ACTION = {
+       SELECT_FILE  : -100,
+       SELECT_FILES : -110,
+       START_UPLOAD : -120
+};
+SWFUpload.CURSOR = {
+       ARROW : -1,
+       HAND : -2
+};
+SWFUpload.WINDOW_MODE = {
+       WINDOW : "window",
+       TRANSPARENT : "transparent",
+       OPAQUE : "opaque"
+};
+
+// Private: takes a URL, determines if it is relative and converts to an absolute URL
+// using the current site. Only processes the URL if it can, otherwise returns the URL untouched
+SWFUpload.completeURL = function(url) {
+       if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//)) {
+               return url;
+       }
+       
+       var currentURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "");
+       
+       var indexSlash = window.location.pathname.lastIndexOf("/");
+       if (indexSlash <= 0) {
+               path = "/";
+       } else {
+               path = window.location.pathname.substr(0, indexSlash) + "/";
+       }
+       
+       return /*currentURL +*/ path + url;
+       
+};
+
+
+/* ******************** */
+/* Instance Members  */
+/* ******************** */
+
+// Private: initSettings ensures that all the
+// settings are set, getting a default value if one was not assigned.
+SWFUpload.prototype.initSettings = function () {
+       this.ensureDefault = function (settingName, defaultValue) {
+               this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];
+       };
+       
+       // Upload backend settings
+       this.ensureDefault("upload_url", "");
+       this.ensureDefault("preserve_relative_urls", false);
+       this.ensureDefault("file_post_name", "Filedata");
+       this.ensureDefault("post_params", {});
+       this.ensureDefault("use_query_string", false);
+       this.ensureDefault("requeue_on_error", false);
+       this.ensureDefault("http_success", []);
+       this.ensureDefault("assume_success_timeout", 0);
+       
+       // File Settings
+       this.ensureDefault("file_types", "*.*");
+       this.ensureDefault("file_types_description", "All Files");
+       this.ensureDefault("file_size_limit", 0);       // Default zero means "unlimited"
+       this.ensureDefault("file_upload_limit", 0);
+       this.ensureDefault("file_queue_limit", 0);
+
+       // Flash Settings
+       this.ensureDefault("flash_url", "swfupload.swf");
+       this.ensureDefault("prevent_swf_caching", true);
+       
+       // Button Settings
+       this.ensureDefault("button_image_url", "");
+       this.ensureDefault("button_width", 1);
+       this.ensureDefault("button_height", 1);
+       this.ensureDefault("button_text", "");
+       this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;");
+       this.ensureDefault("button_text_top_padding", 0);
+       this.ensureDefault("button_text_left_padding", 0);
+       this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES);
+       this.ensureDefault("button_disabled", false);
+       this.ensureDefault("button_placeholder_id", "");
+       this.ensureDefault("button_placeholder", null);
+       this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW);
+       this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW);
+       
+       // Debug Settings
+       this.ensureDefault("debug", false);
+       this.settings.debug_enabled = this.settings.debug;      // Here to maintain v2 API
+       
+       // Event Handlers
+       this.settings.return_upload_start_handler = this.returnUploadStart;
+       this.ensureDefault("swfupload_loaded_handler", null);
+       this.ensureDefault("file_dialog_start_handler", null);
+       this.ensureDefault("file_queued_handler", null);
+       this.ensureDefault("file_queue_error_handler", null);
+       this.ensureDefault("file_dialog_complete_handler", null);
+       
+       this.ensureDefault("upload_start_handler", null);
+       this.ensureDefault("upload_progress_handler", null);
+       this.ensureDefault("upload_error_handler", null);
+       this.ensureDefault("upload_success_handler", null);
+       this.ensureDefault("upload_complete_handler", null);
+       
+       this.ensureDefault("debug_handler", this.debugMessage);
+
+       this.ensureDefault("custom_settings", {});
+
+       // Other settings
+       this.customSettings = this.settings.custom_settings;
+       
+       // Update the flash url if needed
+       if (!!this.settings.prevent_swf_caching) {
+               this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime();
+       }
+       
+       if (!this.settings.preserve_relative_urls) {
+               //this.settings.flash_url = SWFUpload.completeURL(this.settings.flash_url);     // Don't need to do this one since flash doesn't look at it
+               this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url);
+               this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url);
+       }
+       
+       delete this.ensureDefault;
+};
+
+// Private: loadFlash replaces the button_placeholder element with the flash movie.
+SWFUpload.prototype.loadFlash = function () {
+       var targetElement, tempParent;
+
+       // Make sure an element with the ID we are going to use doesn't already exist
+       if (document.getElementById(this.movieName) !== null) {
+               throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
+       }
+
+       // Get the element where we will be placing the flash movie
+       targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder;
+
+       if (targetElement == undefined) {
+               throw "Could not find the placeholder element: " + this.settings.button_placeholder_id;
+       }
+
+       // Append the container and load the flash
+       tempParent = document.createElement("div");
+       tempParent.innerHTML = this.getFlashHTML();     // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
+       targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement);
+
+       // Fix IE Flash/Form bug
+       if (window[this.movieName] == undefined) {
+               window[this.movieName] = this.getMovieElement();
+       }
+       
+};
+
+// Private: getFlashHTML generates the object tag needed to embed the flash in to the document
+SWFUpload.prototype.getFlashHTML = function () {
+       // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
+       return ['<object id="', this.movieName, '" type="application/x-shockwave-flash" data="', this.settings.flash_url, '" width="', this.settings.button_width, '" height="', this.settings.button_height, '" class="swfupload">',
+                               '<param name="wmode" value="', this.settings.button_window_mode, '" />',
+                               '<param name="movie" value="', this.settings.flash_url, '" />',
+                               '<param name="quality" value="high" />',
+                               '<param name="menu" value="false" />',
+                               '<param name="allowScriptAccess" value="always" />',
+                               '<param name="flashvars" value="' + this.getFlashVars() + '" />',
+                               '</object>'].join("");
+};
+
+// Private: getFlashVars builds the parameter string that will be passed
+// to flash in the flashvars param.
+SWFUpload.prototype.getFlashVars = function () {
+       // Build a string from the post param object
+       var paramString = this.buildParamString();
+       var httpSuccessString = this.settings.http_success.join(",");
+       
+       // Build the parameter string
+       return ["movieName=", encodeURIComponent(this.movieName),
+                       "&amp;uploadURL=", encodeURIComponent(this.settings.upload_url),
+                       "&amp;useQueryString=", encodeURIComponent(this.settings.use_query_string),
+                       "&amp;requeueOnError=", encodeURIComponent(this.settings.requeue_on_error),
+                       "&amp;httpSuccess=", encodeURIComponent(httpSuccessString),
+                       "&amp;assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout),
+                       "&amp;params=", encodeURIComponent(paramString),
+                       "&amp;filePostName=", encodeURIComponent(this.settings.file_post_name),
+                       "&amp;fileTypes=", encodeURIComponent(this.settings.file_types),
+                       "&amp;fileTypesDescription=", encodeURIComponent(this.settings.file_types_description),
+                       "&amp;fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit),
+                       "&amp;fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit),
+                       "&amp;fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit),
+                       "&amp;debugEnabled=", encodeURIComponent(this.settings.debug_enabled),
+                       "&amp;buttonImageURL=", encodeURIComponent(this.settings.button_image_url),
+                       "&amp;buttonWidth=", encodeURIComponent(this.settings.button_width),
+                       "&amp;buttonHeight=", encodeURIComponent(this.settings.button_height),
+                       "&amp;buttonText=", encodeURIComponent(this.settings.button_text),
+                       "&amp;buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding),
+                       "&amp;buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding),
+                       "&amp;buttonTextStyle=", encodeURIComponent(this.settings.button_text_style),
+                       "&amp;buttonAction=", encodeURIComponent(this.settings.button_action),
+                       "&amp;buttonDisabled=", encodeURIComponent(this.settings.button_disabled),
+                       "&amp;buttonCursor=", encodeURIComponent(this.settings.button_cursor)
+               ].join("");
+};
+
+// Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload
+// The element is cached after the first lookup
+SWFUpload.prototype.getMovieElement = function () {
+       if (this.movieElement == undefined) {
+               this.movieElement = document.getElementById(this.movieName);
+       }
+
+       if (this.movieElement === null) {
+               throw "Could not find Flash element";
+       }
+       
+       return this.movieElement;
+};
+
+// Private: buildParamString takes the name/value pairs in the post_params setting object
+// and joins them up in to a string formatted "name=value&amp;name=value"
+SWFUpload.prototype.buildParamString = function () {
+       var postParams = this.settings.post_params; 
+       var paramStringPairs = [];
+
+       if (typeof(postParams) === "object") {
+               for (var name in postParams) {
+                       if (postParams.hasOwnProperty(name)) {
+                               paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
+                       }
+               }
+       }
+
+       return paramStringPairs.join("&amp;");
+};
+
+// Public: Used to remove a SWFUpload instance from the page. This method strives to remove
+// all references to the SWF, and other objects so memory is properly freed.
+// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state.
+// Credits: Major improvements provided by steffen
+SWFUpload.prototype.destroy = function () {
+       try {
+               // Make sure Flash is done before we try to remove it
+               this.cancelUpload(null, false);
+               
+
+               // Remove the SWFUpload DOM nodes
+               var movieElement = null;
+               movieElement = this.getMovieElement();
+               
+               if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
+                       // Loop through all the movie's properties and remove all function references (DOM/JS IE 6/7 memory leak workaround)
+                       for (var i in movieElement) {
+                               try {
+                                       if (typeof(movieElement[i]) === "function") {
+                                               movieElement[i] = null;
+                                       }
+                               } catch (ex1) {}
+                       }
+
+                       // Remove the Movie Element from the page
+                       try {
+                               movieElement.parentNode.removeChild(movieElement);
+                       } catch (ex) {}
+               }
+               
+               // Remove IE form fix reference
+               window[this.movieName] = null;
+
+               // Destroy other references
+               SWFUpload.instances[this.movieName] = null;
+               delete SWFUpload.instances[this.movieName];
+
+               this.movieElement = null;
+               this.settings = null;
+               this.customSettings = null;
+               this.eventQueue = null;
+               this.movieName = null;
+               
+               
+               return true;
+       } catch (ex2) {
+               return false;
+       }
+};
+
+
+// Public: displayDebugInfo prints out settings and configuration
+// information about this SWFUpload instance.
+// This function (and any references to it) can be deleted when placing
+// SWFUpload in production.
+SWFUpload.prototype.displayDebugInfo = function () {
+       this.debug(
+               [
+                       "---SWFUpload Instance Info---\n",
+                       "Version: ", SWFUpload.version, "\n",
+                       "Movie Name: ", this.movieName, "\n",
+                       "Settings:\n",
+                       "\t", "upload_url:               ", this.settings.upload_url, "\n",
+                       "\t", "flash_url:                ", this.settings.flash_url, "\n",
+                       "\t", "use_query_string:         ", this.settings.use_query_string.toString(), "\n",
+                       "\t", "requeue_on_error:         ", this.settings.requeue_on_error.toString(), "\n",
+                       "\t", "http_success:             ", this.settings.http_success.join(", "), "\n",
+                       "\t", "assume_success_timeout:   ", this.settings.assume_success_timeout, "\n",
+                       "\t", "file_post_name:           ", this.settings.file_post_name, "\n",
+                       "\t", "post_params:              ", this.settings.post_params.toString(), "\n",
+                       "\t", "file_types:               ", this.settings.file_types, "\n",
+                       "\t", "file_types_description:   ", this.settings.file_types_description, "\n",
+                       "\t", "file_size_limit:          ", this.settings.file_size_limit, "\n",
+                       "\t", "file_upload_limit:        ", this.settings.file_upload_limit, "\n",
+                       "\t", "file_queue_limit:         ", this.settings.file_queue_limit, "\n",
+                       "\t", "debug:                    ", this.settings.debug.toString(), "\n",
+
+                       "\t", "prevent_swf_caching:      ", this.settings.prevent_swf_caching.toString(), "\n",
+
+                       "\t", "button_placeholder_id:    ", this.settings.button_placeholder_id.toString(), "\n",
+                       "\t", "button_placeholder:       ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n",
+                       "\t", "button_image_url:         ", this.settings.button_image_url.toString(), "\n",
+                       "\t", "button_width:             ", this.settings.button_width.toString(), "\n",
+                       "\t", "button_height:            ", this.settings.button_height.toString(), "\n",
+                       "\t", "button_text:              ", this.settings.button_text.toString(), "\n",
+                       "\t", "button_text_style:        ", this.settings.button_text_style.toString(), "\n",
+                       "\t", "button_text_top_padding:  ", this.settings.button_text_top_padding.toString(), "\n",
+                       "\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n",
+                       "\t", "button_action:            ", this.settings.button_action.toString(), "\n",
+                       "\t", "button_disabled:          ", this.settings.button_disabled.toString(), "\n",
+
+                       "\t", "custom_settings:          ", this.settings.custom_settings.toString(), "\n",
+                       "Event Handlers:\n",
+                       "\t", "swfupload_loaded_handler assigned:  ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n",
+                       "\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n",
+                       "\t", "file_queued_handler assigned:       ", (typeof this.settings.file_queued_handler === "function").toString(), "\n",
+                       "\t", "file_queue_error_handler assigned:  ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n",
+                       "\t", "upload_start_handler assigned:      ", (typeof this.settings.upload_start_handler === "function").toString(), "\n",
+                       "\t", "upload_progress_handler assigned:   ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n",
+                       "\t", "upload_error_handler assigned:      ", (typeof this.settings.upload_error_handler === "function").toString(), "\n",
+                       "\t", "upload_success_handler assigned:    ", (typeof this.settings.upload_success_handler === "function").toString(), "\n",
+                       "\t", "upload_complete_handler assigned:   ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n",
+                       "\t", "debug_handler assigned:             ", (typeof this.settings.debug_handler === "function").toString(), "\n"
+               ].join("")
+       );
+};
+
+/* Note: addSetting and getSetting are no longer used by SWFUpload but are included
+       the maintain v2 API compatibility
+*/
+// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used.
+SWFUpload.prototype.addSetting = function (name, value, default_value) {
+    if (value == undefined) {
+        return (this.settings[name] = default_value);
+    } else {
+        return (this.settings[name] = value);
+       }
+};
+
+// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found.
+SWFUpload.prototype.getSetting = function (name) {
+    if (this.settings[name] != undefined) {
+        return this.settings[name];
+       }
+
+    return "";
+};
+
+
+
+// Private: callFlash handles function calls made to the Flash element.
+// Calls are made with a setTimeout for some functions to work around
+// bugs in the ExternalInterface library.
+SWFUpload.prototype.callFlash = function (functionName, argumentArray) {
+       argumentArray = argumentArray || [];
+       
+       var movieElement = this.getMovieElement();
+       var returnValue, returnString;
+
+       // Flash's method if calling ExternalInterface methods (code adapted from MooTools).
+       try {
+               returnString = movieElement.CallFunction('<invoke name="' + functionName + '" returntype="javascript">' + __flash__argumentsToXML(argumentArray, 0) + '</invoke>');
+               returnValue = eval(returnString);
+       } catch (ex) {
+               throw "Call to " + functionName + " failed";
+       }
+       
+       // Unescape file post param values
+       if (returnValue != undefined && typeof returnValue.post === "object") {
+               returnValue = this.unescapeFilePostParams(returnValue);
+       }
+
+       return returnValue;
+};
+
+/* *****************************
+       -- Flash control methods --
+       Your UI should use these
+       to operate SWFUpload
+   ***************************** */
+
+// WARNING: this function does not work in Flash Player 10
+// Public: selectFile causes a File Selection Dialog window to appear.  This
+// dialog only allows 1 file to be selected.
+SWFUpload.prototype.selectFile = function () {
+       this.callFlash("SelectFile");
+};
+
+// WARNING: this function does not work in Flash Player 10
+// Public: selectFiles causes a File Selection Dialog window to appear/ This
+// dialog allows the user to select any number of files
+// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
+// If the selection name length is too long the dialog will fail in an unpredictable manner.  There is no work-around
+// for this bug.
+SWFUpload.prototype.selectFiles = function () {
+       this.callFlash("SelectFiles");
+};
+
+
+// Public: startUpload starts uploading the first file in the queue unless
+// the optional parameter 'fileID' specifies the ID 
+SWFUpload.prototype.startUpload = function (fileID) {
+       this.callFlash("StartUpload", [fileID]);
+};
+
+// Public: cancelUpload cancels any queued file.  The fileID parameter may be the file ID or index.
+// If you do not specify a fileID the current uploading file or first file in the queue is cancelled.
+// If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter.
+SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) {
+       if (triggerErrorEvent !== false) {
+               triggerErrorEvent = true;
+       }
+       this.callFlash("CancelUpload", [fileID, triggerErrorEvent]);
+};
+
+// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue.
+// If nothing is currently uploading then nothing happens.
+SWFUpload.prototype.stopUpload = function () {
+       this.callFlash("StopUpload");
+};
+
+/* ************************
+ * Settings methods
+ *   These methods change the SWFUpload settings.
+ *   SWFUpload settings should not be changed directly on the settings object
+ *   since many of the settings need to be passed to Flash in order to take
+ *   effect.
+ * *********************** */
+
+// Public: getStats gets the file statistics object.
+SWFUpload.prototype.getStats = function () {
+       return this.callFlash("GetStats");
+};
+
+// Public: setStats changes the SWFUpload statistics.  You shouldn't need to 
+// change the statistics but you can.  Changing the statistics does not
+// affect SWFUpload accept for the successful_uploads count which is used
+// by the upload_limit setting to determine how many files the user may upload.
+SWFUpload.prototype.setStats = function (statsObject) {
+       this.callFlash("SetStats", [statsObject]);
+};
+
+// Public: getFile retrieves a File object by ID or Index.  If the file is
+// not found then 'null' is returned.
+SWFUpload.prototype.getFile = function (fileID) {
+       if (typeof(fileID) === "number") {
+               return this.callFlash("GetFileByIndex", [fileID]);
+       } else {
+               return this.callFlash("GetFile", [fileID]);
+       }
+};
+
+// Public: addFileParam sets a name/value pair that will be posted with the
+// file specified by the Files ID.  If the name already exists then the
+// exiting value will be overwritten.
+SWFUpload.prototype.addFileParam = function (fileID, name, value) {
+       return this.callFlash("AddFileParam", [fileID, name, value]);
+};
+
+// Public: removeFileParam removes a previously set (by addFileParam) name/value
+// pair from the specified file.
+SWFUpload.prototype.removeFileParam = function (fileID, name) {
+       this.callFlash("RemoveFileParam", [fileID, name]);
+};
+
+// Public: setUploadUrl changes the upload_url setting.
+SWFUpload.prototype.setUploadURL = function (url) {
+       this.settings.upload_url = url.toString();
+       this.callFlash("SetUploadURL", [url]);
+};
+
+// Public: setPostParams changes the post_params setting
+SWFUpload.prototype.setPostParams = function (paramsObject) {
+       this.settings.post_params = paramsObject;
+       this.callFlash("SetPostParams", [paramsObject]);
+};
+
+// Public: addPostParam adds post name/value pair.  Each name can have only one value.
+SWFUpload.prototype.addPostParam = function (name, value) {
+       this.settings.post_params[name] = value;
+       this.callFlash("SetPostParams", [this.settings.post_params]);
+};
+
+// Public: removePostParam deletes post name/value pair.
+SWFUpload.prototype.removePostParam = function (name) {
+       delete this.settings.post_params[name];
+       this.callFlash("SetPostParams", [this.settings.post_params]);
+};
+
+// Public: setFileTypes changes the file_types setting and the file_types_description setting
+SWFUpload.prototype.setFileTypes = function (types, description) {
+       this.settings.file_types = types;
+       this.settings.file_types_description = description;
+       this.callFlash("SetFileTypes", [types, description]);
+};
+
+// Public: setFileSizeLimit changes the file_size_limit setting
+SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) {
+       this.settings.file_size_limit = fileSizeLimit;
+       this.callFlash("SetFileSizeLimit", [fileSizeLimit]);
+};
+
+// Public: setFileUploadLimit changes the file_upload_limit setting
+SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) {
+       this.settings.file_upload_limit = fileUploadLimit;
+       this.callFlash("SetFileUploadLimit", [fileUploadLimit]);
+};
+
+// Public: setFileQueueLimit changes the file_queue_limit setting
+SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) {
+       this.settings.file_queue_limit = fileQueueLimit;
+       this.callFlash("SetFileQueueLimit", [fileQueueLimit]);
+};
+
+// Public: setFilePostName changes the file_post_name setting
+SWFUpload.prototype.setFilePostName = function (filePostName) {
+       this.settings.file_post_name = filePostName;
+       this.callFlash("SetFilePostName", [filePostName]);
+};
+
+// Public: setUseQueryString changes the use_query_string setting
+SWFUpload.prototype.setUseQueryString = function (useQueryString) {
+       this.settings.use_query_string = useQueryString;
+       this.callFlash("SetUseQueryString", [useQueryString]);
+};
+
+// Public: setRequeueOnError changes the requeue_on_error setting
+SWFUpload.prototype.setRequeueOnError = function (requeueOnError) {
+       this.settings.requeue_on_error = requeueOnError;
+       this.callFlash("SetRequeueOnError", [requeueOnError]);
+};
+
+// Public: setHTTPSuccess changes the http_success setting
+SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) {
+       if (typeof http_status_codes === "string") {
+               http_status_codes = http_status_codes.replace(" ", "").split(",");
+       }
+       
+       this.settings.http_success = http_status_codes;
+       this.callFlash("SetHTTPSuccess", [http_status_codes]);
+};
+
+// Public: setHTTPSuccess changes the http_success setting
+SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) {
+       this.settings.assume_success_timeout = timeout_seconds;
+       this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]);
+};
+
+// Public: setDebugEnabled changes the debug_enabled setting
+SWFUpload.prototype.setDebugEnabled = function (debugEnabled) {
+       this.settings.debug_enabled = debugEnabled;
+       this.callFlash("SetDebugEnabled", [debugEnabled]);
+};
+
+// Public: setButtonImageURL loads a button image sprite
+SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) {
+       if (buttonImageURL == undefined) {
+               buttonImageURL = "";
+       }
+       
+       this.settings.button_image_url = buttonImageURL;
+       this.callFlash("SetButtonImageURL", [buttonImageURL]);
+};
+
+// Public: setButtonDimensions resizes the Flash Movie and button
+SWFUpload.prototype.setButtonDimensions = function (width, height) {
+       this.settings.button_width = width;
+       this.settings.button_height = height;
+       
+       var movie = this.getMovieElement();
+       if (movie != undefined) {
+               movie.style.width = width + "px";
+               movie.style.height = height + "px";
+       }
+       
+       this.callFlash("SetButtonDimensions", [width, height]);
+};
+// Public: setButtonText Changes the text overlaid on the button
+SWFUpload.prototype.setButtonText = function (html) {
+       this.settings.button_text = html;
+       this.callFlash("SetButtonText", [html]);
+};
+// Public: setButtonTextPadding changes the top and left padding of the text overlay
+SWFUpload.prototype.setButtonTextPadding = function (left, top) {
+       this.settings.button_text_top_padding = top;
+       this.settings.button_text_left_padding = left;
+       this.callFlash("SetButtonTextPadding", [left, top]);
+};
+
+// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button
+SWFUpload.prototype.setButtonTextStyle = function (css) {
+       this.settings.button_text_style = css;
+       this.callFlash("SetButtonTextStyle", [css]);
+};
+// Public: setButtonDisabled disables/enables the button
+SWFUpload.prototype.setButtonDisabled = function (isDisabled) {
+       this.settings.button_disabled = isDisabled;
+       this.callFlash("SetButtonDisabled", [isDisabled]);
+};
+// Public: setButtonAction sets the action that occurs when the button is clicked
+SWFUpload.prototype.setButtonAction = function (buttonAction) {
+       this.settings.button_action = buttonAction;
+       this.callFlash("SetButtonAction", [buttonAction]);
+};
+
+// Public: setButtonCursor changes the mouse cursor displayed when hovering over the button
+SWFUpload.prototype.setButtonCursor = function (cursor) {
+       this.settings.button_cursor = cursor;
+       this.callFlash("SetButtonCursor", [cursor]);
+};
+
+/* *******************************
+       Flash Event Interfaces
+       These functions are used by Flash to trigger the various
+       events.
+       
+       All these functions a Private.
+       
+       Because the ExternalInterface library is buggy the event calls
+       are added to a queue and the queue then executed by a setTimeout.
+       This ensures that events are executed in a determinate order and that
+       the ExternalInterface bugs are avoided.
+******************************* */
+
+SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) {
+       // Warning: Don't call this.debug inside here or you'll create an infinite loop
+       
+       if (argumentArray == undefined) {
+               argumentArray = [];
+       } else if (!(argumentArray instanceof Array)) {
+               argumentArray = [argumentArray];
+       }
+       
+       var self = this;
+       if (typeof this.settings[handlerName] === "function") {
+               // Queue the event
+               this.eventQueue.push(function () {
+                       this.settings[handlerName].apply(this, argumentArray);
+               });
+               
+               // Execute the next queued event
+               setTimeout(function () {
+                       self.executeNextEvent();
+               }, 0);
+               
+       } else if (this.settings[handlerName] !== null) {
+               throw "Event handler " + handlerName + " is unknown or is not a function";
+       }
+};
+
+// Private: Causes the next event in the queue to be executed.  Since events are queued using a setTimeout
+// we must queue them in order to garentee that they are executed in order.
+SWFUpload.prototype.executeNextEvent = function () {
+       // Warning: Don't call this.debug inside here or you'll create an infinite loop
+
+       var  f = this.eventQueue ? this.eventQueue.shift() : null;
+       if (typeof(f) === "function") {
+               f.apply(this);
+       }
+};
+
+// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have
+// properties that contain characters that are not valid for JavaScript identifiers. To work around this
+// the Flash Component escapes the parameter names and we must unescape again before passing them along.
+SWFUpload.prototype.unescapeFilePostParams = function (file) {
+       var reg = /[$]([0-9a-f]{4})/i;
+       var unescapedPost = {};
+       var uk;
+
+       if (file != undefined) {
+               for (var k in file.post) {
+                       if (file.post.hasOwnProperty(k)) {
+                               uk = k;
+                               var match;
+                               while ((match = reg.exec(uk)) !== null) {
+                                       uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16)));
+                               }
+                               unescapedPost[uk] = file.post[k];
+                       }
+               }
+
+               file.post = unescapedPost;
+       }
+
+       return file;
+};
+
+// Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working)
+SWFUpload.prototype.testExternalInterface = function () {
+       try {
+               return this.callFlash("TestExternalInterface");
+       } catch (ex) {
+               return false;
+       }
+};
+
+// Private: This event is called by Flash when it has finished loading. Don't modify this.
+// Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded.
+SWFUpload.prototype.flashReady = function () {
+       // Check that the movie element is loaded correctly with its ExternalInterface methods defined
+       var movieElement = this.getMovieElement();
+
+       if (!movieElement) {
+               this.debug("Flash called back ready but the flash movie can't be found.");
+               return;
+       }
+
+       this.cleanUp(movieElement);
+       
+       this.queueEvent("swfupload_loaded_handler");
+};
+
+// Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE.
+// This function is called by Flash each time the ExternalInterface functions are created.
+SWFUpload.prototype.cleanUp = function (movieElement) {
+       // Pro-actively unhook all the Flash functions
+       try {
+               if (this.movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
+                       this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)");
+                       for (var key in movieElement) {
+                               try {
+                                       if (typeof(movieElement[key]) === "function") {
+                                               movieElement[key] = null;
+                                       }
+                               } catch (ex) {
+                               }
+                       }
+               }
+       } catch (ex1) {
+       
+       }
+
+       // Fix Flashes own cleanup code so if the SWFMovie was removed from the page
+       // it doesn't display errors.
+       window["__flash__removeCallback"] = function (instance, name) {
+               try {
+                       if (instance) {
+                               instance[name] = null;
+                       }
+               } catch (flashEx) {
+               
+               }
+       };
+
+};
+
+
+/* This is a chance to do something before the browse window opens */
+SWFUpload.prototype.fileDialogStart = function () {
+       this.queueEvent("file_dialog_start_handler");
+};
+
+
+/* Called when a file is successfully added to the queue. */
+SWFUpload.prototype.fileQueued = function (file) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("file_queued_handler", file);
+};
+
+
+/* Handle errors that occur when an attempt to queue a file fails. */
+SWFUpload.prototype.fileQueueError = function (file, errorCode, message) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("file_queue_error_handler", [file, errorCode, message]);
+};
+
+/* Called after the file dialog has closed and the selected files have been queued.
+       You could call startUpload here if you want the queued files to begin uploading immediately. */
+SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) {
+       this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]);
+};
+
+SWFUpload.prototype.uploadStart = function (file) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("return_upload_start_handler", file);
+};
+
+SWFUpload.prototype.returnUploadStart = function (file) {
+       var returnValue;
+       if (typeof this.settings.upload_start_handler === "function") {
+               file = this.unescapeFilePostParams(file);
+               returnValue = this.settings.upload_start_handler.call(this, file);
+       } else if (this.settings.upload_start_handler != undefined) {
+               throw "upload_start_handler must be a function";
+       }
+
+       // Convert undefined to true so if nothing is returned from the upload_start_handler it is
+       // interpretted as 'true'.
+       if (returnValue === undefined) {
+               returnValue = true;
+       }
+       
+       returnValue = !!returnValue;
+       
+       this.callFlash("ReturnUploadStart", [returnValue]);
+};
+
+
+
+SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]);
+};
+
+SWFUpload.prototype.uploadError = function (file, errorCode, message) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("upload_error_handler", [file, errorCode, message]);
+};
+
+SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("upload_success_handler", [file, serverData, responseReceived]);
+};
+
+SWFUpload.prototype.uploadComplete = function (file) {
+       file = this.unescapeFilePostParams(file);
+       this.queueEvent("upload_complete_handler", file);
+};
+
+/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
+   internal debug console.  You can override this event and have messages written where you want. */
+SWFUpload.prototype.debug = function (message) {
+       this.queueEvent("debug_handler", message);
+};
+
+
+/* **********************************
+       Debug Console
+       The debug console is a self contained, in page location
+       for debug message to be sent.  The Debug Console adds
+       itself to the body if necessary.
+
+       The console is automatically scrolled as messages appear.
+       
+       If you are using your own debug handler or when you deploy to production and
+       have debug disabled you can remove these functions to reduce the file size
+       and complexity.
+********************************** */
+   
+// Private: debugMessage is the default debug_handler.  If you want to print debug messages
+// call the debug() function.  When overriding the function your own function should
+// check to see if the debug setting is true before outputting debug information.
+SWFUpload.prototype.debugMessage = function (message) {
+       if (this.settings.debug) {
+               var exceptionMessage, exceptionValues = [];
+
+               // Check for an exception object and print it nicely
+               if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") {
+                       for (var key in message) {
+                               if (message.hasOwnProperty(key)) {
+                                       exceptionValues.push(key + ": " + message[key]);
+                               }
+                       }
+                       exceptionMessage = exceptionValues.join("\n") || "";
+                       exceptionValues = exceptionMessage.split("\n");
+                       exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: ");
+                       SWFUpload.Console.writeLine(exceptionMessage);
+               } else {
+                       SWFUpload.Console.writeLine(message);
+               }
+       }
+};
+
+SWFUpload.Console = {};
+SWFUpload.Console.writeLine = function (message) {
+       var console, documentForm;
+
+       try {
+               console = document.getElementById("SWFUpload_Console");
+
+               if (!console) {
+                       documentForm = document.createElement("form");
+                       document.getElementsByTagName("body")[0].appendChild(documentForm);
+
+                       console = document.createElement("textarea");
+                       console.id = "SWFUpload_Console";
+                       console.style.fontFamily = "monospace";
+                       console.setAttribute("wrap", "off");
+                       console.wrap = "off";
+                       console.style.overflow = "auto";
+                       console.style.width = "700px";
+                       console.style.height = "350px";
+                       console.style.margin = "5px";
+                       documentForm.appendChild(console);
+               }
+
+               console.value += message + "\n";
+
+               console.scrollTop = console.scrollHeight - console.clientHeight;
+       } catch (ex) {
+               alert("Exception: " + ex.name + " Message: " + ex.message);
+       }
+};
diff --git a/docs/jscripts/infusion/licenses/Infusion-LICENSE.txt b/docs/jscripts/infusion/licenses/Infusion-LICENSE.txt
new file mode 100644 (file)
index 0000000..7de827b
--- /dev/null
@@ -0,0 +1,227 @@
+Fluid is available under either the terms of the New BSD license or the
+Educational Community License, Version 2.0. As a recipient of Fluid, you may
+choose which license to receive this code under (except as noted in per-module
+LICENSE files). All modules are Copyright 2007 University of Toronto except
+where noted otherwise in the code itself, or if the modules reside in a separate
+directory, they may contain explicit declarations of copyright in both the
+LICENSE file in the directory in which they reside and in the code itself. No
+external contributions are allowed under licenses which are fundamentally
+incompatible with the ECL or BSD licenses that Fluid is distributed under.
+
+The text of the ECL and BSD licenses is reproduced below.
+
+Educational Community License, Version 2.0
+*************************************
+Copyright 2007 University of Toronto
+
+Educational Community License, Version 2.0, April 2007
+
+The Educational Community License version 2.0 ("ECL") consists of the Apache 2.0
+license, modified to change the scope of the patent grant in section 3 to be
+specific to the needs of the education communities using this license. The
+original Apache 2.0 license can be found at:
+http://www.apache.org/licenses/LICENSE-2.0
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed. Any
+patent license granted hereby with respect to contributions by an individual
+employed by an institution or organization is limited to patent claims where the
+individual that is the author of the Work is also the inventor of the patent
+claims licensed, and where the organization or institution has the right to
+grant such license under applicable grant and research funding agreements. No
+other express or implied licenses are granted.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+   1. You must give any other recipients of the Work or Derivative Works a copy
+of this License; and
+   2. You must cause any modified files to carry prominent notices stating that
+You changed the files; and
+   3. You must retain, in the Source form of any Derivative Works that You
+distribute, all copyright, patent, trademark, and attribution notices from
+the Source form of the Work, excluding those notices that do not pertain to
+any part of the Derivative Works; and
+   4. If the Work includes a "NOTICE" text file as part of its distribution,
+then any Derivative Works that You distribute must include a readable copy of
+the attribution notices contained within such NOTICE file, excluding those
+notices that do not pertain to any part of the Derivative Works, in at least
+one of the following places: within a NOTICE text file distributed as part of
+the Derivative Works; within the Source form or documentation, if provided
+along with the Derivative Works; or, within a display generated by the
+Derivative Works, if and wherever such third-party notices normally appear.
+The contents of the NOTICE file are for informational purposes only and do
+not modify the License. You may add Your own attribution notices within
+Derivative Works that You distribute, alongside or as an addendum to the
+NOTICE text from the Work, provided that such additional attribution notices
+cannot be construed as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS FOR ECL 2.0
+
+The New BSD license
+**********************
+
+Copyright 2007 University of Toronto.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+    * Neither the name of the University of Toronto nor the names of its
+contributors may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGE.
+
+END OF TERMS AND CONDITIONS FOR THE NEW BSD LICENSE
\ No newline at end of file
diff --git a/docs/jscripts/infusion/licenses/fastXmlPull-LICENSE.txt b/docs/jscripts/infusion/licenses/fastXmlPull-LICENSE.txt
new file mode 100644 (file)
index 0000000..585d49e
--- /dev/null
@@ -0,0 +1,525 @@
+
+The code inside the file
+    fastXmlPull.js
+in this folder is based on code taken from XML for <SCRIPT> (http://xmljs.sourceforge.net/),
+which is distributed under the terms of the GNU Lesser General Public Licence (LGPL) 
+(http://www.gnu.org/copyleft/lesser.html#TOC1) and the zlib/libpng License (http://www.opensource.org/licenses/zlib-license.php). 
+
+The text of these licenses is reproduced below.
+
+========================================================================
+The zlib/libpng License
+
+Copyright (c) 2000 - 2002, 2003 Michael Houghton (mike@idle.org), Raymond Irving and David Joham (djoham@yahoo.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+    1. The origin of this software must not be misrepresented; you must not
+    claim that you wrote the original software. If you use this software
+    in a product, an acknowledgment in the product documentation would be
+    appreciated but is not required.
+
+    2. Altered source versions must be plainly marked as such, and must not be
+    misrepresented as being the original software.
+
+    3. This notice may not be removed or altered from any source
+    distribution.
+    
+========================================================================
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                   59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the 
+    Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+    Boston, MA  02111-1307  USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/docs/jscripts/infusion/licenses/jQuery-LICENSE.txt b/docs/jscripts/infusion/licenses/jQuery-LICENSE.txt
new file mode 100644 (file)
index 0000000..b857d46
--- /dev/null
@@ -0,0 +1,299 @@
+The MIT License
+
+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.
+
+        GNU GENERAL PUBLIC LICENSE
+           Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+          Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+        GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+          NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
diff --git a/docs/jscripts/infusion/licenses/swfobject-LICENSE.txt b/docs/jscripts/infusion/licenses/swfobject-LICENSE.txt
new file mode 100644 (file)
index 0000000..f796334
--- /dev/null
@@ -0,0 +1,4 @@
+SWFObject v2.2 <http://code.google.com/p/swfobject/>
+       Copyright (c) 2007-2008 Geoff Stearns, Michael Williams, and Bobby van der Sluis
+       This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
+       
\ No newline at end of file
diff --git a/docs/jscripts/infusion/licenses/swfupload-LICENSE.txt b/docs/jscripts/infusion/licenses/swfupload-LICENSE.txt
new file mode 100644 (file)
index 0000000..e3b1883
--- /dev/null
@@ -0,0 +1,22 @@
+
+The MIT License
+
+Copyright (c) 2006-2007 Lars Huring, Olov NilzÈn and Mammon Media
+
+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.