From 129728f447361657677e5ec190b1df7aaaa8e24f Mon Sep 17 00:00:00 2001 From: Ariel Backenroth Date: Sat, 12 Aug 2006 15:37:09 +0000 Subject: [PATCH] initial dojo generator. moving to dojo nightly build and checking in the widget libraries as well. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3488 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../web/templating/TemplateInputMethod.java | 2 +- .../web/templating/TemplatingService.java | 25 +- .../web/templating/xforms/DojoGenerator.java | 76 + .../templating/xforms/XFormsInputMethod.java | 14 +- .../xforms/servlet/ChibaServlet.java | 382 +- .../web/jsp/content/xforms/dojo-generator.jsp | 31 + source/web/jsp/content/xforms/xforms.js | 269 + source/web/scripts/ajax/dojo.js | 5688 +---------------- .../web/scripts/ajax/src/AdapterRegistry.js | 79 + source/web/scripts/ajax/src/Deferred.js | 305 + source/web/scripts/ajax/src/DeferredList.js | 78 + source/web/scripts/ajax/src/animation.js | 2 + .../scripts/ajax/src/animation/Animation.js | 233 + .../ajax/src/animation/AnimationEvent.js | 53 + .../ajax/src/animation/AnimationSequence.js | 150 + .../web/scripts/ajax/src/animation/Timer.js | 46 + .../scripts/ajax/src/animation/__package__.js | 8 + source/web/scripts/ajax/src/behavior.js | 238 + source/web/scripts/ajax/src/bootstrap1.js | 461 ++ source/web/scripts/ajax/src/bootstrap2.js | 204 + source/web/scripts/ajax/src/browser_debug.js | 166 + .../scripts/ajax/src/collections/ArrayList.js | 136 + .../ajax/src/collections/BinaryTree.js | 193 + .../ajax/src/collections/Collections.js | 115 + .../ajax/src/collections/Dictionary.js | 119 + .../web/scripts/ajax/src/collections/Graph.js | 143 + .../web/scripts/ajax/src/collections/Queue.js | 77 + .../web/scripts/ajax/src/collections/Set.js | 84 + .../scripts/ajax/src/collections/SkipList.js | 139 + .../ajax/src/collections/SortedList.js | 201 + .../web/scripts/ajax/src/collections/Stack.js | 75 + .../ajax/src/collections/__package__.js | 12 + source/web/scripts/ajax/src/compat/0.2.2.js | 65 + source/web/scripts/ajax/src/crypto.js | 5 + .../web/scripts/ajax/src/crypto/Blowfish.js | 556 ++ source/web/scripts/ajax/src/crypto/LICENSE | 11 + source/web/scripts/ajax/src/crypto/MD5.js | 193 + .../web/scripts/ajax/src/crypto/Rijndael.js | 12 + source/web/scripts/ajax/src/crypto/SHA1.js | 154 + source/web/scripts/ajax/src/crypto/SHA256.js | 10 + .../scripts/ajax/src/crypto/__package__.js | 7 + source/web/scripts/ajax/src/data.js | 5 + source/web/scripts/ajax/src/data/Attribute.js | 52 + source/web/scripts/ajax/src/data/Item.js | 317 + source/web/scripts/ajax/src/data/Kind.js | 18 + .../web/scripts/ajax/src/data/Observable.js | 49 + source/web/scripts/ajax/src/data/ResultSet.js | 60 + .../web/scripts/ajax/src/data/SimpleStore.js | 194 + source/web/scripts/ajax/src/data/Type.js | 15 + source/web/scripts/ajax/src/data/Value.js | 45 + .../web/scripts/ajax/src/data/__package__.js | 12 + .../web/scripts/ajax/src/data/format/Csv.js | 102 + .../web/scripts/ajax/src/data/format/Json.js | 93 + .../scripts/ajax/src/data/provider/Base.js | 173 + .../ajax/src/data/provider/Delicious.js | 75 + .../ajax/src/data/provider/FlatFile.js | 143 + .../scripts/ajax/src/data/provider/JotSpot.js | 17 + .../scripts/ajax/src/data/provider/MySql.js | 17 + source/web/scripts/ajax/src/data/to_do.txt | 45 + source/web/scripts/ajax/src/date.js | 614 ++ source/web/scripts/ajax/src/debug.js | 77 + source/web/scripts/ajax/src/debug/Firebug.js | 9 + .../web/scripts/ajax/src/debug/arrow_hide.gif | Bin 0 -> 163 bytes .../web/scripts/ajax/src/debug/arrow_show.gif | Bin 0 -> 165 bytes source/web/scripts/ajax/src/debug/deep.html | 362 ++ source/web/scripts/ajax/src/debug/spacer.gif | Bin 0 -> 820 bytes .../web/scripts/ajax/src/dnd/DragAndDrop.js | 166 + .../scripts/ajax/src/dnd/HtmlDragAndDrop.js | 501 ++ .../scripts/ajax/src/dnd/HtmlDragManager.js | 495 ++ .../web/scripts/ajax/src/dnd/HtmlDragMove.js | 58 + source/web/scripts/ajax/src/dnd/Sortable.js | 18 + .../scripts/ajax/src/dnd/TreeDragAndDrop.js | 468 ++ .../scripts/ajax/src/dnd/TreeDragAndDropV3.js | 378 ++ .../web/scripts/ajax/src/dnd/__package__.js | 6 + source/web/scripts/ajax/src/doc.js | 622 ++ source/web/scripts/ajax/src/docs.js | 985 +++ source/web/scripts/ajax/src/dom.js | 464 ++ source/web/scripts/ajax/src/event.js | 590 ++ .../web/scripts/ajax/src/event/__package__.js | 6 + source/web/scripts/ajax/src/event/browser.js | 268 + source/web/scripts/ajax/src/event/topic.js | 89 + source/web/scripts/ajax/src/experimental.js | 11 + source/web/scripts/ajax/src/flash.js | 1200 ++++ .../src/flash/flash6/DojoExternalInterface.as | 205 + .../ajax/src/flash/flash6/flash6_gateway.fla | Bin 0 -> 40448 bytes .../src/flash/flash8/DojoExternalInterface.as | 224 + .../ajax/src/flash/flash8/ExpressInstall.as | 71 + source/web/scripts/ajax/src/fx/__package__.js | 15 + source/web/scripts/ajax/src/fx/html.js | 573 ++ source/web/scripts/ajax/src/fx/svg.js | 99 + .../scripts/ajax/src/graphics/Colorspace.js | 934 +++ .../scripts/ajax/src/graphics/__package__.js | 2 + source/web/scripts/ajax/src/graphics/color.js | 156 + .../scripts/ajax/src/graphics/color/hsl.js | 134 + .../scripts/ajax/src/graphics/color/hsv.js | 131 + .../web/scripts/ajax/src/hostenv_adobesvg.js | 561 ++ .../web/scripts/ajax/src/hostenv_browser.js | 387 ++ .../web/scripts/ajax/src/hostenv_dashboard.js | 187 + source/web/scripts/ajax/src/hostenv_jsc.js | 66 + source/web/scripts/ajax/src/hostenv_rhino.js | 198 + .../scripts/ajax/src/hostenv_spidermonkey.js | 69 + source/web/scripts/ajax/src/hostenv_svg.js | 213 + source/web/scripts/ajax/src/hostenv_wsh.js | 36 + source/web/scripts/ajax/src/html.js | 4 + .../web/scripts/ajax/src/html/__package__.js | 5 + source/web/scripts/ajax/src/html/color.js | 23 + source/web/scripts/ajax/src/html/common.js | 206 + source/web/scripts/ajax/src/html/csshack.js | 34 + source/web/scripts/ajax/src/html/display.js | 182 + source/web/scripts/ajax/src/html/extras.js | 428 ++ source/web/scripts/ajax/src/html/iframe.js | 112 + .../scripts/ajax/src/html/images/shadowB.png | Bin 0 -> 470 bytes .../scripts/ajax/src/html/images/shadowBL.png | Bin 0 -> 272 bytes .../scripts/ajax/src/html/images/shadowBR.png | Bin 0 -> 271 bytes .../scripts/ajax/src/html/images/shadowL.png | Bin 0 -> 148 bytes .../scripts/ajax/src/html/images/shadowR.png | Bin 0 -> 149 bytes .../scripts/ajax/src/html/images/shadowT.png | Bin 0 -> 152 bytes .../scripts/ajax/src/html/images/shadowTL.png | Bin 0 -> 271 bytes .../ajax/src/html/images/shadowTR..png | Bin 0 -> 348 bytes .../scripts/ajax/src/html/images/shadowTR.png | Bin 0 -> 287 bytes source/web/scripts/ajax/src/html/layout.js | 436 ++ source/web/scripts/ajax/src/html/selection.js | 292 + source/web/scripts/ajax/src/html/shadow.js | 72 + source/web/scripts/ajax/src/html/style.js | 467 ++ source/web/scripts/ajax/src/html/util.js | 446 ++ .../ajax/src/i18n/calendar/GregorianNames.js | 20 + .../scripts/ajax/src/i18n/calendar/nls/README | 6 + .../src/i18n/calendar/nls/de/gregorian.js | 19 + .../src/i18n/calendar/nls/en/gregorian.js | 20 + .../src/i18n/calendar/nls/es/gregorian.js | 19 + .../src/i18n/calendar/nls/fi/gregorian.js | 22 + .../src/i18n/calendar/nls/fr/gregorian.js | 17 + .../ajax/src/i18n/calendar/nls/gregorian.js | 23 + .../src/i18n/calendar/nls/ja/gregorian.js | 22 + .../src/i18n/calendar/nls/nl/gregorian.js | 17 + .../src/i18n/calendar/nls/sv/gregorian.js | 22 + source/web/scripts/ajax/src/i18n/common.js | 45 + source/web/scripts/ajax/src/i18n/currency.js | 279 + .../scripts/ajax/src/i18n/currency/common.js | 279 + .../scripts/ajax/src/i18n/currency/nls/EUR.js | 4 + .../scripts/ajax/src/i18n/currency/nls/GBP.js | 4 + .../scripts/ajax/src/i18n/currency/nls/INR.js | 4 + .../scripts/ajax/src/i18n/currency/nls/ITL.js | 4 + .../scripts/ajax/src/i18n/currency/nls/JPY.js | 4 + .../scripts/ajax/src/i18n/currency/nls/README | 6 + .../scripts/ajax/src/i18n/currency/nls/USD.js | 4 + .../ajax/src/i18n/currency/nls/en-us/USD.js | 3 + .../ajax/src/i18n/currency/nls/en/EUR.js | 3 + .../ajax/src/i18n/currency/nls/en/GBP.js | 3 + .../ajax/src/i18n/currency/nls/en/INR.js | 3 + .../ajax/src/i18n/currency/nls/en/ITL.js | 3 + .../ajax/src/i18n/currency/nls/en/JPY.js | 3 + .../ajax/src/i18n/currency/nls/en/USD.js | 4 + .../ajax/src/i18n/currency/nls/hi/EUR.js | 3 + .../ajax/src/i18n/currency/nls/hi/GBP.js | 3 + .../ajax/src/i18n/currency/nls/hi/INR.js | 4 + .../ajax/src/i18n/currency/nls/hi/ITL.js | 3 + .../ajax/src/i18n/currency/nls/hi/JPY.js | 3 + .../ajax/src/i18n/currency/nls/hi/USD.js | 3 + .../ajax/src/i18n/currency/nls/ja/EUR.js | 3 + .../ajax/src/i18n/currency/nls/ja/GBP.js | 3 + .../ajax/src/i18n/currency/nls/ja/INR.js | 4 + .../ajax/src/i18n/currency/nls/ja/ITL.js | 3 + .../ajax/src/i18n/currency/nls/ja/JPY.js | 4 + .../ajax/src/i18n/currency/nls/ja/USD.js | 3 + source/web/scripts/ajax/src/i18n/datetime.js | 251 + source/web/scripts/ajax/src/i18n/number.js | 282 + source/web/scripts/ajax/src/iCalendar.js | 804 +++ source/web/scripts/ajax/src/io.js | 366 ++ source/web/scripts/ajax/src/io/BrowserIO.js | 565 ++ source/web/scripts/ajax/src/io/IframeIO.js | 236 + source/web/scripts/ajax/src/io/RepubsubIO.js | 517 ++ source/web/scripts/ajax/src/io/RhinoIO.js | 133 + source/web/scripts/ajax/src/io/ScriptSrcIO.js | 444 ++ source/web/scripts/ajax/src/io/ShortBusIO.js | 171 + .../web/scripts/ajax/src/io/ShortBusInit.html | 75 + .../web/scripts/ajax/src/io/XhrIframeProxy.js | 199 + source/web/scripts/ajax/src/io/__package__.js | 7 + source/web/scripts/ajax/src/io/cometd.js | 916 +++ source/web/scripts/ajax/src/io/cookie.js | 98 + source/web/scripts/ajax/src/io/cookies.js | 14 + .../web/scripts/ajax/src/io/xip_client.html | 200 + .../web/scripts/ajax/src/io/xip_server.html | 267 + source/web/scripts/ajax/src/json.js | 120 + source/web/scripts/ajax/src/lang.js | 4 + source/web/scripts/ajax/src/lang/Lang.js | 12 + .../web/scripts/ajax/src/lang/__package__.js | 14 + source/web/scripts/ajax/src/lang/array.js | 173 + source/web/scripts/ajax/src/lang/assert.js | 112 + source/web/scripts/ajax/src/lang/common.js | 170 + source/web/scripts/ajax/src/lang/declare.js | 148 + source/web/scripts/ajax/src/lang/extras.js | 116 + source/web/scripts/ajax/src/lang/func.js | 138 + source/web/scripts/ajax/src/lang/repr.js | 70 + source/web/scripts/ajax/src/lang/type.js | 230 + source/web/scripts/ajax/src/lfx/Animation.js | 506 ++ .../web/scripts/ajax/src/lfx/__package__.js | 5 + source/web/scripts/ajax/src/lfx/extras.js | 109 + source/web/scripts/ajax/src/lfx/html.js | 608 ++ source/web/scripts/ajax/src/lfx/toggle.js | 44 + source/web/scripts/ajax/src/loader.js | 464 ++ source/web/scripts/ajax/src/loader_xd.js | 404 ++ source/web/scripts/ajax/src/logging/Logger.js | 397 ++ .../scripts/ajax/src/logging/__package__.js | 5 + source/web/scripts/ajax/src/math.js | 124 + source/web/scripts/ajax/src/math/Math.js | 12 + .../web/scripts/ajax/src/math/__package__.js | 8 + source/web/scripts/ajax/src/math/curves.js | 212 + source/web/scripts/ajax/src/math/matrix.js | 295 + source/web/scripts/ajax/src/math/points.js | 37 + source/web/scripts/ajax/src/math/transform.js | 106 + source/web/scripts/ajax/src/namespace.js | 68 + .../web/scripts/ajax/src/namespaces/dojo.js | 103 + source/web/scripts/ajax/src/profile.js | 110 + .../scripts/ajax/src/reflect/__package__.js | 15 + .../scripts/ajax/src/reflect/reflection.js | 198 + source/web/scripts/ajax/src/regexp.js | 596 ++ source/web/scripts/ajax/src/rpc/Deferred.js | 5 + source/web/scripts/ajax/src/rpc/JotService.js | 31 + .../web/scripts/ajax/src/rpc/JsonService.js | 95 + source/web/scripts/ajax/src/rpc/RpcService.js | 106 + .../web/scripts/ajax/src/rpc/YahooService.js | 45 + .../web/scripts/ajax/src/rpc/__package__.js | 4 + source/web/scripts/ajax/src/rpc/yahoo.smd | 263 + .../scripts/ajax/src/selection/Selection.js | 415 ++ source/web/scripts/ajax/src/storage.js | 353 + .../web/scripts/ajax/src/storage/Storage.as | 136 + .../scripts/ajax/src/storage/__package__.js | 7 + .../web/scripts/ajax/src/storage/browser.js | 188 + .../web/scripts/ajax/src/storage/dashboard.js | 42 + .../ajax/src/storage/storage_dialog.fla | Bin 0 -> 344064 bytes source/web/scripts/ajax/src/string.js | 2 + source/web/scripts/ajax/src/string/Builder.js | 106 + .../scripts/ajax/src/string/__package__.js | 9 + source/web/scripts/ajax/src/string/common.js | 77 + source/web/scripts/ajax/src/string/extras.js | 209 + source/web/scripts/ajax/src/style.js | 5 + source/web/scripts/ajax/src/svg.js | 269 + source/web/scripts/ajax/src/text/Builder.js | 16 + source/web/scripts/ajax/src/text/String.js | 15 + source/web/scripts/ajax/src/text/Text.js | 15 + .../web/scripts/ajax/src/text/__package__.js | 7 + .../scripts/ajax/src/text/textDirectory.js | 71 + source/web/scripts/ajax/src/undo/Manager.js | 190 + .../web/scripts/ajax/src/undo/__package__.js | 2 + source/web/scripts/ajax/src/undo/browser.js | 269 + source/web/scripts/ajax/src/uri/Uri.js | 104 + .../web/scripts/ajax/src/uri/__package__.js | 4 + .../ajax/src/uuid/LightweightGenerator.js | 72 + .../ajax/src/uuid/NameBasedGenerator.js | 33 + .../web/scripts/ajax/src/uuid/NilGenerator.js | 28 + .../scripts/ajax/src/uuid/RandomGenerator.js | 34 + .../ajax/src/uuid/TimeBasedGenerator.js | 381 ++ source/web/scripts/ajax/src/uuid/Uuid.js | 413 ++ .../web/scripts/ajax/src/uuid/__package__.js | 12 + source/web/scripts/ajax/src/validate.js | 2 + .../scripts/ajax/src/validate/__package__.js | 11 + source/web/scripts/ajax/src/validate/check.js | 247 + .../web/scripts/ajax/src/validate/common.js | 221 + .../web/scripts/ajax/src/validate/datetime.js | 158 + source/web/scripts/ajax/src/validate/de.js | 20 + source/web/scripts/ajax/src/validate/jp.js | 18 + source/web/scripts/ajax/src/validate/us.js | 84 + source/web/scripts/ajax/src/validate/web.js | 95 + .../ajax/src/widget/AccordionContainer.js | 60 + .../scripts/ajax/src/widget/AccordionPane.js | 84 + .../scripts/ajax/src/widget/AnimatedPng.js | 71 + source/web/scripts/ajax/src/widget/Button.js | 303 + source/web/scripts/ajax/src/widget/Button2.js | 33 + source/web/scripts/ajax/src/widget/Chart.js | 195 + .../web/scripts/ajax/src/widget/Checkbox.js | 76 + .../ajax/src/widget/CiviCrmDatePicker.js | 108 + .../scripts/ajax/src/widget/ColorPalette.js | 146 + .../web/scripts/ajax/src/widget/ComboBox.js | 837 +++ .../scripts/ajax/src/widget/ContentPane.js | 552 ++ .../scripts/ajax/src/widget/ContextMenu.js | 33 + .../web/scripts/ajax/src/widget/DatePicker.js | 379 ++ .../scripts/ajax/src/widget/DebugConsole.js | 16 + source/web/scripts/ajax/src/widget/Dialog.js | 282 + source/web/scripts/ajax/src/widget/DocPane.js | 372 ++ .../web/scripts/ajax/src/widget/DomWidget.js | 617 ++ .../scripts/ajax/src/widget/DropdownButton.js | 29 + .../ajax/src/widget/DropdownContainer.js | 62 + .../ajax/src/widget/DropdownDatePicker.js | 55 + source/web/scripts/ajax/src/widget/Editor.js | 523 ++ source/web/scripts/ajax/src/widget/Editor2.js | 395 ++ .../scripts/ajax/src/widget/Editor2Toolbar.js | 317 + .../scripts/ajax/src/widget/FilteringTable.js | 871 +++ .../scripts/ajax/src/widget/FisheyeList.js | 720 +++ .../scripts/ajax/src/widget/FloatingPane.js | 354 + .../web/scripts/ajax/src/widget/GoogleMap.js | 198 + .../scripts/ajax/src/widget/HslColorPicker.js | 129 + .../web/scripts/ajax/src/widget/HtmlWidget.js | 154 + .../scripts/ajax/src/widget/InlineEditBox.js | 161 + .../ajax/src/widget/LayoutContainer.js | 53 + .../web/scripts/ajax/src/widget/LinkPane.js | 31 + source/web/scripts/ajax/src/widget/Manager.js | 330 + source/web/scripts/ajax/src/widget/Menu.js | 59 + source/web/scripts/ajax/src/widget/Menu2.js | 1047 +++ .../web/scripts/ajax/src/widget/MenuItem.js | 47 + .../ajax/src/widget/MonthlyCalendar.js | 173 + source/web/scripts/ajax/src/widget/Parse.js | 355 + .../scripts/ajax/src/widget/PopUpButton.js | 192 + .../ajax/src/widget/RemoteTabController.js | 171 + .../ajax/src/widget/ResizableTextarea.js | 92 + .../scripts/ajax/src/widget/ResizeHandle.js | 93 + .../web/scripts/ajax/src/widget/RichText.js | 1560 +++++ source/web/scripts/ajax/src/widget/Rounded.js | 700 ++ source/web/scripts/ajax/src/widget/Select.js | 66 + source/web/scripts/ajax/src/widget/Show.js | 231 + .../web/scripts/ajax/src/widget/ShowAction.js | 14 + .../web/scripts/ajax/src/widget/ShowSlide.js | 191 + .../ajax/src/widget/SimpleDropdownButtons.js | 158 + .../web/scripts/ajax/src/widget/SlideShow.js | 131 + source/web/scripts/ajax/src/widget/Slider.js | 864 +++ .../scripts/ajax/src/widget/SortableTable.js | 588 ++ source/web/scripts/ajax/src/widget/Spinner.js | 587 ++ .../scripts/ajax/src/widget/SplitContainer.js | 557 ++ .../web/scripts/ajax/src/widget/SvgButton.js | 131 + .../web/scripts/ajax/src/widget/SvgWidget.js | 79 + .../web/scripts/ajax/src/widget/SwtWidget.js | 54 + .../scripts/ajax/src/widget/TabContainer.js | 297 + source/web/scripts/ajax/src/widget/TaskBar.js | 69 + .../web/scripts/ajax/src/widget/TimePicker.js | 307 + .../web/scripts/ajax/src/widget/TitlePane.js | 55 + source/web/scripts/ajax/src/widget/Toaster.js | 225 + source/web/scripts/ajax/src/widget/Toggler.js | 24 + source/web/scripts/ajax/src/widget/Toolbar.js | 893 +++ source/web/scripts/ajax/src/widget/Tooltip.js | 121 + source/web/scripts/ajax/src/widget/Tree.js | 562 ++ .../ajax/src/widget/TreeBasicController.js | 284 + .../ajax/src/widget/TreeBasicControllerV3.js | 538 ++ .../web/scripts/ajax/src/widget/TreeCommon.js | 121 + .../ajax/src/widget/TreeContextMenu.js | 204 + .../ajax/src/widget/TreeContextMenuV3.js | 149 + .../src/widget/TreeControllerExtension.js | 85 + .../web/scripts/ajax/src/widget/TreeDemo.js | 113 + .../src/widget/TreeDeselectOnDblselect.js | 40 + .../ajax/src/widget/TreeDndControllerV3.js | 142 + .../ajax/src/widget/TreeDocIconExtension.js | 112 + .../web/scripts/ajax/src/widget/TreeEditor.js | 103 + .../ajax/src/widget/TreeEmphaseOnSelect.js | 55 + .../scripts/ajax/src/widget/TreeExtension.js | 20 + .../ajax/src/widget/TreeLinkExtension.js | 72 + .../ajax/src/widget/TreeLoadingController.js | 207 + .../src/widget/TreeLoadingControllerV3.js | 444 ++ .../web/scripts/ajax/src/widget/TreeNode.js | 524 ++ .../web/scripts/ajax/src/widget/TreeNodeV3.js | 707 ++ .../ajax/src/widget/TreeRPCController.js | 161 + .../ajax/src/widget/TreeRpcControllerV3.js | 411 ++ .../scripts/ajax/src/widget/TreeSelector.js | 179 + .../scripts/ajax/src/widget/TreeSelectorV3.js | 235 + .../ajax/src/widget/TreeTimeoutIterator.js | 161 + source/web/scripts/ajax/src/widget/TreeV3.js | 362 ++ .../scripts/ajax/src/widget/TreeWithNode.js | 257 + source/web/scripts/ajax/src/widget/Widget.js | 624 ++ source/web/scripts/ajax/src/widget/Wizard.js | 187 + .../web/scripts/ajax/src/widget/YahooMap.js | 159 + .../scripts/ajax/src/widget/__package__.js | 14 + .../src/widget/demoEngine/DemoContainer.js | 97 + .../ajax/src/widget/demoEngine/DemoItem.js | 61 + .../src/widget/demoEngine/DemoNavigator.js | 178 + .../ajax/src/widget/demoEngine/DemoPane.js | 34 + .../ajax/src/widget/demoEngine/SourcePane.js | 42 + .../ajax/src/widget/demoEngine/__package__.js | 10 + .../demoEngine/templates/DemoContainer.css | 39 + .../demoEngine/templates/DemoContainer.html | 25 + .../widget/demoEngine/templates/DemoItem.css | 58 + .../widget/demoEngine/templates/DemoItem.html | 21 + .../demoEngine/templates/DemoNavigator.css | 28 + .../demoEngine/templates/DemoNavigator.html | 24 + .../widget/demoEngine/templates/DemoPane.css | 18 + .../widget/demoEngine/templates/DemoPane.html | 3 + .../demoEngine/templates/SourcePane.css | 20 + .../demoEngine/templates/SourcePane.html | 3 + .../widget/demoEngine/templates/general.css | 73 + .../templates/images/test_thumb.gif | Bin 0 -> 3198 bytes .../demoEngine/templates/images/viewDemo.png | Bin 0 -> 859 bytes .../ajax/src/widget/html/AccordionPane.js | 98 + .../scripts/ajax/src/widget/html/Button2.js | 25 + .../scripts/ajax/src/widget/html/Checkbox.js | 82 + .../scripts/ajax/src/widget/html/ComboBox.js | 611 ++ .../ajax/src/widget/html/ContentPane.js | 566 ++ .../ajax/src/widget/html/ContextMenu.js | 166 + .../ajax/src/widget/html/DatePicker.js | 346 + .../ajax/src/widget/html/DebugConsole.js | 31 + .../scripts/ajax/src/widget/html/DocPane.js | 210 + .../ajax/src/widget/html/DropdownButton.js | 188 + .../scripts/ajax/src/widget/html/GoogleMap.js | 198 + .../scripts/ajax/src/widget/html/LinkPane.js | 54 + .../web/scripts/ajax/src/widget/html/Menu.js | 51 + .../scripts/ajax/src/widget/html/MenuItem.js | 60 + .../ajax/src/widget/html/MonthlyCalendar.js | 134 + .../web/scripts/ajax/src/widget/html/Show.js | 177 + .../ajax/src/widget/html/ShowAction.js | 28 + .../scripts/ajax/src/widget/html/ShowSlide.js | 189 + .../scripts/ajax/src/widget/html/Slider.js | 500 ++ .../ajax/src/widget/html/SortableTable.js | 582 ++ .../scripts/ajax/src/widget/html/Spinner.js | 383 ++ .../scripts/ajax/src/widget/html/TaskBar.js | 85 + .../ajax/src/widget/html/TimePicker.js | 249 + .../scripts/ajax/src/widget/html/TitlePane.js | 70 + .../scripts/ajax/src/widget/html/Tooltip.js | 183 + .../scripts/ajax/src/widget/html/YahooMap.js | 180 + .../scripts/ajax/src/widget/html/layout.js | 109 + .../scripts/ajax/src/widget/html/loader.js | 831 +++ .../scripts/ajax/src/widget/html/stabile.js | 203 + .../scripts/ajax/src/widget/nls/validate.js | 5 + .../web/scripts/ajax/src/widget/svg/Chart.js | 519 ++ .../src/widget/templates/ButtonTemplate.css | 59 + .../src/widget/templates/ButtonTemplate.html | 6 + .../ajax/src/widget/templates/Checkbox.css | 30 + .../ajax/src/widget/templates/Checkbox.html | 6 + .../widget/templates/CiviCrmDatePicker.html | 12 + .../ajax/src/widget/templates/ComboBox.css | 57 + .../ajax/src/widget/templates/ComboBox.html | 25 + .../widget/templates/ComboButtonTemplate.html | 18 + .../ajax/src/widget/templates/DatePicker.css | 143 + .../ajax/src/widget/templates/DatePicker.html | 141 + .../ajax/src/widget/templates/DemoEngine.html | 24 + .../ajax/src/widget/templates/Dialog.html | 13 + .../ajax/src/widget/templates/DocPane.css | 49 + .../ajax/src/widget/templates/DocPane.html | 79 + .../templates/DropDownButtonTemplate.html | 9 + .../src/widget/templates/EditorToolbar.css | 138 + .../src/widget/templates/EditorToolbar.html | 152 + .../templates/EditorToolbarOneline.html | 274 + .../ajax/src/widget/templates/FisheyeList.css | 27 + .../src/widget/templates/FloatingPane.css | 118 + .../src/widget/templates/FloatingPane.html | 18 + .../src/widget/templates/HslColorPicker.svg | 30 + .../widget/templates/HtmlButtonTemplate.css | 59 + .../widget/templates/HtmlButtonTemplate.html | 6 + .../src/widget/templates/HtmlCheckBox.html | 6 + .../templates/HtmlCiviCrmDatePicker.html | 12 + .../src/widget/templates/HtmlComboBox.css | 40 + .../src/widget/templates/HtmlComboBox.html | 34 + .../templates/HtmlComboButtonTemplate.html | 18 + .../templates/HtmlContextMenuTemplate.html | 3 + .../src/widget/templates/HtmlDatePicker.css | 143 + .../src/widget/templates/HtmlDatePicker.html | 141 + .../ajax/src/widget/templates/HtmlDialog.html | 13 + .../ajax/src/widget/templates/HtmlDocPane.css | 19 + .../src/widget/templates/HtmlDocPane.html | 23 + .../templates/HtmlDropDownButtonTemplate.html | 9 + .../widget/templates/HtmlEditorToolbar.css | 138 + .../widget/templates/HtmlEditorToolbar.html | 152 + .../templates/HtmlEditorToolbarOneline.html | 266 + .../src/widget/templates/HtmlFisheyeList.css | 27 + .../src/widget/templates/HtmlFloatingPane.css | 118 + .../widget/templates/HtmlFloatingPane.html | 18 + .../widget/templates/HtmlInlineEditBox.css | 24 + .../widget/templates/HtmlInlineEditBox.html | 6 + .../ajax/src/widget/templates/HtmlMenu2.css | 202 + .../templates/HtmlMenuItemTemplate.html | 2 + .../widget/templates/HtmlMonthlyCalendar.css | 91 + .../widget/templates/HtmlMonthlyCalendar.html | 110 + .../templates/HtmlResizableTextarea.css | 15 + .../templates/HtmlResizableTextarea.html | 14 + .../src/widget/templates/HtmlResizeHandle.css | 12 + .../ajax/src/widget/templates/HtmlShow.css | 59 + .../ajax/src/widget/templates/HtmlShow.html | 11 + .../src/widget/templates/HtmlShowSlide.css | 12 + .../src/widget/templates/HtmlShowSlide.html | 6 + .../templates/HtmlSimpleDropdownButtons.css | 100 + .../src/widget/templates/HtmlSlideShow.css | 9 + .../src/widget/templates/HtmlSlideShow.html | 15 + .../ajax/src/widget/templates/HtmlSlider.css | 47 + .../ajax/src/widget/templates/HtmlSlider.html | 3 + .../templates/HtmlSliderHorizontal.html | 3 + .../widget/templates/HtmlSliderVertical.html | 3 + .../ajax/src/widget/templates/HtmlSpinner.css | 39 + .../src/widget/templates/HtmlSpinner.html | 28 + .../widget/templates/HtmlSplitContainer.css | 39 + .../src/widget/templates/HtmlTabContainer.css | 173 + .../widget/templates/HtmlTabContainer.html | 4 + .../ajax/src/widget/templates/HtmlTaskBar.css | 29 + .../templates/HtmlTaskBarItemTemplate.html | 2 + .../src/widget/templates/HtmlTimePicker.css | 37 + .../src/widget/templates/HtmlTimePicker.html | 99 + .../ajax/src/widget/templates/HtmlToolbar.css | 54 + .../widget/templates/HtmlTooltipTemplate.css | 9 + .../widget/templates/HtmlTooltipTemplate.html | 2 + .../src/widget/templates/InlineEditBox.css | 24 + .../src/widget/templates/InlineEditBox.html | 6 + .../ajax/src/widget/templates/Menu.css | 60 + .../ajax/src/widget/templates/Menu2.css | 179 + .../src/widget/templates/MonthlyCalendar.css | 91 + .../src/widget/templates/MonthlyCalendar.html | 110 + .../ajax/src/widget/templates/PopUpButton.css | 35 + .../src/widget/templates/RemoteTabControl.css | 61 + .../widget/templates/ResizableTextarea.css | 15 + .../widget/templates/ResizableTextarea.html | 14 + .../src/widget/templates/ResizeHandle.css | 12 + .../ajax/src/widget/templates/Show.css | 59 + .../ajax/src/widget/templates/Show.html | 11 + .../ajax/src/widget/templates/ShowSlide.css | 12 + .../ajax/src/widget/templates/ShowSlide.html | 6 + .../ajax/src/widget/templates/SlideShow.css | 9 + .../ajax/src/widget/templates/SlideShow.html | 15 + .../ajax/src/widget/templates/Slider.css | 61 + .../ajax/src/widget/templates/Slider.html | 54 + .../widget/templates/SliderHorizontal.html | 3 + .../src/widget/templates/SliderVertical.html | 3 + .../ajax/src/widget/templates/Spinner.css | 34 + .../ajax/src/widget/templates/Spinner.html | 24 + .../src/widget/templates/SplitContainer.css | 39 + .../src/widget/templates/TabContainer.css | 181 + .../src/widget/templates/TabContainer.html | 4 + .../ajax/src/widget/templates/TaskBar.css | 29 + .../widget/templates/TaskBarItemTemplate.html | 2 + .../ajax/src/widget/templates/Textbox.html | 5 + .../ajax/src/widget/templates/TimePicker.css | 179 + .../ajax/src/widget/templates/TimePicker.html | 98 + .../ajax/src/widget/templates/TitlePane.html | 4 + .../ajax/src/widget/templates/Toaster.css | 42 + .../ajax/src/widget/templates/Toolbar.css | 54 + .../src/widget/templates/TooltipTemplate.css | 10 + .../ajax/src/widget/templates/Tree.css | 29 + .../ajax/src/widget/templates/TreeDocIcon.css | 55 + .../ajax/src/widget/templates/TreeV3.css | 73 + .../widget/templates/ValidationTextbox.html | 8 + .../ajax/src/widget/templates/Wizard.css | 72 + .../ajax/src/widget/templates/Wizard.html | 11 + .../ajax/src/widget/templates/buttons/-.gif | Bin 0 -> 58 bytes .../widget/templates/buttons/aggregate.gif | Bin 0 -> 4360 bytes .../widget/templates/buttons/backcolor.gif | Bin 0 -> 585 bytes .../src/widget/templates/buttons/bg-fade.png | Bin 0 -> 177 bytes .../src/widget/templates/buttons/bold.gif | Bin 0 -> 346 bytes .../src/widget/templates/buttons/cancel.gif | Bin 0 -> 375 bytes .../src/widget/templates/buttons/copy.gif | Bin 0 -> 210 bytes .../widget/templates/buttons/createlink.gif | Bin 0 -> 1081 bytes .../ajax/src/widget/templates/buttons/cut.gif | Bin 0 -> 112 bytes .../src/widget/templates/buttons/delete.gif | Bin 0 -> 81 bytes .../widget/templates/buttons/forecolor.gif | Bin 0 -> 574 bytes .../widget/templates/buttons/hilitecolor.gif | Bin 0 -> 585 bytes .../src/widget/templates/buttons/indent.gif | Bin 0 -> 83 bytes .../buttons/inserthorizontalrule.gif | Bin 0 -> 117 bytes .../widget/templates/buttons/insertimage.gif | Bin 0 -> 595 bytes .../templates/buttons/insertorderedlist.gif | Bin 0 -> 85 bytes .../widget/templates/buttons/inserttable.gif | Bin 0 -> 233 bytes .../templates/buttons/insertunorderedlist.gif | Bin 0 -> 98 bytes .../src/widget/templates/buttons/italic.gif | Bin 0 -> 309 bytes .../templates/buttons/justifycenter.gif | Bin 0 -> 132 bytes .../widget/templates/buttons/justifyfull.gif | Bin 0 -> 294 bytes .../widget/templates/buttons/justifyleft.gif | Bin 0 -> 194 bytes .../widget/templates/buttons/justifyright.gif | Bin 0 -> 190 bytes .../templates/buttons/left_to_right.gif | Bin 0 -> 138 bytes .../templates/buttons/list_bullet_indent.gif | Bin 0 -> 98 bytes .../templates/buttons/list_bullet_outdent.gif | Bin 0 -> 99 bytes .../templates/buttons/list_num_indent.gif | Bin 0 -> 85 bytes .../templates/buttons/list_num_outdent.gif | Bin 0 -> 85 bytes .../src/widget/templates/buttons/outdent.gif | Bin 0 -> 84 bytes .../src/widget/templates/buttons/paste.gif | Bin 0 -> 241 bytes .../src/widget/templates/buttons/redo.gif | Bin 0 -> 543 bytes .../widget/templates/buttons/removeformat.gif | Bin 0 -> 101 bytes .../templates/buttons/right_to_left.gif | Bin 0 -> 142 bytes .../src/widget/templates/buttons/save.gif | Bin 0 -> 243 bytes .../ajax/src/widget/templates/buttons/sep.gif | Bin 0 -> 58 bytes .../src/widget/templates/buttons/space.gif | Bin 0 -> 43 bytes .../templates/buttons/strikethrough.gif | Bin 0 -> 329 bytes .../widget/templates/buttons/subscript.gif | Bin 0 -> 76 bytes .../widget/templates/buttons/superscript.gif | Bin 0 -> 77 bytes .../widget/templates/buttons/underline.gif | Bin 0 -> 344 bytes .../src/widget/templates/buttons/undo.gif | Bin 0 -> 541 bytes .../src/widget/templates/buttons/wikiword.gif | Bin 0 -> 386 bytes .../ajax/src/widget/templates/check.gif | Bin 0 -> 2154 bytes .../widget/templates/check_disabled_off.gif | Bin 0 -> 345 bytes .../widget/templates/check_disabled_on.gif | Bin 0 -> 606 bytes .../src/widget/templates/check_hover_off.gif | Bin 0 -> 231 bytes .../src/widget/templates/check_hover_on.gif | Bin 0 -> 594 bytes .../ajax/src/widget/templates/check_off.gif | Bin 0 -> 373 bytes .../ajax/src/widget/templates/check_on.gif | Bin 0 -> 594 bytes .../src/widget/templates/decrementMonth.gif | Bin 0 -> 166 bytes .../src/widget/templates/decrementWeek.gif | Bin 0 -> 160 bytes .../ajax/src/widget/templates/grabCorner.gif | Bin 0 -> 77 bytes .../src/widget/templates/images/Tree/Tree.css | 36 + .../widget/templates/images/Tree/blank.gif | Bin 0 -> 55 bytes .../widget/templates/images/Tree/closed.gif | Bin 0 -> 1078 bytes .../widget/templates/images/Tree/document.gif | Bin 0 -> 240 bytes .../widget/templates/images/Tree/minus.gif | Bin 0 -> 545 bytes .../src/widget/templates/images/Tree/plus.gif | Bin 0 -> 539 bytes .../templates/images/Tree/transparent.gif | Bin 0 -> 870 bytes .../templates/images/Tree/treenode_blank.gif | Bin 0 -> 834 bytes .../templates/images/Tree/treenode_child.gif | Bin 0 -> 73 bytes .../images/Tree/treenode_expand_minus.gif | Bin 0 -> 547 bytes .../images/Tree/treenode_expand_plus.gif | Bin 0 -> 542 bytes .../templates/images/Tree/treenode_grid_c.gif | Bin 0 -> 60 bytes .../templates/images/Tree/treenode_grid_l.gif | Bin 0 -> 68 bytes .../templates/images/Tree/treenode_grid_p.gif | Bin 0 -> 63 bytes .../templates/images/Tree/treenode_grid_t.gif | Bin 0 -> 74 bytes .../templates/images/Tree/treenode_grid_v.gif | Bin 0 -> 71 bytes .../templates/images/Tree/treenode_grid_x.gif | Bin 0 -> 60 bytes .../templates/images/Tree/treenode_grid_y.gif | Bin 0 -> 67 bytes .../templates/images/Tree/treenode_grid_z.gif | Bin 0 -> 60 bytes .../images/Tree/treenode_loading.gif | Bin 0 -> 1089 bytes .../images/Tree/treenode_loading.jpg | Bin 0 -> 1390 bytes .../widget/templates/images/TreeV3/closed.gif | Bin 0 -> 162 bytes .../templates/images/TreeV3/document.gif | Bin 0 -> 342 bytes .../templates/images/TreeV3/expand_leaf.gif | Bin 0 -> 68 bytes .../images/TreeV3/expand_loading.gif | Bin 0 -> 1081 bytes .../templates/images/TreeV3/expand_minus.gif | Bin 0 -> 552 bytes .../templates/images/TreeV3/expand_plus.gif | Bin 0 -> 552 bytes .../src/widget/templates/images/TreeV3/i.gif | Bin 0 -> 71 bytes .../templates/images/TreeV3/i_bhalf.gif | Bin 0 -> 60 bytes .../widget/templates/images/TreeV3/i_half.gif | Bin 0 -> 65 bytes .../widget/templates/images/TreeV3/i_long.gif | Bin 0 -> 2113 bytes .../src/widget/templates/images/TreeV3/l.gif | Bin 0 -> 74 bytes .../widget/templates/images/TreeV3/minus.gif | Bin 0 -> 539 bytes .../widget/templates/images/TreeV3/open.gif | Bin 0 -> 139 bytes .../widget/templates/images/TreeV3/plus.gif | Bin 0 -> 539 bytes .../src/widget/templates/images/TreeV3/t.gif | Bin 0 -> 80 bytes .../src/widget/templates/images/TreeV3/x.gif | Bin 0 -> 68 bytes .../widget/templates/images/bdYearBg.1.gif | 0 .../src/widget/templates/images/bdYearBg.gif | Bin 0 -> 74 bytes .../src/widget/templates/images/blank.gif | Bin 0 -> 43 bytes .../templates/images/combo_box_arrow.png | Bin 0 -> 336 bytes .../src/widget/templates/images/dateIcon.gif | Bin 0 -> 392 bytes .../templates/images/decrementMonth.gif | Bin 0 -> 167 bytes .../templates/images/decrementMonth.png | Bin 0 -> 194 bytes .../ajax/src/widget/templates/images/dpBg.gif | Bin 0 -> 522 bytes .../src/widget/templates/images/dpCurveBL.png | Bin 0 -> 280 bytes .../src/widget/templates/images/dpCurveBR.png | Bin 0 -> 275 bytes .../src/widget/templates/images/dpCurveTL.png | Bin 0 -> 305 bytes .../src/widget/templates/images/dpCurveTR.png | Bin 0 -> 295 bytes .../widget/templates/images/dpHorizLine.gif | Bin 0 -> 43 bytes .../templates/images/dpHorizLineFoot.gif | Bin 0 -> 43 bytes .../src/widget/templates/images/dpMonthBg.gif | Bin 0 -> 107 bytes .../src/widget/templates/images/dpMonthBg.png | Bin 0 -> 169 bytes .../widget/templates/images/dpMonthBg2.png | Bin 0 -> 169 bytes .../widget/templates/images/dpVertLine.gif | Bin 0 -> 43 bytes .../src/widget/templates/images/dpYearBg.gif | Bin 0 -> 74 bytes .../src/widget/templates/images/dpYearBg.png | Bin 0 -> 142 bytes .../images/dropdownButtonsArrow-disabled.gif | Bin 0 -> 816 bytes .../templates/images/dropdownButtonsArrow.gif | Bin 0 -> 46 bytes .../templates/images/floatingPaneClose.gif | Bin 0 -> 752 bytes .../templates/images/floatingPaneMaximize.gif | Bin 0 -> 704 bytes .../templates/images/floatingPaneMinimize.gif | Bin 0 -> 692 bytes .../templates/images/floatingPaneRestore.gif | Bin 0 -> 701 bytes .../ajax/src/widget/templates/images/hue.png | Bin 0 -> 590 bytes .../templates/images/incrementMonth.gif | Bin 0 -> 168 bytes .../templates/images/incrementMonth.png | Bin 0 -> 205 bytes .../ajax/src/widget/templates/images/no.gif | Bin 0 -> 179 bytes .../ajax/src/widget/templates/images/no.svg | 11 + .../widget/templates/images/scBackground.gif | Bin 0 -> 715 bytes .../images/slider-bg-progress-vert.gif | Bin 0 -> 1899 bytes .../templates/images/slider-bg-vert.gif | Bin 0 -> 1899 bytes .../src/widget/templates/images/slider-bg.gif | Bin 0 -> 1102 bytes .../templates/images/slider-button-horz.png | Bin 0 -> 439 bytes .../templates/images/slider-button-vert.png | Bin 0 -> 432 bytes .../widget/templates/images/slider-button.png | Bin 0 -> 381 bytes .../src/widget/templates/images/slider.gif | Bin 0 -> 865 bytes .../templates/images/slider_down_arrow.png | Bin 0 -> 382 bytes .../templates/images/slider_left_arrow.png | Bin 0 -> 343 bytes .../templates/images/slider_right_arrow.png | Bin 0 -> 366 bytes .../templates/images/slider_up_arrow.png | Bin 0 -> 378 bytes .../widget/templates/images/soriaActive-c.gif | Bin 0 -> 1694 bytes .../widget/templates/images/soriaActive-l.gif | Bin 0 -> 2892 bytes .../widget/templates/images/soriaActive-r.gif | Bin 0 -> 2849 bytes .../widget/templates/images/soriaBarBg.gif | Bin 0 -> 155 bytes .../widget/templates/images/soriaButton-c.gif | Bin 0 -> 1662 bytes .../widget/templates/images/soriaButton-l.gif | Bin 0 -> 2716 bytes .../widget/templates/images/soriaButton-r.gif | Bin 0 -> 2683 bytes .../templates/images/soriaDisabled-c.gif | Bin 0 -> 625 bytes .../templates/images/soriaDisabled-l.gif | Bin 0 -> 1206 bytes .../templates/images/soriaDisabled-r.gif | Bin 0 -> 1105 bytes .../widget/templates/images/soriaMenuBg.gif | Bin 0 -> 155 bytes .../templates/images/soriaPressed-c.gif | Bin 0 -> 1397 bytes .../templates/images/soriaPressed-l.gif | Bin 0 -> 2624 bytes .../templates/images/soriaPressed-r.gif | Bin 0 -> 2631 bytes .../templates/images/spinnerDecrement.gif | Bin 0 -> 120 bytes .../templates/images/spinnerIncrement.gif | Bin 0 -> 119 bytes .../widget/templates/images/submenu_off.gif | Bin 0 -> 161 bytes .../widget/templates/images/submenu_on.gif | Bin 0 -> 106 bytes .../widget/templates/images/tab_bot_left.gif | Bin 0 -> 296 bytes .../templates/images/tab_bot_left_curr.gif | Bin 0 -> 429 bytes .../widget/templates/images/tab_bot_right.gif | Bin 0 -> 974 bytes .../templates/images/tab_bot_right_curr.gif | Bin 0 -> 1310 bytes .../src/widget/templates/images/tab_close.gif | Bin 0 -> 312 bytes .../widget/templates/images/tab_close_h.gif | Bin 0 -> 313 bytes .../src/widget/templates/images/tab_left.gif | Bin 0 -> 635 bytes .../widget/templates/images/tab_left_r.gif | Bin 0 -> 296 bytes .../templates/images/tab_left_r_curr.gif | Bin 0 -> 429 bytes .../src/widget/templates/images/tab_right.gif | Bin 0 -> 2386 bytes .../widget/templates/images/tab_right_r.gif | Bin 0 -> 974 bytes .../templates/images/tab_right_r_curr.gif | Bin 0 -> 1310 bytes .../widget/templates/images/tab_top_left.gif | Bin 0 -> 2384 bytes .../widget/templates/images/tab_top_right.gif | Bin 0 -> 2386 bytes .../widget/templates/images/toolbar-bg.gif | Bin 0 -> 180 bytes .../widget/templates/images/transparent.gif | Bin 0 -> 870 bytes .../templates/images/treenode_blank.gif | Bin 0 -> 834 bytes .../templates/images/treenode_child.gif | Bin 0 -> 73 bytes .../images/treenode_expand_minus.gif | Bin 0 -> 547 bytes .../templates/images/treenode_expand_plus.gif | Bin 0 -> 542 bytes .../templates/images/treenode_grid_c.gif | Bin 0 -> 60 bytes .../templates/images/treenode_grid_l.gif | Bin 0 -> 68 bytes .../templates/images/treenode_grid_p.gif | Bin 0 -> 63 bytes .../templates/images/treenode_grid_t.gif | Bin 0 -> 74 bytes .../templates/images/treenode_grid_v.gif | Bin 0 -> 71 bytes .../templates/images/treenode_grid_x.gif | Bin 0 -> 60 bytes .../templates/images/treenode_grid_y.gif | Bin 0 -> 67 bytes .../templates/images/treenode_grid_z.gif | Bin 0 -> 60 bytes .../widget/templates/images/treenode_node.gif | Bin 0 -> 195 bytes .../widget/templates/images/verticalbar.gif | Bin 0 -> 158 bytes .../templates/images/whiteDownArrow.gif | Bin 0 -> 46 bytes .../src/widget/templates/incrementMonth.gif | Bin 0 -> 168 bytes .../src/widget/templates/incrementWeek.gif | Bin 0 -> 159 bytes .../src/widget/templates/richtextframe.html | 21 + .../web/scripts/ajax/src/widget/validate.js | 734 +++ .../web/scripts/ajax/src/widget/vml/Chart.js | 414 ++ source/web/scripts/ajax/src/xml/Parse.js | 205 + .../web/scripts/ajax/src/xml/__package__.js | 8 + source/web/scripts/ajax/src/xml/domUtil.js | 84 + source/web/scripts/ajax/src/xml/htmlUtil.js | 121 + source/web/scripts/ajax/src/xml/svgUtil.js | 22 + 715 files changed, 79028 insertions(+), 5781 deletions(-) create mode 100644 source/java/org/alfresco/web/templating/xforms/DojoGenerator.java create mode 100644 source/web/jsp/content/xforms/dojo-generator.jsp create mode 100644 source/web/jsp/content/xforms/xforms.js create mode 100644 source/web/scripts/ajax/src/AdapterRegistry.js create mode 100644 source/web/scripts/ajax/src/Deferred.js create mode 100644 source/web/scripts/ajax/src/DeferredList.js create mode 100644 source/web/scripts/ajax/src/animation.js create mode 100644 source/web/scripts/ajax/src/animation/Animation.js create mode 100644 source/web/scripts/ajax/src/animation/AnimationEvent.js create mode 100644 source/web/scripts/ajax/src/animation/AnimationSequence.js create mode 100644 source/web/scripts/ajax/src/animation/Timer.js create mode 100644 source/web/scripts/ajax/src/animation/__package__.js create mode 100644 source/web/scripts/ajax/src/behavior.js create mode 100644 source/web/scripts/ajax/src/bootstrap1.js create mode 100644 source/web/scripts/ajax/src/bootstrap2.js create mode 100644 source/web/scripts/ajax/src/browser_debug.js create mode 100644 source/web/scripts/ajax/src/collections/ArrayList.js create mode 100644 source/web/scripts/ajax/src/collections/BinaryTree.js create mode 100644 source/web/scripts/ajax/src/collections/Collections.js create mode 100644 source/web/scripts/ajax/src/collections/Dictionary.js create mode 100644 source/web/scripts/ajax/src/collections/Graph.js create mode 100644 source/web/scripts/ajax/src/collections/Queue.js create mode 100644 source/web/scripts/ajax/src/collections/Set.js create mode 100644 source/web/scripts/ajax/src/collections/SkipList.js create mode 100644 source/web/scripts/ajax/src/collections/SortedList.js create mode 100644 source/web/scripts/ajax/src/collections/Stack.js create mode 100644 source/web/scripts/ajax/src/collections/__package__.js create mode 100644 source/web/scripts/ajax/src/compat/0.2.2.js create mode 100644 source/web/scripts/ajax/src/crypto.js create mode 100644 source/web/scripts/ajax/src/crypto/Blowfish.js create mode 100644 source/web/scripts/ajax/src/crypto/LICENSE create mode 100644 source/web/scripts/ajax/src/crypto/MD5.js create mode 100644 source/web/scripts/ajax/src/crypto/Rijndael.js create mode 100644 source/web/scripts/ajax/src/crypto/SHA1.js create mode 100644 source/web/scripts/ajax/src/crypto/SHA256.js create mode 100644 source/web/scripts/ajax/src/crypto/__package__.js create mode 100644 source/web/scripts/ajax/src/data.js create mode 100644 source/web/scripts/ajax/src/data/Attribute.js create mode 100644 source/web/scripts/ajax/src/data/Item.js create mode 100644 source/web/scripts/ajax/src/data/Kind.js create mode 100644 source/web/scripts/ajax/src/data/Observable.js create mode 100644 source/web/scripts/ajax/src/data/ResultSet.js create mode 100644 source/web/scripts/ajax/src/data/SimpleStore.js create mode 100644 source/web/scripts/ajax/src/data/Type.js create mode 100644 source/web/scripts/ajax/src/data/Value.js create mode 100644 source/web/scripts/ajax/src/data/__package__.js create mode 100644 source/web/scripts/ajax/src/data/format/Csv.js create mode 100644 source/web/scripts/ajax/src/data/format/Json.js create mode 100644 source/web/scripts/ajax/src/data/provider/Base.js create mode 100644 source/web/scripts/ajax/src/data/provider/Delicious.js create mode 100644 source/web/scripts/ajax/src/data/provider/FlatFile.js create mode 100644 source/web/scripts/ajax/src/data/provider/JotSpot.js create mode 100644 source/web/scripts/ajax/src/data/provider/MySql.js create mode 100644 source/web/scripts/ajax/src/data/to_do.txt create mode 100644 source/web/scripts/ajax/src/date.js create mode 100644 source/web/scripts/ajax/src/debug.js create mode 100644 source/web/scripts/ajax/src/debug/Firebug.js create mode 100644 source/web/scripts/ajax/src/debug/arrow_hide.gif create mode 100644 source/web/scripts/ajax/src/debug/arrow_show.gif create mode 100644 source/web/scripts/ajax/src/debug/deep.html create mode 100644 source/web/scripts/ajax/src/debug/spacer.gif create mode 100644 source/web/scripts/ajax/src/dnd/DragAndDrop.js create mode 100644 source/web/scripts/ajax/src/dnd/HtmlDragAndDrop.js create mode 100644 source/web/scripts/ajax/src/dnd/HtmlDragManager.js create mode 100644 source/web/scripts/ajax/src/dnd/HtmlDragMove.js create mode 100644 source/web/scripts/ajax/src/dnd/Sortable.js create mode 100644 source/web/scripts/ajax/src/dnd/TreeDragAndDrop.js create mode 100644 source/web/scripts/ajax/src/dnd/TreeDragAndDropV3.js create mode 100644 source/web/scripts/ajax/src/dnd/__package__.js create mode 100644 source/web/scripts/ajax/src/doc.js create mode 100644 source/web/scripts/ajax/src/docs.js create mode 100644 source/web/scripts/ajax/src/dom.js create mode 100644 source/web/scripts/ajax/src/event.js create mode 100644 source/web/scripts/ajax/src/event/__package__.js create mode 100644 source/web/scripts/ajax/src/event/browser.js create mode 100644 source/web/scripts/ajax/src/event/topic.js create mode 100644 source/web/scripts/ajax/src/experimental.js create mode 100644 source/web/scripts/ajax/src/flash.js create mode 100644 source/web/scripts/ajax/src/flash/flash6/DojoExternalInterface.as create mode 100644 source/web/scripts/ajax/src/flash/flash6/flash6_gateway.fla create mode 100644 source/web/scripts/ajax/src/flash/flash8/DojoExternalInterface.as create mode 100644 source/web/scripts/ajax/src/flash/flash8/ExpressInstall.as create mode 100644 source/web/scripts/ajax/src/fx/__package__.js create mode 100644 source/web/scripts/ajax/src/fx/html.js create mode 100644 source/web/scripts/ajax/src/fx/svg.js create mode 100644 source/web/scripts/ajax/src/graphics/Colorspace.js create mode 100644 source/web/scripts/ajax/src/graphics/__package__.js create mode 100644 source/web/scripts/ajax/src/graphics/color.js create mode 100644 source/web/scripts/ajax/src/graphics/color/hsl.js create mode 100644 source/web/scripts/ajax/src/graphics/color/hsv.js create mode 100644 source/web/scripts/ajax/src/hostenv_adobesvg.js create mode 100644 source/web/scripts/ajax/src/hostenv_browser.js create mode 100644 source/web/scripts/ajax/src/hostenv_dashboard.js create mode 100644 source/web/scripts/ajax/src/hostenv_jsc.js create mode 100644 source/web/scripts/ajax/src/hostenv_rhino.js create mode 100644 source/web/scripts/ajax/src/hostenv_spidermonkey.js create mode 100644 source/web/scripts/ajax/src/hostenv_svg.js create mode 100644 source/web/scripts/ajax/src/hostenv_wsh.js create mode 100644 source/web/scripts/ajax/src/html.js create mode 100644 source/web/scripts/ajax/src/html/__package__.js create mode 100644 source/web/scripts/ajax/src/html/color.js create mode 100644 source/web/scripts/ajax/src/html/common.js create mode 100644 source/web/scripts/ajax/src/html/csshack.js create mode 100644 source/web/scripts/ajax/src/html/display.js create mode 100644 source/web/scripts/ajax/src/html/extras.js create mode 100644 source/web/scripts/ajax/src/html/iframe.js create mode 100644 source/web/scripts/ajax/src/html/images/shadowB.png create mode 100644 source/web/scripts/ajax/src/html/images/shadowBL.png create mode 100644 source/web/scripts/ajax/src/html/images/shadowBR.png create mode 100644 source/web/scripts/ajax/src/html/images/shadowL.png create mode 100644 source/web/scripts/ajax/src/html/images/shadowR.png create mode 100644 source/web/scripts/ajax/src/html/images/shadowT.png create mode 100644 source/web/scripts/ajax/src/html/images/shadowTL.png create mode 100644 source/web/scripts/ajax/src/html/images/shadowTR..png create mode 100644 source/web/scripts/ajax/src/html/images/shadowTR.png create mode 100644 source/web/scripts/ajax/src/html/layout.js create mode 100644 source/web/scripts/ajax/src/html/selection.js create mode 100644 source/web/scripts/ajax/src/html/shadow.js create mode 100644 source/web/scripts/ajax/src/html/style.js create mode 100644 source/web/scripts/ajax/src/html/util.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/GregorianNames.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/README create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/de/gregorian.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/en/gregorian.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/es/gregorian.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/fi/gregorian.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/fr/gregorian.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/gregorian.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/ja/gregorian.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/nl/gregorian.js create mode 100644 source/web/scripts/ajax/src/i18n/calendar/nls/sv/gregorian.js create mode 100644 source/web/scripts/ajax/src/i18n/common.js create mode 100644 source/web/scripts/ajax/src/i18n/currency.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/common.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/EUR.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/GBP.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/INR.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/ITL.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/JPY.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/README create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/USD.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/en-us/USD.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/en/EUR.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/en/GBP.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/en/INR.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/en/ITL.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/en/JPY.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/en/USD.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/hi/EUR.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/hi/GBP.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/hi/INR.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/hi/ITL.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/hi/JPY.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/hi/USD.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/ja/EUR.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/ja/GBP.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/ja/INR.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/ja/ITL.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/ja/JPY.js create mode 100644 source/web/scripts/ajax/src/i18n/currency/nls/ja/USD.js create mode 100644 source/web/scripts/ajax/src/i18n/datetime.js create mode 100644 source/web/scripts/ajax/src/i18n/number.js create mode 100644 source/web/scripts/ajax/src/iCalendar.js create mode 100644 source/web/scripts/ajax/src/io.js create mode 100644 source/web/scripts/ajax/src/io/BrowserIO.js create mode 100644 source/web/scripts/ajax/src/io/IframeIO.js create mode 100644 source/web/scripts/ajax/src/io/RepubsubIO.js create mode 100644 source/web/scripts/ajax/src/io/RhinoIO.js create mode 100644 source/web/scripts/ajax/src/io/ScriptSrcIO.js create mode 100644 source/web/scripts/ajax/src/io/ShortBusIO.js create mode 100644 source/web/scripts/ajax/src/io/ShortBusInit.html create mode 100644 source/web/scripts/ajax/src/io/XhrIframeProxy.js create mode 100644 source/web/scripts/ajax/src/io/__package__.js create mode 100644 source/web/scripts/ajax/src/io/cometd.js create mode 100644 source/web/scripts/ajax/src/io/cookie.js create mode 100644 source/web/scripts/ajax/src/io/cookies.js create mode 100644 source/web/scripts/ajax/src/io/xip_client.html create mode 100644 source/web/scripts/ajax/src/io/xip_server.html create mode 100644 source/web/scripts/ajax/src/json.js create mode 100644 source/web/scripts/ajax/src/lang.js create mode 100644 source/web/scripts/ajax/src/lang/Lang.js create mode 100644 source/web/scripts/ajax/src/lang/__package__.js create mode 100644 source/web/scripts/ajax/src/lang/array.js create mode 100644 source/web/scripts/ajax/src/lang/assert.js create mode 100644 source/web/scripts/ajax/src/lang/common.js create mode 100644 source/web/scripts/ajax/src/lang/declare.js create mode 100644 source/web/scripts/ajax/src/lang/extras.js create mode 100644 source/web/scripts/ajax/src/lang/func.js create mode 100644 source/web/scripts/ajax/src/lang/repr.js create mode 100644 source/web/scripts/ajax/src/lang/type.js create mode 100644 source/web/scripts/ajax/src/lfx/Animation.js create mode 100644 source/web/scripts/ajax/src/lfx/__package__.js create mode 100644 source/web/scripts/ajax/src/lfx/extras.js create mode 100644 source/web/scripts/ajax/src/lfx/html.js create mode 100644 source/web/scripts/ajax/src/lfx/toggle.js create mode 100644 source/web/scripts/ajax/src/loader.js create mode 100644 source/web/scripts/ajax/src/loader_xd.js create mode 100644 source/web/scripts/ajax/src/logging/Logger.js create mode 100644 source/web/scripts/ajax/src/logging/__package__.js create mode 100644 source/web/scripts/ajax/src/math.js create mode 100644 source/web/scripts/ajax/src/math/Math.js create mode 100644 source/web/scripts/ajax/src/math/__package__.js create mode 100644 source/web/scripts/ajax/src/math/curves.js create mode 100644 source/web/scripts/ajax/src/math/matrix.js create mode 100644 source/web/scripts/ajax/src/math/points.js create mode 100644 source/web/scripts/ajax/src/math/transform.js create mode 100644 source/web/scripts/ajax/src/namespace.js create mode 100644 source/web/scripts/ajax/src/namespaces/dojo.js create mode 100644 source/web/scripts/ajax/src/profile.js create mode 100644 source/web/scripts/ajax/src/reflect/__package__.js create mode 100644 source/web/scripts/ajax/src/reflect/reflection.js create mode 100644 source/web/scripts/ajax/src/regexp.js create mode 100644 source/web/scripts/ajax/src/rpc/Deferred.js create mode 100644 source/web/scripts/ajax/src/rpc/JotService.js create mode 100644 source/web/scripts/ajax/src/rpc/JsonService.js create mode 100644 source/web/scripts/ajax/src/rpc/RpcService.js create mode 100644 source/web/scripts/ajax/src/rpc/YahooService.js create mode 100644 source/web/scripts/ajax/src/rpc/__package__.js create mode 100644 source/web/scripts/ajax/src/rpc/yahoo.smd create mode 100644 source/web/scripts/ajax/src/selection/Selection.js create mode 100644 source/web/scripts/ajax/src/storage.js create mode 100644 source/web/scripts/ajax/src/storage/Storage.as create mode 100644 source/web/scripts/ajax/src/storage/__package__.js create mode 100644 source/web/scripts/ajax/src/storage/browser.js create mode 100644 source/web/scripts/ajax/src/storage/dashboard.js create mode 100644 source/web/scripts/ajax/src/storage/storage_dialog.fla create mode 100644 source/web/scripts/ajax/src/string.js create mode 100644 source/web/scripts/ajax/src/string/Builder.js create mode 100644 source/web/scripts/ajax/src/string/__package__.js create mode 100644 source/web/scripts/ajax/src/string/common.js create mode 100644 source/web/scripts/ajax/src/string/extras.js create mode 100644 source/web/scripts/ajax/src/style.js create mode 100644 source/web/scripts/ajax/src/svg.js create mode 100644 source/web/scripts/ajax/src/text/Builder.js create mode 100644 source/web/scripts/ajax/src/text/String.js create mode 100644 source/web/scripts/ajax/src/text/Text.js create mode 100644 source/web/scripts/ajax/src/text/__package__.js create mode 100644 source/web/scripts/ajax/src/text/textDirectory.js create mode 100644 source/web/scripts/ajax/src/undo/Manager.js create mode 100644 source/web/scripts/ajax/src/undo/__package__.js create mode 100644 source/web/scripts/ajax/src/undo/browser.js create mode 100644 source/web/scripts/ajax/src/uri/Uri.js create mode 100644 source/web/scripts/ajax/src/uri/__package__.js create mode 100644 source/web/scripts/ajax/src/uuid/LightweightGenerator.js create mode 100644 source/web/scripts/ajax/src/uuid/NameBasedGenerator.js create mode 100644 source/web/scripts/ajax/src/uuid/NilGenerator.js create mode 100644 source/web/scripts/ajax/src/uuid/RandomGenerator.js create mode 100644 source/web/scripts/ajax/src/uuid/TimeBasedGenerator.js create mode 100644 source/web/scripts/ajax/src/uuid/Uuid.js create mode 100644 source/web/scripts/ajax/src/uuid/__package__.js create mode 100644 source/web/scripts/ajax/src/validate.js create mode 100644 source/web/scripts/ajax/src/validate/__package__.js create mode 100644 source/web/scripts/ajax/src/validate/check.js create mode 100644 source/web/scripts/ajax/src/validate/common.js create mode 100644 source/web/scripts/ajax/src/validate/datetime.js create mode 100644 source/web/scripts/ajax/src/validate/de.js create mode 100644 source/web/scripts/ajax/src/validate/jp.js create mode 100644 source/web/scripts/ajax/src/validate/us.js create mode 100644 source/web/scripts/ajax/src/validate/web.js create mode 100644 source/web/scripts/ajax/src/widget/AccordionContainer.js create mode 100644 source/web/scripts/ajax/src/widget/AccordionPane.js create mode 100644 source/web/scripts/ajax/src/widget/AnimatedPng.js create mode 100644 source/web/scripts/ajax/src/widget/Button.js create mode 100644 source/web/scripts/ajax/src/widget/Button2.js create mode 100644 source/web/scripts/ajax/src/widget/Chart.js create mode 100644 source/web/scripts/ajax/src/widget/Checkbox.js create mode 100644 source/web/scripts/ajax/src/widget/CiviCrmDatePicker.js create mode 100644 source/web/scripts/ajax/src/widget/ColorPalette.js create mode 100644 source/web/scripts/ajax/src/widget/ComboBox.js create mode 100644 source/web/scripts/ajax/src/widget/ContentPane.js create mode 100644 source/web/scripts/ajax/src/widget/ContextMenu.js create mode 100644 source/web/scripts/ajax/src/widget/DatePicker.js create mode 100644 source/web/scripts/ajax/src/widget/DebugConsole.js create mode 100644 source/web/scripts/ajax/src/widget/Dialog.js create mode 100644 source/web/scripts/ajax/src/widget/DocPane.js create mode 100644 source/web/scripts/ajax/src/widget/DomWidget.js create mode 100644 source/web/scripts/ajax/src/widget/DropdownButton.js create mode 100644 source/web/scripts/ajax/src/widget/DropdownContainer.js create mode 100644 source/web/scripts/ajax/src/widget/DropdownDatePicker.js create mode 100644 source/web/scripts/ajax/src/widget/Editor.js create mode 100644 source/web/scripts/ajax/src/widget/Editor2.js create mode 100644 source/web/scripts/ajax/src/widget/Editor2Toolbar.js create mode 100644 source/web/scripts/ajax/src/widget/FilteringTable.js create mode 100644 source/web/scripts/ajax/src/widget/FisheyeList.js create mode 100644 source/web/scripts/ajax/src/widget/FloatingPane.js create mode 100644 source/web/scripts/ajax/src/widget/GoogleMap.js create mode 100644 source/web/scripts/ajax/src/widget/HslColorPicker.js create mode 100644 source/web/scripts/ajax/src/widget/HtmlWidget.js create mode 100644 source/web/scripts/ajax/src/widget/InlineEditBox.js create mode 100644 source/web/scripts/ajax/src/widget/LayoutContainer.js create mode 100644 source/web/scripts/ajax/src/widget/LinkPane.js create mode 100644 source/web/scripts/ajax/src/widget/Manager.js create mode 100644 source/web/scripts/ajax/src/widget/Menu.js create mode 100644 source/web/scripts/ajax/src/widget/Menu2.js create mode 100644 source/web/scripts/ajax/src/widget/MenuItem.js create mode 100644 source/web/scripts/ajax/src/widget/MonthlyCalendar.js create mode 100644 source/web/scripts/ajax/src/widget/Parse.js create mode 100644 source/web/scripts/ajax/src/widget/PopUpButton.js create mode 100644 source/web/scripts/ajax/src/widget/RemoteTabController.js create mode 100644 source/web/scripts/ajax/src/widget/ResizableTextarea.js create mode 100644 source/web/scripts/ajax/src/widget/ResizeHandle.js create mode 100644 source/web/scripts/ajax/src/widget/RichText.js create mode 100644 source/web/scripts/ajax/src/widget/Rounded.js create mode 100644 source/web/scripts/ajax/src/widget/Select.js create mode 100644 source/web/scripts/ajax/src/widget/Show.js create mode 100644 source/web/scripts/ajax/src/widget/ShowAction.js create mode 100644 source/web/scripts/ajax/src/widget/ShowSlide.js create mode 100644 source/web/scripts/ajax/src/widget/SimpleDropdownButtons.js create mode 100644 source/web/scripts/ajax/src/widget/SlideShow.js create mode 100644 source/web/scripts/ajax/src/widget/Slider.js create mode 100644 source/web/scripts/ajax/src/widget/SortableTable.js create mode 100644 source/web/scripts/ajax/src/widget/Spinner.js create mode 100644 source/web/scripts/ajax/src/widget/SplitContainer.js create mode 100644 source/web/scripts/ajax/src/widget/SvgButton.js create mode 100644 source/web/scripts/ajax/src/widget/SvgWidget.js create mode 100644 source/web/scripts/ajax/src/widget/SwtWidget.js create mode 100644 source/web/scripts/ajax/src/widget/TabContainer.js create mode 100644 source/web/scripts/ajax/src/widget/TaskBar.js create mode 100644 source/web/scripts/ajax/src/widget/TimePicker.js create mode 100644 source/web/scripts/ajax/src/widget/TitlePane.js create mode 100644 source/web/scripts/ajax/src/widget/Toaster.js create mode 100644 source/web/scripts/ajax/src/widget/Toggler.js create mode 100644 source/web/scripts/ajax/src/widget/Toolbar.js create mode 100644 source/web/scripts/ajax/src/widget/Tooltip.js create mode 100644 source/web/scripts/ajax/src/widget/Tree.js create mode 100644 source/web/scripts/ajax/src/widget/TreeBasicController.js create mode 100644 source/web/scripts/ajax/src/widget/TreeBasicControllerV3.js create mode 100644 source/web/scripts/ajax/src/widget/TreeCommon.js create mode 100644 source/web/scripts/ajax/src/widget/TreeContextMenu.js create mode 100644 source/web/scripts/ajax/src/widget/TreeContextMenuV3.js create mode 100644 source/web/scripts/ajax/src/widget/TreeControllerExtension.js create mode 100644 source/web/scripts/ajax/src/widget/TreeDemo.js create mode 100644 source/web/scripts/ajax/src/widget/TreeDeselectOnDblselect.js create mode 100644 source/web/scripts/ajax/src/widget/TreeDndControllerV3.js create mode 100644 source/web/scripts/ajax/src/widget/TreeDocIconExtension.js create mode 100644 source/web/scripts/ajax/src/widget/TreeEditor.js create mode 100644 source/web/scripts/ajax/src/widget/TreeEmphaseOnSelect.js create mode 100644 source/web/scripts/ajax/src/widget/TreeExtension.js create mode 100644 source/web/scripts/ajax/src/widget/TreeLinkExtension.js create mode 100644 source/web/scripts/ajax/src/widget/TreeLoadingController.js create mode 100644 source/web/scripts/ajax/src/widget/TreeLoadingControllerV3.js create mode 100644 source/web/scripts/ajax/src/widget/TreeNode.js create mode 100644 source/web/scripts/ajax/src/widget/TreeNodeV3.js create mode 100644 source/web/scripts/ajax/src/widget/TreeRPCController.js create mode 100644 source/web/scripts/ajax/src/widget/TreeRpcControllerV3.js create mode 100644 source/web/scripts/ajax/src/widget/TreeSelector.js create mode 100644 source/web/scripts/ajax/src/widget/TreeSelectorV3.js create mode 100644 source/web/scripts/ajax/src/widget/TreeTimeoutIterator.js create mode 100644 source/web/scripts/ajax/src/widget/TreeV3.js create mode 100755 source/web/scripts/ajax/src/widget/TreeWithNode.js create mode 100644 source/web/scripts/ajax/src/widget/Widget.js create mode 100644 source/web/scripts/ajax/src/widget/Wizard.js create mode 100644 source/web/scripts/ajax/src/widget/YahooMap.js create mode 100644 source/web/scripts/ajax/src/widget/__package__.js create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/DemoContainer.js create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/DemoItem.js create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/DemoNavigator.js create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/DemoPane.js create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/SourcePane.js create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/__package__.js create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/DemoContainer.css create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/DemoContainer.html create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/DemoItem.css create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/DemoItem.html create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/DemoNavigator.css create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/DemoNavigator.html create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/DemoPane.css create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/DemoPane.html create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/SourcePane.css create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/SourcePane.html create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/general.css create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/images/test_thumb.gif create mode 100644 source/web/scripts/ajax/src/widget/demoEngine/templates/images/viewDemo.png create mode 100644 source/web/scripts/ajax/src/widget/html/AccordionPane.js create mode 100644 source/web/scripts/ajax/src/widget/html/Button2.js create mode 100644 source/web/scripts/ajax/src/widget/html/Checkbox.js create mode 100644 source/web/scripts/ajax/src/widget/html/ComboBox.js create mode 100644 source/web/scripts/ajax/src/widget/html/ContentPane.js create mode 100644 source/web/scripts/ajax/src/widget/html/ContextMenu.js create mode 100644 source/web/scripts/ajax/src/widget/html/DatePicker.js create mode 100644 source/web/scripts/ajax/src/widget/html/DebugConsole.js create mode 100644 source/web/scripts/ajax/src/widget/html/DocPane.js create mode 100644 source/web/scripts/ajax/src/widget/html/DropdownButton.js create mode 100644 source/web/scripts/ajax/src/widget/html/GoogleMap.js create mode 100644 source/web/scripts/ajax/src/widget/html/LinkPane.js create mode 100644 source/web/scripts/ajax/src/widget/html/Menu.js create mode 100644 source/web/scripts/ajax/src/widget/html/MenuItem.js create mode 100644 source/web/scripts/ajax/src/widget/html/MonthlyCalendar.js create mode 100644 source/web/scripts/ajax/src/widget/html/Show.js create mode 100644 source/web/scripts/ajax/src/widget/html/ShowAction.js create mode 100644 source/web/scripts/ajax/src/widget/html/ShowSlide.js create mode 100644 source/web/scripts/ajax/src/widget/html/Slider.js create mode 100644 source/web/scripts/ajax/src/widget/html/SortableTable.js create mode 100644 source/web/scripts/ajax/src/widget/html/Spinner.js create mode 100644 source/web/scripts/ajax/src/widget/html/TaskBar.js create mode 100644 source/web/scripts/ajax/src/widget/html/TimePicker.js create mode 100644 source/web/scripts/ajax/src/widget/html/TitlePane.js create mode 100644 source/web/scripts/ajax/src/widget/html/Tooltip.js create mode 100644 source/web/scripts/ajax/src/widget/html/YahooMap.js create mode 100644 source/web/scripts/ajax/src/widget/html/layout.js create mode 100644 source/web/scripts/ajax/src/widget/html/loader.js create mode 100644 source/web/scripts/ajax/src/widget/html/stabile.js create mode 100644 source/web/scripts/ajax/src/widget/nls/validate.js create mode 100644 source/web/scripts/ajax/src/widget/svg/Chart.js create mode 100644 source/web/scripts/ajax/src/widget/templates/ButtonTemplate.css create mode 100644 source/web/scripts/ajax/src/widget/templates/ButtonTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/Checkbox.css create mode 100644 source/web/scripts/ajax/src/widget/templates/Checkbox.html create mode 100644 source/web/scripts/ajax/src/widget/templates/CiviCrmDatePicker.html create mode 100644 source/web/scripts/ajax/src/widget/templates/ComboBox.css create mode 100644 source/web/scripts/ajax/src/widget/templates/ComboBox.html create mode 100644 source/web/scripts/ajax/src/widget/templates/ComboButtonTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/DatePicker.css create mode 100644 source/web/scripts/ajax/src/widget/templates/DatePicker.html create mode 100644 source/web/scripts/ajax/src/widget/templates/DemoEngine.html create mode 100644 source/web/scripts/ajax/src/widget/templates/Dialog.html create mode 100644 source/web/scripts/ajax/src/widget/templates/DocPane.css create mode 100644 source/web/scripts/ajax/src/widget/templates/DocPane.html create mode 100644 source/web/scripts/ajax/src/widget/templates/DropDownButtonTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/EditorToolbar.css create mode 100644 source/web/scripts/ajax/src/widget/templates/EditorToolbar.html create mode 100644 source/web/scripts/ajax/src/widget/templates/EditorToolbarOneline.html create mode 100644 source/web/scripts/ajax/src/widget/templates/FisheyeList.css create mode 100644 source/web/scripts/ajax/src/widget/templates/FloatingPane.css create mode 100644 source/web/scripts/ajax/src/widget/templates/FloatingPane.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HslColorPicker.svg create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlButtonTemplate.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlButtonTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlCheckBox.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlCiviCrmDatePicker.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlComboBox.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlComboBox.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlComboButtonTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlContextMenuTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlDatePicker.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlDatePicker.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlDialog.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlDocPane.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlDocPane.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlDropDownButtonTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlEditorToolbar.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlEditorToolbar.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlEditorToolbarOneline.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlFisheyeList.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlFloatingPane.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlFloatingPane.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlInlineEditBox.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlInlineEditBox.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlMenu2.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlMenuItemTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlMonthlyCalendar.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlMonthlyCalendar.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlResizableTextarea.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlResizableTextarea.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlResizeHandle.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlShow.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlShow.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlShowSlide.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlShowSlide.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSimpleDropdownButtons.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSlideShow.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSlideShow.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSlider.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSlider.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSliderHorizontal.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSliderVertical.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSpinner.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSpinner.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlSplitContainer.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlTabContainer.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlTabContainer.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlTaskBar.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlTaskBarItemTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlTimePicker.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlTimePicker.html create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlToolbar.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlTooltipTemplate.css create mode 100644 source/web/scripts/ajax/src/widget/templates/HtmlTooltipTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/InlineEditBox.css create mode 100644 source/web/scripts/ajax/src/widget/templates/InlineEditBox.html create mode 100644 source/web/scripts/ajax/src/widget/templates/Menu.css create mode 100644 source/web/scripts/ajax/src/widget/templates/Menu2.css create mode 100644 source/web/scripts/ajax/src/widget/templates/MonthlyCalendar.css create mode 100644 source/web/scripts/ajax/src/widget/templates/MonthlyCalendar.html create mode 100644 source/web/scripts/ajax/src/widget/templates/PopUpButton.css create mode 100644 source/web/scripts/ajax/src/widget/templates/RemoteTabControl.css create mode 100644 source/web/scripts/ajax/src/widget/templates/ResizableTextarea.css create mode 100644 source/web/scripts/ajax/src/widget/templates/ResizableTextarea.html create mode 100644 source/web/scripts/ajax/src/widget/templates/ResizeHandle.css create mode 100644 source/web/scripts/ajax/src/widget/templates/Show.css create mode 100644 source/web/scripts/ajax/src/widget/templates/Show.html create mode 100644 source/web/scripts/ajax/src/widget/templates/ShowSlide.css create mode 100644 source/web/scripts/ajax/src/widget/templates/ShowSlide.html create mode 100644 source/web/scripts/ajax/src/widget/templates/SlideShow.css create mode 100644 source/web/scripts/ajax/src/widget/templates/SlideShow.html create mode 100644 source/web/scripts/ajax/src/widget/templates/Slider.css create mode 100644 source/web/scripts/ajax/src/widget/templates/Slider.html create mode 100644 source/web/scripts/ajax/src/widget/templates/SliderHorizontal.html create mode 100644 source/web/scripts/ajax/src/widget/templates/SliderVertical.html create mode 100644 source/web/scripts/ajax/src/widget/templates/Spinner.css create mode 100644 source/web/scripts/ajax/src/widget/templates/Spinner.html create mode 100644 source/web/scripts/ajax/src/widget/templates/SplitContainer.css create mode 100644 source/web/scripts/ajax/src/widget/templates/TabContainer.css create mode 100644 source/web/scripts/ajax/src/widget/templates/TabContainer.html create mode 100644 source/web/scripts/ajax/src/widget/templates/TaskBar.css create mode 100644 source/web/scripts/ajax/src/widget/templates/TaskBarItemTemplate.html create mode 100644 source/web/scripts/ajax/src/widget/templates/Textbox.html create mode 100644 source/web/scripts/ajax/src/widget/templates/TimePicker.css create mode 100644 source/web/scripts/ajax/src/widget/templates/TimePicker.html create mode 100644 source/web/scripts/ajax/src/widget/templates/TitlePane.html create mode 100644 source/web/scripts/ajax/src/widget/templates/Toaster.css create mode 100644 source/web/scripts/ajax/src/widget/templates/Toolbar.css create mode 100644 source/web/scripts/ajax/src/widget/templates/TooltipTemplate.css create mode 100644 source/web/scripts/ajax/src/widget/templates/Tree.css create mode 100644 source/web/scripts/ajax/src/widget/templates/TreeDocIcon.css create mode 100755 source/web/scripts/ajax/src/widget/templates/TreeV3.css create mode 100644 source/web/scripts/ajax/src/widget/templates/ValidationTextbox.html create mode 100644 source/web/scripts/ajax/src/widget/templates/Wizard.css create mode 100644 source/web/scripts/ajax/src/widget/templates/Wizard.html create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/-.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/aggregate.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/backcolor.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/bg-fade.png create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/bold.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/cancel.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/copy.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/createlink.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/cut.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/delete.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/forecolor.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/hilitecolor.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/indent.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/inserthorizontalrule.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/insertimage.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/insertorderedlist.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/inserttable.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/insertunorderedlist.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/italic.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/justifycenter.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/justifyfull.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/justifyleft.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/justifyright.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/left_to_right.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/list_bullet_indent.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/list_bullet_outdent.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/list_num_indent.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/list_num_outdent.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/outdent.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/paste.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/redo.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/removeformat.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/right_to_left.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/save.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/sep.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/space.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/strikethrough.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/subscript.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/superscript.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/underline.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/undo.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/buttons/wikiword.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/check.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/check_disabled_off.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/check_disabled_on.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/check_hover_off.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/check_hover_on.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/check_off.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/check_on.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/decrementMonth.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/decrementWeek.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/grabCorner.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/Tree.css create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/blank.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/closed.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/document.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/minus.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/plus.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/transparent.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_blank.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_child.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_expand_minus.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_expand_plus.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_grid_c.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_grid_l.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_grid_p.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_grid_t.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_grid_v.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_grid_x.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_grid_y.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_grid_z.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_loading.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/Tree/treenode_loading.jpg create mode 100644 source/web/scripts/ajax/src/widget/templates/images/TreeV3/closed.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/TreeV3/document.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/TreeV3/expand_leaf.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/TreeV3/expand_loading.gif create mode 100755 source/web/scripts/ajax/src/widget/templates/images/TreeV3/expand_minus.gif create mode 100755 source/web/scripts/ajax/src/widget/templates/images/TreeV3/expand_plus.gif create mode 100755 source/web/scripts/ajax/src/widget/templates/images/TreeV3/i.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/TreeV3/i_bhalf.gif create mode 100755 source/web/scripts/ajax/src/widget/templates/images/TreeV3/i_half.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/TreeV3/i_long.gif create mode 100755 source/web/scripts/ajax/src/widget/templates/images/TreeV3/l.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/TreeV3/minus.gif create mode 100755 source/web/scripts/ajax/src/widget/templates/images/TreeV3/open.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/TreeV3/plus.gif create mode 100755 source/web/scripts/ajax/src/widget/templates/images/TreeV3/t.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/TreeV3/x.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/bdYearBg.1.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/bdYearBg.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/blank.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/combo_box_arrow.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dateIcon.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/decrementMonth.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/decrementMonth.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpBg.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpCurveBL.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpCurveBR.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpCurveTL.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpCurveTR.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpHorizLine.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpHorizLineFoot.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpMonthBg.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpMonthBg.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpMonthBg2.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpVertLine.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpYearBg.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dpYearBg.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dropdownButtonsArrow-disabled.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/dropdownButtonsArrow.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/floatingPaneClose.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/floatingPaneMaximize.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/floatingPaneMinimize.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/floatingPaneRestore.gif create mode 100755 source/web/scripts/ajax/src/widget/templates/images/hue.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/incrementMonth.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/incrementMonth.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/no.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/no.svg create mode 100644 source/web/scripts/ajax/src/widget/templates/images/scBackground.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider-bg-progress-vert.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider-bg-vert.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider-bg.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider-button-horz.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider-button-vert.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider-button.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider_down_arrow.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider_left_arrow.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider_right_arrow.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/slider_up_arrow.png create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaActive-c.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaActive-l.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaActive-r.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaBarBg.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaButton-c.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaButton-l.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaButton-r.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaDisabled-c.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaDisabled-l.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaDisabled-r.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaMenuBg.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaPressed-c.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaPressed-l.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/soriaPressed-r.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/spinnerDecrement.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/spinnerIncrement.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/submenu_off.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/submenu_on.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_bot_left.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_bot_left_curr.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_bot_right.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_bot_right_curr.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_close.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_close_h.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_left.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_left_r.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_left_r_curr.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_right.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_right_r.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_right_r_curr.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_top_left.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/tab_top_right.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/toolbar-bg.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/transparent.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_blank.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_child.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_expand_minus.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_expand_plus.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_grid_c.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_grid_l.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_grid_p.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_grid_t.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_grid_v.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_grid_x.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_grid_y.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_grid_z.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/treenode_node.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/verticalbar.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/images/whiteDownArrow.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/incrementMonth.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/incrementWeek.gif create mode 100644 source/web/scripts/ajax/src/widget/templates/richtextframe.html create mode 100644 source/web/scripts/ajax/src/widget/validate.js create mode 100644 source/web/scripts/ajax/src/widget/vml/Chart.js create mode 100644 source/web/scripts/ajax/src/xml/Parse.js create mode 100644 source/web/scripts/ajax/src/xml/__package__.js create mode 100644 source/web/scripts/ajax/src/xml/domUtil.js create mode 100644 source/web/scripts/ajax/src/xml/htmlUtil.js create mode 100644 source/web/scripts/ajax/src/xml/svgUtil.js diff --git a/source/java/org/alfresco/web/templating/TemplateInputMethod.java b/source/java/org/alfresco/web/templating/TemplateInputMethod.java index 75c31f833d..ae76e9605a 100644 --- a/source/java/org/alfresco/web/templating/TemplateInputMethod.java +++ b/source/java/org/alfresco/web/templating/TemplateInputMethod.java @@ -23,5 +23,5 @@ public interface TemplateInputMethod public String getInputURL(final Document xmlContent, final TemplateType tt); - public String getSchemaInputURL(final TemplateType tt); + // public String getSchemaInputURL(final TemplateType tt); } diff --git a/source/java/org/alfresco/web/templating/TemplatingService.java b/source/java/org/alfresco/web/templating/TemplatingService.java index 01fe985cb6..921878acc2 100644 --- a/source/java/org/alfresco/web/templating/TemplatingService.java +++ b/source/java/org/alfresco/web/templating/TemplatingService.java @@ -76,15 +76,27 @@ public class TemplatingService return new TemplateTypeImpl(name, schema); } - public void writeXML(final Node d, final File output) + public Document newDocument() + throws ParserConfigurationException, + SAXException, + IOException + { + final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + dbf.setValidating(false); + final DocumentBuilder db = dbf.newDocumentBuilder(); + return db.newDocument(); + } + + public void writeXML(final Node n, final Writer output) { try { - System.out.println("writing out a document for " + d.getNodeName() + + System.out.println("writing out a document for " + n.getNodeName() + " to " + output); final TransformerFactory tf = TransformerFactory.newInstance(); final Transformer t = tf.newTransformer(); - t.transform(new DOMSource(d), new StreamResult(output)); + t.transform(new DOMSource(n), new StreamResult(output)); } catch (TransformerException te) { @@ -93,6 +105,13 @@ public class TemplatingService } } + public void writeXML(final Node n, final File output) + throws IOException + { + + this.writeXML(n, new FileWriter(output)); + } + public Document parseXML(final String source) throws ParserConfigurationException, SAXException, diff --git a/source/java/org/alfresco/web/templating/xforms/DojoGenerator.java b/source/java/org/alfresco/web/templating/xforms/DojoGenerator.java new file mode 100644 index 0000000000..a90edebddd --- /dev/null +++ b/source/java/org/alfresco/web/templating/xforms/DojoGenerator.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.web.templating.xforms; + +import org.chiba.tools.xslt.UIGenerator; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import java.io.Writer; +import java.util.HashMap; +import java.util.Iterator; +import javax.servlet.*; +import javax.servlet.http.*; +import java.io.*; +import org.chiba.xml.xforms.exception.XFormsException; + +public class DojoGenerator + implements UIGenerator +{ + + private Node inputNode; + private final HashMap parameters = new HashMap(); + private final HttpServletRequest request; + private final HttpServletResponse response; + + public DojoGenerator(HttpServletRequest request, + HttpServletResponse response) + { + this.request = request; + this.response = response; + } + + public void setInputNode(final Node inputNode) + { + this.inputNode = inputNode; + } + + public void setParameter(final String key, final Object value) + { + this.parameters.put(key, value); + } + + public void setOutput(Object output) + { + // this.output = output; + } + + public void generate() + throws XFormsException + { + try + { + request.setAttribute("xform", this.inputNode); + final RequestDispatcher rd = request.getRequestDispatcher("/jsp/content/xforms/dojo-generator.jsp"); + rd.include(request, response); + } + catch (Exception e) + { + throw new XFormsException(e); + } + } +} + \ No newline at end of file diff --git a/source/java/org/alfresco/web/templating/xforms/XFormsInputMethod.java b/source/java/org/alfresco/web/templating/xforms/XFormsInputMethod.java index a3065c46bd..4a081fcda6 100644 --- a/source/java/org/alfresco/web/templating/xforms/XFormsInputMethod.java +++ b/source/java/org/alfresco/web/templating/xforms/XFormsInputMethod.java @@ -24,6 +24,8 @@ import org.alfresco.util.TempFileProvider; import org.alfresco.web.templating.*; import org.alfresco.web.templating.xforms.schemabuilder.*; import org.chiba.xml.util.DOMUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -31,6 +33,7 @@ import org.w3c.dom.Node; public class XFormsInputMethod implements TemplateInputMethod { + private static final Log LOGGER = LogFactory.getLog(XFormsInputMethod.class); public XFormsInputMethod() { @@ -122,8 +125,15 @@ public class XFormsInputMethod { final TemplatingService ts = TemplatingService.getInstance(); final File schemaFile = TempFileProvider.createTempFile("alfresco", ".schema"); - ts.writeXML(tt.getSchema(), schemaFile); - + try + { + ts.writeXML(tt.getSchema(), schemaFile); + } + catch (IOException ioe) + { + assert false : ioe.getMessage(); + LOGGER.error(ioe); + } final FacesContext fc = FacesContext.getCurrentInstance(); final String cp = fc.getExternalContext().getRequestContextPath(); diff --git a/source/java/org/alfresco/web/templating/xforms/servlet/ChibaServlet.java b/source/java/org/alfresco/web/templating/xforms/servlet/ChibaServlet.java index b4fab79e6b..f132155abf 100644 --- a/source/java/org/alfresco/web/templating/xforms/servlet/ChibaServlet.java +++ b/source/java/org/alfresco/web/templating/xforms/servlet/ChibaServlet.java @@ -1,11 +1,13 @@ package org.alfresco.web.templating.xforms.servlet; import org.alfresco.web.app.Application; +import org.alfresco.web.templating.xforms.DojoGenerator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.httpclient.Cookie; import org.chiba.adapter.ChibaAdapter; import org.alfresco.web.templating.xforms.flux.FluxAdapter; +import org.alfresco.web.templating.TemplatingService; import org.chiba.tools.xslt.StylesheetLoader; import org.chiba.tools.xslt.UIGenerator; import org.chiba.tools.xslt.XSLTGenerator; @@ -44,7 +46,6 @@ public class ChibaServlet extends HttpServlet { //init-params private static Log logger = LogFactory.getLog(ChibaServlet.class); - private static final String FORM_PARAM_NAME = "form"; private static final String XSL_PARAM_NAME = "xslt"; private static final String CSS_PARAM_NAME = "css"; @@ -164,54 +165,68 @@ public class ChibaServlet extends HttpServlet { */ protected void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + throws ServletException, IOException { ChibaAdapter adapter = null; HttpSession session = request.getSession(true); + try { + if (request.getParameter("xxx") != null) + { + response.setContentType("text/xml"); + Writer out = response.getWriter(); + adapter = (ChibaAdapter)session.getAttribute(CHIBA_ADAPTER); + TemplatingService.getInstance().writeXML(adapter.getXForms(), out); + out.close(); + } + else + { + logger.info("--------------- new XForms session ---------------"); + //kill everything that may have lived before + session.removeAttribute(CHIBA_ADAPTER); + session.removeAttribute(CHIBA_UI_GENERATOR); - logger.info("--------------- new XForms session ---------------"); - try { - //kill everything that may have lived before - session.removeAttribute(CHIBA_ADAPTER); - session.removeAttribute(CHIBA_UI_GENERATOR); + // determine Form to load + String formURI = /*getRequestURI(request) +*/ request.getParameter(FORM_PARAM_NAME); + logger.info("formURI: " + formURI); + String xslFile = request.getParameter(XSL_PARAM_NAME); + String css = request.getParameter(CSS_PARAM_NAME); + String actionURL = getActionURL(request, response,true); + logger.info("setting up adapeter"); - // determine Form to load - String formURI = /*getRequestURI(request) +*/ request.getParameter(FORM_PARAM_NAME); - logger.info("formURI: " + formURI); - String xslFile = request.getParameter(XSL_PARAM_NAME); - String css = request.getParameter(CSS_PARAM_NAME); - String actionURL = getActionURL(request, response,true); - logger.info("setting up adapeter"); + //setup Adapter + adapter = setupAdapter(new FluxAdapter(session), session, formURI); + setContextParams(request, adapter); + storeCookies(request, adapter); + adapter.init(); - //setup Adapter - adapter = setupAdapter(new FluxAdapter(session), session, formURI); - setContextParams(request, adapter); - storeCookies(request, adapter); - adapter.init(); + if (load(adapter, response)) return; + if (replaceAll(adapter, response)) return; - if (load(adapter, response)) return; - if (replaceAll(adapter, response)) return; + // response.setContentType("text/html"); + // PrintWriter out = response.getWriter(); - response.setContentType("text/html"); - PrintWriter out = response.getWriter(); + logger.info("generating ui"); - logger.info("generating ui"); + UIGenerator uiGenerator = createUIGenerator(request, + response, + actionURL, + xslFile, + css); + uiGenerator.setInputNode(adapter.getXForms()); + // uiGenerator.setOutput(out); + uiGenerator.generate(); - UIGenerator uiGenerator = createUIGenerator(request, actionURL, xslFile, css); - uiGenerator.setInputNode(adapter.getXForms()); - uiGenerator.setOutput(out); - uiGenerator.generate(); + //store adapter in session + session.setAttribute(CHIBA_ADAPTER, adapter); + session.setAttribute(CHIBA_UI_GENERATOR,uiGenerator); - //store adapter in session - session.setAttribute(CHIBA_ADAPTER, adapter); - session.setAttribute(CHIBA_UI_GENERATOR,uiGenerator); - - out.close(); - logger.info("done!"); - } catch (Exception e) { + // out.close(); + logger.info("done!"); + } + } catch (Exception e) { e.printStackTrace(); - shutdown(adapter, session, e, response, request); - } + shutdown(adapter, session, e, response, request); + } } /** @@ -228,24 +243,24 @@ public class ChibaServlet extends HttpServlet { * @return ServletAdapter */ protected ChibaAdapter setupAdapter(ChibaAdapter adapter, - HttpSession session, - String formPath) + HttpSession session, + String formPath) throws XFormsException, URISyntaxException { - adapter.createXFormsProcessor(); + adapter.createXFormsProcessor(); - if ((configPath != null) && !(configPath.equals(""))) { - adapter.setConfigPath(configPath); - } - adapter.setXForms(new URI(formPath)); - adapter.setBaseURI(formPath); - adapter.setUploadDestination(uploadDir); + if ((configPath != null) && !(configPath.equals(""))) { + adapter.setConfigPath(configPath); + } + adapter.setXForms(new URI(formPath)); + adapter.setBaseURI(formPath); + adapter.setUploadDestination(uploadDir); - Map servletMap = new HashMap(); - servletMap.put(ChibaAdapter.SESSION_ID, session.getId()); - adapter.setContextParam(ChibaAdapter.SUBMISSION_RESPONSE, servletMap); + Map servletMap = new HashMap(); + servletMap.put(ChibaAdapter.SESSION_ID, session.getId()); + adapter.setContextParam(ChibaAdapter.SUBMISSION_RESPONSE, servletMap); - return adapter; + return adapter; } /** @@ -257,21 +272,21 @@ public class ChibaServlet extends HttpServlet { * @param adapter the Chiba adapter instance */ protected void storeCookies(HttpServletRequest request,ChibaAdapter adapter){ - javax.servlet.http.Cookie[] cookiesIn = request.getCookies(); - if (cookiesIn != null) { - Cookie[] commonsCookies = new org.apache.commons.httpclient.Cookie[cookiesIn.length]; - for (int i = 0; i < cookiesIn.length; i += 1) { - javax.servlet.http.Cookie c = cookiesIn[i]; - Cookie newCookie = new Cookie(c.getDomain(), - c.getName(), - c.getValue(), - c.getPath(), - c.getMaxAge(), - c.getSecure()); - commonsCookies[i] = newCookie; - } - adapter.setContextParam(AbstractHTTPConnector.REQUEST_COOKIE,commonsCookies); - } + javax.servlet.http.Cookie[] cookiesIn = request.getCookies(); + if (cookiesIn != null) { + Cookie[] commonsCookies = new org.apache.commons.httpclient.Cookie[cookiesIn.length]; + for (int i = 0; i < cookiesIn.length; i += 1) { + javax.servlet.http.Cookie c = cookiesIn[i]; + Cookie newCookie = new Cookie(c.getDomain(), + c.getName(), + c.getValue(), + c.getPath(), + c.getMaxAge(), + c.getSecure()); + commonsCookies[i] = newCookie; + } + adapter.setContextParam(AbstractHTTPConnector.REQUEST_COOKIE,commonsCookies); + } } /** @@ -285,41 +300,42 @@ public class ChibaServlet extends HttpServlet { * @throws XFormsException */ protected UIGenerator createUIGenerator(HttpServletRequest request, - String actionURL, - String xslFile, - String css) + HttpServletResponse response, + String actionURL, + String xslFile, + String css) throws XFormsException { - StylesheetLoader stylesheetLoader = new StylesheetLoader(stylesPath); - if (xslFile != null){ - stylesheetLoader.setStylesheetFile(xslFile); - } - UIGenerator uiGenerator = new XSLTGenerator(stylesheetLoader); +// StylesheetLoader stylesheetLoader = new StylesheetLoader(stylesPath); +// if (xslFile != null){ +// stylesheetLoader.setStylesheetFile(xslFile); +// } + UIGenerator uiGenerator = new DojoGenerator(request, response); - //set parameters - uiGenerator.setParameter("contextroot",request.getContextPath()); - uiGenerator.setParameter("action-url",actionURL); - uiGenerator.setParameter("debug-enabled", String.valueOf(logger.isDebugEnabled())); - String selectorPrefix = + //set parameters + uiGenerator.setParameter("contextroot",request.getContextPath()); + uiGenerator.setParameter("action-url",actionURL); + uiGenerator.setParameter("debug-enabled", String.valueOf(logger.isDebugEnabled())); + String selectorPrefix = Config.getInstance().getProperty(HttpRequestHandler.SELECTOR_PREFIX_PROPERTY, HttpRequestHandler.SELECTOR_PREFIX_DEFAULT); - uiGenerator.setParameter("selector-prefix", selectorPrefix); - String removeUploadPrefix = + uiGenerator.setParameter("selector-prefix", selectorPrefix); + String removeUploadPrefix = Config.getInstance().getProperty(HttpRequestHandler.REMOVE_UPLOAD_PREFIX_PROPERTY, HttpRequestHandler.REMOVE_UPLOAD_PREFIX_DEFAULT); - uiGenerator.setParameter("remove-upload-prefix", removeUploadPrefix); - if (css != null) - uiGenerator.setParameter("css-file", css); - String dataPrefix = Config.getInstance().getProperty("chiba.web.dataPrefix"); - uiGenerator.setParameter("data-prefix", dataPrefix); + uiGenerator.setParameter("remove-upload-prefix", removeUploadPrefix); + if (css != null) + uiGenerator.setParameter("css-file", css); + String dataPrefix = Config.getInstance().getProperty("chiba.web.dataPrefix"); + uiGenerator.setParameter("data-prefix", dataPrefix); - String triggerPrefix = Config.getInstance().getProperty("chiba.web.triggerPrefix"); - uiGenerator.setParameter("trigger-prefix", triggerPrefix); + String triggerPrefix = Config.getInstance().getProperty("chiba.web.triggerPrefix"); + uiGenerator.setParameter("trigger-prefix", triggerPrefix); - uiGenerator.setParameter("user-agent", request.getHeader("User-Agent")); + uiGenerator.setParameter("user-agent", request.getHeader("User-Agent")); - uiGenerator.setParameter("scripted","true"); + // uiGenerator.setParameter("scripted","true"); - return uiGenerator; + return uiGenerator; } /** @@ -331,39 +347,39 @@ public class ChibaServlet extends HttpServlet { */ protected void setContextParams(HttpServletRequest request, ChibaAdapter chibaAdapter) { - //[1] pass user-agent to Adapter for UI-building - chibaAdapter.setContextParam(ServletAdapter.USERAGENT, request.getHeader("User-Agent")); + //[1] pass user-agent to Adapter for UI-building + chibaAdapter.setContextParam(ServletAdapter.USERAGENT, request.getHeader("User-Agent")); - //[2] read any request params that are *not* Chiba params and pass them into the context map - Enumeration params = request.getParameterNames(); - while (params.hasMoreElements()) { - String s = (String) params.nextElement(); - //store all request-params we don't use in the context map of ChibaBean - if (!(s.equals(FORM_PARAM_NAME) || - s.equals(XSL_PARAM_NAME) || - s.equals(CSS_PARAM_NAME) || - s.equals(ACTIONURL_PARAM_NAME))) { - String value = request.getParameter(s); - //servletAdapter.setContextProperty(s, value); - chibaAdapter.setContextParam(s, value); - if (logger.isDebugEnabled()) { - logger.debug("added request param '" + s + "' added to context"); - } - } - } + //[2] read any request params that are *not* Chiba params and pass them into the context map + Enumeration params = request.getParameterNames(); + while (params.hasMoreElements()) { + String s = (String) params.nextElement(); + //store all request-params we don't use in the context map of ChibaBean + if (!(s.equals(FORM_PARAM_NAME) || + s.equals(XSL_PARAM_NAME) || + s.equals(CSS_PARAM_NAME) || + s.equals(ACTIONURL_PARAM_NAME))) { + String value = request.getParameter(s); + //servletAdapter.setContextProperty(s, value); + chibaAdapter.setContextParam(s, value); + if (logger.isDebugEnabled()) { + logger.debug("added request param '" + s + "' added to context"); + } + } + } } /** * @deprecated should be re-implemented using chiba events on adapter */ protected boolean load(ChibaAdapter adapter, HttpServletResponse response) throws XFormsException, IOException { - if (adapter.getContextParam(ChibaAdapter.LOAD_URI) != null) { - String redirectTo = (String) adapter.removeContextParam(ChibaAdapter.LOAD_URI); - adapter.shutdown(); - response.sendRedirect(response.encodeRedirectURL(redirectTo)); - return true; - } - return false; + if (adapter.getContextParam(ChibaAdapter.LOAD_URI) != null) { + String redirectTo = (String) adapter.removeContextParam(ChibaAdapter.LOAD_URI); + adapter.shutdown(); + response.sendRedirect(response.encodeRedirectURL(redirectTo)); + return true; + } + return false; } /** @@ -371,79 +387,79 @@ public class ChibaServlet extends HttpServlet { */ protected boolean replaceAll(ChibaAdapter chibaAdapter, HttpServletResponse response) throws XFormsException, IOException { - if (chibaAdapter.getContextParam(ChibaAdapter.SUBMISSION_RESPONSE) != null) { - Map forwardMap = (Map) chibaAdapter.removeContextParam(ChibaAdapter.SUBMISSION_RESPONSE); - if (forwardMap.containsKey(ChibaAdapter.SUBMISSION_RESPONSE_STREAM)) { - forwardResponse(forwardMap, response); - chibaAdapter.shutdown(); - return true; - } - } - return false; + if (chibaAdapter.getContextParam(ChibaAdapter.SUBMISSION_RESPONSE) != null) { + Map forwardMap = (Map) chibaAdapter.removeContextParam(ChibaAdapter.SUBMISSION_RESPONSE); + if (forwardMap.containsKey(ChibaAdapter.SUBMISSION_RESPONSE_STREAM)) { + forwardResponse(forwardMap, response); + chibaAdapter.shutdown(); + return true; + } + } + return false; } private String getActionURL(HttpServletRequest request, HttpServletResponse response, boolean scripted) { - String defaultActionURL = getRequestURI(request) + agent; - String encodedDefaultActionURL = response.encodeURL(defaultActionURL); - int sessIdx = encodedDefaultActionURL.indexOf(";jsession"); - String sessionId = null; - if (sessIdx > -1) { - sessionId = encodedDefaultActionURL.substring(sessIdx); - } - String actionURL = request.getParameter(ACTIONURL_PARAM_NAME); - if (null == actionURL) { - actionURL = encodedDefaultActionURL; - } else if (null != sessionId) { - actionURL += sessionId; - } + String defaultActionURL = getRequestURI(request) + agent; + String encodedDefaultActionURL = response.encodeURL(defaultActionURL); + int sessIdx = encodedDefaultActionURL.indexOf(";jsession"); + String sessionId = null; + if (sessIdx > -1) { + sessionId = encodedDefaultActionURL.substring(sessIdx); + } + String actionURL = request.getParameter(ACTIONURL_PARAM_NAME); + if (null == actionURL) { + actionURL = encodedDefaultActionURL; + } else if (null != sessionId) { + actionURL += sessionId; + } - logger.info("actionURL: " + actionURL); - // encode the URL to allow for session id rewriting - return response.encodeURL(actionURL); + logger.info("actionURL: " + actionURL); + // encode the URL to allow for session id rewriting + return response.encodeURL(actionURL); } private String getRequestURI(HttpServletRequest request){ - StringBuffer buffer = new StringBuffer(request.getScheme()); - buffer.append("://"); - buffer.append(request.getServerName()); - buffer.append(":"); - buffer.append(request.getServerPort()) ; - buffer.append(request.getContextPath()); - return buffer.toString(); + StringBuffer buffer = new StringBuffer(request.getScheme()); + buffer.append("://"); + buffer.append(request.getServerName()); + buffer.append(":"); + buffer.append(request.getServerPort()) ; + buffer.append(request.getContextPath()); + return buffer.toString(); } private void forwardResponse(Map forwardMap, HttpServletResponse response) throws IOException { - // fetch response stream - InputStream responseStream = (InputStream) forwardMap.remove(ChibaAdapter.SUBMISSION_RESPONSE_STREAM); + // fetch response stream + InputStream responseStream = (InputStream) forwardMap.remove(ChibaAdapter.SUBMISSION_RESPONSE_STREAM); - // copy header information - Iterator iterator = forwardMap.keySet().iterator(); - while (iterator.hasNext()) { + // copy header information + Iterator iterator = forwardMap.keySet().iterator(); + while (iterator.hasNext()) { - String name = (String) iterator.next(); + String name = (String) iterator.next(); - if ("Transfer-Encoding".equalsIgnoreCase(name)) { - // Some servers (e.g. WebSphere) may set a "Transfer-Encoding" - // with the value "chunked". This may confuse the client since - // ChibaServlet output is not encoded as "chunked", so this - // header is ignored. - continue; - } - String value = (String) forwardMap.get(name); - response.setHeader(name, value); - } + if ("Transfer-Encoding".equalsIgnoreCase(name)) { + // Some servers (e.g. WebSphere) may set a "Transfer-Encoding" + // with the value "chunked". This may confuse the client since + // ChibaServlet output is not encoded as "chunked", so this + // header is ignored. + continue; + } + String value = (String) forwardMap.get(name); + response.setHeader(name, value); + } - // copy stream content - OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); - for (int b = responseStream.read(); - b > -1; - b = responseStream.read()) { - outputStream.write(b); - } + // copy stream content + OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); + for (int b = responseStream.read(); + b > -1; + b = responseStream.read()) { + outputStream.write(b); + } - // close streams - responseStream.close(); - outputStream.close(); + // close streams + responseStream.close(); + outputStream.close(); } protected void shutdown(ChibaAdapter chibaAdapter, @@ -452,15 +468,15 @@ public class ChibaServlet extends HttpServlet { HttpServletResponse response, HttpServletRequest request) throws IOException, - ServletException { - // attempt to shutdown processor - if (chibaAdapter != null) { - try { - chibaAdapter.shutdown(); - } catch (XFormsException xfe) { - xfe.printStackTrace(); - } - } + ServletException { + // attempt to shutdown processor + if (chibaAdapter != null) { + try { + chibaAdapter.shutdown(); + } catch (XFormsException xfe) { + xfe.printStackTrace(); + } + } Application.handleServletError(this.getServletContext(), request, response, diff --git a/source/web/jsp/content/xforms/dojo-generator.jsp b/source/web/jsp/content/xforms/dojo-generator.jsp new file mode 100644 index 0000000000..7d57eaf492 --- /dev/null +++ b/source/web/jsp/content/xforms/dojo-generator.jsp @@ -0,0 +1,31 @@ +<%@ page import="java.io.*, + java.util.Enumeration, + java.text.DateFormat, + java.util.Date, + org.alfresco.web.bean.content.*, + org.alfresco.web.templating.*, + org.w3c.dom.*"%> +<%@ page session="true" %> +<%@ page errorPage="error.jsp" %> +<% +String url = request.getContextPath() + request.getServletPath() + '?' + request.getQueryString() + "&xxx=bla"; +%> + + + + + + + +
+
+
+
+ + \ No newline at end of file diff --git a/source/web/jsp/content/xforms/xforms.js b/source/web/jsp/content/xforms/xforms.js new file mode 100644 index 0000000000..81bd1ca281 --- /dev/null +++ b/source/web/jsp/content/xforms/xforms.js @@ -0,0 +1,269 @@ +dojo.require("dojo.widget.DebugConsole"); +dojo.require("dojo.widget.Button"); +dojo.require("dojo.widget.validate"); +dojo.require("dojo.widget.ComboBox"); +dojo.require("dojo.widget.Checkbox"); +dojo.require("dojo.widget.Editor"); +dojo.require("dojo.widget.Spinner"); +dojo.require("dojo.html.style"); +dojo.hostenv.writeIncludes(); +dojo.addOnLoad(xforms_init); + +var bindings = {}; +var xform = null; +function xforms_init() +{ + dojo.io.bind({ + url: xforms_url, + mimetype: "text/xml", + load: function(type, data, evt) + { + xform = data.documentElement; + var model = xform.getElementsByTagName("model"); + load_bindings(model[0]); + for (var i in bindings) + { + dojo.debug("bindings["+i+"]="+bindings[i].id+", parent = "+ (bindings[i].parent ? bindings[i].parent.id : 'null')); + } + + var body = xform.getElementsByTagName("body"); + load_body(body[0], [ document.getElementById("alf-ui") ]); + }, + error: function(type, e) + { + alert("error " + type + " e = " + e); + } + }); +} + +function get_instance() +{ + var model = xform.getElementsByTagName("model")[0]; + return model.getElementsByTagName("instance")[0]; +} + +function load_bindings(bind, parent) +{ + dojo.lang.forEach(bind.childNodes, function(b) + { + if (b.nodeName.toLowerCase() == "xforms:bind") + { + var id = b.getAttribute("id"); + bindings[id] = { + id: b.getAttribute("id"), + required: b.getAttribute("xforms:required"), + nodeset: b.getAttribute("xforms:nodeset"), + type: b.getAttribute("xforms:type"), + parent: parent + } + load_bindings(b, bindings[id]); + } + }); +} + +function load_body(body, ui_element_stack) +{ + dojo.lang.forEach(body.childNodes, function(o) + { + dojo.debug(o + " nn " + o.nodeName); + switch (o.nodeName.toLowerCase()) + { + case "xforms:group": + var table = document.createElement("table"); + table.setAttribute("style", "width:100%"); + ui_element_stack[ui_element_stack.length - 1].appendChild(table); + ui_element_stack.push(table); + load_body(o, ui_element_stack); + ui_element_stack.pop(); + break; +// case "xforms:label": +// var label = document.createElement("span"); +// label.appendChild(document.createTextNode(dojo.dom.textContent(o))); +// ui_element_stack.last().appendChild(label); +// break; + case "xforms:textarea": + var row = document.createElement("tr"); + ui_element_stack[ui_element_stack.length - 1].appendChild(row); + var label = get_label_node(o); + var cell = document.createElement("td"); + row.appendChild(cell); + if (label) + cell.appendChild(document.createTextNode(dojo.dom.textContent(label))); + cell = document.createElement("td"); + row.appendChild(cell); + var nodeRef = document.createElement("div"); + nodeRef.setAttribute("style", "height: 200px"); + cell.appendChild(nodeRef); + var w = dojo.widget.createWidget("Editor", { items: ["|", "bold", "italic", "underline", "strikethrough", "|", "colorGroup", "|", "createLink", "insertImage" ] }, nodeRef); + break; + case "xforms:input": + var binding = bindings[o.getAttribute("xforms:bind")]; + var row = document.createElement("tr"); + ui_element_stack[ui_element_stack.length - 1].appendChild(row); + var label = get_label_node(o); + var cell = document.createElement("td"); + row.appendChild(cell); + var required = binding.required == "true()"; + if (label) + { + if (required) + { + var req = document.createElement("img"); + req.setAttribute("src", contextPath + "/images/icons/required_field.gif"); + req.setAttribute("style", "margin:5px"); + cell.appendChild(req); + } + cell.appendChild(document.createTextNode(dojo.dom.textContent(label))); + } + cell = document.createElement("td"); + row.appendChild(cell); + var nodeRef = document.createElement("div"); + cell.appendChild(nodeRef); + var value = get_initial_value(binding); + switch (binding.type) + { + case "date": + var dateTextBoxDiv = document.createElement("div"); + nodeRef.appendChild(dateTextBoxDiv); + var dateTextBox = dojo.widget.createWidget("DateTextBox", { format: "YYYY-MM-DD", value: value }, dateTextBoxDiv); + dateTextBox.onfocus = function(o) { + dateTextBox.hide(); dojo.debug("hiding " + o); + dateTextBox.picker.show(); + + }; + var datePickerDiv = document.createElement("div"); + nodeRef.appendChild(datePickerDiv); + dateTextBox.picker = dojo.widget.createWidget("DatePicker", { isHidden: true, value : value }, datePickerDiv); + dateTextBox.picker.hide(); + dojo.event.connect(dateTextBox.picker, + "onSetDate", + function(event) + { + dateTextBox.picker.hide(); + dateTextBox.show(); + dateTextBox.setValue(dojo.widget.DatePicker.util.toRfcDate(dateTextBox.picker.date)); + }); + break; + case "integer": + case "positiveInteger": + case "negativeInteger": + var w = dojo.widget.createWidget("SpinnerIntegerTextBox", { value: value }, nodeRef); + break; + case "double": + var w = dojo.widget.createWidget("SpinnerRealNumberTextBox", { value: value }, nodeRef); + break; + case "string": + default: + var w = dojo.widget.createWidget("ValidationTextBox", { required: required, value: value }, nodeRef); + } + break; + case "xforms:select1": + var binding = bindings[o.getAttribute("xforms:bind")]; + var row = document.createElement("tr"); + ui_element_stack[ui_element_stack.length - 1].appendChild(row); + var cell = document.createElement("td"); + row.appendChild(cell); + var label = get_label_node(o); + if (label) + cell.appendChild(document.createTextNode(dojo.dom.textContent(label))); + var nodeRef = document.createElement("div"); + cell = document.createElement("td"); + row.appendChild(cell); + var nodeRef = document.createElement("div"); + cell.appendChild(nodeRef); + var values = get_select_values(o); + for (var i in values) + { + dojo.debug("values["+ i + "] = " + values[i].id + ", " + values[i].label + ", " + values[i].value); + } + if (binding.type == "boolean") + { + var w = dojo.widget.createWidget("CheckBox", { value: "value" }, nodeRef); + } + else if (values.length <= 5) + { + for (var i in values) + { + var radio = document.createElement("input"); + radio.setAttribute("name", o.getAttribute("id")); + radio.setAttribute("type", "radio"); + radio.setAttribute("value", values[i].value); + nodeRef.appendChild(radio); + nodeRef.appendChild(document.createTextNode(values[i].label)); + } + } + else + { + var combobox = document.createElement("select"); + nodeRef.appendChild(combobox); + for (var i in values) + { + var option = document.createElement("option"); + option.appendChild(document.createTextNode(values[i].label)); + option.setAttribute("value", values[i].value); + combobox.appendChild(option); + } + } + break; + default: + load_body(o, ui_element_stack); + } + }); +} + +function get_label_node(o) +{ + var labels = o.getElementsByTagName("label"); + for (var i = 0; i < labels.length; i++) + { + dojo.debug("parent " + labels[i].parentNode.nodeName + +" o " + o.nodeName); + if (labels[i].parentNode == o) + return labels[i]; + } + return null; +} + +function get_initial_value(binding) +{ + var b = binding; + var a = []; + do + { + a.push(b); + b = b.parent + } + while (b); + var node = get_instance(); + for (var i = a.length - 1; i >= 0; i--) + { + var element_name = (a[i].nodeset.match(/^\//) + ? a[i].nodeset.replace(/^\/(.+)/, "$1") + : a[i].nodeset); + dojo.debug("locating " + a[i].nodeset + "(" + element_name + ")" + + " in " + node.nodeName); + node = node.getElementsByTagName(element_name)[0]; + dojo.debug("got node " + node.nodeName); + } + return dojo.dom.textContent(node); +} + +function get_select_values(o) +{ + var values = o.getElementsByTagName("item"); + var result = []; + for (var v in values) + { + if (values[v].getElementsByTagName) + { + var label = values[v].getElementsByTagName("label")[0]; + var value = values[v].getElementsByTagName("value")[0]; + result.push({ + id: value.getAttribute("id"), + label: dojo.dom.textContent(label), + value: dojo.dom.textContent(value) + }); + } + } + return result; +} diff --git a/source/web/scripts/ajax/dojo.js b/source/web/scripts/ajax/dojo.js index 038afe584f..68364bf877 100644 --- a/source/web/scripts/ajax/dojo.js +++ b/source/web/scripts/ajax/dojo.js @@ -1,5593 +1,97 @@ -/* - Copyright (c) 2004-2006, The Dojo Foundation - All Rights Reserved. - - Licensed under the Academic Free License version 2.1 or above OR the - modified BSD license. For more information on Dojo licensing, see: - - http://dojotoolkit.org/community/licensing.shtml -*/ - -/* - This is a compiled version of Dojo, built for deployment and not for - development. To get an editable version, please visit: - - http://dojotoolkit.org - - for documentation and information on getting the source. -*/ - -if(typeof dojo=="undefined"){ -var dj_global=this; -function dj_undef(_1,_2){ -if(_2==null){ -_2=dj_global; -} -return (typeof _2[_1]=="undefined"); -} -if(dj_undef("djConfig")){ -var djConfig={}; -} -if(dj_undef("dojo")){ -var dojo={}; -} -dojo.version={major:0,minor:3,patch:1,flag:"",revision:Number("$Rev: 4342 $".match(/[0-9]+/)[0]),toString:function(){ -with(dojo.version){ -return major+"."+minor+"."+patch+flag+" ("+revision+")"; -} -}}; -dojo.evalProp=function(_3,_4,_5){ -return (_4&&!dj_undef(_3,_4)?_4[_3]:(_5?(_4[_3]={}):undefined)); +if(typeof dojo == "undefined"){ + dj_usingBootstrap = true; //Needed for bootstrap2.js to work properly. + (function(){ + var hostEnv = "browser"; + var isRhino = false; + var isSpidermonkey = false; + var isDashboard = false; + if((typeof this["load"] == "function")&&((typeof this["Packages"] == "function")||(typeof this["Packages"] == "object"))){ + var isRhino = true; + hostEnv = "rhino"; + }else if(typeof this["load"] == "function"){ + isSpidermonkey = true; + hostEnv = "spidermonkey"; + }else if(window.widget){ + isDashboard = true; + hostEnv = "dashboard"; + } + var tmps = ["bootstrap1.js", "loader.js", "hostenv_"+hostEnv+".js"]; + + if((this["djConfig"])&&((djConfig["forceXDomain"])||(djConfig["useXDomain"]))){ + tmps.push("loader_xd.js"); + } + + if(hostEnv == "dashboard"){ + tmps.splice(1, 0, "hostenv_browser.js"); + } + + tmps.push("bootstrap2.js"); + + if((this["djConfig"])&&(djConfig["baseScriptUri"])){ + var root = djConfig["baseScriptUri"]; + }else if((this["djConfig"])&&(djConfig["baseRelativePath"])){ + var root = djConfig["baseRelativePath"]; + }else{ + var root = "./"; + if(isSpidermonkey){ + // auto-detect the base path via an exception. Hack! + try{ throw new Error(""); }catch(e){ root = e.fileName.split("dojo.js")[0]; }; + } + if(!this["djConfig"]){ + djConfig = { baseRelativePath: root }; + } + + // attempt to figure out the path to dojo if it isn't set in the config + if((this["document"])&&(this["document"]["getElementsByTagName"])){ + var scripts = document.getElementsByTagName("script"); + var rePkg = /(__package__|dojo)\.js([\?\.]|$)/i; + for(var i = 0; i < scripts.length; i++) { + var src = scripts[i].getAttribute("src"); + if(!src) { continue; } + var m = src.match(rePkg); + if(m) { + root = src.substring(0, m.index); + if(!this["djConfig"]) { djConfig = {}; } + djConfig["baseScriptUri"] = djConfig["baseRelativePath"] = root; + break; + } + } + } + } + + if((this["djConfig"])&&((djConfig["isDebug"])||(djConfig["debugAtAllCosts"]))){ + tmps.push("debug.js"); + } + + if((this["djConfig"])&&(djConfig["debugAtAllCosts"])&&(!isRhino)&&(!isDashboard)){ + tmps.push("browser_debug.js"); + } + + //Support compatibility packages. Right now this only allows setting one + //compatibility package. Might need to revisit later down the line to support + //more than one. + if((this["djConfig"])&&(djConfig["compat"])){ + tmps.push("compat/" + djConfig["compat"] + ".js"); + } + + var loaderRoot = root; + if((this["djConfig"])&&(djConfig["baseLoaderUri"])){ + loaderRoot = djConfig["baseLoaderUri"]; + } + + for(var x=0; x < tmps.length; x++){ + var spath = loaderRoot+"src/"+tmps[x]; + if(isRhino||isSpidermonkey){ + load(spath); + } else { + try { + document.write(""); + } catch (e) { + var script = document.createElement("script"); + script.src = spath; + document.getElementsByTagName("head")[0].appendChild(script); + } + } + } + })(); }; -dojo.parseObjPath=function(_6,_7,_8){ -var _9=(_7!=null?_7:dj_global); -var _a=_6.split("."); -var _b=_a.pop(); -for(var i=0,l=_a.length;i1){ -dh.modulesLoadedListeners.push(function(){ -obj[_3d](); -}); -} -} -if(dh.post_load_&&dh.inFlightCount==0&&!dh.loadNotifying){ -dh.callLoaded(); -} -}; -dojo.addOnUnload=function(obj,_40){ -var dh=dojo.hostenv; -if(arguments.length==1){ -dh.unloadListeners.push(obj); -}else{ -if(arguments.length>1){ -dh.unloadListeners.push(function(){ -obj[_40](); -}); -} -} -}; -dojo.hostenv.modulesLoaded=function(){ -if(this.post_load_){ -return; -} -if((this.loadUriStack.length==0)&&(this.getTextStack.length==0)){ -if(this.inFlightCount>0){ -dojo.debug("files still in flight!"); -return; -} -dojo.hostenv.callLoaded(); -} -}; -dojo.hostenv.callLoaded=function(){ -if(typeof setTimeout=="object"){ -setTimeout("dojo.hostenv.loaded();",0); -}else{ -dojo.hostenv.loaded(); -} -}; -dojo.hostenv.getModuleSymbols=function(_42){ -var _43=_42.split("."); -for(var i=_43.length-1;i>0;i--){ -var _45=_43.slice(0,i).join("."); -var _46=this.getModulePrefix(_45); -if(_46!=_45){ -_43.splice(0,i,_46); -break; -} -} -return _43; -}; -dojo.hostenv._global_omit_module_check=false; -dojo.hostenv.loadModule=function(_47,_48,_49){ -if(!_47){ -return; -} -_49=this._global_omit_module_check||_49; -var _4a=this.findModule(_47,false); -if(_4a){ -return _4a; -} -if(dj_undef(_47,this.loading_modules_)){ -this.addedToLoadingCount.push(_47); -} -this.loading_modules_[_47]=1; -var _4b=_47.replace(/\./g,"/")+".js"; -var _4c=this.getModuleSymbols(_47); -var _4d=((_4c[0].charAt(0)!="/")&&(!_4c[0].match(/^\w+:/))); -var _4e=_4c[_4c.length-1]; -var _4f=_47.split("."); -if(_4e=="*"){ -_47=(_4f.slice(0,-1)).join("."); -while(_4c.length){ -_4c.pop(); -_4c.push(this.pkgFileName); -_4b=_4c.join("/")+".js"; -if(_4d&&(_4b.charAt(0)=="/")){ -_4b=_4b.slice(1); -} -ok=this.loadPath(_4b,((!_49)?_47:null)); -if(ok){ -break; -} -_4c.pop(); -} -}else{ -_4b=_4c.join("/")+".js"; -_47=_4f.join("."); -var ok=this.loadPath(_4b,((!_49)?_47:null)); -if((!ok)&&(!_48)){ -_4c.pop(); -while(_4c.length){ -_4b=_4c.join("/")+".js"; -ok=this.loadPath(_4b,((!_49)?_47:null)); -if(ok){ -break; -} -_4c.pop(); -_4b=_4c.join("/")+"/"+this.pkgFileName+".js"; -if(_4d&&(_4b.charAt(0)=="/")){ -_4b=_4b.slice(1); -} -ok=this.loadPath(_4b,((!_49)?_47:null)); -if(ok){ -break; -} -} -} -if((!ok)&&(!_49)){ -dojo.raise("Could not load '"+_47+"'; last tried '"+_4b+"'"); -} -} -if(!_49&&!this["isXDomain"]){ -_4a=this.findModule(_47,false); -if(!_4a){ -dojo.raise("symbol '"+_47+"' is not defined after loading '"+_4b+"'"); -} -} -return _4a; -}; -dojo.hostenv.startPackage=function(_51){ -var _52=dojo.evalObjPath((_51.split(".").slice(0,-1)).join(".")); -this.loaded_modules_[(new String(_51)).toLowerCase()]=_52; -var _53=_51.split(/\./); -if(_53[_53.length-1]=="*"){ -_53.pop(); -} -return dojo.evalObjPath(_53.join("."),true); -}; -dojo.hostenv.findModule=function(_54,_55){ -var lmn=(new String(_54)).toLowerCase(); -if(this.loaded_modules_[lmn]){ -return this.loaded_modules_[lmn]; -} -var _57=dojo.evalObjPath(_54); -if((_54)&&(typeof _57!="undefined")&&(_57)){ -this.loaded_modules_[lmn]=_57; -return _57; -} -if(_55){ -dojo.raise("no loaded module named '"+_54+"'"); -} -return null; -}; -dojo.kwCompoundRequire=function(_58){ -var _59=_58["common"]||[]; -var _5a=(_58[dojo.hostenv.name_])?_59.concat(_58[dojo.hostenv.name_]||[]):_59.concat(_58["default"]||[]); -for(var x=0;x<_5a.length;x++){ -var _5c=_5a[x]; -if(_5c.constructor==Array){ -dojo.hostenv.loadModule.apply(dojo.hostenv,_5c); -}else{ -dojo.hostenv.loadModule(_5c); -} -} -}; -dojo.require=function(){ -dojo.hostenv.loadModule.apply(dojo.hostenv,arguments); -}; -dojo.requireIf=function(){ -if((arguments[0]===true)||(arguments[0]=="common")||(arguments[0]&&dojo.render[arguments[0]].capable)){ -var _5d=[]; -for(var i=1;i1){ -var _67=_66[1]; -var _68=_67.split("&"); -for(var x in _68){ -var sp=_68[x].split("="); -if((sp[0].length>9)&&(sp[0].substr(0,9)=="djConfig.")){ -var opt=sp[0].substr(9); -try{ -djConfig[opt]=eval(sp[1]); -} -catch(e){ -djConfig[opt]=sp[1]; -} -} -} -} -} -if(((djConfig["baseScriptUri"]=="")||(djConfig["baseRelativePath"]==""))&&(document&&document.getElementsByTagName)){ -var _6c=document.getElementsByTagName("script"); -var _6d=/(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i; -for(var i=0;i<_6c.length;i++){ -var src=_6c[i].getAttribute("src"); -if(!src){ -continue; -} -var m=src.match(_6d); -if(m){ -var _71=src.substring(0,m.index); -if(src.indexOf("bootstrap1")>-1){ -_71+="../"; -} -if(!this["djConfig"]){ -djConfig={}; -} -if(djConfig["baseScriptUri"]==""){ -djConfig["baseScriptUri"]=_71; -} -if(djConfig["baseRelativePath"]==""){ -djConfig["baseRelativePath"]=_71; -} -break; -} -} -} -var dr=dojo.render; -var drh=dojo.render.html; -var drs=dojo.render.svg; -var dua=drh.UA=navigator.userAgent; -var dav=drh.AV=navigator.appVersion; -var t=true; -var f=false; -drh.capable=t; -drh.support.builtin=t; -dr.ver=parseFloat(drh.AV); -dr.os.mac=dav.indexOf("Macintosh")>=0; -dr.os.win=dav.indexOf("Windows")>=0; -dr.os.linux=dav.indexOf("X11")>=0; -drh.opera=dua.indexOf("Opera")>=0; -drh.khtml=(dav.indexOf("Konqueror")>=0)||(dav.indexOf("Safari")>=0); -drh.safari=dav.indexOf("Safari")>=0; -var _79=dua.indexOf("Gecko"); -drh.mozilla=drh.moz=(_79>=0)&&(!drh.khtml); -if(drh.mozilla){ -drh.geckoVersion=dua.substring(_79+6,_79+14); -} -drh.ie=(document.all)&&(!drh.opera); -drh.ie50=drh.ie&&dav.indexOf("MSIE 5.0")>=0; -drh.ie55=drh.ie&&dav.indexOf("MSIE 5.5")>=0; -drh.ie60=drh.ie&&dav.indexOf("MSIE 6.0")>=0; -drh.ie70=drh.ie&&dav.indexOf("MSIE 7.0")>=0; -dojo.locale=(drh.ie?navigator.userLanguage:navigator.language).toLowerCase(); -dr.vml.capable=drh.ie; -drs.capable=f; -drs.support.plugin=f; -drs.support.builtin=f; -if(document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature("org.w3c.dom.svg","1.0")){ -drs.capable=t; -drs.support.builtin=t; -drs.support.plugin=f; -} -})(); -dojo.hostenv.startPackage("dojo.hostenv"); -dojo.render.name=dojo.hostenv.name_="browser"; -dojo.hostenv.searchIds=[]; -dojo.hostenv._XMLHTTP_PROGIDS=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"]; -dojo.hostenv.getXmlhttpObject=function(){ -var _7a=null; -var _7b=null; -try{ -_7a=new XMLHttpRequest(); -} -catch(e){ -} -if(!_7a){ -for(var i=0;i<3;++i){ -var _7d=dojo.hostenv._XMLHTTP_PROGIDS[i]; -try{ -_7a=new ActiveXObject(_7d); -} -catch(e){ -_7b=e; -} -if(_7a){ -dojo.hostenv._XMLHTTP_PROGIDS=[_7d]; -break; -} -} -} -if(!_7a){ -return dojo.raise("XMLHTTP not available",_7b); -} -return _7a; -}; -dojo.hostenv.getText=function(uri,_7f,_80){ -var _81=this.getXmlhttpObject(); -if(_7f){ -_81.onreadystatechange=function(){ -if(4==_81.readyState){ -if((!_81["status"])||((200<=_81.status)&&(300>_81.status))){ -_7f(_81.responseText); -} -} -}; -} -_81.open("GET",uri,_7f?true:false); -try{ -_81.send(null); -if(_7f){ -return null; -} -if((_81["status"])&&((200>_81.status)||(300<=_81.status))){ -throw Error("Unable to load "+uri+" status:"+_81.status); -} -} -catch(e){ -if((_80)&&(!_7f)){ -return null; -}else{ -throw e; -} -} -return _81.responseText; -}; -dojo.hostenv.defaultDebugContainerId="dojoDebug"; -dojo.hostenv._println_buffer=[]; -dojo.hostenv._println_safe=false; -dojo.hostenv.println=function(_82){ -if(!dojo.hostenv._println_safe){ -dojo.hostenv._println_buffer.push(_82); -}else{ -try{ -var _83=document.getElementById(djConfig.debugContainerId?djConfig.debugContainerId:dojo.hostenv.defaultDebugContainerId); -if(!_83){ -_83=document.getElementsByTagName("body")[0]||document.body; -} -var div=document.createElement("div"); -div.appendChild(document.createTextNode(_82)); -_83.appendChild(div); -} -catch(e){ -try{ -document.write("
"+_82+"
"); -} -catch(e2){ -window.status=_82; -} -} -} -}; -dojo.addOnLoad(function(){ -dojo.hostenv._println_safe=true; -while(dojo.hostenv._println_buffer.length>0){ -dojo.hostenv.println(dojo.hostenv._println_buffer.shift()); -} -}); -function dj_addNodeEvtHdlr(_85,_86,fp,_88){ -var _89=_85["on"+_86]||function(){ -}; -_85["on"+_86]=function(){ -fp.apply(_85,arguments); -_89.apply(_85,arguments); -}; -return true; -} -dj_addNodeEvtHdlr(window,"load",function(){ -if(arguments.callee.initialized){ -return; -} -arguments.callee.initialized=true; -var _8a=function(){ -if(dojo.render.html.ie){ -dojo.hostenv.makeWidgets(); -} -}; -if(dojo.hostenv.inFlightCount==0){ -_8a(); -dojo.hostenv.modulesLoaded(); -}else{ -dojo.addOnLoad(_8a); -} -}); -dj_addNodeEvtHdlr(window,"unload",function(){ -dojo.hostenv.unloaded(); -}); -dojo.hostenv.makeWidgets=function(){ -var _8b=[]; -if(djConfig.searchIds&&djConfig.searchIds.length>0){ -_8b=_8b.concat(djConfig.searchIds); -} -if(dojo.hostenv.searchIds&&dojo.hostenv.searchIds.length>0){ -_8b=_8b.concat(dojo.hostenv.searchIds); -} -if((djConfig.parseWidgets)||(_8b.length>0)){ -if(dojo.evalObjPath("dojo.widget.Parse")){ -var _8c=new dojo.xml.Parse(); -if(_8b.length>0){ -for(var x=0;x<_8b.length;x++){ -var _8e=document.getElementById(_8b[x]); -if(!_8e){ -continue; -} -var _8f=_8c.parseElement(_8e,null,true); -dojo.widget.getParser().createComponents(_8f); -} -}else{ -if(djConfig.parseWidgets){ -var _8f=_8c.parseElement(document.getElementsByTagName("body")[0]||document.body,null,true); -dojo.widget.getParser().createComponents(_8f); -} -} -} -} -}; -dojo.addOnLoad(function(){ -if(!dojo.render.html.ie){ -dojo.hostenv.makeWidgets(); -} -}); -try{ -if(dojo.render.html.ie){ -document.write(""); -document.write(""); -} -} -catch(e){ -} -dojo.hostenv.writeIncludes=function(){ -}; -dojo.byId=function(id,doc){ -if(id&&(typeof id=="string"||id instanceof String)){ -if(!doc){ -doc=document; -} -return doc.getElementById(id); -} -return id; -}; -(function(){ -if(typeof dj_usingBootstrap!="undefined"){ -return; -} -var _92=false; -var _93=false; -var _94=false; -if((typeof this["load"]=="function")&&((typeof this["Packages"]=="function")||(typeof this["Packages"]=="object"))){ -_92=true; -}else{ -if(typeof this["load"]=="function"){ -_93=true; -}else{ -if(window.widget){ -_94=true; -} -} -} -var _95=[]; -if((this["djConfig"])&&((djConfig["isDebug"])||(djConfig["debugAtAllCosts"]))){ -_95.push("debug.js"); -} -if((this["djConfig"])&&(djConfig["debugAtAllCosts"])&&(!_92)&&(!_94)){ -_95.push("browser_debug.js"); -} -if((this["djConfig"])&&(djConfig["compat"])){ -_95.push("compat/"+djConfig["compat"]+".js"); -} -var _96=djConfig["baseScriptUri"]; -if((this["djConfig"])&&(djConfig["baseLoaderUri"])){ -_96=djConfig["baseLoaderUri"]; -} -for(var x=0;x<_95.length;x++){ -var _98=_96+"src/"+_95[x]; -if(_92||_93){ -load(_98); -}else{ -try{ -document.write(""); -} -catch(e){ -var _99=document.createElement("script"); -_99.src=_98; -document.getElementsByTagName("head")[0].appendChild(_99); -} -} -} -})(); -dojo.fallback_locale="en"; -dojo.normalizeLocale=function(_9a){ -return _9a?_9a.toLowerCase():dojo.locale; -}; -dojo.requireLocalization=function(_9b,_9c,_9d){ -dojo.debug("EXPERIMENTAL: dojo.requireLocalization"); -var _9e=dojo.hostenv.getModuleSymbols(_9b); -var _9f=_9e.concat("nls").join("/"); -_9d=dojo.normalizeLocale(_9d); -var _a0=_9d.split("-"); -var _a1=[]; -for(var i=_a0.length;i>0;i--){ -_a1.push(_a0.slice(0,i).join("-")); -} -if(_a1[_a1.length-1]!=dojo.fallback_locale){ -_a1.push(dojo.fallback_locale); -} -var _a3=[_9b,"_nls",_9c].join("."); -var _a4=dojo.hostenv.startPackage(_a3); -dojo.hostenv.loaded_modules_[_a3]=_a4; -var _a5=false; -for(var i=_a1.length-1;i>=0;i--){ -var loc=_a1[i]; -var pkg=[_a3,loc].join("."); -var _a8=false; -if(!dojo.hostenv.findModule(pkg)){ -dojo.hostenv.loaded_modules_[pkg]=null; -var _a9=[_9f,loc,_9c].join("/")+".js"; -_a8=dojo.hostenv.loadPath(_a9,null,function(_aa){ -_a4[loc]=_aa; -if(_a5){ -for(var x in _a5){ -if(!_a4[loc][x]){ -_a4[loc][x]=_a5[x]; -} -} -} -}); -}else{ -_a8=true; -} -if(_a8&&_a4[loc]){ -_a5=_a4[loc]; -} -} -}; -dojo.provide("dojo.string.common"); -dojo.require("dojo.string"); -dojo.string.trim=function(str,wh){ -if(!str.replace){ -return str; -} -if(!str.length){ -return str; -} -var re=(wh>0)?(/^\s+/):(wh<0)?(/\s+$/):(/^\s+|\s+$/g); -return str.replace(re,""); -}; -dojo.string.trimStart=function(str){ -return dojo.string.trim(str,1); -}; -dojo.string.trimEnd=function(str){ -return dojo.string.trim(str,-1); -}; -dojo.string.repeat=function(str,_b2,_b3){ -var out=""; -for(var i=0;i<_b2;i++){ -out+=str; -if(_b3&&i<_b2-1){ -out+=_b3; -} -} -return out; -}; -dojo.string.pad=function(str,len,c,dir){ -var out=String(str); -if(!c){ -c="0"; -} -if(!dir){ -dir=1; -} -while(out.length0){ -out=c+out; -}else{ -out+=c; -} -} -return out; -}; -dojo.string.padLeft=function(str,len,c){ -return dojo.string.pad(str,len,c,1); -}; -dojo.string.padRight=function(str,len,c){ -return dojo.string.pad(str,len,c,-1); -}; -dojo.provide("dojo.string"); -dojo.require("dojo.string.common"); -dojo.provide("dojo.lang.common"); -dojo.require("dojo.lang"); -dojo.lang._mixin=function(obj,_c2){ -var _c3={}; -for(var x in _c2){ -if(typeof _c3[x]=="undefined"||_c3[x]!=_c2[x]){ -obj[x]=_c2[x]; -} -} -if(dojo.render.html.ie&&dojo.lang.isFunction(_c2["toString"])&&_c2["toString"]!=obj["toString"]){ -obj.toString=_c2.toString; -} -return obj; -}; -dojo.lang.mixin=function(obj,_c6){ -for(var i=1,l=arguments.length;i-1; -}; -dojo.lang.isObject=function(wh){ -if(typeof wh=="undefined"){ -return false; -} -return (typeof wh=="object"||wh===null||dojo.lang.isArray(wh)||dojo.lang.isFunction(wh)); -}; -dojo.lang.isArray=function(wh){ -return (wh instanceof Array||typeof wh=="array"); -}; -dojo.lang.isArrayLike=function(wh){ -if(dojo.lang.isString(wh)){ -return false; -} -if(dojo.lang.isFunction(wh)){ -return false; -} -if(dojo.lang.isArray(wh)){ -return true; -} -if(typeof wh!="undefined"&&wh&&dojo.lang.isNumber(wh.length)&&isFinite(wh.length)){ -return true; -} -return false; -}; -dojo.lang.isFunction=function(wh){ -if(!wh){ -return false; -} -return (wh instanceof Function||typeof wh=="function"); -}; -dojo.lang.isString=function(wh){ -return (wh instanceof String||typeof wh=="string"); -}; -dojo.lang.isAlien=function(wh){ -if(!wh){ -return false; -} -return !dojo.lang.isFunction()&&/\{\s*\[native code\]\s*\}/.test(String(wh)); -}; -dojo.lang.isBoolean=function(wh){ -return (wh instanceof Boolean||typeof wh=="boolean"); -}; -dojo.lang.isNumber=function(wh){ -return (wh instanceof Number||typeof wh=="number"); -}; -dojo.lang.isUndefined=function(wh){ -return ((wh==undefined)&&(typeof wh=="undefined")); -}; -dojo.provide("dojo.lang.extras"); -dojo.require("dojo.lang.common"); -dojo.lang.setTimeout=function(_e2,_e3){ -var _e4=window,argsStart=2; -if(!dojo.lang.isFunction(_e2)){ -_e4=_e2; -_e2=_e3; -_e3=arguments[2]; -argsStart++; -} -if(dojo.lang.isString(_e2)){ -_e2=_e4[_e2]; -} -var _e5=[]; -for(var i=argsStart;i=4){ -this.changeUrl=_f7; -} -} -}; -dojo.lang.extend(dojo.io.Request,{url:"",mimetype:"text/plain",method:"GET",content:undefined,transport:undefined,changeUrl:undefined,formNode:undefined,sync:false,bindSuccess:false,useCache:false,preventCache:false,load:function(_f8,_f9,evt){ -},error:function(_fb,_fc){ -},timeout:function(_fd){ -},handle:function(){ -},timeoutSeconds:0,abort:function(){ -},fromKwArgs:function(_fe){ -if(_fe["url"]){ -_fe.url=_fe.url.toString(); -} -if(_fe["formNode"]){ -_fe.formNode=dojo.byId(_fe.formNode); -} -if(!_fe["method"]&&_fe["formNode"]&&_fe["formNode"].method){ -_fe.method=_fe["formNode"].method; -} -if(!_fe["handle"]&&_fe["handler"]){ -_fe.handle=_fe.handler; -} -if(!_fe["load"]&&_fe["loaded"]){ -_fe.load=_fe.loaded; -} -if(!_fe["changeUrl"]&&_fe["changeURL"]){ -_fe.changeUrl=_fe.changeURL; -} -_fe.encoding=dojo.lang.firstValued(_fe["encoding"],djConfig["bindEncoding"],""); -_fe.sendTransport=dojo.lang.firstValued(_fe["sendTransport"],djConfig["ioSendTransport"],false); -var _ff=dojo.lang.isFunction; -for(var x=0;x0){ -dojo.io.bind(dojo.io._bindQueue.shift()); -}else{ -dojo.io._queueBindInFlight=false; -} -} -}; -dojo.io._bindQueue=[]; -dojo.io._queueBindInFlight=false; -dojo.io.argsFromMap=function(map,_110,last){ -var enc=/utf/i.test(_110||"")?encodeURIComponent:dojo.string.encodeAscii; -var _113=[]; -var _114=new Object(); -for(var name in map){ -var _116=function(elt){ -var val=enc(name)+"="+enc(elt); -_113[(last==name)?"push":"unshift"](val); -}; -if(!_114[name]){ -var _119=map[name]; -if(dojo.lang.isArray(_119)){ -dojo.lang.forEach(_119,_116); -}else{ -_116(_119); -} -} -} -return _113.join("&"); -}; -dojo.io.setIFrameSrc=function(_11a,src,_11c){ -try{ -var r=dojo.render.html; -if(!_11c){ -if(r.safari){ -_11a.location=src; -}else{ -frames[_11a.name].location=src; -} -}else{ -var idoc; -if(r.ie){ -idoc=_11a.contentWindow.document; -}else{ -if(r.safari){ -idoc=_11a.document; -}else{ -idoc=_11a.contentWindow; -} -} -if(!idoc){ -_11a.location=src; -return; -}else{ -idoc.location.replace(src); -} -} -} -catch(e){ -dojo.debug(e); -dojo.debug("setIFrameSrc: "+e); -} -}; -dojo.provide("dojo.lang.array"); -dojo.require("dojo.lang.common"); -dojo.lang.has=function(obj,name){ -try{ -return (typeof obj[name]!="undefined"); -} -catch(e){ -return false; -} -}; -dojo.lang.isEmpty=function(obj){ -if(dojo.lang.isObject(obj)){ -var tmp={}; -var _123=0; -for(var x in obj){ -if(obj[x]&&(!tmp[x])){ -_123++; -break; -} -} -return (_123==0); -}else{ -if(dojo.lang.isArrayLike(obj)||dojo.lang.isString(obj)){ -return obj.length==0; -} -} -}; -dojo.lang.map=function(arr,obj,_127){ -var _128=dojo.lang.isString(arr); -if(_128){ -arr=arr.split(""); -} -if(dojo.lang.isFunction(obj)&&(!_127)){ -_127=obj; -obj=dj_global; -}else{ -if(dojo.lang.isFunction(obj)&&_127){ -var _129=obj; -obj=_127; -_127=_129; -} -} -if(Array.map){ -var _12a=Array.map(arr,_127,obj); -}else{ -var _12a=[]; -for(var i=0;i=3){ -dojo.raise("thisObject doesn't exist!"); -} -_13e=dj_global; -} -var _140=[]; -for(var i=0;i/gm,">").replace(/"/gm,"""); -if(!_183){ -str=str.replace(/'/gm,"'"); -} -return str; -}; -dojo.string.escapeSql=function(str){ -return str.replace(/'/gm,"''"); -}; -dojo.string.escapeRegExp=function(str){ -return str.replace(/\\/gm,"\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm,"\\$1"); -}; -dojo.string.escapeJavaScript=function(str){ -return str.replace(/(["'\f\b\n\t\r])/gm,"\\$1"); -}; -dojo.string.escapeString=function(str){ -return ("\""+str.replace(/(["\\])/g,"\\$1")+"\"").replace(/[\f]/g,"\\f").replace(/[\b]/g,"\\b").replace(/[\n]/g,"\\n").replace(/[\t]/g,"\\t").replace(/[\r]/g,"\\r"); -}; -dojo.string.summary=function(str,len){ -if(!len||str.length<=len){ -return str; -}else{ -return str.substring(0,len).replace(/\.+$/,"")+"..."; -} -}; -dojo.string.endsWith=function(str,end,_18c){ -if(_18c){ -str=str.toLowerCase(); -end=end.toLowerCase(); -} -if((str.length-end.length)<0){ -return false; -} -return str.lastIndexOf(end)==str.length-end.length; -}; -dojo.string.endsWithAny=function(str){ -for(var i=1;i-1){ -return true; -} -} -return false; -}; -dojo.string.normalizeNewlines=function(text,_197){ -if(_197=="\n"){ -text=text.replace(/\r\n/g,"\n"); -text=text.replace(/\r/g,"\n"); -}else{ -if(_197=="\r"){ -text=text.replace(/\r\n/g,"\r"); -text=text.replace(/\n/g,"\r"); -}else{ -text=text.replace(/([^\r])\n/g,"$1\r\n"); -text=text.replace(/\r([^\n])/g,"\r\n$1"); -} -} -return text; -}; -dojo.string.splitEscaped=function(str,_199){ -var _19a=[]; -for(var i=0,prevcomma=0;i5)&&(_1a1[x].indexOf("dojo-")>=0)){ -return "dojo:"+_1a1[x].substr(5).toLowerCase(); -} -} -} -} -} -return _19e.toLowerCase(); -}; -dojo.dom.getUniqueId=function(){ -do{ -var id="dj_unique_"+(++arguments.callee._idIncrement); -}while(document.getElementById(id)); -return id; -}; -dojo.dom.getUniqueId._idIncrement=0; -dojo.dom.firstElement=dojo.dom.getFirstChildElement=function(_1a4,_1a5){ -var node=_1a4.firstChild; -while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE){ -node=node.nextSibling; -} -if(_1a5&&node&&node.tagName&&node.tagName.toLowerCase()!=_1a5.toLowerCase()){ -node=dojo.dom.nextElement(node,_1a5); -} -return node; -}; -dojo.dom.lastElement=dojo.dom.getLastChildElement=function(_1a7,_1a8){ -var node=_1a7.lastChild; -while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE){ -node=node.previousSibling; -} -if(_1a8&&node&&node.tagName&&node.tagName.toLowerCase()!=_1a8.toLowerCase()){ -node=dojo.dom.prevElement(node,_1a8); -} -return node; -}; -dojo.dom.nextElement=dojo.dom.getNextSiblingElement=function(node,_1ab){ -if(!node){ -return null; -} -do{ -node=node.nextSibling; -}while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE); -if(node&&_1ab&&_1ab.toLowerCase()!=node.tagName.toLowerCase()){ -return dojo.dom.nextElement(node,_1ab); -} -return node; -}; -dojo.dom.prevElement=dojo.dom.getPreviousSiblingElement=function(node,_1ad){ -if(!node){ -return null; -} -if(_1ad){ -_1ad=_1ad.toLowerCase(); -} -do{ -node=node.previousSibling; -}while(node&&node.nodeType!=dojo.dom.ELEMENT_NODE); -if(node&&_1ad&&_1ad.toLowerCase()!=node.tagName.toLowerCase()){ -return dojo.dom.prevElement(node,_1ad); -} -return node; -}; -dojo.dom.moveChildren=function(_1ae,_1af,trim){ -var _1b1=0; -if(trim){ -while(_1ae.hasChildNodes()&&_1ae.firstChild.nodeType==dojo.dom.TEXT_NODE){ -_1ae.removeChild(_1ae.firstChild); -} -while(_1ae.hasChildNodes()&&_1ae.lastChild.nodeType==dojo.dom.TEXT_NODE){ -_1ae.removeChild(_1ae.lastChild); -} -} -while(_1ae.hasChildNodes()){ -_1af.appendChild(_1ae.firstChild); -_1b1++; -} -return _1b1; -}; -dojo.dom.copyChildren=function(_1b2,_1b3,trim){ -var _1b5=_1b2.cloneNode(true); -return this.moveChildren(_1b5,_1b3,trim); -}; -dojo.dom.removeChildren=function(node){ -var _1b7=node.childNodes.length; -while(node.hasChildNodes()){ -node.removeChild(node.firstChild); -} -return _1b7; -}; -dojo.dom.replaceChildren=function(node,_1b9){ -dojo.dom.removeChildren(node); -node.appendChild(_1b9); -}; -dojo.dom.removeNode=function(node){ -if(node&&node.parentNode){ -return node.parentNode.removeChild(node); -} -}; -dojo.dom.getAncestors=function(node,_1bc,_1bd){ -var _1be=[]; -var _1bf=dojo.lang.isFunction(_1bc); -while(node){ -if(!_1bf||_1bc(node)){ -_1be.push(node); -} -if(_1bd&&_1be.length>0){ -return _1be[0]; -} -node=node.parentNode; -} -if(_1bd){ -return null; -} -return _1be; -}; -dojo.dom.getAncestorsByTag=function(node,tag,_1c2){ -tag=tag.toLowerCase(); -return dojo.dom.getAncestors(node,function(el){ -return ((el.tagName)&&(el.tagName.toLowerCase()==tag)); -},_1c2); -}; -dojo.dom.getFirstAncestorByTag=function(node,tag){ -return dojo.dom.getAncestorsByTag(node,tag,true); -}; -dojo.dom.isDescendantOf=function(node,_1c7,_1c8){ -if(_1c8&&node){ -node=node.parentNode; -} -while(node){ -if(node==_1c7){ -return true; -} -node=node.parentNode; -} -return false; -}; -dojo.dom.innerXML=function(node){ -if(node.innerXML){ -return node.innerXML; -}else{ -if(node.xml){ -return node.xml; -}else{ -if(typeof XMLSerializer!="undefined"){ -return (new XMLSerializer()).serializeToString(node); -} -} -} -}; -dojo.dom.createDocument=function(){ -var doc=null; -if(!dj_undef("ActiveXObject")){ -var _1cb=["MSXML2","Microsoft","MSXML","MSXML3"]; -for(var i=0;i<_1cb.length;i++){ -try{ -doc=new ActiveXObject(_1cb[i]+".XMLDOM"); -} -catch(e){ -} -if(doc){ -break; -} -} -}else{ -if((document.implementation)&&(document.implementation.createDocument)){ -doc=document.implementation.createDocument("","",null); -} -} -return doc; -}; -dojo.dom.createDocumentFromText=function(str,_1ce){ -if(!_1ce){ -_1ce="text/xml"; -} -if(!dj_undef("DOMParser")){ -var _1cf=new DOMParser(); -return _1cf.parseFromString(str,_1ce); -}else{ -if(!dj_undef("ActiveXObject")){ -var _1d0=dojo.dom.createDocument(); -if(_1d0){ -_1d0.async=false; -_1d0.loadXML(str); -return _1d0; -}else{ -dojo.debug("toXml didn't work?"); -} -}else{ -if(document.createElement){ -var tmp=document.createElement("xml"); -tmp.innerHTML=str; -if(document.implementation&&document.implementation.createDocument){ -var _1d2=document.implementation.createDocument("foo","",null); -for(var i=0;i"); -} -} -catch(e){ -} -if(dojo.render.html.opera){ -dojo.debug("Opera is not supported with dojo.undo.browser, so back/forward detection will not work."); -} -dojo.undo.browser={initialHref:window.location.href,initialHash:window.location.hash,moveForward:false,historyStack:[],forwardStack:[],historyIframe:null,bookmarkAnchor:null,locationTimer:null,setInitialState:function(args){ -this.initialState={"url":this.initialHref,"kwArgs":args,"urlHash":this.initialHash}; -},addToHistory:function(args){ -var hash=null; -if(!this.historyIframe){ -this.historyIframe=window.frames["djhistory"]; -} -if(!this.bookmarkAnchor){ -this.bookmarkAnchor=document.createElement("a"); -(document.body||document.getElementsByTagName("body")[0]).appendChild(this.bookmarkAnchor); -this.bookmarkAnchor.style.display="none"; -} -if((!args["changeUrl"])||(dojo.render.html.ie)){ -var url=dojo.hostenv.getBaseScriptUri()+"iframe_history.html?"+(new Date()).getTime(); -this.moveForward=true; -dojo.io.setIFrameSrc(this.historyIframe,url,false); -} -if(args["changeUrl"]){ -this.changingUrl=true; -hash="#"+((args["changeUrl"]!==true)?args["changeUrl"]:(new Date()).getTime()); -setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;",1); -this.bookmarkAnchor.href=hash; -if(dojo.render.html.ie){ -var _1f4=args["back"]||args["backButton"]||args["handle"]; -var tcb=function(_1f6){ -if(window.location.hash!=""){ -setTimeout("window.location.href = '"+hash+"';",1); -} -_1f4.apply(this,[_1f6]); -}; -if(args["back"]){ -args.back=tcb; -}else{ -if(args["backButton"]){ -args.backButton=tcb; -}else{ -if(args["handle"]){ -args.handle=tcb; -} -} -} -this.forwardStack=[]; -var _1f7=args["forward"]||args["forwardButton"]||args["handle"]; -var tfw=function(_1f9){ -if(window.location.hash!=""){ -window.location.href=hash; -} -if(_1f7){ -_1f7.apply(this,[_1f9]); -} -}; -if(args["forward"]){ -args.forward=tfw; -}else{ -if(args["forwardButton"]){ -args.forwardButton=tfw; -}else{ -if(args["handle"]){ -args.handle=tfw; -} -} -} -}else{ -if(dojo.render.html.moz){ -if(!this.locationTimer){ -this.locationTimer=setInterval("dojo.undo.browser.checkLocation();",200); -} -} -} -} -this.historyStack.push({"url":url,"kwArgs":args,"urlHash":hash}); -},checkLocation:function(){ -if(!this.changingUrl){ -var hsl=this.historyStack.length; -if((window.location.hash==this.initialHash||window.location.href==this.initialHref)&&(hsl==1)){ -this.handleBackButton(); -return; -} -if(this.forwardStack.length>0){ -if(this.forwardStack[this.forwardStack.length-1].urlHash==window.location.hash){ -this.handleForwardButton(); -return; -} -} -if((hsl>=2)&&(this.historyStack[hsl-2])){ -if(this.historyStack[hsl-2].urlHash==window.location.hash){ -this.handleBackButton(); -return; -} -} -} -},iframeLoaded:function(evt,_1fc){ -if(!dojo.render.html.opera){ -var _1fd=this._getUrlQuery(_1fc.href); -if(_1fd==null){ -if(this.historyStack.length==1){ -this.handleBackButton(); -} -return; -} -if(this.moveForward){ -this.moveForward=false; -return; -} -if(this.historyStack.length>=2&&_1fd==this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){ -this.handleBackButton(); -}else{ -if(this.forwardStack.length>0&&_1fd==this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){ -this.handleForwardButton(); -} -} -} -},handleBackButton:function(){ -var _1fe=this.historyStack.pop(); -if(!_1fe){ -return; -} -var last=this.historyStack[this.historyStack.length-1]; -if(!last&&this.historyStack.length==0){ -last=this.initialState; -} -if(last){ -if(last.kwArgs["back"]){ -last.kwArgs["back"](); -}else{ -if(last.kwArgs["backButton"]){ -last.kwArgs["backButton"](); -}else{ -if(last.kwArgs["handle"]){ -last.kwArgs.handle("back"); -} -} -} -} -this.forwardStack.push(_1fe); -},handleForwardButton:function(){ -var last=this.forwardStack.pop(); -if(!last){ -return; -} -if(last.kwArgs["forward"]){ -last.kwArgs.forward(); -}else{ -if(last.kwArgs["forwardButton"]){ -last.kwArgs.forwardButton(); -}else{ -if(last.kwArgs["handle"]){ -last.kwArgs.handle("forward"); -} -} -} -this.historyStack.push(last); -},_getUrlQuery:function(url){ -var _202=url.split("?"); -if(_202.length<2){ -return null; -}else{ -return _202[1]; -} -}}; -dojo.provide("dojo.io.BrowserIO"); -dojo.require("dojo.io"); -dojo.require("dojo.lang.array"); -dojo.require("dojo.lang.func"); -dojo.require("dojo.string.extras"); -dojo.require("dojo.dom"); -dojo.require("dojo.undo.browser"); -dojo.io.checkChildrenForFile=function(node){ -var _204=false; -var _205=node.getElementsByTagName("input"); -dojo.lang.forEach(_205,function(_206){ -if(_204){ -return; -} -if(_206.getAttribute("type")=="file"){ -_204=true; -} -}); -return _204; -}; -dojo.io.formHasFile=function(_207){ -return dojo.io.checkChildrenForFile(_207); -}; -dojo.io.updateNode=function(node,_209){ -node=dojo.byId(node); -var args=_209; -if(dojo.lang.isString(_209)){ -args={url:_209}; -} -args.mimetype="text/html"; -args.load=function(t,d,e){ -while(node.firstChild){ -if(dojo["event"]){ -try{ -dojo.event.browser.clean(node.firstChild); -} -catch(e){ -} -} -node.removeChild(node.firstChild); -} -node.innerHTML=d; -}; -dojo.io.bind(args); -}; -dojo.io.formFilter=function(node){ -var type=(node.type||"").toLowerCase(); -return !node.disabled&&node.name&&!dojo.lang.inArray(type,["file","submit","image","reset","button"]); -}; -dojo.io.encodeForm=function(_210,_211,_212){ -if((!_210)||(!_210.tagName)||(!_210.tagName.toLowerCase()=="form")){ -dojo.raise("Attempted to encode a non-form element."); -} -if(!_212){ -_212=dojo.io.formFilter; -} -var enc=/utf/i.test(_211||"")?encodeURIComponent:dojo.string.encodeAscii; -var _214=[]; -for(var i=0;i<_210.elements.length;i++){ -var elm=_210.elements[i]; -if(!elm||elm.tagName.toLowerCase()=="fieldset"||!_212(elm)){ -continue; -} -var name=enc(elm.name); -var type=elm.type.toLowerCase(); -if(type=="select-multiple"){ -for(var j=0;j=200)&&(http.status<300))||(http.status==304)||(location.protocol=="file:"&&(http.status==0||http.status==undefined))||(location.protocol=="chrome:"&&(http.status==0||http.status==undefined))){ -var ret; -if(_23b.method.toLowerCase()=="head"){ -var _241=http.getAllResponseHeaders(); -ret={}; -ret.toString=function(){ -return _241; -}; -var _242=_241.split(/[\r\n]+/g); -for(var i=0;i<_242.length;i++){ -var pair=_242[i].match(/^([^:]+)\s*:\s*(.+)$/i); -if(pair){ -ret[pair[1]]=pair[2]; -} -} -}else{ -if(_23b.mimetype=="text/javascript"){ -try{ -ret=dj_eval(http.responseText); -} -catch(e){ -dojo.debug(e); -dojo.debug(http.responseText); -ret=null; -} -}else{ -if(_23b.mimetype=="text/json"){ -try{ -ret=dj_eval("("+http.responseText+")"); -} -catch(e){ -dojo.debug(e); -dojo.debug(http.responseText); -ret=false; -} -}else{ -if((_23b.mimetype=="application/xml")||(_23b.mimetype=="text/xml")){ -ret=http.responseXML; -if(!ret||typeof ret=="string"||!http.getResponseHeader("Content-Type")){ -ret=dojo.dom.createDocumentFromText(http.responseText); -} -}else{ -ret=http.responseText; -} -} -} -} -if(_23f){ -addToCache(url,_23e,_23b.method,http); -} -_23b[(typeof _23b.load=="function")?"load":"handle"]("load",ret,http,_23b); -}else{ -var _245=new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText); -_23b[(typeof _23b.error=="function")?"error":"handle"]("error",_245,http,_23b); -} -} -function setHeaders(http,_247){ -if(_247["headers"]){ -for(var _248 in _247["headers"]){ -if(_248.toLowerCase()=="content-type"&&!_247["contentType"]){ -_247["contentType"]=_247["headers"][_248]; -}else{ -http.setRequestHeader(_248,_247["headers"][_248]); -} -} -} -} -this.inFlight=[]; -this.inFlightTimer=null; -this.startWatchingInFlight=function(){ -if(!this.inFlightTimer){ -this.inFlightTimer=setInterval("dojo.io.XMLHTTPTransport.watchInFlight();",10); -} -}; -this.watchInFlight=function(){ -var now=null; -for(var x=this.inFlight.length-1;x>=0;x--){ -var tif=this.inFlight[x]; -if(!tif){ -this.inFlight.splice(x,1); -continue; -} -if(4==tif.http.readyState){ -this.inFlight.splice(x,1); -doLoad(tif.req,tif.http,tif.url,tif.query,tif.useCache); -}else{ -if(tif.startTime){ -if(!now){ -now=(new Date()).getTime(); -} -if(tif.startTime+(tif.req.timeoutSeconds*1000)-1){ -dojo.debug("Warning: dojo.io.bind: stripping hash values from url:",url); -url=url.split("#")[0]; -} -if(_24e["file"]){ -_24e.method="post"; -} -if(!_24e["method"]){ -_24e.method="get"; -} -if(_24e.method.toLowerCase()=="get"){ -_24e.multipart=false; -}else{ -if(_24e["file"]){ -_24e.multipart=true; -}else{ -if(!_24e["multipart"]){ -_24e.multipart=false; -} -} -} -if(_24e["backButton"]||_24e["back"]||_24e["changeUrl"]){ -dojo.undo.browser.addToHistory(_24e); -} -var _253=_24e["content"]||{}; -if(_24e.sendTransport){ -_253["dojo.transport"]="xmlhttp"; -} -do{ -if(_24e.postContent){ -_250=_24e.postContent; -break; -} -if(_253){ -_250+=dojo.io.argsFromMap(_253,_24e.encoding); -} -if(_24e.method.toLowerCase()=="get"||!_24e.multipart){ -break; -} -var t=[]; -if(_250.length){ -var q=_250.split("&"); -for(var i=0;i-1?"&":"?")+_250; -} -if(_25a){ -_260+=(dojo.string.endsWithAny(_260,"?","&")?"":(_260.indexOf("?")>-1?"&":"?"))+"dojo.preventCache="+new Date().valueOf(); -} -http.open(_24e.method.toUpperCase(),_260,_259); -setHeaders(http,_24e); -try{ -http.send(null); -} -catch(e){ -if(typeof http.abort=="function"){ -http.abort(); -} -doLoad(_24e,{status:404},url,_250,_25b); -} -} -if(!_259){ -doLoad(_24e,http,url,_250,_25b); -} -_24e.abort=function(){ -return http.abort(); -}; -return; -}; -dojo.io.transports.addTransport("XMLHTTPTransport"); -}; -dojo.provide("dojo.event"); -dojo.require("dojo.lang.array"); -dojo.require("dojo.lang.extras"); -dojo.require("dojo.lang.func"); -dojo.event=new function(){ -this.canTimeout=dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]); -function interpolateArgs(args,_262){ -var dl=dojo.lang; -var ao={srcObj:dj_global,srcFunc:null,adviceObj:dj_global,adviceFunc:null,aroundObj:null,aroundFunc:null,adviceType:(args.length>2)?args[0]:"after",precedence:"last",once:false,delay:null,rate:0,adviceMsg:false}; -switch(args.length){ -case 0: -return; -case 1: -return; -case 2: -ao.srcFunc=args[0]; -ao.adviceFunc=args[1]; -break; -case 3: -if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){ -ao.adviceType="after"; -ao.srcObj=args[0]; -ao.srcFunc=args[1]; -ao.adviceFunc=args[2]; -}else{ -if((dl.isString(args[1]))&&(dl.isString(args[2]))){ -ao.srcFunc=args[1]; -ao.adviceFunc=args[2]; -}else{ -if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){ -ao.adviceType="after"; -ao.srcObj=args[0]; -ao.srcFunc=args[1]; -var _265=dl.nameAnonFunc(args[2],ao.adviceObj,_262); -ao.adviceFunc=_265; -}else{ -if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){ -ao.adviceType="after"; -ao.srcObj=dj_global; -var _265=dl.nameAnonFunc(args[0],ao.srcObj,_262); -ao.srcFunc=_265; -ao.adviceObj=args[1]; -ao.adviceFunc=args[2]; -} -} -} -} -break; -case 4: -if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){ -ao.adviceType="after"; -ao.srcObj=args[0]; -ao.srcFunc=args[1]; -ao.adviceObj=args[2]; -ao.adviceFunc=args[3]; -}else{ -if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){ -ao.adviceType=args[0]; -ao.srcObj=dj_global; -ao.srcFunc=args[1]; -ao.adviceObj=args[2]; -ao.adviceFunc=args[3]; -}else{ -if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){ -ao.adviceType=args[0]; -ao.srcObj=dj_global; -var _265=dl.nameAnonFunc(args[1],dj_global,_262); -ao.srcFunc=_265; -ao.adviceObj=args[2]; -ao.adviceFunc=args[3]; -}else{ -if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){ -ao.srcObj=args[1]; -ao.srcFunc=args[2]; -var _265=dl.nameAnonFunc(args[3],dj_global,_262); -ao.adviceObj=dj_global; -ao.adviceFunc=_265; -}else{ -if(dl.isObject(args[1])){ -ao.srcObj=args[1]; -ao.srcFunc=args[2]; -ao.adviceObj=dj_global; -ao.adviceFunc=args[3]; -}else{ -if(dl.isObject(args[2])){ -ao.srcObj=dj_global; -ao.srcFunc=args[1]; -ao.adviceObj=args[2]; -ao.adviceFunc=args[3]; -}else{ -ao.srcObj=ao.adviceObj=ao.aroundObj=dj_global; -ao.srcFunc=args[1]; -ao.adviceFunc=args[2]; -ao.aroundFunc=args[3]; -} -} -} -} -} -} -break; -case 6: -ao.srcObj=args[1]; -ao.srcFunc=args[2]; -ao.adviceObj=args[3]; -ao.adviceFunc=args[4]; -ao.aroundFunc=args[5]; -ao.aroundObj=dj_global; -break; -default: -ao.srcObj=args[1]; -ao.srcFunc=args[2]; -ao.adviceObj=args[3]; -ao.adviceFunc=args[4]; -ao.aroundObj=args[5]; -ao.aroundFunc=args[6]; -ao.once=args[7]; -ao.delay=args[8]; -ao.rate=args[9]; -ao.adviceMsg=args[10]; -break; -} -if(dl.isFunction(ao.aroundFunc)){ -var _265=dl.nameAnonFunc(ao.aroundFunc,ao.aroundObj,_262); -ao.aroundFunc=_265; -} -if(dl.isFunction(ao.srcFunc)){ -ao.srcFunc=dl.getNameInObj(ao.srcObj,ao.srcFunc); -} -if(dl.isFunction(ao.adviceFunc)){ -ao.adviceFunc=dl.getNameInObj(ao.adviceObj,ao.adviceFunc); -} -if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){ -ao.aroundFunc=dl.getNameInObj(ao.aroundObj,ao.aroundFunc); -} -if(!ao.srcObj){ -dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc); -} -if(!ao.adviceObj){ -dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc); -} -return ao; -} -this.connect=function(){ -if(arguments.length==1){ -var ao=arguments[0]; -}else{ -var ao=interpolateArgs(arguments,true); -} -if(dojo.lang.isArray(ao.srcObj)&&ao.srcObj!=""){ -var _267={}; -for(var x in ao){ -_267[x]=ao[x]; -} -var mjps=[]; -dojo.lang.forEach(ao.srcObj,function(src){ -if((dojo.render.html.capable)&&(dojo.lang.isString(src))){ -src=dojo.byId(src); -} -_267.srcObj=src; -mjps.push(dojo.event.connect.call(dojo.event,_267)); -}); -return mjps; -} -var mjp=dojo.event.MethodJoinPoint.getForMethod(ao.srcObj,ao.srcFunc); -if(ao.adviceFunc){ -var mjp2=dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj,ao.adviceFunc); -} -mjp.kwAddAdvice(ao); -return mjp; -}; -this.log=function(a1,a2){ -var _26f; -if((arguments.length==1)&&(typeof a1=="object")){ -_26f=a1; -}else{ -_26f={srcObj:a1,srcFunc:a2}; -} -_26f.adviceFunc=function(){ -var _270=[]; -for(var x=0;x=this.jp_.around.length){ -return this.jp_.object[this.jp_.methodname].apply(this.jp_.object,this.args); -}else{ -var ti=this.jp_.around[this.around_index]; -var mobj=ti[0]||dj_global; -var meth=ti[1]; -return mobj[meth].call(mobj,this); -} -}; -dojo.event.MethodJoinPoint=function(obj,_287){ -this.object=obj||dj_global; -this.methodname=_287; -this.methodfunc=this.object[_287]; -this.before=[]; -this.after=[]; -this.around=[]; -}; -dojo.event.MethodJoinPoint.getForMethod=function(obj,_289){ -if(!obj){ -obj=dj_global; -} -if(!obj[_289]){ -obj[_289]=function(){ -}; -if(!obj[_289]){ -dojo.raise("Cannot set do-nothing method on that object "+_289); -} -}else{ -if((!dojo.lang.isFunction(obj[_289]))&&(!dojo.lang.isAlien(obj[_289]))){ -return null; -} -} -var _28a=_289+"$joinpoint"; -var _28b=_289+"$joinpoint$method"; -var _28c=obj[_28a]; -if(!_28c){ -var _28d=false; -if(dojo.event["browser"]){ -if((obj["attachEvent"])||(obj["nodeType"])||(obj["addEventListener"])){ -_28d=true; -dojo.event.browser.addClobberNodeAttrs(obj,[_28a,_28b,_289]); -} -} -var _28e=obj[_289].length; -obj[_28b]=obj[_289]; -_28c=obj[_28a]=new dojo.event.MethodJoinPoint(obj,_28b); -obj[_289]=function(){ -var args=[]; -if((_28d)&&(!arguments.length)){ -var evt=null; -try{ -if(obj.ownerDocument){ -evt=obj.ownerDocument.parentWindow.event; -}else{ -if(obj.documentElement){ -evt=obj.documentElement.ownerDocument.parentWindow.event; -}else{ -evt=window.event; -} -} -} -catch(e){ -evt=window.event; -} -if(evt){ -args.push(dojo.event.browser.fixEvent(evt,this)); -} -}else{ -for(var x=0;x0){ -dojo.lang.forEach(this.before,_296); -} -var _2a6; -if(this.around.length>0){ -var mi=new dojo.event.MethodInvocation(this,obj,args); -_2a6=mi.proceed(); -}else{ -if(this.methodfunc){ -_2a6=this.object[this.methodname].apply(this.object,args); -} -} -if(this.after.length>0){ -dojo.lang.forEach(this.after,_296); -} -return (this.methodfunc)?_2a6:null; -},getArr:function(kind){ -var arr=this.after; -if((typeof kind=="string")&&(kind.indexOf("before")!=-1)){ -arr=this.before; -}else{ -if(kind=="around"){ -arr=this.around; -} -} -return arr; -},kwAddAdvice:function(args){ -this.addAdvice(args["adviceObj"],args["adviceFunc"],args["aroundObj"],args["aroundFunc"],args["adviceType"],args["precedence"],args["once"],args["delay"],args["rate"],args["adviceMsg"]); -},addAdvice:function(_2ab,_2ac,_2ad,_2ae,_2af,_2b0,once,_2b2,rate,_2b4){ -var arr=this.getArr(_2af); -if(!arr){ -dojo.raise("bad this: "+this); -} -var ao=[_2ab,_2ac,_2ad,_2ae,_2b2,rate,_2b4]; -if(once){ -if(this.hasAdvice(_2ab,_2ac,_2af,arr)>=0){ -return; -} -} -if(_2b0=="first"){ -arr.unshift(ao); -}else{ -arr.push(ao); -} -},hasAdvice:function(_2b7,_2b8,_2b9,arr){ -if(!arr){ -arr=this.getArr(_2b9); -} -var ind=-1; -for(var x=0;x=0;i=i-1){ -var el=na[i]; -if(el["__clobberAttrs__"]){ -for(var j=0;j0){ -this.duration=_319; -} -if(_31c){ -this.repeatCount=_31c; -} -if(rate){ -this.rate=rate; -} -if(_318){ -this.handler=_318.handler; -this.beforeBegin=_318.beforeBegin; -this.onBegin=_318.onBegin; -this.onEnd=_318.onEnd; -this.onPlay=_318.onPlay; -this.onPause=_318.onPause; -this.onStop=_318.onStop; -this.onAnimate=_318.onAnimate; -} -if(_31b&&dojo.lang.isFunction(_31b)){ -this.easing=_31b; -} -}; -dojo.inherits(dojo.lfx.Animation,dojo.lfx.IAnimation); -dojo.lang.extend(dojo.lfx.Animation,{_startTime:null,_endTime:null,_timer:null,_percent:0,_startRepeatCount:0,play:function(_31e,_31f){ -if(_31f){ -clearTimeout(this._timer); -this._active=false; -this._paused=false; -this._percent=0; -}else{ -if(this._active&&!this._paused){ -return this; -} -} -this.fire("handler",["beforeBegin"]); -this.fire("beforeBegin"); -if(_31e>0){ -setTimeout(dojo.lang.hitch(this,function(){ -this.play(null,_31f); -}),_31e); -return this; -} -this._startTime=new Date().valueOf(); -if(this._paused){ -this._startTime-=(this.duration*this._percent/100); -} -this._endTime=this._startTime+this.duration; -this._active=true; -this._paused=false; -var step=this._percent/100; -var _321=this.curve.getValue(step); -if(this._percent==0){ -if(!this._startRepeatCount){ -this._startRepeatCount=this.repeatCount; -} -this.fire("handler",["begin",_321]); -this.fire("onBegin",[_321]); -} -this.fire("handler",["play",_321]); -this.fire("onPlay",[_321]); -this._cycle(); -return this; -},pause:function(){ -clearTimeout(this._timer); -if(!this._active){ -return this; -} -this._paused=true; -var _322=this.curve.getValue(this._percent/100); -this.fire("handler",["pause",_322]); -this.fire("onPause",[_322]); -return this; -},gotoPercent:function(pct,_324){ -clearTimeout(this._timer); -this._active=true; -this._paused=true; -this._percent=pct; -if(_324){ -this.play(); -} -},stop:function(_325){ -clearTimeout(this._timer); -var step=this._percent/100; -if(_325){ -step=1; -} -var _327=this.curve.getValue(step); -this.fire("handler",["stop",_327]); -this.fire("onStop",[_327]); -this._active=false; -this._paused=false; -return this; -},status:function(){ -if(this._active){ -return this._paused?"paused":"playing"; -}else{ -return "stopped"; -} -},_cycle:function(){ -clearTimeout(this._timer); -if(this._active){ -var curr=new Date().valueOf(); -var step=(curr-this._startTime)/(this._endTime-this._startTime); -if(step>=1){ -step=1; -this._percent=100; -}else{ -this._percent=step*100; -} -if((this.easing)&&(dojo.lang.isFunction(this.easing))){ -step=this.easing(step); -} -var _32a=this.curve.getValue(step); -this.fire("handler",["animate",_32a]); -this.fire("onAnimate",[_32a]); -if(step<1){ -this._timer=setTimeout(dojo.lang.hitch(this,"_cycle"),this.rate); -}else{ -this._active=false; -this.fire("handler",["end"]); -this.fire("onEnd"); -if(this.repeatCount>0){ -this.repeatCount--; -this.play(null,true); -}else{ -if(this.repeatCount==-1){ -this.play(null,true); -}else{ -if(this._startRepeatCount){ -this.repeatCount=this._startRepeatCount; -this._startRepeatCount=0; -} -} -} -} -} -return this; -}}); -dojo.lfx.Combine=function(){ -dojo.lfx.IAnimation.call(this); -this._anims=[]; -this._animsEnded=0; -var _32b=arguments; -if(_32b.length==1&&(dojo.lang.isArray(_32b[0])||dojo.lang.isArrayLike(_32b[0]))){ -_32b=_32b[0]; -} -var _32c=this; -dojo.lang.forEach(_32b,function(anim){ -_32c._anims.push(anim); -var _32e=(anim["onEnd"])?dojo.lang.hitch(anim,"onEnd"):function(){ -}; -anim.onEnd=function(){ -_32e(); -_32c._onAnimsEnded(); -}; -}); -}; -dojo.inherits(dojo.lfx.Combine,dojo.lfx.IAnimation); -dojo.lang.extend(dojo.lfx.Combine,{_animsEnded:0,play:function(_32f,_330){ -if(!this._anims.length){ -return this; -} -this.fire("beforeBegin"); -if(_32f>0){ -setTimeout(dojo.lang.hitch(this,function(){ -this.play(null,_330); -}),_32f); -return this; -} -if(_330||this._anims[0].percent==0){ -this.fire("onBegin"); -} -this.fire("onPlay"); -this._animsCall("play",null,_330); -return this; -},pause:function(){ -this.fire("onPause"); -this._animsCall("pause"); -return this; -},stop:function(_331){ -this.fire("onStop"); -this._animsCall("stop",_331); -return this; -},_onAnimsEnded:function(){ -this._animsEnded++; -if(this._animsEnded>=this._anims.length){ -this.fire("onEnd"); -} -return this; -},_animsCall:function(_332){ -var args=[]; -if(arguments.length>1){ -for(var i=1;i0){ -setTimeout(dojo.lang.hitch(this,function(){ -this.play(null,_33e); -}),_33d); -return this; -} -if(_33f){ -if(this._currAnim==0){ -this.fire("handler",["begin",this._currAnim]); -this.fire("onBegin",[this._currAnim]); -} -this.fire("onPlay",[this._currAnim]); -_33f.play(null,_33e); -} -return this; -},pause:function(){ -if(this._anims[this._currAnim]){ -this._anims[this._currAnim].pause(); -this.fire("onPause",[this._currAnim]); -} -return this; -},playPause:function(){ -if(this._anims.length==0){ -return this; -} -if(this._currAnim==-1){ -this._currAnim=0; -} -var _340=this._anims[this._currAnim]; -if(_340){ -if(!_340._active||_340._paused){ -this.play(); -}else{ -this.pause(); -} -} -return this; -},stop:function(){ -var _341=this._anims[this._currAnim]; -if(_341){ -_341.stop(); -this.fire("onStop",[this._currAnim]); -} -return _341; -},_playNext:function(){ -if(this._currAnim==-1||this._anims.length==0){ -return this; -} -this._currAnim++; -if(this._anims[this._currAnim]){ -this._anims[this._currAnim].play(null,true); -} -return this; -}}); -dojo.lfx.combine=function(){ -var _342=arguments; -if(dojo.lang.isArray(arguments[0])){ -_342=arguments[0]; -} -return new dojo.lfx.Combine(_342); -}; -dojo.lfx.chain=function(){ -var _343=arguments; -if(dojo.lang.isArray(arguments[0])){ -_343=arguments[0]; -} -return new dojo.lfx.Chain(_343); -}; -dojo.provide("dojo.graphics.color"); -dojo.require("dojo.lang.array"); -dojo.graphics.color.Color=function(r,g,b,a){ -if(dojo.lang.isArray(r)){ -this.r=r[0]; -this.g=r[1]; -this.b=r[2]; -this.a=r[3]||1; -}else{ -if(dojo.lang.isString(r)){ -var rgb=dojo.graphics.color.extractRGB(r); -this.r=rgb[0]; -this.g=rgb[1]; -this.b=rgb[2]; -this.a=g||1; -}else{ -if(r instanceof dojo.graphics.color.Color){ -this.r=r.r; -this.b=r.b; -this.g=r.g; -this.a=r.a; -}else{ -this.r=r; -this.g=g; -this.b=b; -this.a=a; -} -} -} -}; -dojo.graphics.color.Color.fromArray=function(arr){ -return new dojo.graphics.color.Color(arr[0],arr[1],arr[2],arr[3]); -}; -dojo.lang.extend(dojo.graphics.color.Color,{toRgb:function(_34a){ -if(_34a){ -return this.toRgba(); -}else{ -return [this.r,this.g,this.b]; -} -},toRgba:function(){ -return [this.r,this.g,this.b,this.a]; -},toHex:function(){ -return dojo.graphics.color.rgb2hex(this.toRgb()); -},toCss:function(){ -return "rgb("+this.toRgb().join()+")"; -},toString:function(){ -return this.toHex(); -},blend:function(_34b,_34c){ -return dojo.graphics.color.blend(this.toRgb(),new dojo.graphics.color.Color(_34b).toRgb(),_34c); -}}); -dojo.graphics.color.named={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],blue:[0,0,255],navy:[0,0,128],gray:[128,128,128],silver:[192,192,192]}; -dojo.graphics.color.blend=function(a,b,_34f){ -if(typeof a=="string"){ -return dojo.graphics.color.blendHex(a,b,_34f); -} -if(!_34f){ -_34f=0; -}else{ -if(_34f>1){ -_34f=1; -}else{ -if(_34f<-1){ -_34f=-1; -} -} -} -var c=new Array(3); -for(var i=0;i<3;i++){ -var half=Math.abs(a[i]-b[i])/2; -c[i]=Math.floor(Math.min(a[i],b[i])+half+(half*_34f)); -} -return c; -}; -dojo.graphics.color.blendHex=function(a,b,_355){ -return dojo.graphics.color.rgb2hex(dojo.graphics.color.blend(dojo.graphics.color.hex2rgb(a),dojo.graphics.color.hex2rgb(b),_355)); -}; -dojo.graphics.color.extractRGB=function(_356){ -var hex="0123456789abcdef"; -_356=_356.toLowerCase(); -if(_356.indexOf("rgb")==0){ -var _358=_356.match(/rgba*\((\d+), *(\d+), *(\d+)/i); -var ret=_358.splice(1,3); -return ret; -}else{ -var _35a=dojo.graphics.color.hex2rgb(_356); -if(_35a){ -return _35a; -}else{ -return dojo.graphics.color.named[_356]||[255,255,255]; -} -} -}; -dojo.graphics.color.hex2rgb=function(hex){ -var _35c="0123456789ABCDEF"; -var rgb=new Array(3); -if(hex.indexOf("#")==0){ -hex=hex.substring(1); -} -hex=hex.toUpperCase(); -if(hex.replace(new RegExp("["+_35c+"]","g"),"")!=""){ -return null; -} -if(hex.length==3){ -rgb[0]=hex.charAt(0)+hex.charAt(0); -rgb[1]=hex.charAt(1)+hex.charAt(1); -rgb[2]=hex.charAt(2)+hex.charAt(2); -}else{ -rgb[0]=hex.substring(0,2); -rgb[1]=hex.substring(2,4); -rgb[2]=hex.substring(4); -} -for(var i=0;i0&&!(j==1&&segs[0]=="")&&segs[j]==".."&&segs[j-1]!=".."){ -if(j==segs.length-1){ -segs.splice(j,1); -segs[j-1]=""; -}else{ -segs.splice(j-1,2); -j-=2; -} -} -} -} -_36a.path=segs.join("/"); -} -} -} -} -uri=""; -if(_36a.scheme!=null){ -uri+=_36a.scheme+":"; -} -if(_36a.authority!=null){ -uri+="//"+_36a.authority; -} -uri+=_36a.path; -if(_36a.query!=null){ -uri+="?"+_36a.query; -} -if(_36a.fragment!=null){ -uri+="#"+_36a.fragment; -} -} -this.uri=uri.toString(); -var _36f="^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"; -var r=this.uri.match(new RegExp(_36f)); -this.scheme=r[2]||(r[1]?"":null); -this.authority=r[4]||(r[3]?"":null); -this.path=r[5]; -this.query=r[7]||(r[6]?"":null); -this.fragment=r[9]||(r[8]?"":null); -if(this.authority!=null){ -_36f="^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$"; -r=this.authority.match(new RegExp(_36f)); -this.user=r[3]||null; -this.password=r[4]||null; -this.host=r[5]; -this.port=r[7]||null; -} -this.toString=function(){ -return this.uri; -}; -}; -}; -dojo.provide("dojo.style"); -dojo.require("dojo.graphics.color"); -dojo.require("dojo.uri.Uri"); -dojo.require("dojo.lang.common"); -(function(){ -var h=dojo.render.html; -var ds=dojo.style; -var db=document["body"]||document["documentElement"]; -ds.boxSizing={MARGIN_BOX:"margin-box",BORDER_BOX:"border-box",PADDING_BOX:"padding-box",CONTENT_BOX:"content-box"}; -var bs=ds.boxSizing; -ds.getBoxSizing=function(node){ -if((h.ie)||(h.opera)){ -var cm=document["compatMode"]; -if((cm=="BackCompat")||(cm=="QuirksMode")){ -return bs.BORDER_BOX; -}else{ -return bs.CONTENT_BOX; -} -}else{ -if(arguments.length==0){ -node=document.documentElement; -} -var _377=ds.getStyle(node,"-moz-box-sizing"); -if(!_377){ -_377=ds.getStyle(node,"box-sizing"); -} -return (_377?_377:bs.CONTENT_BOX); -} -}; -ds.isBorderBox=function(node){ -return (ds.getBoxSizing(node)==bs.BORDER_BOX); -}; -ds.getUnitValue=function(node,_37a,_37b){ -var s=ds.getComputedStyle(node,_37a); -if((!s)||((s=="auto")&&(_37b))){ -return {value:0,units:"px"}; -} -if(dojo.lang.isUndefined(s)){ -return ds.getUnitValue.bad; -} -var _37d=s.match(/(\-?[\d.]+)([a-z%]*)/i); -if(!_37d){ -return ds.getUnitValue.bad; -} -return {value:Number(_37d[1]),units:_37d[2].toLowerCase()}; -}; -ds.getUnitValue.bad={value:NaN,units:""}; -ds.getPixelValue=function(node,_37f,_380){ -var _381=ds.getUnitValue(node,_37f,_380); -if(isNaN(_381.value)){ -return 0; -} -if((_381.value)&&(_381.units!="px")){ -return NaN; -} -return _381.value; -}; -ds.getNumericStyle=function(){ -dojo.deprecated("dojo.(style|html).getNumericStyle","in favor of dojo.(style|html).getPixelValue","0.4"); -return ds.getPixelValue.apply(this,arguments); -}; -ds.setPositivePixelValue=function(node,_383,_384){ -if(isNaN(_384)){ -return false; -} -node.style[_383]=Math.max(0,_384)+"px"; -return true; -}; -ds._sumPixelValues=function(node,_386,_387){ -var _388=0; -for(var x=0;x<_386.length;x++){ -_388+=ds.getPixelValue(node,_386[x],_387); -} -return _388; -}; -ds.isPositionAbsolute=function(node){ -return (ds.getComputedStyle(node,"position")=="absolute"); -}; -ds.getBorderExtent=function(node,side){ -return (ds.getStyle(node,"border-"+side+"-style")=="none"?0:ds.getPixelValue(node,"border-"+side+"-width")); -}; -ds.getMarginWidth=function(node){ -return ds._sumPixelValues(node,["margin-left","margin-right"],ds.isPositionAbsolute(node)); -}; -ds.getBorderWidth=function(node){ -return ds.getBorderExtent(node,"left")+ds.getBorderExtent(node,"right"); -}; -ds.getPaddingWidth=function(node){ -return ds._sumPixelValues(node,["padding-left","padding-right"],true); -}; -ds.getPadBorderWidth=function(node){ -return ds.getPaddingWidth(node)+ds.getBorderWidth(node); -}; -ds.getContentBoxWidth=function(node){ -node=dojo.byId(node); -return node.offsetWidth-ds.getPadBorderWidth(node); -}; -ds.getBorderBoxWidth=function(node){ -node=dojo.byId(node); -return node.offsetWidth; -}; -ds.getMarginBoxWidth=function(node){ -return ds.getInnerWidth(node)+ds.getMarginWidth(node); -}; -ds.setContentBoxWidth=function(node,_395){ -node=dojo.byId(node); -if(ds.isBorderBox(node)){ -_395+=ds.getPadBorderWidth(node); -} -return ds.setPositivePixelValue(node,"width",_395); -}; -ds.setMarginBoxWidth=function(node,_397){ -node=dojo.byId(node); -if(!ds.isBorderBox(node)){ -_397-=ds.getPadBorderWidth(node); -} -_397-=ds.getMarginWidth(node); -return ds.setPositivePixelValue(node,"width",_397); -}; -ds.getContentWidth=ds.getContentBoxWidth; -ds.getInnerWidth=ds.getBorderBoxWidth; -ds.getOuterWidth=ds.getMarginBoxWidth; -ds.setContentWidth=ds.setContentBoxWidth; -ds.setOuterWidth=ds.setMarginBoxWidth; -ds.getMarginHeight=function(node){ -return ds._sumPixelValues(node,["margin-top","margin-bottom"],ds.isPositionAbsolute(node)); -}; -ds.getBorderHeight=function(node){ -return ds.getBorderExtent(node,"top")+ds.getBorderExtent(node,"bottom"); -}; -ds.getPaddingHeight=function(node){ -return ds._sumPixelValues(node,["padding-top","padding-bottom"],true); -}; -ds.getPadBorderHeight=function(node){ -return ds.getPaddingHeight(node)+ds.getBorderHeight(node); -}; -ds.getContentBoxHeight=function(node){ -node=dojo.byId(node); -return node.offsetHeight-ds.getPadBorderHeight(node); -}; -ds.getBorderBoxHeight=function(node){ -node=dojo.byId(node); -return node.offsetHeight; -}; -ds.getMarginBoxHeight=function(node){ -return ds.getInnerHeight(node)+ds.getMarginHeight(node); -}; -ds.setContentBoxHeight=function(node,_3a0){ -node=dojo.byId(node); -if(ds.isBorderBox(node)){ -_3a0+=ds.getPadBorderHeight(node); -} -return ds.setPositivePixelValue(node,"height",_3a0); -}; -ds.setMarginBoxHeight=function(node,_3a2){ -node=dojo.byId(node); -if(!ds.isBorderBox(node)){ -_3a2-=ds.getPadBorderHeight(node); -} -_3a2-=ds.getMarginHeight(node); -return ds.setPositivePixelValue(node,"height",_3a2); -}; -ds.getContentHeight=ds.getContentBoxHeight; -ds.getInnerHeight=ds.getBorderBoxHeight; -ds.getOuterHeight=ds.getMarginBoxHeight; -ds.setContentHeight=ds.setContentBoxHeight; -ds.setOuterHeight=ds.setMarginBoxHeight; -ds.getAbsolutePosition=ds.abs=function(node,_3a4){ -node=dojo.byId(node); -var ret=[]; -ret.x=ret.y=0; -var st=dojo.html.getScrollTop(); -var sl=dojo.html.getScrollLeft(); -if(h.ie){ -with(node.getBoundingClientRect()){ -ret.x=left-2; -ret.y=top-2; -} -}else{ -if(document.getBoxObjectFor){ -var bo=document.getBoxObjectFor(node); -ret.x=bo.x-ds.sumAncestorProperties(node,"scrollLeft"); -ret.y=bo.y-ds.sumAncestorProperties(node,"scrollTop"); -}else{ -if(node["offsetParent"]){ -var _3a9; -if((h.safari)&&(node.style.getPropertyValue("position")=="absolute")&&(node.parentNode==db)){ -_3a9=db; -}else{ -_3a9=db.parentNode; -} -if(node.parentNode!=db){ -var nd=node; -if(window.opera){ -nd=db; -} -ret.x-=ds.sumAncestorProperties(nd,"scrollLeft"); -ret.y-=ds.sumAncestorProperties(nd,"scrollTop"); -} -do{ -var n=node["offsetLeft"]; -ret.x+=isNaN(n)?0:n; -var m=node["offsetTop"]; -ret.y+=isNaN(m)?0:m; -node=node.offsetParent; -}while((node!=_3a9)&&(node!=null)); -}else{ -if(node["x"]&&node["y"]){ -ret.x+=isNaN(node.x)?0:node.x; -ret.y+=isNaN(node.y)?0:node.y; -} -} -} -} -if(_3a4){ -ret.y+=st; -ret.x+=sl; -} -ret[0]=ret.x; -ret[1]=ret.y; -return ret; -}; -ds.sumAncestorProperties=function(node,prop){ -node=dojo.byId(node); -if(!node){ -return 0; -} -var _3af=0; -while(node){ -var val=node[prop]; -if(val){ -_3af+=val-0; -if(node==document.body){ -break; -} -} -node=node.parentNode; -} -return _3af; -}; -ds.getTotalOffset=function(node,type,_3b3){ -return ds.abs(node,_3b3)[(type=="top")?"y":"x"]; -}; -ds.getAbsoluteX=ds.totalOffsetLeft=function(node,_3b5){ -return ds.getTotalOffset(node,"left",_3b5); -}; -ds.getAbsoluteY=ds.totalOffsetTop=function(node,_3b7){ -return ds.getTotalOffset(node,"top",_3b7); -}; -ds.styleSheet=null; -ds.insertCssRule=function(_3b8,_3b9,_3ba){ -if(!ds.styleSheet){ -if(document.createStyleSheet){ -ds.styleSheet=document.createStyleSheet(); -}else{ -if(document.styleSheets[0]){ -ds.styleSheet=document.styleSheets[0]; -}else{ -return null; -} -} -} -if(arguments.length<3){ -if(ds.styleSheet.cssRules){ -_3ba=ds.styleSheet.cssRules.length; -}else{ -if(ds.styleSheet.rules){ -_3ba=ds.styleSheet.rules.length; -}else{ -return null; -} -} -} -if(ds.styleSheet.insertRule){ -var rule=_3b8+" { "+_3b9+" }"; -return ds.styleSheet.insertRule(rule,_3ba); -}else{ -if(ds.styleSheet.addRule){ -return ds.styleSheet.addRule(_3b8,_3b9,_3ba); -}else{ -return null; -} -} -}; -ds.removeCssRule=function(_3bc){ -if(!ds.styleSheet){ -dojo.debug("no stylesheet defined for removing rules"); -return false; -} -if(h.ie){ -if(!_3bc){ -_3bc=ds.styleSheet.rules.length; -ds.styleSheet.removeRule(_3bc); -} -}else{ -if(document.styleSheets[0]){ -if(!_3bc){ -_3bc=ds.styleSheet.cssRules.length; -} -ds.styleSheet.deleteRule(_3bc); -} -} -return true; -}; -ds.insertCssFile=function(URI,doc,_3bf){ -if(!URI){ -return; -} -if(!doc){ -doc=document; -} -var _3c0=dojo.hostenv.getText(URI); -_3c0=ds.fixPathsInCssText(_3c0,URI); -if(_3bf){ -var _3c1=doc.getElementsByTagName("style"); -var _3c2=""; -for(var i=0;i<_3c1.length;i++){ -_3c2=(_3c1[i].styleSheet&&_3c1[i].styleSheet.cssText)?_3c1[i].styleSheet.cssText:_3c1[i].innerHTML; -if(_3c0==_3c2){ -return; -} -} -} -var _3c4=ds.insertCssText(_3c0); -if(_3c4&&djConfig.isDebug){ -_3c4.setAttribute("dbgHref",URI); -} -return _3c4; -}; -ds.insertCssText=function(_3c5,doc,URI){ -if(!_3c5){ -return; -} -if(!doc){ -doc=document; -} -if(URI){ -_3c5=ds.fixPathsInCssText(_3c5,URI); -} -var _3c8=doc.createElement("style"); -_3c8.setAttribute("type","text/css"); -var head=doc.getElementsByTagName("head")[0]; -if(!head){ -dojo.debug("No head tag in document, aborting styles"); -return; -}else{ -head.appendChild(_3c8); -} -if(_3c8.styleSheet){ -_3c8.styleSheet.cssText=_3c5; -}else{ -var _3ca=doc.createTextNode(_3c5); -_3c8.appendChild(_3ca); -} -return _3c8; -}; -ds.fixPathsInCssText=function(_3cb,URI){ -if(!_3cb||!URI){ -return; -} -var pos=0; -var str=""; -var url=""; -while(pos!=-1){ -pos=0; -url=""; -pos=_3cb.indexOf("url(",pos); -if(pos<0){ -break; -} -str+=_3cb.slice(0,pos+4); -_3cb=_3cb.substring(pos+4,_3cb.length); -url+=_3cb.match(/^[\t\s\w()\/.\\'"-:#=&?]*\)/)[0]; -_3cb=_3cb.substring(url.length-1,_3cb.length); -url=url.replace(/^[\s\t]*(['"]?)([\w()\/.\\'"-:#=&?]*)\1[\s\t]*?\)/,"$2"); -if(url.search(/(file|https?|ftps?):\/\//)==-1){ -url=(new dojo.uri.Uri(URI,url).toString()); -} -str+=url; -} -return str+_3cb; -}; -ds.getBackgroundColor=function(node){ -node=dojo.byId(node); -var _3d1; -do{ -_3d1=ds.getStyle(node,"background-color"); -if(_3d1.toLowerCase()=="rgba(0, 0, 0, 0)"){ -_3d1="transparent"; -} -if(node==document.getElementsByTagName("body")[0]){ -node=null; -break; -} -node=node.parentNode; -}while(node&&dojo.lang.inArray(_3d1,["transparent",""])); -if(_3d1=="transparent"){ -_3d1=[255,255,255,0]; -}else{ -_3d1=dojo.graphics.color.extractRGB(_3d1); -} -return _3d1; -}; -ds.getComputedStyle=function(node,_3d3,_3d4){ -node=dojo.byId(node); -var _3d3=ds.toSelectorCase(_3d3); -var _3d5=ds.toCamelCase(_3d3); -if(!node||!node.style){ -return _3d4; -}else{ -if(document.defaultView){ -try{ -var cs=document.defaultView.getComputedStyle(node,""); -if(cs){ -return cs.getPropertyValue(_3d3); -} -} -catch(e){ -if(node.style.getPropertyValue){ -return node.style.getPropertyValue(_3d3); -}else{ -return _3d4; -} -} -}else{ -if(node.currentStyle){ -return node.currentStyle[_3d5]; -} -} -} -if(node.style.getPropertyValue){ -return node.style.getPropertyValue(_3d3); -}else{ -return _3d4; -} -}; -ds.getStyleProperty=function(node,_3d8){ -node=dojo.byId(node); -return (node&&node.style?node.style[ds.toCamelCase(_3d8)]:undefined); -}; -ds.getStyle=function(node,_3da){ -var _3db=ds.getStyleProperty(node,_3da); -return (_3db?_3db:ds.getComputedStyle(node,_3da)); -}; -ds.setStyle=function(node,_3dd,_3de){ -node=dojo.byId(node); -if(node&&node.style){ -var _3df=ds.toCamelCase(_3dd); -node.style[_3df]=_3de; -} -}; -ds.toCamelCase=function(_3e0){ -var arr=_3e0.split("-"),cc=arr[0]; -for(var i=1;i=1){ -if(h.ie){ -ds.clearOpacity(node); -return; -}else{ -_3e5=0.999999; -} -}else{ -if(_3e5<0){ -_3e5=0; -} -} -} -if(h.ie){ -if(node.nodeName.toLowerCase()=="tr"){ -var tds=node.getElementsByTagName("td"); -for(var x=0;x=0.999999?1:Number(opac); -}; -ds.clearOpacity=function clearOpacity(node){ -node=dojo.byId(node); -var ns=node.style; -if(h.ie){ -try{ -if(node.filters&&node.filters.alpha){ -ns.filter=""; -} -} -catch(e){ -} -}else{ -if(h.moz){ -ns.opacity=1; -ns.MozOpacity=1; -}else{ -if(h.safari){ -ns.opacity=1; -ns.KhtmlOpacity=1; -}else{ -ns.opacity=1; -} -} -} -}; -ds.setStyleAttributes=function(node,_3ee){ -var _3ef={"opacity":dojo.style.setOpacity,"content-height":dojo.style.setContentHeight,"content-width":dojo.style.setContentWidth,"outer-height":dojo.style.setOuterHeight,"outer-width":dojo.style.setOuterWidth}; -var _3f0=_3ee.replace(/(;)?\s*$/,"").split(";"); -for(var i=0;i<_3f0.length;i++){ -var _3f2=_3f0[i].split(":"); -var name=_3f2[0].replace(/\s*$/,"").replace(/^\s*/,"").toLowerCase(); -var _3f4=_3f2[1].replace(/\s*$/,"").replace(/^\s*/,""); -if(dojo.lang.has(_3ef,name)){ -_3ef[name](node,_3f4); -}else{ -node.style[dojo.style.toCamelCase(name)]=_3f4; -} -} -}; -ds._toggle=function(node,_3f6,_3f7){ -node=dojo.byId(node); -_3f7(node,!_3f6(node)); -return _3f6(node); -}; -ds.show=function(node){ -node=dojo.byId(node); -if(ds.getStyleProperty(node,"display")=="none"){ -ds.setStyle(node,"display",(node.dojoDisplayCache||"")); -node.dojoDisplayCache=undefined; -} -}; -ds.hide=function(node){ -node=dojo.byId(node); -if(typeof node["dojoDisplayCache"]=="undefined"){ -var d=ds.getStyleProperty(node,"display"); -if(d!="none"){ -node.dojoDisplayCache=d; -} -} -ds.setStyle(node,"display","none"); -}; -ds.setShowing=function(node,_3fc){ -ds[(_3fc?"show":"hide")](node); -}; -ds.isShowing=function(node){ -return (ds.getStyleProperty(node,"display")!="none"); -}; -ds.toggleShowing=function(node){ -return ds._toggle(node,ds.isShowing,ds.setShowing); -}; -ds.displayMap={tr:"",td:"",th:"",img:"inline",span:"inline",input:"inline",button:"inline"}; -ds.suggestDisplayByTagName=function(node){ -node=dojo.byId(node); -if(node&&node.tagName){ -var tag=node.tagName.toLowerCase(); -return (tag in ds.displayMap?ds.displayMap[tag]:"block"); -} -}; -ds.setDisplay=function(node,_402){ -ds.setStyle(node,"display",(dojo.lang.isString(_402)?_402:(_402?ds.suggestDisplayByTagName(node):"none"))); -}; -ds.isDisplayed=function(node){ -return (ds.getComputedStyle(node,"display")!="none"); -}; -ds.toggleDisplay=function(node){ -return ds._toggle(node,ds.isDisplayed,ds.setDisplay); -}; -ds.setVisibility=function(node,_406){ -ds.setStyle(node,"visibility",(dojo.lang.isString(_406)?_406:(_406?"visible":"hidden"))); -}; -ds.isVisible=function(node){ -return (ds.getComputedStyle(node,"visibility")!="hidden"); -}; -ds.toggleVisibility=function(node){ -return ds._toggle(node,ds.isVisible,ds.setVisibility); -}; -ds.toCoordinateArray=function(_409,_40a){ -if(dojo.lang.isArray(_409)){ -while(_409.length<4){ -_409.push(0); -} -while(_409.length>4){ -_409.pop(); -} -var ret=_409; -}else{ -var node=dojo.byId(_409); -var pos=ds.getAbsolutePosition(node,_40a); -var ret=[pos.x,pos.y,ds.getBorderBoxWidth(node),ds.getBorderBoxHeight(node)]; -} -ret.x=ret[0]; -ret.y=ret[1]; -ret.w=ret[2]; -ret.h=ret[3]; -return ret; -}; -})(); -dojo.provide("dojo.html"); -dojo.require("dojo.lang.func"); -dojo.require("dojo.dom"); -dojo.require("dojo.style"); -dojo.require("dojo.string"); -dojo.lang.mixin(dojo.html,dojo.dom); -dojo.lang.mixin(dojo.html,dojo.style); -dojo.html.clearSelection=function(){ -try{ -if(window["getSelection"]){ -if(dojo.render.html.safari){ -window.getSelection().collapse(); -}else{ -window.getSelection().removeAllRanges(); -} -}else{ -if(document.selection){ -if(document.selection.empty){ -document.selection.empty(); -}else{ -if(document.selection.clear){ -document.selection.clear(); -} -} -} -} -return true; -} -catch(e){ -dojo.debug(e); -return false; -} -}; -dojo.html.disableSelection=function(_40e){ -_40e=dojo.byId(_40e)||document.body; -var h=dojo.render.html; -if(h.mozilla){ -_40e.style.MozUserSelect="none"; -}else{ -if(h.safari){ -_40e.style.KhtmlUserSelect="none"; -}else{ -if(h.ie){ -_40e.unselectable="on"; -}else{ -return false; -} -} -} -return true; -}; -dojo.html.enableSelection=function(_410){ -_410=dojo.byId(_410)||document.body; -var h=dojo.render.html; -if(h.mozilla){ -_410.style.MozUserSelect=""; -}else{ -if(h.safari){ -_410.style.KhtmlUserSelect=""; -}else{ -if(h.ie){ -_410.unselectable="off"; -}else{ -return false; -} -} -} -return true; -}; -dojo.html.selectElement=function(_412){ -_412=dojo.byId(_412); -if(document.selection&&document.body.createTextRange){ -var _413=document.body.createTextRange(); -_413.moveToElementText(_412); -_413.select(); -}else{ -if(window["getSelection"]){ -var _414=window.getSelection(); -if(_414["selectAllChildren"]){ -_414.selectAllChildren(_412); -} -} -} -}; -dojo.html.selectInputText=function(_415){ -_415=dojo.byId(_415); -if(document.selection&&document.body.createTextRange){ -var _416=_415.createTextRange(); -_416.moveStart("character",0); -_416.moveEnd("character",_415.value.length); -_416.select(); -}else{ -if(window["getSelection"]){ -var _417=window.getSelection(); -_415.setSelectionRange(0,_415.value.length); -} -} -_415.focus(); -}; -dojo.html.isSelectionCollapsed=function(){ -if(document["selection"]){ -return document.selection.createRange().text==""; -}else{ -if(window["getSelection"]){ -var _418=window.getSelection(); -if(dojo.lang.isString(_418)){ -return _418==""; -}else{ -return _418.isCollapsed; -} -} -} -}; -dojo.html.getEventTarget=function(evt){ -if(!evt){ -evt=window.event||{}; -} -var t=(evt.srcElement?evt.srcElement:(evt.target?evt.target:null)); -while((t)&&(t.nodeType!=1)){ -t=t.parentNode; -} -return t; -}; -dojo.html.getDocumentWidth=function(){ -dojo.deprecated("dojo.html.getDocument*","replaced by dojo.html.getViewport*","0.4"); -return dojo.html.getViewportWidth(); -}; -dojo.html.getDocumentHeight=function(){ -dojo.deprecated("dojo.html.getDocument*","replaced by dojo.html.getViewport*","0.4"); -return dojo.html.getViewportHeight(); -}; -dojo.html.getDocumentSize=function(){ -dojo.deprecated("dojo.html.getDocument*","replaced of dojo.html.getViewport*","0.4"); -return dojo.html.getViewportSize(); -}; -dojo.html.getViewportWidth=function(){ -var w=0; -if(window.innerWidth){ -w=window.innerWidth; -} -if(dojo.exists(document,"documentElement.clientWidth")){ -var w2=document.documentElement.clientWidth; -if(!w||w2&&w2=left&&_456.x<=_45a&&_456.y>=top&&_456.y<=_458); -}; -dojo.html.setActiveStyleSheet=function(_45b){ -var i=0,a,els=document.getElementsByTagName("link"); -while(a=els[i++]){ -if(a.getAttribute("rel").indexOf("style")!=-1&&a.getAttribute("title")){ -a.disabled=true; -if(a.getAttribute("title")==_45b){ -a.disabled=false; -} -} -} -}; -dojo.html.getActiveStyleSheet=function(){ -var i=0,a,els=document.getElementsByTagName("link"); -while(a=els[i++]){ -if(a.getAttribute("rel").indexOf("style")!=-1&&a.getAttribute("title")&&!a.disabled){ -return a.getAttribute("title"); -} -} -return null; -}; -dojo.html.getPreferredStyleSheet=function(){ -var i=0,a,els=document.getElementsByTagName("link"); -while(a=els[i++]){ -if(a.getAttribute("rel").indexOf("style")!=-1&&a.getAttribute("rel").indexOf("alt")==-1&&a.getAttribute("title")){ -return a.getAttribute("title"); -} -} -return null; -}; -dojo.html.body=function(){ -return document.body||document.getElementsByTagName("body")[0]; -}; -dojo.html.isTag=function(node){ -node=dojo.byId(node); -if(node&&node.tagName){ -var arr=dojo.lang.map(dojo.lang.toArray(arguments,1),function(a){ -return String(a).toLowerCase(); -}); -return arr[dojo.lang.find(node.tagName.toLowerCase(),arr)]||""; -} -return ""; -}; -dojo.html.copyStyle=function(_462,_463){ -if(dojo.lang.isUndefined(_463.style.cssText)){ -_462.setAttribute("style",_463.getAttribute("style")); -}else{ -_462.style.cssText=_463.style.cssText; -} -dojo.html.addClass(_462,dojo.html.getClass(_463)); -}; -dojo.html._callExtrasDeprecated=function(_464,args){ -var _466="dojo.html.extras"; -dojo.deprecated("dojo.html."+_464,"moved to "+_466,"0.4"); -dojo["require"](_466); -return dojo.html[_464].apply(dojo.html,args); -}; -dojo.html.createNodesFromText=function(){ -return dojo.html._callExtrasDeprecated("createNodesFromText",arguments); -}; -dojo.html.gravity=function(){ -return dojo.html._callExtrasDeprecated("gravity",arguments); -}; -dojo.html.placeOnScreen=function(){ -return dojo.html._callExtrasDeprecated("placeOnScreen",arguments); -}; -dojo.html.placeOnScreenPoint=function(){ -return dojo.html._callExtrasDeprecated("placeOnScreenPoint",arguments); -}; -dojo.html.renderedTextContent=function(){ -return dojo.html._callExtrasDeprecated("renderedTextContent",arguments); -}; -dojo.html.BackgroundIframe=function(){ -return dojo.html._callExtrasDeprecated("BackgroundIframe",arguments); -}; -dojo.provide("dojo.lfx.html"); -dojo.require("dojo.lfx.Animation"); -dojo.require("dojo.html"); -dojo.lfx.html._byId=function(_467){ -if(!_467){ -return []; -} -if(dojo.lang.isArray(_467)){ -if(!_467.alreadyChecked){ -var n=[]; -dojo.lang.forEach(_467,function(node){ -n.push(dojo.byId(node)); -}); -n.alreadyChecked=true; -return n; -}else{ -return _467; -} -}else{ -var n=[]; -n.push(dojo.byId(_467)); -n.alreadyChecked=true; -return n; -} -}; -dojo.lfx.html.propertyAnimation=function(_46a,_46b,_46c,_46d){ -_46a=dojo.lfx.html._byId(_46a); -if(_46a.length==1){ -dojo.lang.forEach(_46b,function(prop){ -if(typeof prop["start"]=="undefined"){ -if(prop.property!="opacity"){ -prop.start=parseInt(dojo.style.getComputedStyle(_46a[0],prop.property)); -}else{ -prop.start=dojo.style.getOpacity(_46a[0]); -} -} -}); -} -var _46f=function(_470){ -var _471=new Array(_470.length); -for(var i=0;i<_470.length;i++){ -_471[i]=Math.round(_470[i]); -} -return _471; -}; -var _473=function(n,_475){ -n=dojo.byId(n); -if(!n||!n.style){ -return; -} -for(var s in _475){ -if(s=="opacity"){ -dojo.style.setOpacity(n,_475[s]); -}else{ -n.style[s]=_475[s]; -} -} -}; -var _477=function(_478){ -this._properties=_478; -this.diffs=new Array(_478.length); -dojo.lang.forEach(_478,function(prop,i){ -if(dojo.lang.isArray(prop.start)){ -this.diffs[i]=null; -}else{ -if(prop.start instanceof dojo.graphics.color.Color){ -prop.startRgb=prop.start.toRgb(); -prop.endRgb=prop.end.toRgb(); -}else{ -this.diffs[i]=prop.end-prop.start; -} -} -},this); -this.getValue=function(n){ -var ret={}; -dojo.lang.forEach(this._properties,function(prop,i){ -var _47f=null; -if(dojo.lang.isArray(prop.start)){ -}else{ -if(prop.start instanceof dojo.graphics.color.Color){ -_47f=(prop.units||"rgb")+"("; -for(var j=0;j1){ -return dojo.lfx.combine(_4a2); -}else{ -return _4a2[0]; -} -}; -dojo.lfx.html.wipeOut=function(_4a7,_4a8,_4a9,_4aa){ -_4a7=dojo.lfx.html._byId(_4a7); -var _4ab=[]; -dojo.lang.forEach(_4a7,function(node){ -var _4ad=dojo.style.getStyle(node,"overflow"); -if(_4ad=="visible"){ -node.style.overflow="hidden"; -} -dojo.style.show(node); -var anim=dojo.lfx.propertyAnimation(node,[{property:"height",start:dojo.style.getContentBoxHeight(node),end:0}],_4a8,_4a9); -var _4af=(anim["onEnd"])?dojo.lang.hitch(anim,"onEnd"):function(){ -}; -anim.onEnd=function(){ -_4af(); -dojo.style.hide(node); -node.style.overflow=_4ad; -if(_4aa){ -_4aa(node,anim); -} -}; -_4ab.push(anim); -}); -if(_4a7.length>1){ -return dojo.lfx.combine(_4ab); -}else{ -return _4ab[0]; -} -}; -dojo.lfx.html.slideTo=function(_4b0,_4b1,_4b2,_4b3,_4b4){ -_4b0=dojo.lfx.html._byId(_4b0); -var _4b5=[]; -dojo.lang.forEach(_4b0,function(node){ -var top=null; -var left=null; -var init=(function(){ -var _4ba=node; -return function(){ -top=_4ba.offsetTop; -left=_4ba.offsetLeft; -if(!dojo.style.isPositionAbsolute(_4ba)){ -var ret=dojo.style.abs(_4ba,true); -dojo.style.setStyleAttributes(_4ba,"position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;"); -top=ret.y; -left=ret.x; -} -}; -})(); -init(); -var anim=dojo.lfx.propertyAnimation(node,[{property:"top",start:top,end:_4b1[0]},{property:"left",start:left,end:_4b1[1]}],_4b2,_4b3); -var _4bd=(anim["beforeBegin"])?dojo.lang.hitch(anim,"beforeBegin"):function(){ -}; -anim.beforeBegin=function(){ -_4bd(); -init(); -}; -if(_4b4){ -var _4be=(anim["onEnd"])?dojo.lang.hitch(anim,"onEnd"):function(){ -}; -anim.onEnd=function(){ -_4be(); -_4b4(_4b0,anim); -}; -} -_4b5.push(anim); -}); -if(_4b0.length>1){ -return dojo.lfx.combine(_4b5); -}else{ -return _4b5[0]; -} -}; -dojo.lfx.html.slideBy=function(_4bf,_4c0,_4c1,_4c2,_4c3){ -_4bf=dojo.lfx.html._byId(_4bf); -var _4c4=[]; -dojo.lang.forEach(_4bf,function(node){ -var top=null; -var left=null; -var init=(function(){ -var _4c9=node; -return function(){ -top=node.offsetTop; -left=node.offsetLeft; -if(!dojo.style.isPositionAbsolute(_4c9)){ -var ret=dojo.style.abs(_4c9); -dojo.style.setStyleAttributes(_4c9,"position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;"); -top=ret.y; -left=ret.x; -} -}; -})(); -init(); -var anim=dojo.lfx.propertyAnimation(node,[{property:"top",start:top,end:top+_4c0[0]},{property:"left",start:left,end:left+_4c0[1]}],_4c1,_4c2); -var _4cc=(anim["beforeBegin"])?dojo.lang.hitch(anim,"beforeBegin"):function(){ -}; -anim.beforeBegin=function(){ -_4cc(); -init(); -}; -if(_4c3){ -var _4cd=(anim["onEnd"])?dojo.lang.hitch(anim,"onEnd"):function(){ -}; -anim.onEnd=function(){ -_4cd(); -_4c3(_4bf,anim); -}; -} -_4c4.push(anim); -}); -if(_4bf.length>1){ -return dojo.lfx.combine(_4c4); -}else{ -return _4c4[0]; -} -}; -dojo.lfx.html.explode=function(_4ce,_4cf,_4d0,_4d1,_4d2){ -_4ce=dojo.byId(_4ce); -_4cf=dojo.byId(_4cf); -var _4d3=dojo.style.toCoordinateArray(_4ce,true); -var _4d4=document.createElement("div"); -dojo.html.copyStyle(_4d4,_4cf); -with(_4d4.style){ -position="absolute"; -display="none"; -} -document.body.appendChild(_4d4); -with(_4cf.style){ -visibility="hidden"; -display="block"; -} -var _4d5=dojo.style.toCoordinateArray(_4cf,true); -with(_4cf.style){ -display="none"; -visibility="visible"; -} -var anim=new dojo.lfx.propertyAnimation(_4d4,[{property:"height",start:_4d3[3],end:_4d5[3]},{property:"width",start:_4d3[2],end:_4d5[2]},{property:"top",start:_4d3[1],end:_4d5[1]},{property:"left",start:_4d3[0],end:_4d5[0]},{property:"opacity",start:0.3,end:1}],_4d0,_4d1); -anim.beforeBegin=function(){ -dojo.style.setDisplay(_4d4,"block"); -}; -anim.onEnd=function(){ -dojo.style.setDisplay(_4cf,"block"); -_4d4.parentNode.removeChild(_4d4); -}; -if(_4d2){ -var _4d7=(anim["onEnd"])?dojo.lang.hitch(anim,"onEnd"):function(){ -}; -anim.onEnd=function(){ -_4d7(); -_4d2(_4cf,anim); -}; -} -return anim; -}; -dojo.lfx.html.implode=function(_4d8,end,_4da,_4db,_4dc){ -_4d8=dojo.byId(_4d8); -end=dojo.byId(end); -var _4dd=dojo.style.toCoordinateArray(_4d8,true); -var _4de=dojo.style.toCoordinateArray(end,true); -var _4df=document.createElement("div"); -dojo.html.copyStyle(_4df,_4d8); -dojo.style.setOpacity(_4df,0.3); -with(_4df.style){ -position="absolute"; -display="none"; -} -document.body.appendChild(_4df); -var anim=new dojo.lfx.propertyAnimation(_4df,[{property:"height",start:_4dd[3],end:_4de[3]},{property:"width",start:_4dd[2],end:_4de[2]},{property:"top",start:_4dd[1],end:_4de[1]},{property:"left",start:_4dd[0],end:_4de[0]},{property:"opacity",start:1,end:0.3}],_4da,_4db); -anim.beforeBegin=function(){ -dojo.style.hide(_4d8); -dojo.style.show(_4df); -}; -anim.onEnd=function(){ -_4df.parentNode.removeChild(_4df); -}; -if(_4dc){ -var _4e1=(anim["onEnd"])?dojo.lang.hitch(anim,"onEnd"):function(){ -}; -anim.onEnd=function(){ -_4e1(); -_4dc(_4d8,anim); -}; -} -return anim; -}; -dojo.lfx.html.highlight=function(_4e2,_4e3,_4e4,_4e5,_4e6){ -_4e2=dojo.lfx.html._byId(_4e2); -var _4e7=[]; -dojo.lang.forEach(_4e2,function(node){ -var _4e9=dojo.style.getBackgroundColor(node); -var bg=dojo.style.getStyle(node,"background-color").toLowerCase(); -var _4eb=dojo.style.getStyle(node,"background-image"); -var _4ec=(bg=="transparent"||bg=="rgba(0, 0, 0, 0)"); -while(_4e9.length>3){ -_4e9.pop(); -} -var rgb=new dojo.graphics.color.Color(_4e3); -var _4ee=new dojo.graphics.color.Color(_4e9); -var anim=dojo.lfx.propertyAnimation(node,[{property:"background-color",start:rgb,end:_4ee}],_4e4,_4e5); -var _4f0=(anim["beforeBegin"])?dojo.lang.hitch(anim,"beforeBegin"):function(){ -}; -anim.beforeBegin=function(){ -_4f0(); -if(_4eb){ -node.style.backgroundImage="none"; -} -node.style.backgroundColor="rgb("+rgb.toRgb().join(",")+")"; -}; -var _4f1=(anim["onEnd"])?dojo.lang.hitch(anim,"onEnd"):function(){ -}; -anim.onEnd=function(){ -_4f1(); -if(_4eb){ -node.style.backgroundImage=_4eb; -} -if(_4ec){ -node.style.backgroundColor="transparent"; -} -if(_4e6){ -_4e6(node,anim); -} -}; -_4e7.push(anim); -}); -if(_4e2.length>1){ -return dojo.lfx.combine(_4e7); -}else{ -return _4e7[0]; -} -}; -dojo.lfx.html.unhighlight=function(_4f2,_4f3,_4f4,_4f5,_4f6){ -_4f2=dojo.lfx.html._byId(_4f2); -var _4f7=[]; -dojo.lang.forEach(_4f2,function(node){ -var _4f9=new dojo.graphics.color.Color(dojo.style.getBackgroundColor(node)); -var rgb=new dojo.graphics.color.Color(_4f3); -var _4fb=dojo.style.getStyle(node,"background-image"); -var anim=dojo.lfx.propertyAnimation(node,[{property:"background-color",start:_4f9,end:rgb}],_4f4,_4f5); -var _4fd=(anim["beforeBegin"])?dojo.lang.hitch(anim,"beforeBegin"):function(){ -}; -anim.beforeBegin=function(){ -_4fd(); -if(_4fb){ -node.style.backgroundImage="none"; -} -node.style.backgroundColor="rgb("+_4f9.toRgb().join(",")+")"; -}; -var _4fe=(anim["onEnd"])?dojo.lang.hitch(anim,"onEnd"):function(){ -}; -anim.onEnd=function(){ -_4fe(); -if(_4f6){ -_4f6(node,anim); -} -}; -_4f7.push(anim); -}); -if(_4f2.length>1){ -return dojo.lfx.combine(_4f7); -}else{ -return _4f7[0]; -} -}; -dojo.lang.mixin(dojo.lfx,dojo.lfx.html); -dojo.kwCompoundRequire({browser:["dojo.lfx.html"],dashboard:["dojo.lfx.html"]}); -dojo.provide("dojo.lfx.*"); - diff --git a/source/web/scripts/ajax/src/AdapterRegistry.js b/source/web/scripts/ajax/src/AdapterRegistry.js new file mode 100644 index 0000000000..8e1e281724 --- /dev/null +++ b/source/web/scripts/ajax/src/AdapterRegistry.js @@ -0,0 +1,79 @@ +dojo.provide("dojo.AdapterRegistry"); +dojo.require("dojo.lang.func"); + +dojo.AdapterRegistry = function(/*boolean, optional*/returnWrappers){ + // summary: + // A registry to make contextual calling/searching easier. + // description: + // Objects of this class keep list of arrays in the form [name, check, + // wrap, directReturn] that are used to determine what the contextual + // result of a set of checked arguments is. All check/wrap functions + // in this registry should be of the same arity. + this.pairs = []; + this.returnWrappers = returnWrappers || false; +} + +dojo.lang.extend(dojo.AdapterRegistry, { + register: function( /*string*/ name, /*function*/ check, /*function*/ wrap, + /*boolean, optional*/ directReturn, + /*boolean, optional*/ override){ + // summary: + // register a check function to determine if the wrap function or + // object gets selected + // name: a way to identify this matcher. + // check: + // a function that arguments are passed to from the adapter's + // match() function. The check function should return true if the + // given arguments are appropriate for the wrap function. + // directReturn: + // If directReturn is true, the value passed in for wrap will be + // returned instead of being called. Alternately, the + // AdapterRegistry can be set globally to "return not call" using + // the returnWrappers property. Either way, this behavior allows + // the registry to act as a "search" function instead of a + // function interception library. + // override: + // If override is given and true, the check function will be given + // highest priority. Otherwise, it will be the lowest priority + // adapter. + + var type = (override) ? "unshift" : "push"; + this.pairs[type]([name, check, wrap, directReturn]); + }, + + match: function(/* ... */){ + // summary: + // Find an adapter for the given arguments. If no suitable adapter + // is found, throws an exception. match() accepts any number of + // arguments, all of which are passed to all matching functions + // from the registered pairs. + for(var i = 0; i < this.pairs.length; i++){ + var pair = this.pairs[i]; + if(pair[1].apply(this, arguments)){ + if((pair[3])||(this.returnWrappers)){ + return pair[2]; + }else{ + return pair[2].apply(this, arguments); + } + } + } + throw new Error("No match found"); + // dojo.raise("No match found"); + }, + + unregister: function(name){ + // summary: Remove a named adapter from the registry + + // FIXME: this is kind of a dumb way to handle this. On a large + // registry this will be slow-ish and we can use the name as a lookup + // should we choose to trade memory for speed. + for(var i = 0; i < this.pairs.length; i++){ + var pair = this.pairs[i]; + if(pair[0] == name){ + this.pairs.splice(i, 1); + return true; + } + } + return false; + } +}); diff --git a/source/web/scripts/ajax/src/Deferred.js b/source/web/scripts/ajax/src/Deferred.js new file mode 100644 index 0000000000..6f99fe8767 --- /dev/null +++ b/source/web/scripts/ajax/src/Deferred.js @@ -0,0 +1,305 @@ +dojo.provide("dojo.Deferred"); +dojo.require("dojo.lang.func"); + +dojo.Deferred = function(/* optional */ canceller){ + /* + NOTE: this namespace and documentation are imported wholesale + from MochiKit + + Encapsulates a sequence of callbacks in response to a value that + may not yet be available. This is modeled after the Deferred class + from Twisted . + + Why do we want this? JavaScript has no threads, and even if it did, + threads are hard. Deferreds are a way of abstracting non-blocking + events, such as the final response to an XMLHttpRequest. + + The sequence of callbacks is internally represented as a list + of 2-tuples containing the callback/errback pair. For example, + the following call sequence:: + + var d = new Deferred(); + d.addCallback(myCallback); + d.addErrback(myErrback); + d.addBoth(myBoth); + d.addCallbacks(myCallback, myErrback); + + is translated into a Deferred with the following internal + representation:: + + [ + [myCallback, null], + [null, myErrback], + [myBoth, myBoth], + [myCallback, myErrback] + ] + + The Deferred also keeps track of its current status (fired). + Its status may be one of three things: + + -1: no value yet (initial condition) + 0: success + 1: error + + A Deferred will be in the error state if one of the following + three conditions are met: + + 1. The result given to callback or errback is "instanceof" Error + 2. The previous callback or errback raised an exception while + executing + 3. The previous callback or errback returned a value "instanceof" + Error + + Otherwise, the Deferred will be in the success state. The state of + the Deferred determines the next element in the callback sequence to + run. + + When a callback or errback occurs with the example deferred chain, + something equivalent to the following will happen (imagine that + exceptions are caught and returned):: + + // d.callback(result) or d.errback(result) + if(!(result instanceof Error)){ + result = myCallback(result); + } + if(result instanceof Error){ + result = myErrback(result); + } + result = myBoth(result); + if(result instanceof Error){ + result = myErrback(result); + }else{ + result = myCallback(result); + } + + The result is then stored away in case another step is added to the + callback sequence. Since the Deferred already has a value available, + any new callbacks added will be called immediately. + + There are two other "advanced" details about this implementation that + are useful: + + Callbacks are allowed to return Deferred instances themselves, so you + can build complicated sequences of events with ease. + + The creator of the Deferred may specify a canceller. The canceller + is a function that will be called if Deferred.cancel is called before + the Deferred fires. You can use this to implement clean aborting of + an XMLHttpRequest, etc. Note that cancel will fire the deferred with + a CancelledError (unless your canceller returns another kind of + error), so the errbacks should be prepared to handle that error for + cancellable Deferreds. + + */ + + this.chain = []; + this.id = this._nextId(); + this.fired = -1; + this.paused = 0; + this.results = [null, null]; + this.canceller = canceller; + this.silentlyCancelled = false; +}; + +dojo.lang.extend(dojo.Deferred, { + getFunctionFromArgs: function(){ + var a = arguments; + if((a[0])&&(!a[1])){ + if(dojo.lang.isFunction(a[0])){ + return a[0]; + }else if(dojo.lang.isString(a[0])){ + return dj_global[a[0]]; + } + }else if((a[0])&&(a[1])){ + return dojo.lang.hitch(a[0], a[1]); + } + return null; + }, + + makeCalled: function() { + var deferred = new dojo.Deferred(); + deferred.callback(); + return deferred; + }, + + repr: function(){ + var state; + if(this.fired == -1){ + state = 'unfired'; + }else if(this.fired == 0){ + state = 'success'; + } else { + state = 'error'; + } + return 'Deferred(' + this.id + ', ' + state + ')'; + }, + + toString: dojo.lang.forward("repr"), + + _nextId: (function(){ + var n = 1; + return function(){ return n++; }; + })(), + + cancel: function(){ + /*** + Cancels a Deferred that has not yet received a value, or is + waiting on another Deferred as its value. + + If a canceller is defined, the canceller is called. If the + canceller did not return an error, or there was no canceller, + then the errback chain is started with CancelledError. + ***/ + if(this.fired == -1){ + if (this.canceller){ + this.canceller(this); + }else{ + this.silentlyCancelled = true; + } + if(this.fired == -1){ + this.errback(new Error(this.repr())); + } + }else if( (this.fired == 0)&& + (this.results[0] instanceof dojo.Deferred)){ + this.results[0].cancel(); + } + }, + + + _pause: function(){ + // Used internally to signal that it's waiting on another Deferred + this.paused++; + }, + + _unpause: function(){ + // Used internally to signal that it's no longer waiting on + // another Deferred. + this.paused--; + if ((this.paused == 0) && (this.fired >= 0)) { + this._fire(); + } + }, + + _continue: function(res){ + // Used internally when a dependent deferred fires. + this._resback(res); + this._unpause(); + }, + + _resback: function(res){ + // The primitive that means either callback or errback + this.fired = ((res instanceof Error) ? 1 : 0); + this.results[this.fired] = res; + this._fire(); + }, + + _check: function(){ + if(this.fired != -1){ + if(!this.silentlyCancelled){ + dojo.raise("already called!"); + } + this.silentlyCancelled = false; + return; + } + }, + + callback: function(res){ + /* + Begin the callback sequence with a non-error value. + + callback or errback should only be called once on a given + Deferred. + */ + this._check(); + this._resback(res); + }, + + errback: function(res){ + // Begin the callback sequence with an error result. + this._check(); + if(!(res instanceof Error)){ + res = new Error(res); + } + this._resback(res); + }, + + addBoth: function(cb, cbfn){ + /* + Add the same function as both a callback and an errback as the + next element on the callback sequence. This is useful for code + that you want to guarantee to run, e.g. a finalizer. + */ + var enclosed = this.getFunctionFromArgs(cb, cbfn); + if(arguments.length > 2){ + enclosed = dojo.lang.curryArguments(null, enclosed, arguments, 2); + } + return this.addCallbacks(enclosed, enclosed); + }, + + addCallback: function(cb, cbfn){ + // Add a single callback to the end of the callback sequence. + var enclosed = this.getFunctionFromArgs(cb, cbfn); + if(arguments.length > 2){ + enclosed = dojo.lang.curryArguments(null, enclosed, arguments, 2); + } + return this.addCallbacks(enclosed, null); + }, + + addErrback: function(cb, cbfn){ + // Add a single callback to the end of the callback sequence. + var enclosed = this.getFunctionFromArgs(cb, cbfn); + if(arguments.length > 2){ + enclosed = dojo.lang.curryArguments(null, enclosed, arguments, 2); + } + return this.addCallbacks(null, enclosed); + return this.addCallbacks(null, cbfn); + }, + + addCallbacks: function (cb, eb) { + // Add separate callback and errback to the end of the callback + // sequence. + this.chain.push([cb, eb]) + if (this.fired >= 0) { + this._fire(); + } + return this; + }, + + _fire: function(){ + // Used internally to exhaust the callback sequence when a result + // is available. + var chain = this.chain; + var fired = this.fired; + var res = this.results[fired]; + var self = this; + var cb = null; + while (chain.length > 0 && this.paused == 0) { + // Array + var pair = chain.shift(); + var f = pair[fired]; + if (f == null) { + continue; + } + try { + res = f(res); + fired = ((res instanceof Error) ? 1 : 0); + if(res instanceof dojo.Deferred) { + cb = function(res){ + self._continue(res); + } + this._pause(); + } + }catch(err){ + fired = 1; + res = err; + } + } + this.fired = fired; + this.results[fired] = res; + if((cb)&&(this.paused)){ + // this is for "tail recursion" in case the dependent + // deferred is already fired + res.addBoth(cb); + } + } +}); diff --git a/source/web/scripts/ajax/src/DeferredList.js b/source/web/scripts/ajax/src/DeferredList.js new file mode 100644 index 0000000000..0905e53bfb --- /dev/null +++ b/source/web/scripts/ajax/src/DeferredList.js @@ -0,0 +1,78 @@ +dojo.require("dojo.Deferred"); + +dojo.provide("dojo.DeferredList"); + + +dojo.DeferredList = function (list, /* optional */fireOnOneCallback, fireOnOneErrback, consumeErrors, canceller) { + this.list = list; + this.resultList = new Array(this.list.length); + + // Deferred init + this.chain = []; + this.id = this._nextId(); + this.fired = -1; + this.paused = 0; + this.results = [null, null]; + this.canceller = canceller; + this.silentlyCancelled = false; + + if (this.list.length === 0 && !fireOnOneCallback) { + this.callback(this.resultList); + } + + this.finishedCount = 0; + this.fireOnOneCallback = fireOnOneCallback; + this.fireOnOneErrback = fireOnOneErrback; + this.consumeErrors = consumeErrors; + + var index = 0; + + var _this = this; + + dojo.lang.forEach(this.list, function(d) { + var _index = index; + //dojo.debug("add cb/errb index "+_index); + d.addCallback(function(r) { _this._cbDeferred(_index, true, r) }); + d.addErrback(function(r) { _this._cbDeferred(_index, false, r) }); + index++; + }); + +}; + + +dojo.inherits(dojo.DeferredList, dojo.Deferred); + +dojo.lang.extend(dojo.DeferredList, { + + _cbDeferred: function (index, succeeded, result) { + //dojo.debug("Fire "+index+" succ "+succeeded+" res "+result); + this.resultList[index] = [succeeded, result]; + this.finishedCount += 1; + if (this.fired !== 0) { + if (succeeded && this.fireOnOneCallback) { + this.callback([index, result]); + } else if (!succeeded && this.fireOnOneErrback) { + this.errback(result); + } else if (this.finishedCount == this.list.length) { + this.callback(this.resultList); + } + } + if (!succeeded && this.consumeErrors) { + result = null; + } + return result; + }, + + gatherResults: function (deferredList) { + var d = new dojo.DeferredList(deferredList, false, true, false); + d.addCallback(function (results) { + var ret = []; + for (var i = 0; i < results.length; i++) { + ret.push(results[i][1]); + } + return ret; + }); + return d; + } +}); + diff --git a/source/web/scripts/ajax/src/animation.js b/source/web/scripts/ajax/src/animation.js new file mode 100644 index 0000000000..5755dc8f0a --- /dev/null +++ b/source/web/scripts/ajax/src/animation.js @@ -0,0 +1,2 @@ +dojo.provide("dojo.animation"); +dojo.require("dojo.animation.Animation"); diff --git a/source/web/scripts/ajax/src/animation/Animation.js b/source/web/scripts/ajax/src/animation/Animation.js new file mode 100644 index 0000000000..278bb6f4ba --- /dev/null +++ b/source/web/scripts/ajax/src/animation/Animation.js @@ -0,0 +1,233 @@ +dojo.provide("dojo.animation.Animation"); +dojo.require("dojo.animation.AnimationEvent"); + +dojo.require("dojo.lang.func"); +dojo.require("dojo.math"); +dojo.require("dojo.math.curves"); + +/* +Animation package based off of Dan Pupius' work on Animations: +http://pupius.co.uk/js/Toolkit.Drawing.js +*/ + +dojo.animation.Animation = function(/*dojo.math.curves.* */ curve, /*int*/ duration, /*Decimal?*/ accel, /*int?*/ repeatCount, /*int?*/ rate) { + // summary: Animation object iterates a set of numbers over a curve for a given amount of time, calling 'onAnimate' at each step. + // curve: Curve to animate over. + // duration: Duration of the animation, in milliseconds. + // accel: Either an integer or curve representing amount of acceleration. (?) Default is linear acceleration. + // repeatCount: Number of times to repeat the animation. Default is 0. + // rate: Time between animation steps, in milliseconds. Default is 25. + // description: Calls the following events: "onBegin", "onAnimate", "onEnd", "onPlay", "onPause", "onStop" + // If the animation implements a "handler" function, that will be called before each event is called. + + if(dojo.lang.isArray(curve)) { + // curve: Array + // id: i + curve = new dojo.math.curves.Line(curve[0], curve[1]); + } + this.curve = curve; + this.duration = duration; + this.repeatCount = repeatCount || 0; + this.rate = rate || 25; + if(accel) { + // accel: Decimal + // id: j + if(dojo.lang.isFunction(accel.getValue)) { + // accel: dojo.math.curves.CatmullRom + // id: k + this.accel = accel; + } else { + var i = 0.35*accel+0.5; // 0.15 <= i <= 0.85 + this.accel = new dojo.math.curves.CatmullRom([[0], [i], [1]], 0.45); + } + } +} + +dojo.lang.extend(dojo.animation.Animation, { + // public properties + curve: null, + duration: 0, + repeatCount: 0, + accel: null, + + // events + onBegin: null, + onAnimate: null, + onEnd: null, + onPlay: null, + onPause: null, + onStop: null, + handler: null, + + // "private" properties + _animSequence: null, + _startTime: null, + _endTime: null, + _lastFrame: null, + _timer: null, + _percent: 0, + _active: false, + _paused: false, + _startRepeatCount: 0, + + // public methods + play: function(/*Boolean?*/ gotoStart) { + // summary: Play the animation. + // goToStart: If true, will restart the animation from the beginning. + // Otherwise, starts from current play counter. + // description: Sends an "onPlay" event to any observers. + // Also sends an "onBegin" event if starting from the beginning. + if( gotoStart ) { + clearTimeout(this._timer); + this._active = false; + this._paused = false; + this._percent = 0; + } else if( this._active && !this._paused ) { + return; + } + + this._startTime = new Date().valueOf(); + if( this._paused ) { + this._startTime -= (this.duration * this._percent / 100); + } + this._endTime = this._startTime + this.duration; + this._lastFrame = this._startTime; + + var e = new dojo.animation.AnimationEvent(this, null, this.curve.getValue(this._percent), + this._startTime, this._startTime, this._endTime, this.duration, this._percent, 0); + + this._active = true; + this._paused = false; + + if( this._percent == 0 ) { + if(!this._startRepeatCount) { + this._startRepeatCount = this.repeatCount; + } + e.type = "begin"; + if(typeof this.handler == "function") { this.handler(e); } + if(typeof this.onBegin == "function") { this.onBegin(e); } + } + + e.type = "play"; + if(typeof this.handler == "function") { this.handler(e); } + if(typeof this.onPlay == "function") { this.onPlay(e); } + + if(this._animSequence) { this._animSequence._setCurrent(this); } + + this._cycle(); + }, + + pause: function() { + // summary: Temporarily stop the animation, leaving the play counter at the current location. + // Resume later with sequence.play() + // description: Sends an "onPause" AnimationEvent to any observers. + clearTimeout(this._timer); + if( !this._active ) { return; } + this._paused = true; + var e = new dojo.animation.AnimationEvent(this, "pause", this.curve.getValue(this._percent), + this._startTime, new Date().valueOf(), this._endTime, this.duration, this._percent, 0); + if(typeof this.handler == "function") { this.handler(e); } + if(typeof this.onPause == "function") { this.onPause(e); } + }, + + playPause: function() { + // summary: Toggle between play and paused states. + if( !this._active || this._paused ) { + this.play(); + } else { + this.pause(); + } + }, + + gotoPercent: function(/*int*/ pct, /*Boolean*/ andPlay) { + // summary: Set the play counter at a certain point in the animation. + // pct: Point to set the play counter to, expressed as a percentage (0 to 100). + // andPlay: If true, will start the animation at the counter automatically. + clearTimeout(this._timer); + this._active = true; + this._paused = true; + this._percent = pct; + if( andPlay ) { this.play(); } + }, + + stop: function(/*Boolean?*/ gotoEnd) { + // summary: Stop the animation. + // gotoEnd: If true, will advance play counter to the end before sending the event. + // description: Sends an "onStop" AnimationEvent to any observers. + clearTimeout(this._timer); + var step = this._percent / 100; + if( gotoEnd ) { + step = 1; + } + var e = new dojo.animation.AnimationEvent(this, "stop", this.curve.getValue(step), + this._startTime, new Date().valueOf(), this._endTime, this.duration, this._percent); + if(typeof this.handler == "function") { this.handler(e); } + if(typeof this.onStop == "function") { this.onStop(e); } + this._active = false; + this._paused = false; + }, + + status: function() { + // summary: Return the status of the animation. + // description: Returns one of "playing", "paused" or "stopped". + if( this._active ) { + return this._paused ? "paused" : "playing"; /* String */ + } else { + return "stopped"; /* String */ + } + }, + + // "private" methods + _cycle: function() { + // summary: Perform once 'cycle' or step of the animation. + clearTimeout(this._timer); + if( this._active ) { + var curr = new Date().valueOf(); + var step = (curr - this._startTime) / (this._endTime - this._startTime); + var fps = 1000 / (curr - this._lastFrame); + this._lastFrame = curr; + + if( step >= 1 ) { + step = 1; + this._percent = 100; + } else { + this._percent = step * 100; + } + + // Perform accelleration + if(this.accel && this.accel.getValue) { + step = this.accel.getValue(step); + } + + var e = new dojo.animation.AnimationEvent(this, "animate", this.curve.getValue(step), + this._startTime, curr, this._endTime, this.duration, this._percent, Math.round(fps)); + + if(typeof this.handler == "function") { this.handler(e); } + if(typeof this.onAnimate == "function") { this.onAnimate(e); } + + if( step < 1 ) { + this._timer = setTimeout(dojo.lang.hitch(this, "_cycle"), this.rate); + } else { + e.type = "end"; + this._active = false; + if(typeof this.handler == "function") { this.handler(e); } + if(typeof this.onEnd == "function") { this.onEnd(e); } + + if( this.repeatCount > 0 ) { + this.repeatCount--; + this.play(true); + } else if( this.repeatCount == -1 ) { + this.play(true); + } else { + if(this._startRepeatCount) { + this.repeatCount = this._startRepeatCount; + this._startRepeatCount = 0; + } + if( this._animSequence ) { + this._animSequence._playNext(); + } + } + } + } + } +}); diff --git a/source/web/scripts/ajax/src/animation/AnimationEvent.js b/source/web/scripts/ajax/src/animation/AnimationEvent.js new file mode 100644 index 0000000000..fa8086da89 --- /dev/null +++ b/source/web/scripts/ajax/src/animation/AnimationEvent.js @@ -0,0 +1,53 @@ +dojo.provide("dojo.animation.AnimationEvent"); + +dojo.require("dojo.lang"); + +dojo.animation.AnimationEvent = function( + /*dojo.animation.Animation*/ animation, + /*String*/type, + /*int[] */ coords, + /*int*/ startTime, + /*int*/ currentTime, + /*int*/ endTime, + /*int*/ duration, + /*int*/ percent, + /*int?*/ fps) { + // summary: Event sent at various points during an Animation. + // animation: Animation throwing the event. + // type: One of: "animate", "begin", "end", "play", "pause" or "stop". + // coords: Current coordinates of the animation. + // startTime: Time the animation was started, as milliseconds. + // currentTime: Time the event was thrown, as milliseconds. + // endTime: Time the animation is expected to complete, as milliseconds. + // duration: Duration of the animation, in milliseconds. + // percent: Percent of the animation that has completed, between 0 and 100. + // fps: Frames currently shown per second. (Only sent for "animate" event). + // description: The AnimationEvent has public properties of the same name as + // all constructor arguments, plus "x", "y" and "z". + + this.type = type; // "animate", "begin", "end", "play", "pause", "stop" + this.animation = animation; + + this.coords = coords; + this.x = coords[0]; + this.y = coords[1]; + this.z = coords[2]; + + this.startTime = startTime; + this.currentTime = currentTime; + this.endTime = endTime; + + this.duration = duration; + this.percent = percent; + this.fps = fps; +}; +dojo.lang.extend(dojo.animation.AnimationEvent, { + coordsAsInts: function() { + // summary: Coerce the coordinates into integers. + var cints = new Array(this.coords.length); + for(var i = 0; i < this.coords.length; i++) { + cints[i] = Math.round(this.coords[i]); + } + return cints; + } +}); diff --git a/source/web/scripts/ajax/src/animation/AnimationSequence.js b/source/web/scripts/ajax/src/animation/AnimationSequence.js new file mode 100644 index 0000000000..7ca352f63b --- /dev/null +++ b/source/web/scripts/ajax/src/animation/AnimationSequence.js @@ -0,0 +1,150 @@ +dojo.provide("dojo.animation.AnimationSequence"); +dojo.require("dojo.animation.AnimationEvent"); +dojo.require("dojo.animation.Animation"); + +dojo.animation.AnimationSequence = function(/*int?*/ repeatCount){ + // summary: Sequence of Animations, played one after the other. + // repeatCount: Number of times to repeat the entire sequence. Default is 0 (play once only). + // description: Calls the following events: "onBegin", "onEnd", "onNext" + // If the animation implements a "handler" function, that will be called before each event is called. + this._anims = []; + this.repeatCount = repeatCount || 0; +} + +dojo.lang.extend(dojo.animation.AnimationSequence, { + repeatCount: 0, + + _anims: [], + _currAnim: -1, + + onBegin: null, + onEnd: null, + onNext: null, + handler: null, + + add: function() { + // summary: Add one or more Animations to the sequence. + // description: args: Animations (dojo.animation.Animation) to add to the sequence. + for(var i = 0; i < arguments.length; i++) { + this._anims.push(arguments[i]); + arguments[i]._animSequence = this; + } + }, + + remove: function(/*dojo.animation.Animation*/ anim) { + // summary: Remove one particular animation from the sequence. + // amim: Animation to remove. + for(var i = 0; i < this._anims.length; i++) { + if( this._anims[i] == anim ) { + this._anims[i]._animSequence = null; + this._anims.splice(i, 1); + break; + } + } + }, + + removeAll: function() { + // summary: Remove all animations from the sequence. + for(var i = 0; i < this._anims.length; i++) { + this._anims[i]._animSequence = null; + } + this._anims = []; + this._currAnim = -1; + }, + + clear: function() { + // summary: Remove all animations from the sequence. + this.removeAll(); + }, + + play: function(/*Boolean?*/ gotoStart) { + // summary: Play the animation sequence. + // gotoStart: If true, will start at the beginning of the first sequence. + // Otherwise, starts at the current play counter of the current animation. + // description: Sends an "onBegin" event to any observers. + if( this._anims.length == 0 ) { return; } + if( gotoStart || !this._anims[this._currAnim] ) { + this._currAnim = 0; + } + if( this._anims[this._currAnim] ) { + if( this._currAnim == 0 ) { + var e = {type: "begin", animation: this._anims[this._currAnim]}; + if(typeof this.handler == "function") { this.handler(e); } + if(typeof this.onBegin == "function") { this.onBegin(e); } + } + this._anims[this._currAnim].play(gotoStart); + } + }, + + pause: function() { + // summary: temporarily stop the current animation. Resume later with sequence.play() + if( this._anims[this._currAnim] ) { + this._anims[this._currAnim].pause(); + } + }, + + playPause: function() { + // summary: Toggle between play and paused states. + if( this._anims.length == 0 ) { return; } + if( this._currAnim == -1 ) { this._currAnim = 0; } + if( this._anims[this._currAnim] ) { + this._anims[this._currAnim].playPause(); + } + }, + + stop: function() { + // summary: Stop the current animation. + if( this._anims[this._currAnim] ) { + this._anims[this._currAnim].stop(); + } + }, + + status: function() { + // summary: Return the status of the current animation. + // description: Returns one of "playing", "paused" or "stopped". + if( this._anims[this._currAnim] ) { + return this._anims[this._currAnim].status(); + } else { + return "stopped"; + } + }, + + _setCurrent: function(/*dojo.animation.Animation*/ anim) { + // summary: Set the current animation. + // anim: Animation to make current, must have already been added to the sequence. + for(var i = 0; i < this._anims.length; i++) { + if( this._anims[i] == anim ) { + this._currAnim = i; + break; + } + } + }, + + _playNext: function() { + // summary: Play the next animation in the sequence. + // description: Sends an "onNext" event to any observers. + // Also sends "onEnd" if the last animation is finished. + if( this._currAnim == -1 || this._anims.length == 0 ) { return; } + this._currAnim++; + if( this._anims[this._currAnim] ) { + var e = {type: "next", animation: this._anims[this._currAnim]}; + if(typeof this.handler == "function") { this.handler(e); } + if(typeof this.onNext == "function") { this.onNext(e); } + this._anims[this._currAnim].play(true); + } else { + var e = {type: "end", animation: this._anims[this._anims.length-1]}; + if(typeof this.handler == "function") { this.handler(e); } + if(typeof this.onEnd == "function") { this.onEnd(e); } + if(this.repeatCount > 0) { + this._currAnim = 0; + this.repeatCount--; + this._anims[this._currAnim].play(true); + } else if(this.repeatCount == -1) { + this._currAnim = 0; + this._anims[this._currAnim].play(true); + } else { + this._currAnim = -1; + } + } + } +}); diff --git a/source/web/scripts/ajax/src/animation/Timer.js b/source/web/scripts/ajax/src/animation/Timer.js new file mode 100644 index 0000000000..e0d9330e7e --- /dev/null +++ b/source/web/scripts/ajax/src/animation/Timer.js @@ -0,0 +1,46 @@ +dojo.provide("dojo.animation.Timer"); +dojo.require("dojo.lang.func"); + +dojo.animation.Timer = function(/*int*/ interval){ + // summary: Timer object executes an "onTick()" method repeatedly at a specified interval. + // repeatedly at a given interval. + // interval: Interval between function calls, in milliseconds. + this.timer = null; + this.isRunning = false; + this.interval = interval; + + this.onStart = null; + this.onStop = null; +}; + +dojo.lang.extend(dojo.animation.Timer, { + onTick : function(){ + // summary: Method called every time the interval passes. Override to do something useful. + }, + + setInterval : function(interval){ + // summary: Reset the interval of a timer, whether running or not. + // interval: New interval, in milliseconds. + if (this.isRunning) dj_global.clearInterval(this.timer); + this.interval = interval; + if (this.isRunning) this.timer = dj_global.setInterval(dojo.lang.hitch(this, "onTick"), this.interval); + }, + + start : function(){ + // summary: Start the timer ticking. + // description: Calls the "onStart()" handler, if defined. + // Note that the onTick() function is not called right away, + // only after first interval passes. + if (typeof this.onStart == "function") this.onStart(); + this.isRunning = true; + this.timer = dj_global.setInterval(this.onTick, this.interval); + }, + + stop : function(){ + // summary: Stop the timer. + // description: Calls the "onStop()" handler, if defined. + if (typeof this.onStop == "function") this.onStop(); + this.isRunning = false; + dj_global.clearInterval(this.timer); + } +}); diff --git a/source/web/scripts/ajax/src/animation/__package__.js b/source/web/scripts/ajax/src/animation/__package__.js new file mode 100644 index 0000000000..de7aeb2404 --- /dev/null +++ b/source/web/scripts/ajax/src/animation/__package__.js @@ -0,0 +1,8 @@ +dojo.kwCompoundRequire({ + common: [ + "dojo.animation.AnimationEvent", + "dojo.animation.Animation", + "dojo.animation.AnimationSequence" + ] +}); +dojo.provide("dojo.animation.*"); diff --git a/source/web/scripts/ajax/src/behavior.js b/source/web/scripts/ajax/src/behavior.js new file mode 100644 index 0000000000..d32435f54a --- /dev/null +++ b/source/web/scripts/ajax/src/behavior.js @@ -0,0 +1,238 @@ +dojo.provide("dojo.behavior"); +dojo.require("dojo.event.*"); + +dojo.require("dojo.experimental"); +dojo.experimental("dojo.behavior"); + +dojo.behavior = new function(){ + function arrIn(obj, name){ + if(!obj[name]){ obj[name] = []; } + return obj[name]; + } + + function forIn(obj, scope, func){ + var tmpObj = {}; + for(var x in obj){ + if(typeof tmpObj[x] == "undefined"){ + if(!func){ + scope(obj[x], x); + }else{ + func.call(scope, obj[x], x); + } + } + } + } + + // FIXME: need a better test so we don't exclude nightly Safari's! + this.behaviors = {}; + this.add = function(behaviorObj){ + /* behavior objects are specified in the following format: + * + * { + * "#id": { + * "found": function(element){ + * // ... + * }, + * + * "onblah": {targetObj: foo, targetFunc: "bar"}, + * + * "onblarg": "/foo/bar/baz/blarg", + * + * "onevent": function(evt){ + * }, + * + * "onotherevent: function(evt){ + * // ... + * } + * }, + * + * "#id2": { + * // ... + * }, + * + * "#id3": function(element){ + * // ... + * }, + * + * // publish the match on a topic + * "#id4": "/found/topic/name", + * + * // match all direct descendants + * "#id4 > *": function(element){ + * // ... + * }, + * + * // match the first child node that's an element + * "#id4 > @firstElement": { ... }, + * + * // match the last child node that's an element + * "#id4 > @lastElement": { ... }, + * + * // all elements of type tagname + * "tagname": { + * // ... + * }, + * + * // maps to roughly: + * // dojo.lang.forEach(body.getElementsByTagName("tagname1"), function(node){ + * // dojo.lang.forEach(node.getElementsByTagName("tagname2"), function(node2){ + * // dojo.lang.forEach(node2.getElementsByTagName("tagname3", function(node3){ + * // // apply rules + * // }); + * // }); + * // }); + * "tagname1 tagname2 tagname3": { + * // ... + * }, + * + * ".classname": { + * // ... + * }, + * + * "tagname.classname": { + * // ... + * }, + * } + * + * The "found" method is a generalized handler that's called as soon + * as the node matches the selector. Rules for values that follow also + * apply to the "found" key. + * + * The "on*" handlers are attached with dojo.event.connect(). If the + * value is not a function but is rather an object, it's assumed to be + * the "other half" of a dojo.event.kwConnect() argument object. It + * may contain any/all properties of such a connection modifier save + * for the sourceObj and sourceFunc properties which are filled in by + * the system automatically. If a string is instead encountered, the + * node publishes the specified event on the topic contained in the + * string value. + * + * If the value corresponding to the ID key is a function and not a + * list, it's treated as though it was the value of "found". + * + */ + + var tmpObj = {}; + forIn(behaviorObj, this, function(behavior, name){ + var tBehavior = arrIn(this.behaviors, name); + if((dojo.lang.isString(behavior))||(dojo.lang.isFunction(behavior))){ + behavior = { found: behavior }; + } + forIn(behavior, function(rule, ruleName){ + arrIn(tBehavior, ruleName).push(rule); + }); + }); + } + + this.apply = function(){ + dojo.profile.start("dojo.behavior.apply"); + var r = dojo.render.html; + // note, we apply one way for fast queries and one way for slow + // iteration. So be it. + var safariGoodEnough = (!r.safari); + if(r.safari){ + // Anything over release #420 should work the fast way + var uas = r.UA.split("AppleWebKit/")[1]; + if(parseInt(uas.match(/[0-9.]{3,}/)) >= 420){ + safariGoodEnough = true; + } + } + if((dj_undef("behaviorFastParse", djConfig) ? (safariGoodEnough) : djConfig["behaviorFastParse"])){ + this.applyFast(); + }else{ + this.applySlow(); + } + dojo.profile.end("dojo.behavior.apply"); + } + + this.matchCache = {}; + + this.elementsById = function(id, handleRemoved){ + var removed = []; + var added = []; + arrIn(this.matchCache, id); + if(handleRemoved){ + var nodes = this.matchCache[id]; + for(var x=0; x"); + } catch (e) { + var script = document.createElement("script"); + script.src = spath; + document.getElementsByTagName("head")[0].appendChild(script); + } + } + } +})(); + +// Localization routines + +/** + * Returns canonical form of locale, as used by Dojo. All variants are case-insensitive and are separated by '-' + * as specified in RFC 3066 + */ +dojo.normalizeLocale = function(locale) { + return locale ? locale.toLowerCase() : dojo.locale; +}; + +dojo.searchLocalePath = function(locale, down, searchFunc){ + locale = dojo.normalizeLocale(locale); + + var elements = locale.split('-'); + var searchlist = []; + for(var i = elements.length; i > 0; i--){ + searchlist.push(elements.slice(0, i).join('-')); + } + searchlist.push(false); + if(down){searchlist.reverse();} + + for(var j = searchlist.length - 1; j >= 0; j--){ + var loc = searchlist[j] || "ROOT"; + var stop = searchFunc(loc); + if(stop){ break; } + } +} + +/** + * requireLocalization() is for loading translated bundles provided within a package in the namespace. + * Contents are typically strings, but may be any name/value pair, represented in JSON format. + * A bundle is structured in a program as follows, where modulename is mycode.mywidget and + * bundlename is mybundle: + * + * mycode/ + * mywidget/ + * nls/ + * mybundle.js (the fallback translation, English in this example) + * de/ + * mybundle.js + * de-at/ + * mybundle.js + * en/ + * (empty; use the fallback translation) + * en-us/ + * mybundle.js + * en-gb/ + * mybundle.js + * es/ + * mybundle.js + * ...etc + * + * Each directory is named for a locale as specified by RFC 3066, (http://www.ietf.org/rfc/rfc3066.txt), + * normalized in lowercase. + * + * For a given locale, bundles will be loaded for that locale and all less-specific locales above it, as well + * as a fallback at the root. For example, a search for the "de-at" locale will first load nls/de-at/mybundle.js, + * then nls/de/mybundle.js and finally nls/mybundle.js. Lookups will traverse the locales in this same order + * and flatten all the values into a JS object (see dojo.i18n.getLocalization). A build step can preload the + * bundles to avoid data redundancy and extra network hits. + * + * @param modulename package in which the bundle is found + * @param bundlename bundle name, typically the filename without the '.js' suffix + * @param locale the locale to load (optional) By default, the browser's user locale as defined + * in dojo.locale + */ +dojo.requireLocalization = function(modulename, bundlename, locale /*optional*/){ + var bundlepackage = [modulename, "_nls", bundlename].join("."); + var bundle = dojo.hostenv.startPackage(bundlepackage); + dojo.hostenv.loaded_modules_[bundlepackage] = bundle; // this seems to be necessary. why? + + if(!dj_undef("dj_localesBuilt", dj_global) && dojo.hostenv.loaded_modules_[bundlepackage]){ + locale = dojo.normalizeLocale(locale); + for(var i=0; i=0; x--){ + dojo.clobberLastObject(removals[x]); + } + var depList = []; + var seen = dojo.hostenv._writtenIncludes; + for(var x=0; x"); + } + document.write(""); + dojo.hostenv._loadedUrisListStart = 0; + if (!willCallAgain) { + // turn off debugAtAllCosts, so that dojo.require() calls inside of ContentPane hrefs + // work correctly + dj_eval = old_dj_eval; + dojo.hostenv.loadUri = dojo.hostenv.oldLoadUri; + } +} diff --git a/source/web/scripts/ajax/src/collections/ArrayList.js b/source/web/scripts/ajax/src/collections/ArrayList.js new file mode 100644 index 0000000000..7248722395 --- /dev/null +++ b/source/web/scripts/ajax/src/collections/ArrayList.js @@ -0,0 +1,136 @@ +dojo.provide("dojo.collections.ArrayList"); +dojo.require("dojo.collections.Collections"); + +dojo.collections.ArrayList=function(/* array? */arr){ + // summary + // Returns a new object of type dojo.collections.ArrayList + var items=[]; + if(arr) items=items.concat(arr); + this.count=items.length; + this.add=function(/* object */obj){ + // summary + // Add an element to the collection. + items.push(obj); + this.count=items.length; + }; + this.addRange=function(/* array */a){ + // summary + // Add a range of objects to the ArrayList + if(a.getIterator){ + var e=a.getIterator(); + while(!e.atEnd()){ + this.add(e.get()); + } + this.count=items.length; + }else{ + for(var i=0; i=0) { + items.splice(i,1); + } + this.count=items.length; + }; + this.removeAt=function(/* int */ i){ + // summary + // return an array with function applied to all elements + items.splice(i,1); + this.count=items.length; + }; + this.reverse=function(){ + // summary + // Reverse the internal array + items.reverse(); + }; + this.sort=function(/* function? */ fn){ + // summary + // sort the internal array + if(fn){ + items.sort(fn); + }else{ + items.sort(); + } + }; + this.setByIndex=function(/* int */ i, /* object */ obj){ + // summary + // Set an element in the array by the passed index. + items[i]=obj; + this.count=items.length; + }; + this.toArray=function(){ + // summary + // Return a new array with all of the items of the internal array concatenated. + return [].concat(items); + } + this.toString=function(/* string */ delim){ + // summary + // implementation of toString, follows [].toString(); + return items.join((delim||",")); + }; +}; diff --git a/source/web/scripts/ajax/src/collections/BinaryTree.js b/source/web/scripts/ajax/src/collections/BinaryTree.js new file mode 100644 index 0000000000..83f3f7647b --- /dev/null +++ b/source/web/scripts/ajax/src/collections/BinaryTree.js @@ -0,0 +1,193 @@ +dojo.provide("dojo.collections.BinaryTree"); +dojo.require("dojo.collections.Collections"); +dojo.require("dojo.experimental"); + +dojo.experimental("dojo.collections.BinaryTree"); + +dojo.collections.BinaryTree=function(data){ + function node(data, rnode, lnode){ + this.value=data||null; + this.right=rnode||null; + this.left=lnode||null; + this.clone=function(){ + var c=new node(); + if (this.value.value) c.value=this.value.clone(); + else c.value=this.value; + if (this.left) c.left=this.left.clone(); + if (this.right) c.right=this.right.clone(); + } + this.compare=function(n){ + if (this.value > n.value) return 1; + if (this.value < n.value) return -1; + return 0; + } + this.compareData=function(d){ + if (this.value > d) return 1; + if (this.value < d) return -1; + return 0; + } + } + + function inorderTraversalBuildup(current, a){ + if (current){ + inorderTraversalBuildup(current.left, a); + a.add(current); + inorderTraversalBuildup(current.right, a); + } + } + + function preorderTraversal(current, sep){ + var s=""; + if (current){ + s=current.value.toString() + sep; + s += preorderTraversal(current.left, sep); + s += preorderTraversal(current.right, sep); + } + return s; + } + function inorderTraversal(current, sep){ + var s=""; + if (current){ + s=inorderTraversal(current.left, sep); + s += current.value.toString() + sep; + s += inorderTraversal(current.right, sep); + } + return s; + } + function postorderTraversal(current, sep){ + var s=""; + if (current){ + s=postorderTraversal(current.left, sep); + s += postorderTraversal(current.right, sep); + s += current.value.toString() + sep; + } + return s; + } + + function searchHelper(current, data){ + if (!current) return null; + var i=current.compareData(data); + if (i==0) return current; + if (i>0) return searchHelper(current.left, data); + else return searchHelper(current.right, data); + } + + this.add=function(data){ + var n=new node(data); + var i; + var current=root; + var parent=null; + while (current){ + i=current.compare(n); + if (i == 0) return; + parent=current; + if (i > 0) current=current.left; + else current=current.right; + } + this.count++; + if (!parent) root=n; + else { + i=parent.compare(n); + if (i > 0) parent.left=n; + else parent.right=n; + } + }; + this.clear=function(){ + root=null; + this.count=0; + }; + this.clone=function(){ + var c=new dojo.collections.BinaryTree(); + c.root=root.clone(); + c.count=this.count; + return c; + }; + this.contains=function(data){ + return this.search(data) != null; + }; + this.deleteData=function(data){ + var current=root; + var parent=null; + var i=current.compareData(data); + while (i != 0 && current != null){ + if (i > 0){ + parent=current; + current=current.left; + } else if (i < 0) { + parent=current; + current=current.right; + } + i=current.compareData(data); + } + if (!current) return; + this.count--; + if (!current.right) { + if (!parent) root=current.left; + else { + i=parent.compare(current); + if (i > 0) parent.left=current.left; + else if (i < 0) parent.right=current.left; + } + } else if (!current.right.left){ + if (!parent) root=current.right; + else { + i=parent.compare(current); + if (i > 0) parent.left=current.right; + else if (i < 0) parent.right=current.right; + } + } else { + var leftmost=current.right.left; + var lmParent=current.right; + while (leftmost.left != null){ + lmParent=leftmost; + leftmost=leftmost.left; + } + lmParent.left=leftmost.right; + leftmost.left=current.left; + leftmost.right=current.right; + if (!parent) root=leftmost; + else { + i=parent.compare(current); + if (i > 0) parent.left=leftmost; + else if (i < 0) parent.right=leftmost; + } + } + }; + this.getIterator=function(){ + var a=[]; + inorderTraversalBuildup(root, a); + return new dojo.collections.Iterator(a); + }; + this.search=function(data){ + return searchHelper(root, data); + }; + this.toString=function(order, sep){ + if (!order) var order=dojo.collections.BinaryTree.TraversalMethods.Inorder; + if (!sep) var sep=" "; + var s=""; + switch (order){ + case dojo.collections.BinaryTree.TraversalMethods.Preorder: + s=preorderTraversal(root, sep); + break; + case dojo.collections.BinaryTree.TraversalMethods.Inorder: + s=inorderTraversal(root, sep); + break; + case dojo.collections.BinaryTree.TraversalMethods.Postorder: + s=postorderTraversal(root, sep); + break; + }; + if (s.length == 0) return ""; + else return s.substring(0, s.length - sep.length); + }; + + this.count=0; + var root=this.root=null; + if (data) { + this.add(data); + } +} +dojo.collections.BinaryTree.TraversalMethods={ + Preorder : 0, + Inorder : 1, + Postorder : 2 +}; diff --git a/source/web/scripts/ajax/src/collections/Collections.js b/source/web/scripts/ajax/src/collections/Collections.js new file mode 100644 index 0000000000..3e4d1436a0 --- /dev/null +++ b/source/web/scripts/ajax/src/collections/Collections.js @@ -0,0 +1,115 @@ +dojo.provide("dojo.collections.Collections"); + +dojo.collections={Collections:true}; +dojo.collections.DictionaryEntry=function(/* string */k, /* object */v){ + // summary + // return an object of type dojo.collections.DictionaryEntry + this.key=k; + this.value=v; + this.valueOf=function(){ + return this.value; // object + }; + this.toString=function(){ + return String(this.value); // string + }; +} + +/* Iterators + * The collections.Iterators (Iterator and DictionaryIterator) are built to + * work with the Collections included in this namespace. However, they *can* + * be used with arrays and objects, respectively, should one choose to do so. + */ +dojo.collections.Iterator=function(/* array */arr){ + // summary + // return an object of type dojo.collections.Iterator + var a=arr; + var position=0; + this.element=a[position]||null; + this.atEnd=function(){ + // summary + // Test to see if the internal cursor has reached the end of the internal collection. + return (position>=a.length); // bool + }; + this.get=function(){ + // summary + // Test to see if the internal cursor has reached the end of the internal collection. + if(this.atEnd()){ + return null; // object + } + this.element=a[position++]; + return this.element; // object + }; + this.map=function(/* function */fn, /* object? */scope){ + // summary + // Functional iteration with optional scope. + var s=scope||dj_global; + if(Array.map){ + return Array.map(a,fn,s); // array + }else{ + var arr=[]; + for(var i=0; i=a.length); // bool + }; + this.get=function(){ + // summary + // Test to see if the internal cursor has reached the end of the internal collection. + if(this.atEnd()){ + return null; // object + } + this.element=a[position++]; + return this.element; // object + }; + this.map=function(/* function */fn, /* object? */scope){ + // summary + // Functional iteration with optional scope. + var s=scope||dj_global; + if(Array.map){ + return Array.map(a,fn,s); // array + }else{ + var arr=[]; + for(var i=0; i=o.length); + } + this.get=function(){ + if(this.atEnd()){ + return null; // object + } + this.element=o[position++]; + return this.element; // object + }; + this.map=function(/* function */fn, /* object? */scope){ + var s=scope||dj_global; + if(Array.map){ + return Array.map(o,fn,s); // array + }else{ + var arr=[]; + for(var i=0; i 1){ + n=new node(arguments[0],arguments[1]); + } + if(!this.nodes.containsKey(n.key)){ + this.nodes.add(n); + this.count++; + } + }; + this.addDirectedEdge=function(uKey, vKey, cost){ + var uNode,vNode; + if(uKey.constructor!= node){ + uNode=this.nodes.item(uKey); + vNode=this.nodes.item(vKey); + }else{ + uNode=uKey; + vNode=vKey; + } + var c=cost||0; + uNode.addDirected(vNode,c); + }; + this.addUndirectedEdge=function(uKey, vKey, cost){ + var uNode, vNode; + if(uKey.constructor!=node){ + uNode=this.nodes.item(uKey); + vNode=this.nodes.item(vKey); + }else{ + uNode=uKey; + vNode=vKey; + } + var c=cost||0; + uNode.addDirected(vNode,c); + vNode.addDirected(uNode,c); + }; + this.contains=function(n){ + return this.nodes.containsKey(n.key); + }; + this.containsKey=function(k){ + return this.nodes.containsKey(k); + }; +} diff --git a/source/web/scripts/ajax/src/collections/Queue.js b/source/web/scripts/ajax/src/collections/Queue.js new file mode 100644 index 0000000000..47fbf82983 --- /dev/null +++ b/source/web/scripts/ajax/src/collections/Queue.js @@ -0,0 +1,77 @@ +dojo.provide("dojo.collections.Queue"); +dojo.require("dojo.collections.Collections"); + +dojo.collections.Queue=function(/* array? */arr){ + // summary + // return an object of type dojo.collections.Queue + var q=[]; + if (arr){ + q=q.concat(arr); + } + this.count=q.length; + this.clear=function(){ + // summary + // clears the internal collection + q=[]; + this.count=q.length; + }; + this.clone=function(){ + // summary + // creates a new Queue based on this one + return new dojo.collections.Queue(q); // dojo.collections.Queue + }; + this.contains=function(/* object */ o){ + // summary + // Check to see if the passed object is an element in this queue + for(var i=0; i val) return 1; + if (this.value < val) return -1; + return 0; + } + this.incrementHeight = function(){ + this.nodes.incrementHeight(); + this.height++; + }; + this.decrementHeight = function(){ + this.nodes.decrementHeight(); + this.height--; + }; + } + function nodeList(height){ + var arr = []; + this.height = height; + for (var i = 0; i < height; i++) arr[i] = null; + this.item = function(i){ + return arr[i]; + }; + this.incrementHeight = function(){ + this.height++; + arr[this.height] = null; + }; + this.decrementHeight = function(){ + arr.splice(arr.length - 1, 1); + this.height--; + }; + } + function iterator(list){ + this.element = list.head; + this.atEnd = function(){ + return (this.element==null); + } + this.get = function(){ + if(this.atEnd()){ + return null; + } + this.element=this.element.nodes[0]; + return this.element; + } + this.reset = function(){ + this.element = list.head; + } + } + + function chooseRandomHeight(max){ + var level = 1; + while (Math.random() < PROB && level < max) level++; + return level; + } + + var PROB = 0.5; + var comparisons = 0; + + this.head = new node(1); + this.count = 0; + this.add = function(val){ + var updates = []; + var current = this.head; + for (var i = this.head.height; i >= 0; i--){ + if (!(current.nodes[i] != null && current.nodes[i].compare(val) < 0)) comparisons++; + while (current.nodes[i] != null && current.nodes[i].compare(val) < 0){ + current = current.nodes[i]; + comparisons++; + } + updates[i] = current; + } + if (current.nodes[0] != null && current.nodes[0].compare(val) == 0) return; + var n = new node(val, chooseRandomHeight(this.head.height + 1)); + this.count++; + if (n.height > this.head.height){ + this.head.incrementHeight(); + this.head.nodes[this.head.height - 1] = n; + } + for (i = 0; i < n.height; i++){ + if (i < updates.length) { + n.nodes[i] = updates[i].nodes[i]; + updates[i].nodes[i] = n; + } + } + }; + + this.contains = function(val){ + var current = this.head; + var i; + for (i = this.head.height - 1; i >= 0; i--) { + while (current.item(i) != null) { + comparisons++; + var result = current.nodes[i].compare(val); + if (result == 0) return true; + else if (result < 0) current = current.nodes[i]; + else break; + } + } + return false; + }; + this.getIterator = function(){ + return new iterator(this); + }; + + this.remove = function(val){ + var updates = []; + var current = this.head; + for (var i = this.head.height - 1; i >= 0; i--){ + if (!(current.nodes[i] != null && current.nodes[i].compare(val) < 0)) comparisons++; + while (current.nodes[i] != null && current.nodes[i].compare(val) < 0) { + current = current.nodes[i]; + comparisons++; + } + updates[i] = current; + } + + current = current.nodes[0]; + if (current != null && current.compare(val) == 0){ + this.count--; + for (var i = 0; i < this.head.height; i++){ + if (updates[i].nodes[i] != current) break; + else updates[i].nodes[i] = current.nodes[i]; + } + if (this.head.nodes[this.head.height - 1] == null) this.head.decrementHeight(); + } + }; + this.resetComparisons = function(){ + comparisons = 0; + }; +} diff --git a/source/web/scripts/ajax/src/collections/SortedList.js b/source/web/scripts/ajax/src/collections/SortedList.js new file mode 100644 index 0000000000..c8d6df3287 --- /dev/null +++ b/source/web/scripts/ajax/src/collections/SortedList.js @@ -0,0 +1,201 @@ +dojo.provide("dojo.collections.SortedList"); +dojo.require("dojo.collections.Collections"); + +dojo.collections.SortedList=function(/* object? */ dictionary){ + // summary + // creates a collection that acts like a dictionary but is also internally sorted. + // Note that the act of adding any elements forces an internal resort, making this object potentially slow. + var _this=this; + var items={}; + var q=[]; + var sorter=function(a,b){ + if (a.key > b.key) return 1; + if (a.key < b.key) return -1; + return 0; + }; + var build=function(){ + q=[]; + var e=_this.getIterator(); + while (!e.atEnd()){ + q.push(e.get()); + } + q.sort(sorter); + }; + var testObject={}; + + this.count=q.length; + this.add=function(/* string */ k,/* object */v){ + // summary + // add the passed value to the dictionary at location k + if (!items[k]) { + items[k]=new dojo.collections.DictionaryEntry(k,v); + this.count=q.push(items[k]); + q.sort(sorter); + } + }; + this.clear=function(){ + // summary + // clear the internal collections + items={}; + q=[]; + this.count=q.length; + }; + this.clone=function(){ + // summary + // create a clone of this sorted list + return new dojo.collections.SortedList(this); // dojo.collections.SortedList + }; + this.contains=this.containsKey=function(/* string */ k){ + // summary + // Check to see if the list has a location k + if(testObject[k]){ + return false; // bool + } + return (items[k]!=null); // bool + }; + this.containsValue=function(/* object */ o){ + // summary + // Check to see if this list contains the passed object + var e=this.getIterator(); + while (!e.atEnd()){ + var item=e.get(); + if(item.value==o){ + return true; // bool + } + } + return false; // bool + }; + this.copyTo=function(/* array */ arr, /* int */ i){ + // summary + // copy the contents of the list into array arr at index i + var e=this.getIterator(); + var idx=i; + while(!e.atEnd()){ + arr.splice(idx,0,e.get()); + idx++; + } + }; + this.entry=function(/* string */ k){ + // summary + // return the object at location k + return items[k]; // dojo.collections.DictionaryEntry + }; + this.forEach=function(/* function */ fn, /* object? */ scope){ + // summary + // functional iterator, following the mozilla spec. + var s=scope||dj_global; + if(Array.forEach){ + Array.forEach(q, fn, s); + }else{ + for(var i=0; i>16)^0xffff))+(((sum&0xffff)^0xffff)+1); + } + return sum; + } + function split(x){ + var r=x&0xffffffff; + if(r<0) { + r=-r; + return [((r&0xffff)^0xffff)+1,(r>>16)^0xffff]; + } + return [r&0xffff,(r>>16)]; + } + function xor(x,y){ + var xs=split(x); + var ys=split(y); + return (0x10000*(xs[1]^ys[1]))+(xs[0]^ys[0]); + } + function $(v, box){ + var d=v&0xff; v>>=8; + var c=v&0xff; v>>=8; + var b=v&0xff; v>>=8; + var a=v&0xff; + var r=add(box.s0[a],box.s1[b]); + r=xor(r,box.s2[c]); + return add(r,box.s3[d]); + } +//////////////////////////////////////////////////////////////////////////// + function eb(o, box){ + var l=o.left; + var r=o.right; + l=xor(l,box.p[0]); + r=xor(r,xor($(l,box),box.p[1])); + l=xor(l,xor($(r,box),box.p[2])); + r=xor(r,xor($(l,box),box.p[3])); + l=xor(l,xor($(r,box),box.p[4])); + r=xor(r,xor($(l,box),box.p[5])); + l=xor(l,xor($(r,box),box.p[6])); + r=xor(r,xor($(l,box),box.p[7])); + l=xor(l,xor($(r,box),box.p[8])); + r=xor(r,xor($(l,box),box.p[9])); + l=xor(l,xor($(r,box),box.p[10])); + r=xor(r,xor($(l,box),box.p[11])); + l=xor(l,xor($(r,box),box.p[12])); + r=xor(r,xor($(l,box),box.p[13])); + l=xor(l,xor($(r,box),box.p[14])); + r=xor(r,xor($(l,box),box.p[15])); + l=xor(l,xor($(r,box),box.p[16])); + o.right=l; + o.left=xor(r,box.p[17]); + } + + function db(o, box){ + var l=o.left; + var r=o.right; + l=xor(l,box.p[17]); + r=xor(r,xor($(l,box),box.p[16])); + l=xor(l,xor($(r,box),box.p[15])); + r=xor(r,xor($(l,box),box.p[14])); + l=xor(l,xor($(r,box),box.p[13])); + r=xor(r,xor($(l,box),box.p[12])); + l=xor(l,xor($(r,box),box.p[11])); + r=xor(r,xor($(l,box),box.p[10])); + l=xor(l,xor($(r,box),box.p[9])); + r=xor(r,xor($(l,box),box.p[8])); + l=xor(l,xor($(r,box),box.p[7])); + r=xor(r,xor($(l,box),box.p[6])); + l=xor(l,xor($(r,box),box.p[5])); + r=xor(r,xor($(l,box),box.p[4])); + l=xor(l,xor($(r,box),box.p[3])); + r=xor(r,xor($(l,box),box.p[2])); + l=xor(l,xor($(r,box),box.p[1])); + o.right=l; + o.left=xor(r,box.p[0]); + } + + // Note that we aren't caching contexts here; it might take a little longer + // but we should be more secure this way. + function init(key){ + var k=key; + if (typeof(k)=="string"){ + var a=[]; + for(var i=0; i>>18)&0x3f)); + s.push(tab.charAt((t>>>12)&0x3f)); + s.push(tab.charAt((t>>>6)&0x3f)); + s.push(tab.charAt(t&0x3f)); + } + // deal with trailers, based on patch from Peter Wood. + switch(rm){ + case 2:{ + var t=ba[i++]<<16|ba[i++]<<8; + s.push(tab.charAt((t>>>18)&0x3f)); + s.push(tab.charAt((t>>>12)&0x3f)); + s.push(tab.charAt((t>>>6)&0x3f)); + s.push(p); + break; + } + case 1:{ + var t=ba[i++]<<16; + s.push(tab.charAt((t>>>18)&0x3f)); + s.push(tab.charAt((t>>>12)&0x3f)); + s.push(p); + s.push(p); + break; + } + } + return s.join(""); + } + function fromBase64(str){ + var s=str.split(""); + var p="="; + var tab="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var out=[]; + var l=s.length; + while(s[--l]==p){ } + for (var i=0; i>>16)&0xff); + out.push((t>>>8)&0xff); + out.push(t&0xff); + } + return out; + } +//////////////////////////////////////////////////////////////////////////// +// PUBLIC FUNCTIONS +// 0.2: Only supporting ECB mode for now. +//////////////////////////////////////////////////////////////////////////// + this.getIV=function(outputType){ + var out=outputType||dojo.crypto.outputTypes.Base64; + switch(out){ + case dojo.crypto.outputTypes.Hex:{ + var s=[]; + for(var i=0; i> 3; + var pos=0; + var o={}; + var isCBC=(mode==dojo.crypto.cipherModes.CBC); + var vector={left:iv.left||null, right:iv.right||null}; + for(var i=0; i>24)&0xff); + cipher.push((o.left>>16)&0xff); + cipher.push((o.left>>8)&0xff); + cipher.push(o.left&0xff); + cipher.push((o.right>>24)&0xff); + cipher.push((o.right>>16)&0xff); + cipher.push((o.right>>8)&0xff); + cipher.push(o.right&0xff); + pos+=8; + } + switch(out){ + case dojo.crypto.outputTypes.Hex:{ + var s=[]; + for(var i=0; i> 3; + var pos=0; + var o={}; + var isCBC=(mode==dojo.crypto.cipherModes.CBC); + var vector={left:iv.left||null, right:iv.right||null}; + for(var i=0; i>24)&0xff); + pt.push((o.left>>16)&0xff); + pt.push((o.left>>8)&0xff); + pt.push(o.left&0xff); + pt.push((o.right>>24)&0xff); + pt.push((o.right>>16)&0xff); + pt.push((o.right>>8)&0xff); + pt.push(o.right&0xff); + pos+=8; + } + + // check for padding, and remove. + if(pt[pt.length-1]==pt[pt.length-2]||pt[pt.length-1]==0x01){ + var n=pt[pt.length-1]; + pt.splice(pt.length-n, n); + } + + // convert to string + for(var i=0; i>5]|=(s.charCodeAt(i/chrsz)&mask)<<(i%32); + return wa; + } + function toString(wa){ + var s=[]; + for(var i=0; i>5]>>>(i%32))&mask)); + return s.join(""); + } + function toHex(wa) { + var h="0123456789abcdef"; + var s=[]; + for(var i=0; i>2]>>((i%4)*8+4))&0xF)+h.charAt((wa[i>>2]>>((i%4)*8))&0xF)); + } + return s.join(""); + } + function toBase64(wa){ + var p="="; + var tab="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var s=[]; + for(var i=0; i>2]>>8*(i%4))&0xFF)<<16)|(((wa[i+1>>2]>>8*((i+1)%4))&0xFF)<<8)|((wa[i+2>>2]>>8*((i+2)%4))&0xFF); + for(var j=0; j<4; j++){ + if(i*8+j*6>wa.length*32) s.push(p); + else s.push(tab.charAt((t>>6*(3-j))&0x3F)); + } + } + return s.join(""); + } + function add(x,y) { + var l=(x&0xFFFF)+(y&0xFFFF); + var m=(x>>16)+(y>>16)+(l>>16); + return (m<<16)|(l&0xFFFF); + } + function R(n,c){ return (n<>>(32-c)); } + function C(q,a,b,x,s,t){ return add(R(add(add(a,q),add(x,t)),s),b); } + function FF(a,b,c,d,x,s,t){ return C((b&c)|((~b)&d),a,b,x,s,t); } + function GG(a,b,c,d,x,s,t){ return C((b&d)|(c&(~d)),a,b,x,s,t); } + function HH(a,b,c,d,x,s,t){ return C(b^c^d,a,b,x,s,t); } + function II(a,b,c,d,x,s,t){ return C(c^(b|(~d)),a,b,x,s,t); } + function core(x,len){ + x[len>>5]|=0x80<<((len)%32); + x[(((len+64)>>>9)<<4)+14]=len; + var a= 1732584193; + var b=-271733879; + var c=-1732584194; + var d= 271733878; + for(var i=0; i16) wa=core(wa,key.length*chrsz); + var l=[], r=[]; + for(var i=0; i<16; i++){ + l[i]=wa[i]^0x36363636; + r[i]=wa[i]^0x5c5c5c5c; + } + var h=core(l.concat(toWord(data)),512+data.length*chrsz); + return core(r.concat(h),640); + } + + // Public functions + this.compute=function(data,outputType){ + var out=outputType||dojo.crypto.outputTypes.Base64; + switch(out){ + case dojo.crypto.outputTypes.Hex:{ + return toHex(core(toWord(data),data.length*chrsz)); + } + case dojo.crypto.outputTypes.String:{ + return toString(core(toWord(data),data.length*chrsz)); + } + default:{ + return toBase64(core(toWord(data),data.length*chrsz)); + } + } + }; + this.getHMAC=function(data,key,outputType){ + var out=outputType||dojo.crypto.outputTypes.Base64; + switch(out){ + case dojo.crypto.outputTypes.Hex:{ + return toHex(hmac(data,key)); + } + case dojo.crypto.outputTypes.String:{ + return toString(hmac(data,key)); + } + default:{ + return toBase64(hmac(data,key)); + } + } + }; +}(); diff --git a/source/web/scripts/ajax/src/crypto/Rijndael.js b/source/web/scripts/ajax/src/crypto/Rijndael.js new file mode 100644 index 0000000000..6eddf57ad2 --- /dev/null +++ b/source/web/scripts/ajax/src/crypto/Rijndael.js @@ -0,0 +1,12 @@ +dojo.provide("dojo.crypto.Rijndael"); +dojo.require("dojo.crypto"); +dojo.require("dojo.experimental"); + +dojo.experimental("dojo.crypto.Rijndael"); + +dojo.crypto.Rijndael = new function(){ + this.encrypt=function(plaintext, key){ + }; + this.decrypt=function(ciphertext, key){ + }; +}(); diff --git a/source/web/scripts/ajax/src/crypto/SHA1.js b/source/web/scripts/ajax/src/crypto/SHA1.js new file mode 100644 index 0000000000..7119a7c1c9 --- /dev/null +++ b/source/web/scripts/ajax/src/crypto/SHA1.js @@ -0,0 +1,154 @@ +dojo.require("dojo.crypto"); +dojo.provide("dojo.crypto.SHA1"); +dojo.require("dojo.experimental"); + +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * + * Version 2.1a Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + * + * Dojo port by Tom Trenka + */ +dojo.experimental("dojo.crypto.SHA1"); + +dojo.crypto.SHA1 = new function(){ + var chrsz=8; + var mask=(1<>5]|=(s.charCodeAt(i/chrsz)&mask)<<(i%32); + return wa; + } + function toString(wa){ + var s=[]; + for(var i=0; i>5]>>>(i%32))&mask)); + return s.join(""); + } + function toHex(wa) { + var h="0123456789abcdef"; + var s=[]; + for(var i=0; i>2]>>((i%4)*8+4))&0xF)+h.charAt((wa[i>>2]>>((i%4)*8))&0xF)); + } + return s.join(""); + } + function toBase64(wa){ + var p="="; + var tab="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var s=[]; + for(var i=0; i>2]>>8*(i%4))&0xFF)<<16)|(((wa[i+1>>2]>>8*((i+1)%4))&0xFF)<<8)|((wa[i+2>>2]>>8*((i+2)%4))&0xFF); + for(var j=0; j<4; j++){ + if(i*8+j*6>wa.length*32) s.push(p); + else s.push(tab.charAt((t>>6*(3-j))&0x3F)); + } + } + return s.join(""); + } + + // math + function add(x,y){ + var l=(x&0xffff)+(y&0xffff); + var m=(x>>16)+(y>>16)+(l>>16); + return (m<<16)|(l&0xffff); + } + function r(x,n){ return (x<>>(32-n)); } + + // SHA rounds + function f(u,v,w){ return ((u&v)|(~u&w)); } + function g(u,v,w){ return ((u&v)|(u&w)|(v&w)); } + function h(u,v,w){ return (u^v^w); } + + function fn(i,u,v,w){ + if(i<20) return f(u,v,w); + if(i<40) return h(u,v,w); + if(i<60) return g(u,v,w); + return h(u,v,w); + } + function cnst(i){ + if(i<20) return 1518500249; + if(i<40) return 1859775393; + if(i<60) return -1894007588; + return -899497514; + } + + function core(x,len){ + x[len>>5]|=0x80<<(24-len%32); + x[((len+64>>9)<<4)+15]=len; + + var w=[]; + var a= 1732584193; // 0x67452301 + var b=-271733879; // 0xefcdab89 + var c=-1732584194; // 0x98badcfe + var d= 271733878; // 0x10325476 + var e=-1009589776; // 0xc3d2e1f0 + + for(var i=0; i16) wa=core(wa,key.length*chrsz); + var l=[], r=[]; + for(var i=0; i<16; i++){ + l[i]=wa[i]^0x36363636; + r[i]=wa[i]^0x5c5c5c5c; + } + var h=core(l.concat(toWord(data)),512+data.length*chrsz); + return core(r.concat(h),640); + } + + this.compute=function(data,outputType){ + var out=outputType||dojo.crypto.outputTypes.Base64; + switch(out){ + case dojo.crypto.outputTypes.Hex:{ + return toHex(core(toWord(data),data.length*chrsz)); + } + case dojo.crypto.outputTypes.String:{ + return toString(core(toWord(data),data.length*chrsz)); + } + default:{ + return toBase64(core(toWord(data),data.length*chrsz)); + } + } + }; + this.getHMAC=function(data,key,outputType){ + var out=outputType||dojo.crypto.outputTypes.Base64; + switch(out){ + case dojo.crypto.outputTypes.Hex:{ + return toHex(hmac(data,key)); + } + case dojo.crypto.outputTypes.String:{ + return toString(hmac(data,key)); + } + default:{ + return toBase64(hmac(data,key)); + } + } + }; +}(); diff --git a/source/web/scripts/ajax/src/crypto/SHA256.js b/source/web/scripts/ajax/src/crypto/SHA256.js new file mode 100644 index 0000000000..163eb8bcb8 --- /dev/null +++ b/source/web/scripts/ajax/src/crypto/SHA256.js @@ -0,0 +1,10 @@ +dojo.provide("dojo.crypto.SHA256"); +dojo.require("dojo.crypto"); +dojo.require("dojo.experimental"); + +dojo.experimental("dojo.crypto.SHA256"); + +dojo.crypto.SHA256 = new function(){ + this.compute=function(s){ + }; +}(); diff --git a/source/web/scripts/ajax/src/crypto/__package__.js b/source/web/scripts/ajax/src/crypto/__package__.js new file mode 100644 index 0000000000..494f789bcb --- /dev/null +++ b/source/web/scripts/ajax/src/crypto/__package__.js @@ -0,0 +1,7 @@ +dojo.kwCompoundRequire({ + common: [ + "dojo.crypto", + "dojo.crypto.MD5" + ] +}); +dojo.provide("dojo.crypto.*"); diff --git a/source/web/scripts/ajax/src/data.js b/source/web/scripts/ajax/src/data.js new file mode 100644 index 0000000000..5b6de2fc59 --- /dev/null +++ b/source/web/scripts/ajax/src/data.js @@ -0,0 +1,5 @@ +dojo.provide("dojo.data"); + +// currently a stub for dojo.data + +dojo.data = {}; diff --git a/source/web/scripts/ajax/src/data/Attribute.js b/source/web/scripts/ajax/src/data/Attribute.js new file mode 100644 index 0000000000..c3d0c5602e --- /dev/null +++ b/source/web/scripts/ajax/src/data/Attribute.js @@ -0,0 +1,52 @@ +dojo.provide("dojo.data.Attribute"); +dojo.require("dojo.data.Item"); +dojo.require("dojo.lang.assert"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.Attribute = function(/* dojo.data.provider.Base */ dataProvider, /* string */ attributeId) { + /** + * summary: + * An Attribute object represents something like a column in + * a relational database. + */ + dojo.lang.assertType(dataProvider, dojo.data.provider.Base, {optional: true}); + dojo.lang.assertType(attributeId, String); + dojo.data.Item.call(this, dataProvider); + this._attributeId = attributeId; +}; +dojo.inherits(dojo.data.Attribute, dojo.data.Item); + +// ------------------------------------------------------------------- +// Public instance methods +// ------------------------------------------------------------------- +dojo.data.Attribute.prototype.toString = function() { + return this._attributeId; // string +}; + +dojo.data.Attribute.prototype.getAttributeId = function() { + /** + * summary: + * Returns the string token that uniquely identifies this + * attribute within the context of a data provider. + * For a data provider that accesses relational databases, + * typical attributeIds might be tokens like "name", "age", + * "ssn", or "dept_key". + */ + return this._attributeId; // string +}; + +dojo.data.Attribute.prototype.getType = function() { + /** + * summary: Returns the data type of the values of this attribute. + */ + return this.get('type'); // dojo.data.Type or null +}; + +dojo.data.Attribute.prototype.setType = function(/* dojo.data.Type or null */ type) { + /** + * summary: Sets the data type for this attribute. + */ + this.set('type', type); +}; diff --git a/source/web/scripts/ajax/src/data/Item.js b/source/web/scripts/ajax/src/data/Item.js new file mode 100644 index 0000000000..df0b9ddf8a --- /dev/null +++ b/source/web/scripts/ajax/src/data/Item.js @@ -0,0 +1,317 @@ +dojo.provide("dojo.data.Item"); +dojo.require("dojo.data.Observable"); +dojo.require("dojo.data.Value"); +dojo.require("dojo.lang.common"); +dojo.require("dojo.lang.assert"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.Item = function(/* dojo.data.provider.Base */ dataProvider) { + /** + * summary: + * An Item has attributes and attribute values, sort of like + * a record in a database, or a 'struct' in C. Instances of + * the Item class know how to store and retrieve their + * attribute values. + */ + dojo.lang.assertType(dataProvider, dojo.data.provider.Base, {optional: true}); + dojo.data.Observable.call(this); + this._dataProvider = dataProvider; + this._dictionaryOfAttributeValues = {}; +}; +dojo.inherits(dojo.data.Item, dojo.data.Observable); + +// ------------------------------------------------------------------- +// Public class methods +// ------------------------------------------------------------------- +dojo.data.Item.compare = function(/* dojo.data.Item */ itemOne, /* dojo.data.Item */ itemTwo) { + /** + * summary: + * Given two Items to compare, this method returns 0, 1, or -1. + * This method is designed to be used by sorting routines, like + * the JavaScript built-in Array sort() method. + * + * Example: + *
+	 *   var a = dataProvider.newItem("kermit");
+	 *   var b = dataProvider.newItem("elmo");
+	 *   var c = dataProvider.newItem("grover");
+	 *   var array = new Array(a, b, c);
+	 *   array.sort(dojo.data.Item.compare);
+	 * 
+ */ + dojo.lang.assertType(itemOne, dojo.data.Item); + if (!dojo.lang.isOfType(itemTwo, dojo.data.Item)) { + return -1; + } + var nameOne = itemOne.getName(); + var nameTwo = itemTwo.getName(); + if (nameOne == nameTwo) { + var attributeArrayOne = itemOne.getAttributes(); + var attributeArrayTwo = itemTwo.getAttributes(); + if (attributeArrayOne.length != attributeArrayTwo.length) { + if (attributeArrayOne.length > attributeArrayTwo.length) { + return 1; + } else { + return -1; + } + } + for (var i in attributeArrayOne) { + var attribute = attributeArrayOne[i]; + var arrayOfValuesOne = itemOne.getValues(attribute); + var arrayOfValuesTwo = itemTwo.getValues(attribute); + dojo.lang.assert(arrayOfValuesOne && (arrayOfValuesOne.length > 0)); + if (!arrayOfValuesTwo) { + return 1; + } + if (arrayOfValuesOne.length != arrayOfValuesTwo.length) { + if (arrayOfValuesOne.length > arrayOfValuesTwo.length) { + return 1; + } else { + return -1; + } + } + for (var j in arrayOfValuesOne) { + var value = arrayOfValuesOne[j]; + if (!itemTwo.hasAttributeValue(value)) { + return 1; + } + } + return 0; + } + } else { + if (nameOne > nameTwo) { + return 1; + } else { + return -1; // 0, 1, or -1 + } + } +}; + +// ------------------------------------------------------------------- +// Public instance methods +// ------------------------------------------------------------------- +dojo.data.Item.prototype.toString = function() { + /** + * Returns a simple string representation of the item. + */ + var arrayOfStrings = []; + var attributes = this.getAttributes(); + for (var i in attributes) { + var attribute = attributes[i]; + var arrayOfValues = this.getValues(attribute); + var valueString; + if (arrayOfValues.length == 1) { + valueString = arrayOfValues[0]; + } else { + valueString = '['; + valueString += arrayOfValues.join(', '); + valueString += ']'; + } + arrayOfStrings.push(' ' + attribute + ': ' + valueString); + } + var returnString = '{ '; + returnString += arrayOfStrings.join(',\n'); + returnString += ' }'; + return returnString; // string +}; + +dojo.data.Item.prototype.compare = function(/* dojo.data.Item */ otherItem) { + /** + * summary: Compares this Item to another Item, and returns 0, 1, or -1. + */ + return dojo.data.Item.compare(this, otherItem); // 0, 1, or -1 +}; + +dojo.data.Item.prototype.isEqual = function(/* dojo.data.Item */ otherItem) { + /** + * summary: Returns true if this Item is equal to the otherItem, or false otherwise. + */ + return (this.compare(otherItem) == 0); // boolean +}; + +dojo.data.Item.prototype.getName = function() { + return this.get('name'); +}; + +dojo.data.Item.prototype.get = function(/* string or dojo.data.Attribute */ attributeId) { + /** + * summary: Returns a single literal value, like "foo" or 33. + */ + // dojo.lang.assertType(attributeId, [String, dojo.data.Attribute]); + var literalOrValueOrArray = this._dictionaryOfAttributeValues[attributeId]; + if (dojo.lang.isUndefined(literalOrValueOrArray)) { + return null; // null + } + if (literalOrValueOrArray instanceof dojo.data.Value) { + return literalOrValueOrArray.getValue(); // literal + } + if (dojo.lang.isArray(literalOrValueOrArray)) { + var dojoDataValue = literalOrValueOrArray[0]; + return dojoDataValue.getValue(); // literal + } + return literalOrValueOrArray; // literal +}; + +dojo.data.Item.prototype.getValue = function(/* string or dojo.data.Attribute */ attributeId) { + /** + * summary: Returns a single instance of dojo.data.Value. + */ + // dojo.lang.assertType(attributeId, [String, dojo.data.Attribute]); + var literalOrValueOrArray = this._dictionaryOfAttributeValues[attributeId]; + if (dojo.lang.isUndefined(literalOrValueOrArray)) { + return null; // null + } + if (literalOrValueOrArray instanceof dojo.data.Value) { + return literalOrValueOrArray; // dojo.data.Value + } + if (dojo.lang.isArray(literalOrValueOrArray)) { + var dojoDataValue = literalOrValueOrArray[0]; + return dojoDataValue; // dojo.data.Value + } + var literal = literalOrValueOrArray; + dojoDataValue = new dojo.data.Value(literal); + this._dictionaryOfAttributeValues[attributeId] = dojoDataValue; + return dojoDataValue; // dojo.data.Value +}; + +dojo.data.Item.prototype.getValues = function(/* string or dojo.data.Attribute */ attributeId) { + /** + * summary: Returns an array of dojo.data.Value objects. + */ + // dojo.lang.assertType(attributeId, [String, dojo.data.Attribute]); + var literalOrValueOrArray = this._dictionaryOfAttributeValues[attributeId]; + if (dojo.lang.isUndefined(literalOrValueOrArray)) { + return null; // null + } + if (literalOrValueOrArray instanceof dojo.data.Value) { + var array = [literalOrValueOrArray]; + this._dictionaryOfAttributeValues[attributeId] = array; + return array; // Array + } + if (dojo.lang.isArray(literalOrValueOrArray)) { + return literalOrValueOrArray; // Array + } + var literal = literalOrValueOrArray; + var dojoDataValue = new dojo.data.Value(literal); + array = [dojoDataValue]; + this._dictionaryOfAttributeValues[attributeId] = array; + return array; // Array +}; + +dojo.data.Item.prototype.load = function(/* string or dojo.data.Attribute */ attributeId, /* anything */ value) { + /** + * summary: + * Used for loading an attribute value into an item when + * the item is first being loaded into memory from some + * data store (such as a file). + */ + // dojo.lang.assertType(attributeId, [String, dojo.data.Attribute]); + this._dataProvider.registerAttribute(attributeId); + var literalOrValueOrArray = this._dictionaryOfAttributeValues[attributeId]; + if (dojo.lang.isUndefined(literalOrValueOrArray)) { + this._dictionaryOfAttributeValues[attributeId] = value; + return; + } + if (!(value instanceof dojo.data.Value)) { + value = new dojo.data.Value(value); + } + if (literalOrValueOrArray instanceof dojo.data.Value) { + var array = [literalOrValueOrArray, value]; + this._dictionaryOfAttributeValues[attributeId] = array; + return; + } + if (dojo.lang.isArray(literalOrValueOrArray)) { + literalOrValueOrArray.push(value); + return; + } + var literal = literalOrValueOrArray; + var dojoDataValue = new dojo.data.Value(literal); + array = [dojoDataValue, value]; + this._dictionaryOfAttributeValues[attributeId] = array; +}; + +dojo.data.Item.prototype.set = function(/* string or dojo.data.Attribute */ attributeId, /* anything */ value) { + /** + * summary: + * Used for setting an attribute value as a result of a + * user action. + */ + // dojo.lang.assertType(attributeId, [String, dojo.data.Attribute]); + this._dataProvider.registerAttribute(attributeId); + this._dictionaryOfAttributeValues[attributeId] = value; + this._dataProvider.noteChange(this, attributeId, value); +}; + +dojo.data.Item.prototype.setValue = function(/* string or dojo.data.Attribute */ attributeId, /* dojo.data.Value */ value) { + this.set(attributeId, value); +}; + +dojo.data.Item.prototype.addValue = function(/* string or dojo.data.Attribute */ attributeId, /* anything */ value) { + /** + * summary: + * Used for adding an attribute value as a result of a + * user action. + */ + this.load(attributeId, value); + this._dataProvider.noteChange(this, attributeId, value); +}; + +dojo.data.Item.prototype.setValues = function(/* string or dojo.data.Attribute */ attributeId, /* Array */ arrayOfValues) { + /** + * summary: + * Used for setting an array of attribute values as a result of a + * user action. + */ + // dojo.lang.assertType(attributeId, [String, dojo.data.Attribute]); + dojo.lang.assertType(arrayOfValues, Array); + this._dataProvider.registerAttribute(attributeId); + var finalArray = []; + this._dictionaryOfAttributeValues[attributeId] = finalArray; + for (var i in arrayOfValues) { + var value = arrayOfValues[i]; + if (!(value instanceof dojo.data.Value)) { + value = new dojo.data.Value(value); + } + finalArray.push(value); + this._dataProvider.noteChange(this, attributeId, value); + } +}; + +dojo.data.Item.prototype.getAttributes = function() { + /** + * summary: + * Returns an array containing all of the attributes for which + * this item has attribute values. + */ + var arrayOfAttributes = []; + for (var key in this._dictionaryOfAttributeValues) { + arrayOfAttributes.push(this._dataProvider.getAttribute(key)); + } + return arrayOfAttributes; // Array +}; + +dojo.data.Item.prototype.hasAttribute = function(/* string or dojo.data.Attribute */ attributeId) { + /** + * summary: Returns true if the given attribute of the item has been assigned any value. + */ + // dojo.lang.assertType(attributeId, [String, dojo.data.Attribute]); + return (attributeId in this._dictionaryOfAttributeValues); // boolean +}; + +dojo.data.Item.prototype.hasAttributeValue = function(/* string or dojo.data.Attribute */ attributeId, /* anything */ value) { + /** + * summary: Returns true if the given attribute of the item has been assigned the given value. + */ + var arrayOfValues = this.getValues(attributeId); + for (var i in arrayOfValues) { + var candidateValue = arrayOfValues[i]; + if (candidateValue.isEqual(value)) { + return true; // boolean + } + } + return false; // boolean +}; + + diff --git a/source/web/scripts/ajax/src/data/Kind.js b/source/web/scripts/ajax/src/data/Kind.js new file mode 100644 index 0000000000..b7f26c54ae --- /dev/null +++ b/source/web/scripts/ajax/src/data/Kind.js @@ -0,0 +1,18 @@ +dojo.provide("dojo.data.Kind"); +dojo.require("dojo.data.Item"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.Kind = function(/* dojo.data.provider.Base */ dataProvider) { + /** + * summary: + * A Kind represents a kind of item. In the dojo data model + * the item Snoopy might belong to the 'kind' Dog, where in + * a Java program the object Snoopy would belong to the 'class' + * Dog, and in MySQL the record for Snoopy would be in the + * table Dog. + */ + dojo.data.Item.call(this, dataProvider); +}; +dojo.inherits(dojo.data.Kind, dojo.data.Item); diff --git a/source/web/scripts/ajax/src/data/Observable.js b/source/web/scripts/ajax/src/data/Observable.js new file mode 100644 index 0000000000..327f00bab6 --- /dev/null +++ b/source/web/scripts/ajax/src/data/Observable.js @@ -0,0 +1,49 @@ +dojo.provide("dojo.data.Observable"); +dojo.require("dojo.lang.common"); +dojo.require("dojo.lang.assert"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.Observable = function() { +}; + +// ------------------------------------------------------------------- +// Public instance methods +// ------------------------------------------------------------------- +dojo.data.Observable.prototype.addObserver = function(/* object */ observer) { + /** + * summary: Registers an object as an observer of this item, + * so that the object will be notified when the item changes. + */ + dojo.lang.assertType(observer, Object); + dojo.lang.assertType(observer.observedObjectHasChanged, Function); + if (!this._arrayOfObservers) { + this._arrayOfObservers = []; + } + if (!dojo.lang.inArray(this._arrayOfObservers, observer)) { + this._arrayOfObservers.push(observer); + } +}; + +dojo.data.Observable.prototype.removeObserver = function(/* object */ observer) { + /** + * summary: Removes the observer registration for a previously + * registered object. + */ + if (!this._arrayOfObservers) { + return; + } + var index = dojo.lang.indexOf(this._arrayOfObservers, observer); + if (index != -1) { + this._arrayOfObservers.splice(index, 1); + } +}; + +dojo.data.Observable.prototype.getObservers = function() { + /** + * summary: Returns an array with all the observers of this item. + */ + return this._arrayOfObservers; // Array or undefined +}; + diff --git a/source/web/scripts/ajax/src/data/ResultSet.js b/source/web/scripts/ajax/src/data/ResultSet.js new file mode 100644 index 0000000000..a5839971e2 --- /dev/null +++ b/source/web/scripts/ajax/src/data/ResultSet.js @@ -0,0 +1,60 @@ +dojo.provide("dojo.data.ResultSet"); +dojo.require("dojo.lang.assert"); +dojo.require("dojo.collections.Collections"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.ResultSet = function(/* dojo.data.provider.Base */ dataProvider, /* Array */ arrayOfItems) { + /** + * summary: + * A ResultSet holds a collection of Items. A data provider + * returns a ResultSet in reponse to a query. + * (The name "Result Set" comes from the MySQL terminology.) + */ + dojo.lang.assertType(dataProvider, dojo.data.provider.Base, {optional: true}); + dojo.lang.assertType(arrayOfItems, Array, {optional: true}); + dojo.data.Observable.call(this); + this._dataProvider = dataProvider; + this._arrayOfItems = []; + if (arrayOfItems) { + this._arrayOfItems = arrayOfItems; + } +}; +dojo.inherits(dojo.data.ResultSet, dojo.data.Observable); + +// ------------------------------------------------------------------- +// Public instance methods +// ------------------------------------------------------------------- +dojo.data.ResultSet.prototype.toString = function() { + var returnString = this._arrayOfItems.join(', '); + return returnString; // string +}; + +dojo.data.ResultSet.prototype.toArray = function() { + return this._arrayOfItems; // Array +}; + +dojo.data.ResultSet.prototype.getIterator = function() { + return new dojo.collections.Iterator(this._arrayOfItems); +}; + +dojo.data.ResultSet.prototype.getLength = function() { + return this._arrayOfItems.length; // integer +}; + +dojo.data.ResultSet.prototype.getItemAt = function(/* numeric */ index) { + return this._arrayOfItems[index]; +}; + +dojo.data.ResultSet.prototype.indexOf = function(/* dojo.data.Item */ item) { + return dojo.lang.indexOf(this._arrayOfItems, item); // integer +}; + +dojo.data.ResultSet.prototype.contains = function(/* dojo.data.Item */ item) { + return dojo.lang.inArray(this._arrayOfItems, item); // boolean +}; + +dojo.data.ResultSet.prototype.getDataProvider = function() { + return this._dataProvider; // dojo.data.provider.Base +}; \ No newline at end of file diff --git a/source/web/scripts/ajax/src/data/SimpleStore.js b/source/web/scripts/ajax/src/data/SimpleStore.js new file mode 100644 index 0000000000..29d830fd77 --- /dev/null +++ b/source/web/scripts/ajax/src/data/SimpleStore.js @@ -0,0 +1,194 @@ +dojo.provide("dojo.data.SimpleStore"); +dojo.require("dojo.lang"); + +dojo.require("dojo.experimental"); +dojo.experimental("dojo.data.SimpleStore"); + +/* SimpleStore + * Designed to be a simple store of data with access methods... + * specifically to be mixed into other objects (such as widgets). + * + * This *might* be better in collections, we'll see. + */ +dojo.data.SimpleStore = function(/* array? */json){ + // summary + // Data Store with accessor methods. + var data = []; + this.keyField = "Id"; + + this.get = function(){ + // summary + // Get the internal data array, should not be used. + return data; // array + }; + this.getByKey = function(/* string */key){ + // summary + // Find the internal data object by key. + for(var i=0; i-1){ + data.splice(idx,1); + } + this.onRemoveData(o); + }; + this.removeDataByKey = function(/*string*/key){ + // summary + // remove the object at key from the internal data array. + this.removeData(this.getDataByKey(key)); + }; + this.removeDataByIndex = function(/*number*/idx){ + // summary + // remove the object at idx from the internal data array. + this.removeData(this.getDataByIndex(idx)); + }; + + if(json && json.length && json[0]){ + this.setData(json); + } +}; + +dojo.lang.extend(dojo.data.SimpleStore, { + getField:function(/*object*/obj, /*string*/field){ + // helper to get the nested value if needed. + var parts=field.split("."), i=0, o=obj; + do{ + if(parts[i].indexOf("()")>-1){ + var temp=parts[i++].split("()")[0]; + if(!o[temp]){ + dojo.raise("dojo.data.SimpleStore.getField(obj, '" + field + "'): '" + field + "' is not a property of the passed object."); + } else { + o = o[temp](); + } + } else { + o = o[parts[i++]]; + } + } while (i + * "Title, Year, Producer \n Alien, 1979, Ridley Scott \n Blade Runner, 1982, Ridley Scott" + * + * We will return this data structure: + *
+		 *   [["Title", "Year", "Producer"]
+		 *    ["Alien", "1979", "Ridley Scott"],  
+		 *    ["Blade Runner", "1982", "Ridley Scott"]]
+		 * 
+ */ + dojo.lang.assertType(csvFileContents, String); + + var lineEndingCharacters = new RegExp("\r\n|\n|\r"); + var leadingWhiteSpaceCharacters = new RegExp("^\\s+",'g'); + var trailingWhiteSpaceCharacters = new RegExp("\\s+$",'g'); + var doubleQuotes = new RegExp('""','g'); + var arrayOfOutputRecords = []; + + var arrayOfInputLines = csvFileContents.split(lineEndingCharacters); + for (var i in arrayOfInputLines) { + var singleLine = arrayOfInputLines[i]; + if (singleLine.length > 0) { + var listOfFields = singleLine.split(','); + var j = 0; + while (j < listOfFields.length) { + var space_field_space = listOfFields[j]; + var field_space = space_field_space.replace(leadingWhiteSpaceCharacters, ''); // trim leading whitespace + var field = field_space.replace(trailingWhiteSpaceCharacters, ''); // trim trailing whitespace + var firstChar = field.charAt(0); + var lastChar = field.charAt(field.length - 1); + var secondToLastChar = field.charAt(field.length - 2); + var thirdToLastChar = field.charAt(field.length - 3); + if ((firstChar == '"') && + ((lastChar != '"') || + ((lastChar == '"') && (secondToLastChar == '"') && (thirdToLastChar != '"')) )) { + if (j+1 === listOfFields.length) { + // alert("The last field in record " + i + " is corrupted:\n" + field); + return null; + } + var nextField = listOfFields[j+1]; + listOfFields[j] = field_space + ',' + nextField; + listOfFields.splice(j+1, 1); // delete element [j+1] from the list + } else { + if ((firstChar == '"') && (lastChar == '"')) { + field = field.slice(1, (field.length - 1)); // trim the " characters off the ends + field = field.replace(doubleQuotes, '"'); // replace "" with " + } + listOfFields[j] = field; + j += 1; + } + } + arrayOfOutputRecords.push(listOfFields); + } + } + return arrayOfOutputRecords; // Array + }; + + this.loadDataProviderFromFileContents = function(/* dojo.data.provider.Base */ dataProvider, /* string */ csvFileContents) { + dojo.lang.assertType(dataProvider, dojo.data.provider.Base); + dojo.lang.assertType(csvFileContents, String); + var arrayOfArrays = this.getArrayStructureFromCsvFileContents(csvFileContents); + if (arrayOfArrays) { + var arrayOfKeys = arrayOfArrays[0]; + for (var i = 1; i < arrayOfArrays.length; ++i) { + var row = arrayOfArrays[i]; + var item = dataProvider.getNewItemToLoad(); + for (var j in row) { + var value = row[j]; + var key = arrayOfKeys[j]; + item.load(key, value); + } + } + } + }; + + this.getCsvStringFromResultSet = function(/* dojo.data.ResultSet */ resultSet) { + dojo.unimplemented('dojo.data.format.Csv.getCsvStringFromResultSet'); + var csvString = null; + return csvString; // String + }; + +}(); diff --git a/source/web/scripts/ajax/src/data/format/Json.js b/source/web/scripts/ajax/src/data/format/Json.js new file mode 100644 index 0000000000..16e3d07863 --- /dev/null +++ b/source/web/scripts/ajax/src/data/format/Json.js @@ -0,0 +1,93 @@ +dojo.provide("dojo.data.format.Json"); +dojo.require("dojo.lang.assert"); + +dojo.data.format.Json = new function() { + + // ------------------------------------------------------------------- + // Public functions + // ------------------------------------------------------------------- + this.loadDataProviderFromFileContents = function(/* dojo.data.provider.Base */ dataProvider, /* string */ jsonFileContents) { + dojo.lang.assertType(dataProvider, dojo.data.provider.Base); + dojo.lang.assertType(jsonFileContents, String); + var arrayOfJsonData = eval("(" + jsonFileContents + ")"); + this.loadDataProviderFromArrayOfJsonData(dataProvider, arrayOfJsonData); + }; + + this.loadDataProviderFromArrayOfJsonData = function(/* dojo.data.provider.Base */ dataProvider, /* Array */ arrayOfJsonData) { + dojo.lang.assertType(arrayOfJsonData, Array, {optional: true}); + if (arrayOfJsonData && (arrayOfJsonData.length > 0)) { + var firstRow = arrayOfJsonData[0]; + dojo.lang.assertType(firstRow, [Array, "pureobject"]); + if (dojo.lang.isArray(firstRow)) { + _loadDataProviderFromArrayOfArrays(dataProvider, arrayOfJsonData); + } else { + dojo.lang.assertType(firstRow, "pureobject"); + _loadDataProviderFromArrayOfObjects(dataProvider, arrayOfJsonData); + } + } + }; + + this.getJsonStringFromResultSet = function(/* dojo.data.ResultSet */ resultSet) { + dojo.unimplemented('dojo.data.format.Json.getJsonStringFromResultSet'); + var jsonString = null; + return jsonString; // String + }; + + // ------------------------------------------------------------------- + // Private functions + // ------------------------------------------------------------------- + function _loadDataProviderFromArrayOfArrays(/* dojo.data.provider.Base */ dataProvider, /* Array */ arrayOfJsonData) { + /** + * Example: + * var arrayOfJsonStates = [ + * [ "abbr", "population", "name" ] + * [ "WA", 5894121, "Washington" ], + * [ "WV", 1808344, "West Virginia" ], + * [ "WI", 5453896, "Wisconsin" ], + * [ "WY", 493782, "Wyoming" ] ]; + * this._loadFromArrayOfArrays(arrayOfJsonStates); + */ + var arrayOfKeys = arrayOfJsonData[0]; + for (var i = 1; i < arrayOfJsonData.length; ++i) { + var row = arrayOfJsonData[i]; + var item = dataProvider.getNewItemToLoad(); + for (var j in row) { + var value = row[j]; + var key = arrayOfKeys[j]; + item.load(key, value); + } + } + } + + function _loadDataProviderFromArrayOfObjects(/* dojo.data.provider.Base */ dataProvider, /* Array */ arrayOfJsonData) { + /** + * Example: + * var arrayOfJsonStates = [ + * { abbr: "WA", name: "Washington" }, + * { abbr: "WV", name: "West Virginia" }, + * { abbr: "WI", name: "Wisconsin", song: "On, Wisconsin!" }, + * { abbr: "WY", name: "Wyoming", cities: ["Lander", "Cheyenne", "Laramie"] } ]; + * this._loadFromArrayOfArrays(arrayOfJsonStates); + */ + // dojo.debug("_loadDataProviderFromArrayOfObjects"); + for (var i in arrayOfJsonData) { + var row = arrayOfJsonData[i]; + var item = dataProvider.getNewItemToLoad(); + for (var key in row) { + var value = row[key]; + if (dojo.lang.isArray(value)) { + var arrayOfValues = value; + for (var j in arrayOfValues) { + value = arrayOfValues[j]; + item.load(key, value); + // dojo.debug("loaded: " + key + " = " + value); + } + } else { + item.load(key, value); + } + } + } + } + +}(); + diff --git a/source/web/scripts/ajax/src/data/provider/Base.js b/source/web/scripts/ajax/src/data/provider/Base.js new file mode 100644 index 0000000000..2dd45ce85e --- /dev/null +++ b/source/web/scripts/ajax/src/data/provider/Base.js @@ -0,0 +1,173 @@ +dojo.provide("dojo.data.provider.Base"); +dojo.require("dojo.lang.assert"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.provider.Base = function() { + /** + * summary: + * A Data Provider serves as a connection to some data source, + * like a relational database. This data provider Base class + * serves as an abstract superclass for other data provider + * classes. + */ + this._countOfNestedTransactions = 0; + this._changesInCurrentTransaction = null; +}; + +// ------------------------------------------------------------------- +// Public instance methods +// ------------------------------------------------------------------- +dojo.data.provider.Base.prototype.beginTransaction = function() { + /** + * Marks the beginning of a transaction. + * + * Each time you call beginTransaction() you open a new transaction, + * which you need to close later using endTransaction(). Transactions + * may be nested, but the beginTransaction and endTransaction calls + * always need to come in pairs. + */ + if (this._countOfNestedTransactions === 0) { + this._changesInCurrentTransaction = []; + } + this._countOfNestedTransactions += 1; +}; + +dojo.data.provider.Base.prototype.endTransaction = function() { + /** + * Marks the end of a transaction. + */ + this._countOfNestedTransactions -= 1; + dojo.lang.assert(this._countOfNestedTransactions >= 0); + + if (this._countOfNestedTransactions === 0) { + var listOfChangesMade = this._saveChanges(); + this._changesInCurrentTransaction = null; + if (listOfChangesMade.length > 0) { + // dojo.debug("endTransaction: " + listOfChangesMade.length + " changes made"); + this._notifyObserversOfChanges(listOfChangesMade); + } + } +}; + +dojo.data.provider.Base.prototype.getNewItemToLoad = function() { + return this._newItem(); // dojo.data.Item +}; + +dojo.data.provider.Base.prototype.newItem = function(/* string */ itemName) { + /** + * Creates a new item. + */ + dojo.lang.assertType(itemName, String, {optional: true}); + var item = this._newItem(); + if (itemName) { + item.set('name', itemName); + } + return item; // dojo.data.Item +}; + +dojo.data.provider.Base.prototype.newAttribute = function(/* string */ attributeId) { + /** + * Creates a new attribute. + */ + dojo.lang.assertType(attributeId, String, {optional: true}); + var attribute = this._newAttribute(attributeId); + return attribute; // dojo.data.Attribute +}; + +dojo.data.provider.Base.prototype.getAttribute = function(/* string */ attributeId) { + dojo.unimplemented('dojo.data.provider.Base'); + var attribute; + return attribute; // dojo.data.Attribute +}; + +dojo.data.provider.Base.prototype.getAttributes = function() { + dojo.unimplemented('dojo.data.provider.Base'); + return this._arrayOfAttributes; // Array +}; + +dojo.data.provider.Base.prototype.fetchArray = function() { + dojo.unimplemented('dojo.data.provider.Base'); + return []; // Array +}; + +dojo.data.provider.Base.prototype.fetchResultSet = function() { + dojo.unimplemented('dojo.data.provider.Base'); + var resultSet; + return resultSet; // dojo.data.ResultSet +}; + +dojo.data.provider.Base.prototype.noteChange = function(/* dojo.data.Item */ item, /* string or dojo.data.Attribute */ attribute, /* anything */ value) { + var change = {item: item, attribute: attribute, value: value}; + if (this._countOfNestedTransactions === 0) { + this.beginTransaction(); + this._changesInCurrentTransaction.push(change); + this.endTransaction(); + } else { + this._changesInCurrentTransaction.push(change); + } +}; + +dojo.data.provider.Base.prototype.addItemObserver = function(/* dojo.data.Item */ item, /* object */ observer) { + /** + * summary: Registers an object as an observer of an item, + * so that the object will be notified when the item changes. + */ + dojo.lang.assertType(item, dojo.data.Item); + item.addObserver(observer); +}; + +dojo.data.provider.Base.prototype.removeItemObserver = function(/* dojo.data.Item */ item, /* object */ observer) { + /** + * summary: Removes the observer registration for a previously + * registered object. + */ + dojo.lang.assertType(item, dojo.data.Item); + item.removeObserver(observer); +}; + +// ------------------------------------------------------------------- +// Private instance methods +// ------------------------------------------------------------------- +dojo.data.provider.Base.prototype._newItem = function() { + var item = new dojo.data.Item(this); + return item; // dojo.data.Item +}; + +dojo.data.provider.Base.prototype._newAttribute = function(/* String */ attributeId) { + var attribute = new dojo.data.Attribute(this); + return attribute; // dojo.data.Attribute +}; + +dojo.data.provider.Base.prototype._saveChanges = function() { + var arrayOfChangesMade = this._changesInCurrentTransaction; + return arrayOfChangesMade; // Array +}; + +dojo.data.provider.Base.prototype._notifyObserversOfChanges = function(/* Array */ arrayOfChanges) { + var arrayOfResultSets = this._getResultSets(); + for (var i in arrayOfChanges) { + var change = arrayOfChanges[i]; + var changedItem = change.item; + var arrayOfItemObservers = changedItem.getObservers(); + for (var j in arrayOfItemObservers) { + var observer = arrayOfItemObservers[j]; + observer.observedObjectHasChanged(changedItem, change); + } + for (var k in arrayOfResultSets) { + var resultSet = arrayOfResultSets[k]; + var arrayOfResultSetObservers = resultSet.getObservers(); + for (var m in arrayOfResultSetObservers) { + observer = arrayOfResultSetObservers[m]; + observer.observedObjectHasChanged(resultSet, change); + } + } + } +}; + +dojo.data.provider.Base.prototype._getResultSets = function() { + dojo.unimplemented('dojo.data.provider.Base'); + return []; // Array +}; + diff --git a/source/web/scripts/ajax/src/data/provider/Delicious.js b/source/web/scripts/ajax/src/data/provider/Delicious.js new file mode 100644 index 0000000000..307b8e9207 --- /dev/null +++ b/source/web/scripts/ajax/src/data/provider/Delicious.js @@ -0,0 +1,75 @@ +dojo.provide("dojo.data.provider.Delicious"); +dojo.require("dojo.data.provider.FlatFile"); +dojo.require("dojo.data.format.Json"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.provider.Delicious = function() { + /** + * summary: + * The Delicious Data Provider can be used to take data from + * del.icio.us and make it available as dojo.data.Items + * In order to use the Delicious Data Provider, you need + * to have loaded a script tag that looks like this: + * + */ + dojo.data.provider.FlatFile.call(this); + // Delicious = null; + if (Delicious && Delicious.posts) { + dojo.data.format.Json.loadDataProviderFromArrayOfJsonData(this, Delicious.posts); + } else { + // document.write(""); + /* + document.write(""); + document.write(""); + document.write(""); + fetchComplete(); + */ + // dojo.debug("Delicious line 29: constructor"); + } + var u = this.registerAttribute('u'); + var d = this.registerAttribute('d'); + var t = this.registerAttribute('t'); + + u.load('name', 'Bookmark'); + d.load('name', 'Description'); + t.load('name', 'Tags'); + + u.load('type', 'String'); + d.load('type', 'String'); + t.load('type', 'String'); +}; +dojo.inherits(dojo.data.provider.Delicious, dojo.data.provider.FlatFile); + +/******************************************************************** + * FIXME: the rest of this is work in progress + * + +dojo.data.provider.Delicious.prototype.getNewItemToLoad = function() { + var newItem = this._newItem(); + this._currentArray.push(newItem); + return newItem; // dojo.data.Item +}; + +dojo.data.provider.Delicious.prototype.fetchArray = function(query) { + if (!query) { + query = "gumption"; + } + this._currentArray = []; + alert("Delicious line 60: loadDataProviderFromArrayOfJsonData"); + alert("Delicious line 61: " + dojo); + var sourceUrl = "http://del.icio.us/feeds/json/" + query + "?count=8"; + document.write(""); + document.write(""); + document.write(""); + alert("line 66"); + dojo.data.format.Json.loadDataProviderFromArrayOfJsonData(this, Delicious.posts); + return this._currentArray; // Array +}; + +callMe = function() { + alert("callMe!"); +}; + +*/ diff --git a/source/web/scripts/ajax/src/data/provider/FlatFile.js b/source/web/scripts/ajax/src/data/provider/FlatFile.js new file mode 100644 index 0000000000..23b0b395af --- /dev/null +++ b/source/web/scripts/ajax/src/data/provider/FlatFile.js @@ -0,0 +1,143 @@ +dojo.provide("dojo.data.provider.FlatFile"); +dojo.require("dojo.data.provider.Base"); +dojo.require("dojo.data.Item"); +dojo.require("dojo.data.Attribute"); +dojo.require("dojo.data.ResultSet"); +dojo.require("dojo.data.format.Json"); +dojo.require("dojo.data.format.Csv"); +dojo.require("dojo.lang.assert"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.provider.FlatFile = function(/* keywords */ keywordParameters) { + /** + * summary: + * A Json Data Provider knows how to read in simple JSON data + * tables and make their contents accessable as Items. + */ + dojo.lang.assertType(keywordParameters, "pureobject", {optional: true}); + dojo.data.provider.Base.call(this); + this._arrayOfItems = []; + this._resultSet = null; + this._dictionaryOfAttributes = {}; + + if (keywordParameters) { + var jsonObjects = keywordParameters["jsonObjects"]; + var jsonString = keywordParameters["jsonString"]; + var fileUrl = keywordParameters["url"]; + if (jsonObjects) { + dojo.data.format.Json.loadDataProviderFromArrayOfJsonData(this, jsonObjects); + } + if (jsonString) { + dojo.data.format.Json.loadDataProviderFromFileContents(this, jsonString); + } + if (fileUrl) { + var arrayOfParts = fileUrl.split('.'); + var lastPart = arrayOfParts[(arrayOfParts.length - 1)]; + var formatParser = null; + if (lastPart == "json") { + formatParser = dojo.data.format.Json; + } + if (lastPart == "csv") { + formatParser = dojo.data.format.Csv; + } + if (formatParser) { + var fileContents = dojo.hostenv.getText(fileUrl); + formatParser.loadDataProviderFromFileContents(this, fileContents); + } else { + dojo.lang.assert(false, "new dojo.data.provider.FlatFile({url: }) was passed a file without a .csv or .json suffix"); + } + } + } +}; +dojo.inherits(dojo.data.provider.FlatFile, dojo.data.provider.Base); + +// ------------------------------------------------------------------- +// Public instance methods +// ------------------------------------------------------------------- +dojo.data.provider.FlatFile.prototype.getProviderCapabilities = function(/* string */ keyword) { + dojo.lang.assertType(keyword, String, {optional: true}); + if (!this._ourCapabilities) { + this._ourCapabilities = { + transactions: false, + undo: false, + login: false, + versioning: false, + anonymousRead: true, + anonymousWrite: false, + permissions: false, + queries: false, + strongTyping: false, + datatypes: [String, Date, Number] + }; + } + if (keyword) { + return this._ourCapabilities[keyword]; + } else { + return this._ourCapabilities; + } +}; + +dojo.data.provider.FlatFile.prototype.registerAttribute = function(/* string or dojo.data.Attribute */ attributeId) { + var registeredAttribute = this.getAttribute(attributeId); + if (!registeredAttribute) { + var newAttribute = new dojo.data.Attribute(this, attributeId); + this._dictionaryOfAttributes[attributeId] = newAttribute; + registeredAttribute = newAttribute; + } + return registeredAttribute; // dojo.data.Attribute +}; + +dojo.data.provider.FlatFile.prototype.getAttribute = function(/* string or dojo.data.Attribute */ attributeId) { + var attribute = (this._dictionaryOfAttributes[attributeId] || null); + return attribute; // dojo.data.Attribute or null +}; + +dojo.data.provider.FlatFile.prototype.getAttributes = function() { + var arrayOfAttributes = []; + for (var key in this._dictionaryOfAttributes) { + var attribute = this._dictionaryOfAttributes[key]; + arrayOfAttributes.push(attribute); + } + return arrayOfAttributes; // Array +}; + +dojo.data.provider.FlatFile.prototype.fetchArray = function(query) { + /** + * summary: Returns an Array containing all of the Items. + */ + return this._arrayOfItems; // Array +}; + +dojo.data.provider.FlatFile.prototype.fetchResultSet = function(query) { + /** + * summary: Returns a ResultSet containing all of the Items. + */ + if (!this._resultSet) { + this._resultSet = new dojo.data.ResultSet(this, this.fetchArray(query)); + } + return this._resultSet; // dojo.data.ResultSet +}; + +// ------------------------------------------------------------------- +// Private instance methods +// ------------------------------------------------------------------- +dojo.data.provider.FlatFile.prototype._newItem = function() { + var item = new dojo.data.Item(this); + this._arrayOfItems.push(item); + return item; // dojo.data.Item +}; + +dojo.data.provider.FlatFile.prototype._newAttribute = function(/* String */ attributeId) { + dojo.lang.assertType(attributeId, String); + dojo.lang.assert(this.getAttribute(attributeId) === null); + var attribute = new dojo.data.Attribute(this, attributeId); + this._dictionaryOfAttributes[attributeId] = attribute; + return attribute; // dojo.data.Attribute +}; + +dojo.data.provider.Base.prototype._getResultSets = function() { + return [this._resultSet]; // Array +}; + diff --git a/source/web/scripts/ajax/src/data/provider/JotSpot.js b/source/web/scripts/ajax/src/data/provider/JotSpot.js new file mode 100644 index 0000000000..e5c7cc3989 --- /dev/null +++ b/source/web/scripts/ajax/src/data/provider/JotSpot.js @@ -0,0 +1,17 @@ +dojo.provide("dojo.data.provider.JotSpot"); +dojo.require("dojo.data.provider.Base"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.provider.JotSpot = function() { + /** + * summary: + * A JotSpot Data Provider knows how to read data from a JotSpot data + * store and make the contents accessable as dojo.data.Items. + */ + dojo.unimplemented('dojo.data.provider.JotSpot'); +}; + +dojo.inherits(dojo.data.provider.JotSpot, dojo.data.provider.Base); + diff --git a/source/web/scripts/ajax/src/data/provider/MySql.js b/source/web/scripts/ajax/src/data/provider/MySql.js new file mode 100644 index 0000000000..6b3036075d --- /dev/null +++ b/source/web/scripts/ajax/src/data/provider/MySql.js @@ -0,0 +1,17 @@ +dojo.provide("dojo.data.provider.MySql"); +dojo.require("dojo.data.provider.Base"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +dojo.data.provider.MySql = function() { + /** + * summary: + * A MySql Data Provider knows how to connect to a MySQL database + * on a server and and make the content records available as + * dojo.data.Items. + */ + dojo.unimplemented('dojo.data.provider.MySql'); +}; + +dojo.inherits(dojo.data.provider.MySql, dojo.data.provider.Base); diff --git a/source/web/scripts/ajax/src/data/to_do.txt b/source/web/scripts/ajax/src/data/to_do.txt new file mode 100644 index 0000000000..6e705fc6c3 --- /dev/null +++ b/source/web/scripts/ajax/src/data/to_do.txt @@ -0,0 +1,45 @@ +Existing Features + * can import data from .json or .csv format files + * can import data from del.icio.us + * can create and modify data programmatically + * can bind data to dojo.widget.Chart + * can bind data to dojo.widget.SortableTable + * can bind one data set to multiple widgets + * notifications: widgets are notified when data changes + * notification available per-item or per-resultSet + * can create ad-hoc attributes + * attributes can be loosely-typed + * attributes can have meta-data like type and display name + * half-implemented support for sorting + * half-implemented support for export to .json + * API for getting data in simple arrays + * API for getting ResultSets with iterators (precursor to support for something like the openrico.org live grid) + +~~~~~~~~~~~~~~~~~~~~~~~~ +To-Do List + * be able to import data from an html
+ * think about being able to import data from some type of XML + * think about integration with dojo.undo.Manager + * think more about how to represent the notion of different data types + * think about what problems we'll run into when we have a MySQL data provider + * in TableBindingHack, improve support for data types in the SortableTable binding + * deal with ids (including MySQL multi-field keys) + * add support for item-references: employeeItem.set('department', departmentItem); + * deal with Attributes as instances of Items, not just subclasses of Items + * unit tests for compare/sort code + * unit tests for everything + * implement item.toString('json') and item.toString('xml') + * implement dataProvider.newItem({name: 'foo', age: 26}) + * deal better with transactions + * add support for deleting items + * don't send out multiple notifications to the same observer + * deal with item versions + * prototype a Yahoo data provider -- http://developer.yahoo.net/common/json.html + * prototype a data provider that enforces strong typing + * prototype a data provider that prevents ad-hoc attributes + * prototype a data provider that enforces single-kind item + * prototype a data provider that allows for login/authentication + * have loosely typed result sets play nicely with widgets that expect strong typing + * prototype an example of spreadsheet-style formulas or derivation rules + * experiment with some sort of fetch() that returns only a subset of a data provider's items + diff --git a/source/web/scripts/ajax/src/date.js b/source/web/scripts/ajax/src/date.js new file mode 100644 index 0000000000..0f976eec19 --- /dev/null +++ b/source/web/scripts/ajax/src/date.js @@ -0,0 +1,614 @@ +dojo.provide("dojo.date"); + + +/* Supplementary Date Functions + *******************************/ + +dojo.date.setDayOfYear = function (dateObject, dayofyear) { + dateObject.setMonth(0); + dateObject.setDate(dayofyear); + return dateObject; +} + +dojo.date.getDayOfYear = function (dateObject) { + var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1); + return Math.floor((dateObject.getTime() - + firstDayOfYear.getTime()) / 86400000); +} + + + + +dojo.date.setWeekOfYear = function (dateObject, week, firstDay) { + if (arguments.length == 1) { firstDay = 0; } // Sunday + dojo.unimplemented("dojo.date.setWeekOfYear"); +} + +dojo.date.getWeekOfYear = function (dateObject, firstDay) { + if (arguments.length == 1) { firstDay = 0; } // Sunday + + // work out the first day of the year corresponding to the week + var firstDayOfYear = new Date(dateObject.getFullYear(), 0, 1); + var day = firstDayOfYear.getDay(); + firstDayOfYear.setDate(firstDayOfYear.getDate() - + day + firstDay - (day > firstDay ? 7 : 0)); + + return Math.floor((dateObject.getTime() - + firstDayOfYear.getTime()) / 604800000); +} + + + + +dojo.date.setIsoWeekOfYear = function (dateObject, week, firstDay) { + if (arguments.length == 1) { firstDay = 1; } // Monday + dojo.unimplemented("dojo.date.setIsoWeekOfYear"); +} + +dojo.date.getIsoWeekOfYear = function (dateObject, firstDay) { + if (arguments.length == 1) { firstDay = 1; } // Monday + dojo.unimplemented("dojo.date.getIsoWeekOfYear"); +} + + + + +/* ISO 8601 Functions + *********************/ + +dojo.date.setIso8601 = function (dateObject, string){ + var comps = (string.indexOf("T") == -1) ? string.split(" ") : string.split("T"); + dojo.date.setIso8601Date(dateObject, comps[0]); + if (comps.length == 2) { dojo.date.setIso8601Time(dateObject, comps[1]); } + return dateObject; +} + +dojo.date.fromIso8601 = function (string) { + return dojo.date.setIso8601(new Date(0, 0), string); +} + + + + +dojo.date.setIso8601Date = function (dateObject, string) { + var regexp = "^([0-9]{4})((-?([0-9]{2})(-?([0-9]{2}))?)|" + + "(-?([0-9]{3}))|(-?W([0-9]{2})(-?([1-7]))?))?$"; + var d = string.match(new RegExp(regexp)); + if(!d) { + dojo.debug("invalid date string: " + string); + return false; + } + var year = d[1]; + var month = d[4]; + var date = d[6]; + var dayofyear = d[8]; + var week = d[10]; + var dayofweek = (d[12]) ? d[12] : 1; + + dateObject.setYear(year); + + if (dayofyear) { dojo.date.setDayOfYear(dateObject, Number(dayofyear)); } + else if (week) { + dateObject.setMonth(0); + dateObject.setDate(1); + var gd = dateObject.getDay(); + var day = (gd) ? gd : 7; + var offset = Number(dayofweek) + (7 * Number(week)); + + if (day <= 4) { dateObject.setDate(offset + 1 - day); } + else { dateObject.setDate(offset + 8 - day); } + } else { + if (month) { + dateObject.setDate(1); + dateObject.setMonth(month - 1); + } + if (date) { dateObject.setDate(date); } + } + + return dateObject; +} + +dojo.date.fromIso8601Date = function (string) { + return dojo.date.setIso8601Date(new Date(0, 0), string); +} + + + + +dojo.date.setIso8601Time = function (dateObject, string) { + // first strip timezone info from the end + var timezone = "Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$"; + var d = string.match(new RegExp(timezone)); + + var offset = 0; // local time if no tz info + if (d) { + if (d[0] != 'Z') { + offset = (Number(d[3]) * 60) + Number(d[5]); + offset *= ((d[2] == '-') ? 1 : -1); + } + offset -= dateObject.getTimezoneOffset(); + string = string.substr(0, string.length - d[0].length); + } + + // then work out the time + var regexp = "^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?)?$"; + var d = string.match(new RegExp(regexp)); + if(!d) { + dojo.debug("invalid time string: " + string); + return false; + } + var hours = d[1]; + var mins = Number((d[3]) ? d[3] : 0); + var secs = (d[5]) ? d[5] : 0; + var ms = d[7] ? (Number("0." + d[7]) * 1000) : 0; + + dateObject.setHours(hours); + dateObject.setMinutes(mins); + dateObject.setSeconds(secs); + dateObject.setMilliseconds(ms); + + if (offset != 0) { + dateObject.setTime(dateObject.getTime() + offset * 60000); + } + return dateObject; +} + +dojo.date.fromIso8601Time = function (string) { + return dojo.date.setIso8601Time(new Date(0, 0), string); +} + + + +/* Informational Functions + **************************/ + +dojo.date.shortTimezones = ["IDLW", "BET", "HST", "MART", "AKST", "PST", "MST", + "CST", "EST", "AST", "NFT", "BST", "FST", "AT", "GMT", "CET", "EET", "MSK", + "IRT", "GST", "AFT", "AGTT", "IST", "NPT", "ALMT", "MMT", "JT", "AWST", + "JST", "ACST", "AEST", "LHST", "VUT", "NFT", "NZT", "CHAST", "PHOT", + "LINT"]; +dojo.date.timezoneOffsets = [-720, -660, -600, -570, -540, -480, -420, -360, + -300, -240, -210, -180, -120, -60, 0, 60, 120, 180, 210, 240, 270, 300, + 330, 345, 360, 390, 420, 480, 540, 570, 600, 630, 660, 690, 720, 765, 780, + 840]; +/* +dojo.date.timezones = ["International Date Line West", "Bering Standard Time", + "Hawaiian Standard Time", "Marquesas Time", "Alaska Standard Time", + "Pacific Standard Time (USA)", "Mountain Standard Time", + "Central Standard Time (USA)", "Eastern Standard Time (USA)", + "Atlantic Standard Time", "Newfoundland Time", "Brazil Standard Time", + "Fernando de Noronha Standard Time (Brazil)", "Azores Time", + "Greenwich Mean Time", "Central Europe Time", "Eastern Europe Time", + "Moscow Time", "Iran Standard Time", "Gulf Standard Time", + "Afghanistan Time", "Aqtobe Time", "Indian Standard Time", "Nepal Time", + "Almaty Time", "Myanmar Time", "Java Time", + "Australian Western Standard Time", "Japan Standard Time", + "Australian Central Standard Time", "Lord Hove Standard Time (Australia)", + "Vanuata Time", "Norfolk Time (Australia)", "New Zealand Standard Time", + "Chatham Standard Time (New Zealand)", "Phoenix Islands Time (Kribati)", + "Line Islands Time (Kribati)"]; +*/ +dojo.date.months = ["January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"]; +dojo.date.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "June", + "July", "Aug", "Sep", "Oct", "Nov", "Dec"]; +dojo.date.days = ["Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"]; +dojo.date.shortDays = ["Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat"]; + + +dojo.date.getDaysInMonth = function (dateObject) { + var month = dateObject.getMonth(); + var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + if (month == 1 && dojo.date.isLeapYear(dateObject)) { return 29; } + else { return days[month]; } +} + +dojo.date.isLeapYear = function (dateObject) { + /* + * Leap years are years with an additional day YYYY-02-29, where the year + * number is a multiple of four with the following exception: If a year + * is a multiple of 100, then it is only a leap year if it is also a + * multiple of 400. For example, 1900 was not a leap year, but 2000 is one. + */ + var year = dateObject.getFullYear(); + return (year%400 == 0) ? true : (year%100 == 0) ? false : (year%4 == 0) ? true : false; +} + + + +dojo.date.getDayName = function (dateObject) { + return dojo.date.days[dateObject.getDay()]; +} + +dojo.date.getDayShortName = function (dateObject) { + return dojo.date.shortDays[dateObject.getDay()]; +} + + + + +dojo.date.getMonthName = function (dateObject) { + return dojo.date.months[dateObject.getMonth()]; +} + +dojo.date.getMonthShortName = function (dateObject) { + return dojo.date.shortMonths[dateObject.getMonth()]; +} + + + + +dojo.date.getTimezoneName = function (dateObject) { + // need to negate timezones to get it right + // i.e UTC+1 is CET winter, but getTimezoneOffset returns -60 + var timezoneOffset = -(dateObject.getTimezoneOffset()); + + for (var i = 0; i < dojo.date.timezoneOffsets.length; i++) { + if (dojo.date.timezoneOffsets[i] == timezoneOffset) { + return dojo.date.shortTimezones[i]; + } + } + + // we don't know so return it formatted as "+HH:MM" + function $ (s) { s = String(s); while (s.length < 2) { s = "0" + s; } return s; } + return (timezoneOffset < 0 ? "-" : "+") + $(Math.floor(Math.abs( + timezoneOffset)/60)) + ":" + $(Math.abs(timezoneOffset)%60); +} + + + + +dojo.date.getOrdinal = function (dateObject) { + var date = dateObject.getDate(); + + if (date%100 != 11 && date%10 == 1) { return "st"; } + else if (date%100 != 12 && date%10 == 2) { return "nd"; } + else if (date%100 != 13 && date%10 == 3) { return "rd"; } + else { return "th"; } +} + + + +/* Date Formatter Functions + ***************************/ + +// POSIX strftime +// see +dojo.date.format = dojo.date.strftime = function (dateObject, format) { + + // zero pad + var padChar = null; + function _ (s, n) { + s = String(s); + n = (n || 2) - s.length; + while (n-- > 0) { s = (padChar == null ? "0" : padChar) + s; } + return s; + } + + function $ (property) { + switch (property) { + case "a": // abbreviated weekday name according to the current locale + return dojo.date.getDayShortName(dateObject); break; + + case "A": // full weekday name according to the current locale + return dojo.date.getDayName(dateObject); break; + + case "b": + case "h": // abbreviated month name according to the current locale + return dojo.date.getMonthShortName(dateObject); break; + + case "B": // full month name according to the current locale + return dojo.date.getMonthName(dateObject); break; + + case "c": // preferred date and time representation for the current + // locale + return dateObject.toLocaleString(); break; + + case "C": // century number (the year divided by 100 and truncated + // to an integer, range 00 to 99) + return _(Math.floor(dateObject.getFullYear()/100)); break; + + case "d": // day of the month as a decimal number (range 01 to 31) + return _(dateObject.getDate()); break; + + case "D": // same as %m/%d/%y + return $("m") + "/" + $("d") + "/" + $("y"); break; + + case "e": // day of the month as a decimal number, a single digit is + // preceded by a space (range ' 1' to '31') + if (padChar == null) { padChar = " "; } + return _(dateObject.getDate(), 2); break; + + case "f": // month as a decimal number, a single digit is + // preceded by a space (range ' 1' to '12') + if (padChar == null) { padChar = " "; } + return _(dateObject.getMonth()+1, 2); break; + + case "g": // like %G, but without the century. + break; + + case "G": // The 4-digit year corresponding to the ISO week number + // (see %V). This has the same format and value as %Y, + // except that if the ISO week number belongs to the + // previous or next year, that year is used instead. + break; + + case "F": // same as %Y-%m-%d + return $("Y") + "-" + $("m") + "-" + $("d"); break; + + case "H": // hour as a decimal number using a 24-hour clock (range + // 00 to 23) + return _(dateObject.getHours()); break; + + case "I": // hour as a decimal number using a 12-hour clock (range + // 01 to 12) + return _(dateObject.getHours() % 12 || 12); break; + + case "j": // day of the year as a decimal number (range 001 to 366) + return _(dojo.date.getDayOfYear(dateObject), 3); break; + + case "m": // month as a decimal number (range 01 to 12) + return _(dateObject.getMonth() + 1); break; + + case "M": // minute as a decimal numbe + return _(dateObject.getMinutes()); break; + + case "n": + return "\n"; break; + + case "p": // either `am' or `pm' according to the given time value, + // or the corresponding strings for the current locale + return dateObject.getHours() < 12 ? "am" : "pm"; break; + + case "r": // time in a.m. and p.m. notation + return $("I") + ":" + $("M") + ":" + $("S") + " " + $("p"); break; + + case "R": // time in 24 hour notation + return $("H") + ":" + $("M"); break; + + case "S": // second as a decimal number + return _(dateObject.getSeconds()); break; + + case "t": + return "\t"; break; + + case "T": // current time, equal to %H:%M:%S + return $("H") + ":" + $("M") + ":" + $("S"); break; + + case "u": // weekday as a decimal number [1,7], with 1 representing + // Monday + return String(dateObject.getDay() || 7); break; + + case "U": // week number of the current year as a decimal number, + // starting with the first Sunday as the first day of the + // first week + return _(dojo.date.getWeekOfYear(dateObject)); break; + + case "V": // week number of the year (Monday as the first day of the + // week) as a decimal number [01,53]. If the week containing + // 1 January has four or more days in the new year, then it + // is considered week 1. Otherwise, it is the last week of + // the previous year, and the next week is week 1. + return _(dojo.date.getIsoWeekOfYear(dateObject)); break; + + case "W": // week number of the current year as a decimal number, + // starting with the first Monday as the first day of the + // first week + return _(dojo.date.getWeekOfYear(dateObject, 1)); break; + + case "w": // day of the week as a decimal, Sunday being 0 + return String(dateObject.getDay()); break; + + case "x": // preferred date representation for the current locale + // without the time + break; + + case "X": // preferred date representation for the current locale + // without the time + break; + + case "y": // year as a decimal number without a century (range 00 to + // 99) + return _(dateObject.getFullYear()%100); break; + + case "Y": // year as a decimal number including the century + return String(dateObject.getFullYear()); break; + + case "z": // time zone or name or abbreviation + var timezoneOffset = dateObject.getTimezoneOffset(); + return (timezoneOffset > 0 ? "-" : "+") + + _(Math.floor(Math.abs(timezoneOffset)/60)) + ":" + + _(Math.abs(timezoneOffset)%60); break; + + case "Z": // time zone or name or abbreviation + return dojo.date.getTimezoneName(dateObject); break; + + case "%": + return "%"; break; + } + } + + // parse the formatting string and construct the resulting string + var string = ""; + var i = 0, index = 0, switchCase; + while ((index = format.indexOf("%", i)) != -1) { + string += format.substring(i, index++); + + // inspect modifier flag + switch (format.charAt(index++)) { + case "_": // Pad a numeric result string with spaces. + padChar = " "; break; + case "-": // Do not pad a numeric result string. + padChar = ""; break; + case "0": // Pad a numeric result string with zeros. + padChar = "0"; break; + case "^": // Convert characters in result string to upper case. + switchCase = "upper"; break; + case "#": // Swap the case of the result string. + switchCase = "swap"; break; + default: // no modifer flag so decremenet the index + padChar = null; index--; break; + } + + // toggle case if a flag is set + var property = $(format.charAt(index++)); + if (switchCase == "upper" || + (switchCase == "swap" && /[a-z]/.test(property))) { + property = property.toUpperCase(); + } else if (switchCase == "swap" && !/[a-z]/.test(property)) { + property = property.toLowerCase(); + } + var swicthCase = null; + + string += property; + i = index; + } + string += format.substring(i); + + return string; +} + +/* compare and add + ******************/ +dojo.date.compareTypes={ + // summary + // bitmask for comparison operations. + DATE:1, TIME:2 +}; +dojo.date.compare=function(/* Date */ dateA, /* Date */ dateB, /* int */ options){ + // summary + // Compare two date objects by date, time, or both. + var dA=dateA; + var dB=dateB||new Date(); + var now=new Date(); + var opt=options||(dojo.date.compareTypes.DATE|dojo.date.compareTypes.TIME); + var d1=new Date( + ((opt&dojo.date.compareTypes.DATE)?(dA.getFullYear()):now.getFullYear()), + ((opt&dojo.date.compareTypes.DATE)?(dA.getMonth()):now.getMonth()), + ((opt&dojo.date.compareTypes.DATE)?(dA.getDate()):now.getDate()), + ((opt&dojo.date.compareTypes.TIME)?(dA.getHours()):0), + ((opt&dojo.date.compareTypes.TIME)?(dA.getMinutes()):0), + ((opt&dojo.date.compareTypes.TIME)?(dA.getSeconds()):0) + ); + var d2=new Date( + ((opt&dojo.date.compareTypes.DATE)?(dB.getFullYear()):now.getFullYear()), + ((opt&dojo.date.compareTypes.DATE)?(dB.getMonth()):now.getMonth()), + ((opt&dojo.date.compareTypes.DATE)?(dB.getDate()):now.getDate()), + ((opt&dojo.date.compareTypes.TIME)?(dB.getHours()):0), + ((opt&dojo.date.compareTypes.TIME)?(dB.getMinutes()):0), + ((opt&dojo.date.compareTypes.TIME)?(dB.getSeconds()):0) + ); + if(d1.valueOf()>d2.valueOf()){ + return 1; // int + } + if(d1.valueOf() 0.4"); + } +} diff --git a/source/web/scripts/ajax/src/debug/arrow_hide.gif b/source/web/scripts/ajax/src/debug/arrow_hide.gif new file mode 100644 index 0000000000000000000000000000000000000000..8bd6b346caa4903059ab2d0892bb2a8e905af52e GIT binary patch literal 163 zcmZ?wbhEHbCaIz)p`@c~;u4i!qG92oX69z$5#bmRFQaZ8 zl9-=XRPPd;q-E`sQPNOY(;=^8p{Q?DP}8n#=pd=AC!=BV|NnmmY(ViR3nK%A1cMGp v5@aU>tCB)nU{a)8Z=_S(;-sEWB3VTtK?zC+Ojk^1eW + +Deep Debugger + + + + + +

Javascript Object Browser

+ +
+ + + \ No newline at end of file diff --git a/source/web/scripts/ajax/src/debug/spacer.gif b/source/web/scripts/ajax/src/debug/spacer.gif new file mode 100644 index 0000000000000000000000000000000000000000..2ce50b58677e3bb065de38173936ec6562f036d4 GIT binary patch literal 820 zcmZuwF=!J}82+!lOD-rve-DYoA%zU3WC%_Xmm8v<;1F@m5UXH^3Q-bplv+v#hdWUb zTmnKVcIlu{B!i6(Sp+L7X7D6M$=!QW2N#hb@q2e|=-}~q{P%s||NZa%|J|j_7mMev zrzl0gHL`7+Nav6~>$iH%!gxIHHRm}nEg~;@mMCE**AZuT$~+tp5sMrLg+o@il8$2g zeGabt56pRqA1oH#pJ^{yggHFD#6|>evG@+d$n%TZgPVT6!LkjpJGuw4JG$dHW|cy# zVe=b#i!9V4N&{hmEL*Y2mAQ#qj2zi*mUB}HCqky+b&#voP=dp}pLtHS3^u4c9L~it zhY$UX5ywPH7Pg*1H%K#nkv2p3pK8XZNr~a|rs`p&v947)v^jLadPX<9#Y)7Ybngh*ioqxaUD81fS4wX-K16W=ub4=$dFt-~AzpPfp99Ms zErC%iCL(MEID=!jERVx*?^qFr&fTJ8JR_P$d)?htH)yL|QFVhZOEYf1HXf@Rg=#=C z=ozYANcU1oPt=V{y!TR%qKQD3_V-Wt``LxH?jF&?n!3*9(NuJrMH!`8melufXxQm| zg&t3*^KEiAfAJ-hk+{q`+x+?dr899+$?M;>% yw`}=Kb(ZT8U=0T^aJzVFvww7)O{2U@RR08-Gc;|UaW-4q56qMEm2RzS)A@h1$O;(% literal 0 HcmV?d00001 diff --git a/source/web/scripts/ajax/src/dnd/DragAndDrop.js b/source/web/scripts/ajax/src/dnd/DragAndDrop.js new file mode 100644 index 0000000000..dd286aef79 --- /dev/null +++ b/source/web/scripts/ajax/src/dnd/DragAndDrop.js @@ -0,0 +1,166 @@ +dojo.require("dojo.lang"); +dojo.require("dojo.lang.declare"); +dojo.provide("dojo.dnd.DragSource"); +dojo.provide("dojo.dnd.DropTarget"); +dojo.provide("dojo.dnd.DragObject"); +dojo.provide("dojo.dnd.DragAndDrop"); + +dojo.declare("dojo.dnd.DragSource", null, { + type: "", + + onDragEnd: function(){ + }, + + onDragStart: function(){ + }, + + /* + * This function gets called when the DOM element was + * selected for dragging by the HtmlDragAndDropManager. + */ + onSelected: function(){ + }, + + unregister: function(){ + dojo.dnd.dragManager.unregisterDragSource(this); + }, + + reregister: function(){ + dojo.dnd.dragManager.registerDragSource(this); + } +}, function(){ + + //dojo.profile.start("DragSource"); + + var dm = dojo.dnd.dragManager; + if(dm["registerDragSource"]){ // side-effect prevention + dm.registerDragSource(this); + } + + //dojo.profile.end("DragSource"); + +}); + +dojo.declare("dojo.dnd.DragObject", null, { + type: "", + + onDragStart: function(){ + // gets called directly after being created by the DragSource + // default action is to clone self as icon + }, + + onDragMove: function(){ + // this changes the UI for the drag icon + // "it moves itself" + }, + + onDragOver: function(){ + }, + + onDragOut: function(){ + }, + + onDragEnd: function(){ + }, + + // normal aliases + onDragLeave: this.onDragOut, + onDragEnter: this.onDragOver, + + // non-camel aliases + ondragout: this.onDragOut, + ondragover: this.onDragOver +}, function(){ + var dm = dojo.dnd.dragManager; + if(dm["registerDragObject"]){ // side-effect prevention + dm.registerDragObject(this); + } +}); + +dojo.declare("dojo.dnd.DropTarget", null, { + + acceptsType: function(type){ + if(!dojo.lang.inArray(this.acceptedTypes, "*")){ // wildcard + if(!dojo.lang.inArray(this.acceptedTypes, type)) { return false; } + } + return true; + }, + + accepts: function(dragObjects){ + if(!dojo.lang.inArray(this.acceptedTypes, "*")){ // wildcard + for (var i = 0; i < dragObjects.length; i++) { + if (!dojo.lang.inArray(this.acceptedTypes, + dragObjects[i].type)) { return false; } + } + } + return true; + }, + + unregister: function(){ + dojo.dnd.dragManager.unregisterDropTarget(this); + }, + + onDragOver: function(){ + }, + + onDragOut: function(){ + }, + + onDragMove: function(){ + }, + + onDropStart: function(){ + }, + + onDrop: function(){ + }, + + onDropEnd: function(){ + } +}, function(){ + if (this.constructor == dojo.dnd.DropTarget) { return; } // need to be subclassed + this.acceptedTypes = []; + dojo.dnd.dragManager.registerDropTarget(this); +}); + +// NOTE: this interface is defined here for the convenience of the DragManager +// implementor. It is expected that in most cases it will be satisfied by +// extending a native event (DOM event in HTML and SVG). +dojo.dnd.DragEvent = function(){ + this.dragSource = null; + this.dragObject = null; + this.target = null; + this.eventStatus = "success"; + // + // can be one of: + // [ "dropSuccess", "dropFailure", "dragMove", + // "dragStart", "dragEnter", "dragLeave"] + // +} +/* + * The DragManager handles listening for low-level events and dispatching + * them to higher-level primitives like drag sources and drop targets. In + * order to do this, it must keep a list of the items. + */ +dojo.declare("dojo.dnd.DragManager", null, { + selectedSources: [], + dragObjects: [], + dragSources: [], + registerDragSource: function(){}, + dropTargets: [], + registerDropTarget: function(){}, + lastDragTarget: null, + currentDragTarget: null, + onKeyDown: function(){}, + onMouseOut: function(){}, + onMouseMove: function(){}, + onMouseUp: function(){} +}); + +// NOTE: despite the existance of the DragManager class, there will be a +// singleton drag manager provided by the renderer-specific D&D support code. +// It is therefore sane for us to assign instance variables to the DragManager +// prototype + +// The renderer-specific file will define the following object: +// dojo.dnd.dragManager = null; diff --git a/source/web/scripts/ajax/src/dnd/HtmlDragAndDrop.js b/source/web/scripts/ajax/src/dnd/HtmlDragAndDrop.js new file mode 100644 index 0000000000..ef3b4be037 --- /dev/null +++ b/source/web/scripts/ajax/src/dnd/HtmlDragAndDrop.js @@ -0,0 +1,501 @@ +dojo.provide("dojo.dnd.HtmlDragAndDrop"); +dojo.provide("dojo.dnd.HtmlDragSource"); +dojo.provide("dojo.dnd.HtmlDropTarget"); +dojo.provide("dojo.dnd.HtmlDragObject"); + +dojo.require("dojo.dnd.HtmlDragManager"); +dojo.require("dojo.dnd.DragAndDrop"); + +dojo.require("dojo.html.*"); +dojo.require("dojo.html.display"); +dojo.require("dojo.html.util"); +dojo.require("dojo.html.selection"); +dojo.require("dojo.html.iframe"); +dojo.require("dojo.lang.extras"); +dojo.require("dojo.lfx.*"); +dojo.require("dojo.event"); + +dojo.declare("dojo.dnd.HtmlDragSource", dojo.dnd.DragSource, { + dragClass: "", // CSS classname(s) applied to node when it is being dragged + + onDragStart: function(){ + var dragObj = new dojo.dnd.HtmlDragObject(this.dragObject, this.type); + if(this.dragClass) { dragObj.dragClass = this.dragClass; } + + if (this.constrainToContainer) { + dragObj.constrainTo(this.constrainingContainer || this.domNode.parentNode); + } + + return dragObj; + }, + + setDragHandle: function(node){ + node = dojo.byId(node); + dojo.dnd.dragManager.unregisterDragSource(this); + this.domNode = node; + dojo.dnd.dragManager.registerDragSource(this); + }, + + setDragTarget: function(node){ + this.dragObject = node; + }, + + constrainTo: function(container) { + this.constrainToContainer = true; + if (container) { + this.constrainingContainer = container; + } + }, + + /* + * + * see dojo.dnd.DragSource.onSelected + */ + onSelected: function() { + for (var i=0; i this.constraints.maxX) { x = this.constraints.maxX; } + if (y > this.constraints.maxY) { y = this.constraints.maxY; } + } + + this.setAbsolutePosition(x, y); + + dojo.event.topic.publish('dragMove', { source: this } ); + }, + + /** + * Set the position of the drag clone. (x,y) is relative to . + */ + setAbsolutePosition: function(x, y){ + // The drag clone is attached to document.body so this is trivial + if(!this.disableY) { this.dragClone.style.top = y + "px"; } + if(!this.disableX) { this.dragClone.style.left = x + "px"; } + }, + + + /** + * If the drag operation returned a success we reomve the clone of + * ourself from the original position. If the drag operation returned + * failure we slide back over to where we came from and end the operation + * with a little grace. + */ + onDragEnd: function(e){ + switch(e.dragStatus){ + + case "dropSuccess": + dojo.html.removeNode(this.dragClone); + this.dragClone = null; + break; + + case "dropFailure": // slide back to the start + var startCoords = dojo.html.getAbsolutePosition(this.dragClone, true); + // offset the end so the effect can be seen + var endCoords = { left: this.dragStartPosition.x + 1, + top: this.dragStartPosition.y + 1}; + + // animate + var anim = dojo.lfx.slideTo(this.dragClone, endCoords, 500, dojo.lfx.easeOut); + var dragObject = this; + dojo.event.connect(anim, "onEnd", function (e) { + // pause for a second (not literally) and disappear + dojo.lang.setTimeout(function() { + dojo.html.removeNode(dragObject.dragClone); + // Allow drag clone to be gc'ed + dragObject.dragClone = null; + }, + 200); + }); + anim.play(); + break; + } + + dojo.event.topic.publish('dragEnd', { source: this } ); + }, + + squelchOnClick: function(e){ + // squelch this onClick() event because it's the result of a drag (it's not a real click) + dojo.event.browser.stopEvent(e); + + // disconnect after a short delay to prevent "Null argument to unrollAdvice()" warning + dojo.lang.setTimeout(function() { + dojo.event.disconnect(this.domNode, "onclick", this, "squelchOnClick"); + },50); + }, + + constrainTo: function(container) { + this.constrainToContainer=true; + if (container) { + this.constrainingContainer = container; + } else { + this.constrainingContainer = this.domNode.parentNode; + } + } +}, function(node, type){ + this.domNode = dojo.byId(node); + this.type = type; + this.constrainToContainer = false; + this.dragSource = null; +}); + +dojo.declare("dojo.dnd.HtmlDropTarget", dojo.dnd.DropTarget, { + vertical: false, + onDragOver: function(e){ + if(!this.accepts(e.dragObjects)){ return false; } + + // cache the positions of the child nodes + this.childBoxes = []; + for (var i = 0, child; i < this.domNode.childNodes.length; i++) { + child = this.domNode.childNodes[i]; + if (child.nodeType != dojo.html.ELEMENT_NODE) { continue; } + var pos = dojo.html.getAbsolutePosition(child, true); + var inner = dojo.html.getBorderBox(child); + this.childBoxes.push({top: pos.y, bottom: pos.y+inner.height, + left: pos.x, right: pos.x+inner.width, height: inner.height, + width: inner.width, node: child}); + } + + // TODO: use dummy node + + return true; + }, + + _getNodeUnderMouse: function(e){ + // find the child + for (var i = 0, child; i < this.childBoxes.length; i++) { + with (this.childBoxes[i]) { + if (e.pageX >= left && e.pageX <= right && + e.pageY >= top && e.pageY <= bottom) { return i; } + } + } + + return -1; + }, + + createDropIndicator: function() { + this.dropIndicator = document.createElement("div"); + with (this.dropIndicator.style) { + position = "absolute"; + zIndex = 999; + if(this.vertical){ + borderLeftWidth = "1px"; + borderLeftColor = "black"; + borderLeftStyle = "solid"; + height = dojo.html.getBorderBox(this.domNode).height + "px"; + top = dojo.html.getAbsolutePosition(this.domNode, true).y + "px"; + }else{ + borderTopWidth = "1px"; + borderTopColor = "black"; + borderTopStyle = "solid"; + width = dojo.html.getBorderBox(this.domNode).width + "px"; + left = dojo.html.getAbsolutePosition(this.domNode, true).x + "px"; + } + } + }, + + onDragMove: function(e, dragObjects){ + var i = this._getNodeUnderMouse(e); + + if(!this.dropIndicator){ + this.createDropIndicator(); + } + + var gravity = this.vertical ? dojo.html.gravity.WEST : dojo.html.gravity.NORTH; + var hide = false; + if(i < 0) { + if(this.childBoxes.length) { + var before = (dojo.html.gravity(this.childBoxes[0].node, e) & gravity); + if(before){ hide = true; } + } else { + var before = true; + } + } else { + var child = this.childBoxes[i]; + var before = (dojo.html.gravity(child.node, e) & gravity); + if(child.node === dragObjects[0].dragSource.domNode){ + hide = true; + }else{ + var currentPosChild = before ? + (i>0?this.childBoxes[i-1]:child) : + (i + * (draggable selection) + * (dragObject generation) + * mouse-move -> + * (draggable movement) + * (droppable detection) + * (inform droppable) + * (inform dragObject) + * mouse-up + * (inform/destroy dragObject) + * (inform draggable) + * (inform droppable) + * 2.) mouse-down -> mouse-down + * (click-hold context menu) + * 3.) mouse-click -> + * (draggable selection) + * shift-mouse-click -> + * (augment draggable selection) + * mouse-down -> + * (dragObject generation) + * mouse-move -> + * (draggable movement) + * (droppable detection) + * (inform droppable) + * (inform dragObject) + * mouse-up + * (inform draggable) + * (inform droppable) + * 4.) mouse-up + * (clobber draggable selection) + */ + disabled: false, // to kill all dragging! + nestedTargets: false, + mouseDownTimer: null, // used for click-hold operations + dsCounter: 0, + dsPrefix: "dojoDragSource", + + // dimension calculation cache for use durring drag + dropTargetDimensions: [], + + currentDropTarget: null, + // currentDropTargetPoints: null, + previousDropTarget: null, + _dragTriggered: false, + + selectedSources: [], + dragObjects: [], + + // mouse position properties + currentX: null, + currentY: null, + lastX: null, + lastY: null, + mouseDownX: null, + mouseDownY: null, + threshold: 7, + + dropAcceptable: false, + + cancelEvent: function(e){ e.stopPropagation(); e.preventDefault();}, + + // method over-rides + registerDragSource: function(ds){ + //dojo.profile.start("register DragSource"); + + if(ds["domNode"]){ + // FIXME: dragSource objects SHOULD have some sort of property that + // references their DOM node, we shouldn't just be passing nodes and + // expecting it to work. + //dojo.profile.start("register DragSource 1"); + var dp = this.dsPrefix; + var dpIdx = dp+"Idx_"+(this.dsCounter++); + ds.dragSourceId = dpIdx; + this.dragSources[dpIdx] = ds; + ds.domNode.setAttribute(dp, dpIdx); + //dojo.profile.end("register DragSource 1"); + + //dojo.profile.start("register DragSource 2"); + + // so we can drag links + if(dojo.render.html.ie){ + //dojo.profile.start("register DragSource IE"); + + dojo.event.browser.addListener(ds.domNode, "ondragstart", this.cancelEvent); + // terribly slow + //dojo.event.connect(ds.domNode, "ondragstart", this.cancelEvent); + //dojo.profile.end("register DragSource IE"); + + } + //dojo.profile.end("register DragSource 2"); + + } + //dojo.profile.end("register DragSource"); + }, + + unregisterDragSource: function(ds){ + if (ds["domNode"]){ + var dp = this.dsPrefix; + var dpIdx = ds.dragSourceId; + delete ds.dragSourceId; + delete this.dragSources[dpIdx]; + ds.domNode.setAttribute(dp, null); + if(dojo.render.html.ie){ + dojo.event.browser.removeListener(ds.domNode, "ondragstart", this.cancelEvent); + } + } + }, + + registerDropTarget: function(dt){ + this.dropTargets.push(dt); + }, + + unregisterDropTarget: function(dt){ + var index = dojo.lang.find(this.dropTargets, dt, true); + if (index>=0) { + this.dropTargets.splice(index, 1); + } + }, + + /** + * Get the DOM element that is meant to drag. + * Loop through the parent nodes of the event target until + * the element is found that was created as a DragSource and + * return it. + * + * @param event object The event for which to get the drag source. + */ + getDragSource: function(e){ + var tn = e.target; + if(tn === dojo.body()){ return; } + var ta = dojo.html.getAttribute(tn, this.dsPrefix); + while((!ta)&&(tn)){ + tn = tn.parentNode; + if((!tn)||(tn === dojo.body())){ return; } + ta = dojo.html.getAttribute(tn, this.dsPrefix); + } + return this.dragSources[ta]; + }, + + onKeyDown: function(e){ + }, + + onMouseDown: function(e){ + if(this.disabled) { return; } + + // only begin on left click + if(dojo.render.html.ie) { + if(e.button != 1) { return; } + } else if(e.which != 1) { + return; + } + + var target = e.target.nodeType == dojo.html.TEXT_NODE ? + e.target.parentNode : e.target; + + // do not start drag involvement if the user is interacting with + // a form element. + if(dojo.html.isTag(target, "button", "textarea", "input", "select", "option")) { + return; + } + + // find a selection object, if one is a parent of the source node + var ds = this.getDragSource(e); + + // this line is important. if we aren't selecting anything then + // we need to return now, so preventDefault() isn't called, and thus + // the event is propogated to other handling code + if(!ds){ return; } + + if(!dojo.lang.inArray(this.selectedSources, ds)){ + this.selectedSources.push(ds); + ds.onSelected(); + } + + this.mouseDownX = e.pageX; + this.mouseDownY = e.pageY; + + // Must stop the mouse down from being propogated, or otherwise can't + // drag links in firefox. + // WARNING: preventing the default action on all mousedown events + // prevents user interaction with the contents. + e.preventDefault(); + + dojo.event.connect(document, "onmousemove", this, "onMouseMove"); + }, + + onMouseUp: function(e, cancel){ + // if we aren't dragging then ignore the mouse-up + // (in particular, don't call preventDefault(), because other + // code may need to process this event) + if(this.selectedSources.length==0){ + return; + } + + this.mouseDownX = null; + this.mouseDownY = null; + this._dragTriggered = false; + // e.preventDefault(); + e.dragSource = this.dragSource; + // let ctrl be used for multiselect or another action + // if I use same key to trigger treeV3 node selection and here, + // I have bugs with drag'n'drop. why ?? no idea.. + if((!e.shiftKey)&&(!e.ctrlKey)){ + //if(!e.shiftKey){ + if(this.currentDropTarget) { + this.currentDropTarget.onDropStart(); + } + dojo.lang.forEach(this.dragObjects, function(tempDragObj){ + var ret = null; + if(!tempDragObj){ return; } + if(this.currentDropTarget) { + e.dragObject = tempDragObj; + + // NOTE: we can't get anything but the current drop target + // here since the drag shadow blocks mouse-over events. + // This is probelematic for dropping "in" something + var ce = this.currentDropTarget.domNode.childNodes; + if(ce.length > 0){ + e.dropTarget = ce[0]; + while(e.dropTarget == tempDragObj.domNode){ + e.dropTarget = e.dropTarget.nextSibling; + } + }else{ + e.dropTarget = this.currentDropTarget.domNode; + } + if(this.dropAcceptable){ + ret = this.currentDropTarget.onDrop(e); + }else{ + this.currentDropTarget.onDragOut(e); + } + } + + e.dragStatus = this.dropAcceptable && ret ? "dropSuccess" : "dropFailure"; + // decouple the calls for onDragEnd, so they don't block the execution here + // ie. if the onDragEnd would call an alert, the execution here is blocked until the + // user has confirmed the alert box and then the rest of the dnd code is executed + // while the mouse doesnt "hold" the dragged object anymore ... and so on + dojo.lang.delayThese([ + function() { + // in FF1.5 this throws an exception, see + // http://dojotoolkit.org/pipermail/dojo-interest/2006-April/006751.html + try{ + tempDragObj.dragSource.onDragEnd(e) + } catch(err) { + // since the problem seems passing e, we just copy all + // properties and try the copy ... + var ecopy = {}; + for (var i in e) { + if (i=="type") { // the type property contains the exception, no idea why... + ecopy.type = "mouseup"; + continue; + } + ecopy[i] = e[i]; + } + tempDragObj.dragSource.onDragEnd(ecopy); + } + } + , function() {tempDragObj.onDragEnd(e)}]); + }, this); + + this.selectedSources = []; + this.dragObjects = []; + this.dragSource = null; + if(this.currentDropTarget) { + this.currentDropTarget.onDropEnd(); + } + } else { + //dojo.debug("special click"); + } + + dojo.event.disconnect(document, "onmousemove", this, "onMouseMove"); + this.currentDropTarget = null; + }, + + onScroll: function(){ + //dojo.profile.start("DNDManager updateoffset"); + for(var i = 0; i < this.dragObjects.length; i++) { + if(this.dragObjects[i].updateDragOffset) { + this.dragObjects[i].updateDragOffset(); + } + } + //dojo.profile.end("DNDManager updateoffset"); + + // TODO: do not recalculate, only adjust coordinates + if (this.dragObjects.length) { + this.cacheTargetLocations(); + } + }, + + _dragStartDistance: function(x, y){ + if((!this.mouseDownX)||(!this.mouseDownX)){ + return; + } + var dx = Math.abs(x-this.mouseDownX); + var dx2 = dx*dx; + var dy = Math.abs(y-this.mouseDownY); + var dy2 = dy*dy; + return parseInt(Math.sqrt(dx2+dy2), 10); + }, + + cacheTargetLocations: function(){ + dojo.profile.start("cacheTargetLocations"); + + this.dropTargetDimensions = []; + dojo.lang.forEach(this.dropTargets, function(tempTarget){ + var tn = tempTarget.domNode; + //only cache dropTarget which can accept current dragSource + if(!tn || dojo.lang.find(tempTarget.acceptedTypes, this.dragSource.type) < 0){ return; } + var abs = dojo.html.getAbsolutePosition(tn, true); + var bb = dojo.html.getBorderBox(tn); + this.dropTargetDimensions.push([ + [abs.x, abs.y], // upper-left + // lower-right + [ abs.x+bb.width, abs.y+bb.height ], + tempTarget + ]); + //dojo.debug("Cached for "+tempTarget) + }, this); + + dojo.profile.end("cacheTargetLocations"); + + //dojo.debug("Cache locations") + }, + + onMouseMove: function(e){ + if((dojo.render.html.ie)&&(e.button != 1)){ + // Oooops - mouse up occurred - e.g. when mouse was not over the + // window. I don't think we can detect this for FF - but at least + // we can be nice in IE. + this.currentDropTarget = null; + this.onMouseUp(e, true); + return; + } + + // if we've got some sources, but no drag objects, we need to send + // onDragStart to all the right parties and get things lined up for + // drop target detection + + if( (this.selectedSources.length)&& + (!this.dragObjects.length) ){ + var dx; + var dy; + if(!this._dragTriggered){ + this._dragTriggered = (this._dragStartDistance(e.pageX, e.pageY) > this.threshold); + if(!this._dragTriggered){ return; } + dx = e.pageX - this.mouseDownX; + dy = e.pageY - this.mouseDownY; + } + + // the first element is always our dragSource, if there are multiple + // selectedSources (elements that move along) then the first one is the master + // and for it the events will be fired etc. + this.dragSource = this.selectedSources[0]; + + dojo.lang.forEach(this.selectedSources, function(tempSource){ + if(!tempSource){ return; } + var tdo = tempSource.onDragStart(e); + if(tdo){ + tdo.onDragStart(e); + + // "bump" the drag object to account for the drag threshold + tdo.dragOffset.y += dy; + tdo.dragOffset.x += dx; + tdo.dragSource = tempSource; + + this.dragObjects.push(tdo); + } + }, this); + + /* clean previous drop target in dragStart */ + this.previousDropTarget = null; + + this.cacheTargetLocations(); + } + + // FIXME: we need to add dragSources and dragObjects to e + dojo.lang.forEach(this.dragObjects, function(dragObj){ + if(dragObj){ dragObj.onDragMove(e); } + }); + + // if we have a current drop target, check to see if we're outside of + // it. If so, do all the actions that need doing. + if(this.currentDropTarget){ + //dojo.debug(dojo.html.hasParent(this.currentDropTarget.domNode)) + var c = dojo.html.toCoordinateObject(this.currentDropTarget.domNode, true); + // var dtp = this.currentDropTargetPoints; + var dtp = [ + [c.x,c.y], [c.x+c.width, c.y+c.height] + ]; + } + + if((!this.nestedTargets)&&(dtp)&&(this.isInsideBox(e, dtp))){ + if(this.dropAcceptable){ + this.currentDropTarget.onDragMove(e, this.dragObjects); + } + }else{ + // FIXME: need to fix the event object! + // see if we can find a better drop target + var bestBox = this.findBestTarget(e); + + if(bestBox.target === null){ + if(this.currentDropTarget){ + this.currentDropTarget.onDragOut(e); + this.previousDropTarget = this.currentDropTarget; + this.currentDropTarget = null; + // this.currentDropTargetPoints = null; + } + this.dropAcceptable = false; + return; + } + + if(this.currentDropTarget !== bestBox.target){ + if(this.currentDropTarget){ + this.previousDropTarget = this.currentDropTarget; + this.currentDropTarget.onDragOut(e); + } + this.currentDropTarget = bestBox.target; + // this.currentDropTargetPoints = bestBox.points; + e.dragObjects = this.dragObjects; + this.dropAcceptable = this.currentDropTarget.onDragOver(e); + + }else{ + if(this.dropAcceptable){ + this.currentDropTarget.onDragMove(e, this.dragObjects); + } + } + } + }, + + findBestTarget: function(e) { + var _this = this; + var bestBox = new Object(); + bestBox.target = null; + bestBox.points = null; + dojo.lang.every(this.dropTargetDimensions, function(tmpDA) { + if(!_this.isInsideBox(e, tmpDA)){ + return true; + } + + bestBox.target = tmpDA[2]; + bestBox.points = tmpDA; + // continue iterating only if _this.nestedTargets == true + return Boolean(_this.nestedTargets); + }); + + return bestBox; + }, + + isInsideBox: function(e, coords){ + if( (e.pageX > coords[0][0])&& + (e.pageX < coords[1][0])&& + (e.pageY > coords[0][1])&& + (e.pageY < coords[1][1]) ){ + return true; + } + return false; + }, + + onMouseOver: function(e){ + }, + + onMouseOut: function(e){ + } +}); + +dojo.dnd.dragManager = new dojo.dnd.HtmlDragManager(); + +// global namespace protection closure +(function(){ + var d = document; + var dm = dojo.dnd.dragManager; + //TODO: when focus manager is ready, dragManager should be rewritten to use it + // set up event handlers on the document (or no?) + dojo.event.connect(d, "onkeydown", dm, "onKeyDown"); + dojo.event.connect(d, "onmouseover", dm, "onMouseOver"); + dojo.event.connect(d, "onmouseout", dm, "onMouseOut"); + dojo.event.connect(d, "onmousedown", dm, "onMouseDown"); + dojo.event.connect(d, "onmouseup", dm, "onMouseUp"); + // TODO: process scrolling of elements, not only window (focus manager would + // probably come to rescue here as well) + dojo.event.connect(window, "onscroll", dm, "onScroll"); +})(); diff --git a/source/web/scripts/ajax/src/dnd/HtmlDragMove.js b/source/web/scripts/ajax/src/dnd/HtmlDragMove.js new file mode 100644 index 0000000000..39880a6dae --- /dev/null +++ b/source/web/scripts/ajax/src/dnd/HtmlDragMove.js @@ -0,0 +1,58 @@ +dojo.provide("dojo.dnd.HtmlDragMove"); +dojo.provide("dojo.dnd.HtmlDragMoveSource"); +dojo.provide("dojo.dnd.HtmlDragMoveObject"); +dojo.require("dojo.dnd.*"); + +dojo.declare("dojo.dnd.HtmlDragMoveSource", dojo.dnd.HtmlDragSource, { + onDragStart: function(){ + var dragObj = new dojo.dnd.HtmlDragMoveObject(this.dragObject, this.type); + if (this.constrainToContainer) { + dragObj.constrainTo(this.constrainingContainer); + } + return dragObj; + }, + /* + * see dojo.dnd.HtmlDragSource.onSelected + */ + onSelected: function() { + for (var i=0; i. + */ + setAbsolutePosition: function(x, y){ + // The drag clone is attached to it's constraining container so offset for that + if(!this.disableY) { this.domNode.style.top = (y-this.containingBlockPosition.y) + "px"; } + if(!this.disableX) { this.domNode.style.left = (x-this.containingBlockPosition.x) + "px"; } + } +}); diff --git a/source/web/scripts/ajax/src/dnd/Sortable.js b/source/web/scripts/ajax/src/dnd/Sortable.js new file mode 100644 index 0000000000..d38d53541a --- /dev/null +++ b/source/web/scripts/ajax/src/dnd/Sortable.js @@ -0,0 +1,18 @@ +dojo.provide("dojo.dnd.Sortable"); +dojo.require("dojo.dnd.*"); + +dojo.dnd.Sortable = function () {} + +dojo.lang.extend(dojo.dnd.Sortable, { + + ondragstart: function (e) { + var dragObject = e.target; + while (dragObject.parentNode && dragObject.parentNode != this) { + dragObject = dragObject.parentNode; + } + // TODO: should apply HtmlDropTarget interface to self + // TODO: should apply HtmlDragObject interface? + return dragObject; + } + +}); diff --git a/source/web/scripts/ajax/src/dnd/TreeDragAndDrop.js b/source/web/scripts/ajax/src/dnd/TreeDragAndDrop.js new file mode 100644 index 0000000000..632d42b434 --- /dev/null +++ b/source/web/scripts/ajax/src/dnd/TreeDragAndDrop.js @@ -0,0 +1,468 @@ +/** + * TreeDrag* specialized on managing subtree drags + * It selects nodes and visualises what's going on, + * but delegates real actions upon tree to the controller + * + * This code is considered a part of controller +*/ + +dojo.provide("dojo.dnd.TreeDragAndDrop"); +dojo.provide("dojo.dnd.TreeDragSource"); +dojo.provide("dojo.dnd.TreeDropTarget"); +dojo.provide("dojo.dnd.TreeDNDController"); + +dojo.require("dojo.dnd.HtmlDragAndDrop"); +dojo.require("dojo.lang.func"); +dojo.require("dojo.lang.array"); +dojo.require("dojo.lang.extras"); +dojo.require("dojo.html.layout"); + +dojo.dnd.TreeDragSource = function(node, syncController, type, treeNode){ + this.controller = syncController; + this.treeNode = treeNode; + + dojo.dnd.HtmlDragSource.call(this, node, type); +} + +dojo.inherits(dojo.dnd.TreeDragSource, dojo.dnd.HtmlDragSource); + +dojo.lang.extend(dojo.dnd.TreeDragSource, { + onDragStart: function(){ + /* extend adds functions to prototype */ + var dragObject = dojo.dnd.HtmlDragSource.prototype.onDragStart.call(this); + //dojo.debugShallow(dragObject) + + dragObject.treeNode = this.treeNode; + + dragObject.onDragStart = dojo.lang.hitch(dragObject, function(e) { + + /* save selection */ + this.savedSelectedNode = this.treeNode.tree.selector.selectedNode; + if (this.savedSelectedNode) { + this.savedSelectedNode.unMarkSelected(); + } + + var result = dojo.dnd.HtmlDragObject.prototype.onDragStart.apply(this, arguments); + + + /* remove background grid from cloned object */ + var cloneGrid = this.dragClone.getElementsByTagName('img'); + for(var i=0; i it was allowed before, no accept check is needed + if (position=="onto" || + (!this.isAdjacentNode(sourceTreeNode, position) + && this.controller.canMove(sourceTreeNode, this.treeNode.parent) + ) + ) { + return position; + } else { + return false; + } + + }, + + onDragOut: function(e) { + this.clearAutoExpandTimer(); + + this.hideIndicator(); + }, + + + clearAutoExpandTimer: function() { + if (this.autoExpandTimer) { + clearTimeout(this.autoExpandTimer); + this.autoExpandTimer = null; + } + }, + + + + onDragMove: function(e, dragObjects){ + + var sourceTreeNode = dragObjects[0].treeNode; + + var position = this.getAcceptPosition(e, sourceTreeNode); + + if (position) { + this.showIndicator(position); + } + + }, + + isAdjacentNode: function(sourceNode, position) { + + if (sourceNode === this.treeNode) return true; + if (sourceNode.getNextSibling() === this.treeNode && position=="before") return true; + if (sourceNode.getPreviousSibling() === this.treeNode && position=="after") return true; + + return false; + }, + + + /* get DNDMode and see which position e fits */ + getPosition: function(e, DNDMode) { + var node = dojo.byId(this.treeNode.labelNode); + var mousey = e.pageY || e.clientY + dojo.body().scrollTop; + var nodey = dojo.html.getAbsolutePosition(node).y; + var height = dojo.html.getBorderBox(node).height; + + var relY = mousey - nodey; + var p = relY / height; + + var position = ""; // "" <=> forbidden + if (DNDMode & dojo.widget.Tree.prototype.DNDModes.ONTO + && DNDMode & dojo.widget.Tree.prototype.DNDModes.BETWEEN) { + if (p<=0.3) { + position = "before"; + } else if (p<=0.7) { + position = "onto"; + } else { + position = "after"; + } + } else if (DNDMode & dojo.widget.Tree.prototype.DNDModes.BETWEEN) { + if (p<=0.5) { + position = "before"; + } else { + position = "after"; + } + } + else if (DNDMode & dojo.widget.Tree.prototype.DNDModes.ONTO) { + position = "onto"; + } + + + return position; + }, + + + + getTargetParentIndex: function(sourceTreeNode, position) { + + var index = position == "before" ? this.treeNode.getParentIndex() : this.treeNode.getParentIndex()+1; + if (this.treeNode.parent === sourceTreeNode.parent + && this.treeNode.getParentIndex() > sourceTreeNode.getParentIndex()) { + index--; // dragging a node is different for simple move bacause of before-after issues + } + + return index; + }, + + + onDrop: function(e){ + // onDragOut will clean position + + + var position = this.position; + +//dojo.debug(position); + + this.onDragOut(e); + + var sourceTreeNode = e.dragObject.treeNode; + + if (!dojo.lang.isObject(sourceTreeNode)) { + dojo.raise("TreeNode not found in dragObject") + } + + if (position == "onto") { + return this.controller.move(sourceTreeNode, this.treeNode, 0); + } else { + var index = this.getTargetParentIndex(sourceTreeNode, position); + return this.controller.move(sourceTreeNode, this.treeNode.parent, index); + } + + //dojo.debug('drop2'); + + + + } + + +}); + + + +dojo.dnd.TreeDNDController = function(treeController) { + + // I use this controller to perform actions + this.treeController = treeController; + + this.dragSources = {}; + + this.dropTargets = {}; + +} + +dojo.lang.extend(dojo.dnd.TreeDNDController, { + + + listenTree: function(tree) { + //dojo.debug("Listen tree "+tree); + dojo.event.topic.subscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode"); + dojo.event.topic.subscribe(tree.eventNames.moveFrom, this, "onMoveFrom"); + dojo.event.topic.subscribe(tree.eventNames.moveTo, this, "onMoveTo"); + dojo.event.topic.subscribe(tree.eventNames.addChild, this, "onAddChild"); + dojo.event.topic.subscribe(tree.eventNames.removeNode, this, "onRemoveNode"); + dojo.event.topic.subscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy"); + }, + + + unlistenTree: function(tree) { + //dojo.debug("Listen tree "+tree); + dojo.event.topic.unsubscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode"); + dojo.event.topic.unsubscribe(tree.eventNames.moveFrom, this, "onMoveFrom"); + dojo.event.topic.unsubscribe(tree.eventNames.moveTo, this, "onMoveTo"); + dojo.event.topic.unsubscribe(tree.eventNames.addChild, this, "onAddChild"); + dojo.event.topic.unsubscribe(tree.eventNames.removeNode, this, "onRemoveNode"); + dojo.event.topic.unsubscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy"); + }, + + onTreeDestroy: function(message) { + this.unlistenTree(message.source); + // I'm not widget so don't use destroy() call and dieWithTree + }, + + onCreateDOMNode: function(message) { + this.registerDNDNode(message.source); + }, + + onAddChild: function(message) { + this.registerDNDNode(message.child); + }, + + onMoveFrom: function(message) { + var _this = this; + dojo.lang.forEach( + message.child.getDescendants(), + function(node) { _this.unregisterDNDNode(node); } + ); + }, + + onMoveTo: function(message) { + var _this = this; + dojo.lang.forEach( + message.child.getDescendants(), + function(node) { _this.registerDNDNode(node); } + ); + }, + + /** + * Controller(node model) creates DNDNodes because it passes itself to node for synchroneous drops processing + * I can't process DnD with events cause an event can't return result success/false + */ + registerDNDNode: function(node) { + if (!node.tree.DNDMode) return; + +//dojo.debug("registerDNDNode "+node); + + /* I drag label, not domNode, because large domNodes are very slow to copy and large to drag */ + + var source = null; + var target = null; + + if (!node.actionIsDisabled(node.actions.MOVE)) { + //dojo.debug("reg source") + var source = new dojo.dnd.TreeDragSource(node.labelNode, this, node.tree.widgetId, node); + this.dragSources[node.widgetId] = source; + } + + var target = new dojo.dnd.TreeDropTarget(node.labelNode, this.treeController, node.tree.DNDAcceptTypes, node); + + this.dropTargets[node.widgetId] = target; + + }, + + + unregisterDNDNode: function(node) { + + if (this.dragSources[node.widgetId]) { + dojo.dnd.dragManager.unregisterDragSource(this.dragSources[node.widgetId]); + delete this.dragSources[node.widgetId]; + } + + if (this.dropTargets[node.widgetId]) { + dojo.dnd.dragManager.unregisterDropTarget(this.dropTargets[node.widgetId]); + delete this.dropTargets[node.widgetId]; + } + } + + + + + +}); diff --git a/source/web/scripts/ajax/src/dnd/TreeDragAndDropV3.js b/source/web/scripts/ajax/src/dnd/TreeDragAndDropV3.js new file mode 100644 index 0000000000..5aaed906c6 --- /dev/null +++ b/source/web/scripts/ajax/src/dnd/TreeDragAndDropV3.js @@ -0,0 +1,378 @@ +/** + * TreeDrag* specialized on managing subtree drags + * It selects nodes and visualises what's going on, + * but delegates real actions upon tree to the controller + * + * This code is considered a part of controller +*/ + +dojo.provide("dojo.dnd.TreeDragAndDropV3"); +dojo.provide("dojo.dnd.TreeDragSourceV3"); +dojo.provide("dojo.dnd.TreeDropTargetV3"); + +dojo.require("dojo.dnd.HtmlDragAndDrop"); +dojo.require("dojo.lang.func"); +dojo.require("dojo.lang.array"); +dojo.require("dojo.lang.extras"); +dojo.require("dojo.Deferred"); +dojo.require("dojo.html.layout"); + +// FIXME: if controller can't move then skip node on move start +dojo.dnd.TreeDragSourceV3 = function(node, syncController, type, treeNode){ + //dojo.profile.start("TreeDragSourceV3 "+treeNode); + this.controller = syncController; + this.treeNode = treeNode; + + dojo.dnd.HtmlDragSource.call(this, node, type); + //dojo.profile.end("TreeDragSourceV3 "+treeNode); + +} + +dojo.inherits(dojo.dnd.TreeDragSourceV3, dojo.dnd.HtmlDragSource); + + +// ....................................... + +dojo.dnd.TreeDropTargetV3 = function(domNode, controller, type, treeNode){ + + this.treeNode = treeNode; + this.controller = controller; // I will sync-ly process drops + + dojo.dnd.HtmlDropTarget.call(this, domNode, type); +} + +dojo.inherits(dojo.dnd.TreeDropTargetV3, dojo.dnd.HtmlDropTarget); + +dojo.lang.extend(dojo.dnd.TreeDropTargetV3, { + + autoExpandDelay: 1500, + autoExpandTimer: null, + + + position: null, + + indicatorStyle: "2px black groove", + + showIndicator: function(position) { + + // do not change style too often, cause of blinking possible + if (this.position == position) { + return; + } + + //dojo.debug("set position for "+this.treeNode) + + this.hideIndicator(); + + this.position = position; + + var node = this.treeNode; + + + node.contentNode.style.width = dojo.html.getBorderBox(node.labelNode).width + "px"; + + if (position == "onto") { + node.contentNode.style.border = this.indicatorStyle; + } else { + // FIXME: bottom-top or highlight should cover ONLY top/bottom or div itself, + // not span whole line (try Dnd) + // FAILURE: Can't put span inside div: multiline bottom-top will span multiple lines + if (position == "before") { + node.contentNode.style.borderTop = this.indicatorStyle; + } else if (position == "after") { + node.contentNode.style.borderBottom = this.indicatorStyle; + } + } + }, + + hideIndicator: function() { + this.treeNode.contentNode.style.borderBottom = ""; + this.treeNode.contentNode.style.borderTop = ""; + this.treeNode.contentNode.style.border = ""; + this.treeNode.contentNode.style.width="" + this.position = null; + }, + + + + // is the target possibly ok ? + // This function is run on dragOver, but drop possibility is also determined by position over node + // that's why acceptsWithPosition is called + // doesnt take index into account ( can change while moving mouse w/o changing target ) + /** + * Coarse (tree-level) access check. + * We can't determine real accepts status w/o position + */ + onDragOver: function(e){ + //dojo.debug("onDragOver for "+e); + + var accepts = dojo.dnd.HtmlDropTarget.prototype.onDragOver.apply(this, arguments); + + //dojo.debug("TreeDropTarget.onDragOver accepts:"+accepts) + + if (accepts && this.treeNode.isFolder && !this.treeNode.isExpanded) { + this.setAutoExpandTimer(); + } + + if (accepts) { + this.cacheNodeCoords(); + } + + + return accepts; + }, + + /* Parent.onDragOver calls this function to get accepts status */ + accepts: function(dragObjects) { + + var accepts = dojo.dnd.HtmlDropTarget.prototype.accepts.apply(this, arguments); + + //dojo.debug("accepts "+accepts); + + if (!accepts) return false; + + for(var i=0; i it was allowed before, no accept check is needed + if (position=="onto") { + return position; + } + + for(var i=0; i forbidden + if (DndMode & dojo.widget.TreeV3.prototype.DndModes.ONTO + && DndMode & dojo.widget.TreeV3.prototype.DndModes.BETWEEN) { + //dojo.debug("BOTH"); + if (p<=0.33) { + position = "before"; + // if children are expanded then I ignore understrike, cause it is confusing with firstChild + // but for last nodes I put understrike there + } else if (p<=0.66 || this.treeNode.isExpanded && this.treeNode.children.length && !this.treeNode.isLastNode()) { + position = "onto"; + } else { + position = "after"; + } + } else if (DndMode & dojo.widget.TreeV3.prototype.DndModes.BETWEEN) { + //dojo.debug("BETWEEN"); + if (p<=0.5 || this.treeNode.isExpanded && this.treeNode.children.length && !this.treeNode.isLastNode()) { + position = "before"; + } else { + position = "after"; + } + } + else if (DndMode & dojo.widget.TreeV3.prototype.DndModes.ONTO) { + //dojo.debug("ONTO"); + position = "onto"; + } + + //dojo.debug(position); + + return position; + }, + + + + getTargetParentIndex: function(source, position) { + + var index = position == "before" ? this.treeNode.getParentIndex() : this.treeNode.getParentIndex()+1; + if (source.treeNode + && this.treeNode.parent === source.treeNode.parent + && this.treeNode.getParentIndex() > source.treeNode.getParentIndex()) { + index--; // dragging a node is different for simple move bacause of before-after issues + } + + return index; + }, + + + onDrop: function(e){ + // onDropEnd will clean position + + + var position = this.position; + +//dojo.debug(position); + var source = e.dragObject.dragSource; + + //dojo.debug("onDrop "+source.treeNode+" " + position + " "+this.treeNode); + + + var targetParent, targetIndex; + if (position == "onto") { + targetParent = this.treeNode; + targetIndex = 0; + } else { + targetIndex = this.getTargetParentIndex(source, position); + targetParent = this.treeNode.parent; + } + + //dojo.profile.start("onDrop "+sourceTreeNode); + var r = this.getDropHandler(e, source, targetParent, targetIndex)(); + + //dojo.profile.end("onDrop "+sourceTreeNode); + + return r; + + }, + + /** + * determine, which action I should perform with nodes + * e.g move, clone.. + */ + getDropHandler: function(e, source, targetParent, targetIndex) { + var handler; + var _this = this; + handler = function () { + //dojo.debug("Move "+source.treeNode+" to parent "+targetParent+":"+targetIndex); + if (source.treeNode) { + var result = _this.controller.move(source.treeNode, targetParent, targetIndex, true); + //dojo.debug("moved "+result); + } else { + if (source.onDrop) { + source.onDrop(targetParent, targetIndex); + } + var result = _this.controller.createChild(targetParent, targetIndex, source.getTreeNode(), true); + } + + if (result instanceof dojo.Deferred) { + // return error status + //TODO: need handle errors somehow + //result.addErrback(function(r) { dojo.debugShallow(r); }); + return (!result.fired) ? true : false; + } else { + return result; + } + } + + return handler; + } + + +}); + diff --git a/source/web/scripts/ajax/src/dnd/__package__.js b/source/web/scripts/ajax/src/dnd/__package__.js new file mode 100644 index 0000000000..c0bdc8f771 --- /dev/null +++ b/source/web/scripts/ajax/src/dnd/__package__.js @@ -0,0 +1,6 @@ +dojo.kwCompoundRequire({ + common: ["dojo.dnd.DragAndDrop"], + browser: ["dojo.dnd.HtmlDragAndDrop"], + dashboard: ["dojo.dnd.HtmlDragAndDrop"] +}); +dojo.provide("dojo.dnd.*"); diff --git a/source/web/scripts/ajax/src/doc.js b/source/web/scripts/ajax/src/doc.js new file mode 100644 index 0000000000..2f65ae7f58 --- /dev/null +++ b/source/web/scripts/ajax/src/doc.js @@ -0,0 +1,622 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.provide("dojo.doc"); +dojo.require("dojo.io.*"); +dojo.require("dojo.event.topic"); +dojo.require("dojo.rpc.JotService"); +dojo.require("dojo.dom"); + +/* + * TODO: + * + * Package summary needs to compensate for "is" + * Handle host environments + * Deal with dojo.widget weirdness + * Parse parameters + * Limit function parameters to only the valid ones (Involves packing parameters onto meta during rewriting) + * Package display page + * + */ + +dojo.doc._count = 0; +dojo.doc._keys = {}; +dojo.doc._myKeys = []; +dojo.doc._callbacks = {function_names: []}; +dojo.doc._cache = {}; // Saves the JSON objects in cache +dojo.doc._rpc = new dojo.rpc.JotService; +dojo.doc._rpc.serviceUrl = "http://dojotoolkit.org/~pottedmeat/jsonrpc.php"; + +dojo.lang.mixin(dojo.doc, { + functionNames: function(/*mixed*/ selectKey, /*Function*/ callback){ + // summary: Returns an ordered list of package and function names. + dojo.debug("functionNames()"); + if(!selectKey){ + selectKey = ++dojo.doc._count; + } + dojo.doc._buildCache({ + type: "function_names", + callbacks: [dojo.doc._functionNames, callback], + selectKey: selectKey + }); + }, + + _functionNames: function(/*String*/ type, /*Array*/ data, /*Object*/ evt){ + dojo.debug("_functionNames()"); + var searchData = []; + for(var key in data){ + // Add the package if it doesn't exist in its children + if(!dojo.lang.inArray(data[key], key)){ + searchData.push([key, key]); + } + // Add the functions + for(var pkg_key in data[key]){ + searchData.push([data[key][pkg_key], data[key][pkg_key]]); + } + } + + searchData = searchData.sort(dojo.doc._sort); + + if(evt.callbacks && evt.callbacks.length){ + var callback = evt.callbacks.shift(); + callback.call(null, type, searchData, evt); + } + }, + + getMeta: function(/*mixed*/ selectKey, /*Function*/ callback, /*Function*/ name, /*String?*/ id){ + // summary: Gets information about a function in regards to its meta data + dojo.debug("getMeta(" + name + ")"); + if(!selectKey){ + selectKey = ++dojo.doc._count; + } + dojo.doc._buildCache({ + type: "meta", + callbacks: [callback], + name: name, + id: id, + selectKey: selectKey + }); + }, + + _getMeta: function(/*String*/ type, /*Object*/ data, /*Object*/ evt){ + dojo.debug("_getMeta(" + evt.name + ") has package: " + evt.pkg + " with: " + type); + if("load" == type && evt.pkg){ + evt.type = "meta"; + dojo.doc._buildCache(evt); + }else{ + if(evt.callbacks && evt.callbacks.length){ + var callback = evt.callbacks.shift(); + callback.call(null, "error", {}, evt); + } + } + }, + + getSrc: function(/*mixed*/ selectKey, /*Function*/ callback, /*String*/ name, /*String?*/ id){ + // summary: Gets src file (created by the doc parser) + dojo.debug("getSrc()"); + if(!selectKey){ + selectKey = ++dojo.doc._count; + } + dojo.doc._buildCache({ + type: "src", + callbacks: [callback], + name: name, + id: id, + selectKey: selectKey + }); + }, + + _getSrc: function(/*String*/ type, /*Object*/ data, /*Object*/ evt){ + dojo.debug("_getSrc()"); + if(evt.pkg){ + evt.type = "src"; + dojo.doc._buildCache(evt); + }else{ + if(evt.callbacks && evt.callbacks.length){ + var callback = evt.callbacks.shift(); + callback.call(null, "error", {}, evt); + } + } + }, + + getDoc: function(/*mixed*/ selectKey, /*Function*/ callback, /*String*/ name, /*String?*/ id){ + // summary: Gets external documentation stored on jot + dojo.debug("getDoc()"); + if(!selectKey){ + selectKey = ++dojo.doc._count; + } + var input = { + type: "doc", + callbacks: [callback], + name: name, + id: id, + selectKey: selectKey + } + dojo.doc.functionPackage(dojo.doc._getDoc, input); + }, + + _getDoc: function(/*String*/ type, /*Object*/ data, /*Object*/ evt){ + dojo.debug("_getDoc(" + evt.pkg + "/" + evt.name + ")"); + + dojo.doc._keys[evt.selectKey] = {count: 0}; + + var search = {}; + search.forFormName = "DocFnForm"; + search.limit = 1; + + if(!evt.id){ + search.filter = "it/DocFnForm/require = '" + evt.pkg + "' and it/DocFnForm/name = '" + evt.name + "' and not(it/DocFnForm/id)"; + }else{ + search.filter = "it/DocFnForm/require = '" + evt.pkg + "' and it/DocFnForm/name = '" + evt.name + "' and it/DocFnForm/id = '" + evt.id + "'"; + } + dojo.debug(dojo.json.serialize(search)); + + dojo.doc._rpc.callRemote("search", search).addCallbacks(function(data){ evt.type = "fn"; dojo.doc._gotDoc("load", data.list[0], evt); }, function(data){ evt.type = "fn"; dojo.doc._gotDoc("error", {}, evt); }); + + search.forFormName = "DocParamForm"; + + if(!evt.id){ + search.filter = "it/DocParamForm/fns = '" + evt.pkg + "=>" + evt.name + "'"; + }else{ + search.filter = "it/DocParamForm/fns = '" + evt.pkg + "=>" + evt.name + "=>" + evt.id + "'"; + } + delete search.limit; + + dojo.doc._rpc.callRemote("search", search).addCallbacks(function(data){ evt.type = "param"; dojo.doc._gotDoc("load", data.list, evt); }, function(data){ evt.type = "param"; dojo.doc._gotDoc("error", {}, evt); }); + }, + + _gotDoc: function(/*String*/ type, /*Array*/ data, /*Object*/ evt){ + dojo.debug("_gotDoc(" + evt.type + ") for " + evt.selectKey); + dojo.doc._keys[evt.selectKey][evt.type] = data; + if(++dojo.doc._keys[evt.selectKey].count == 2){ + dojo.debug("_gotDoc() finished"); + var keys = dojo.doc._keys[evt.selectKey]; + var description = ''; + if(!keys.fn){ + keys.fn = {} + } + if(keys.fn["main/text"]){ + description = dojo.dom.createDocumentFromText(keys.fn["main/text"]).childNodes[0].innerHTML; + if(!description){ + description = keys.fn["main/text"]; + } + } + data = { + description: description, + returns: keys.fn["DocFnForm/returns"], + id: keys.fn["DocFnForm/id"], + parameters: {}, + variables: [] + } + for(var i = 0, param; param = keys["param"][i]; i++){ + data.parameters[param["DocParamForm/name"]] = { + description: param["DocParamForm/desc"] + }; + } + + delete dojo.doc._keys[evt.selectKey]; + + if(evt.callbacks && evt.callbacks.length){ + var callback = evt.callbacks.shift(); + callback.call(null, "load", data, evt); + } + } + }, + + getPkgMeta: function(/*mixed*/ selectKey, /*Function*/ callback, /*String*/ name){ + dojo.debug("getPkgMeta(" + name + ")"); + if(!selectKey){ + selectKey = ++dojo.doc._count; + } + dojo.doc._buildCache({ + type: "pkgmeta", + callbacks: [callback], + name: name, + selectKey: selectKey + }); + }, + + _getPkgMeta: function(/*Object*/ input){ + dojo.debug("_getPkgMeta(" + input.name + ")"); + input.type = "pkgmeta"; + dojo.doc._buildCache(input); + }, + + _onDocSearch: function(/*Object*/ input){ + dojo.debug("_onDocSearch(" + input.name + ")"); + if(!input.name){ + return; + } + if(!input.selectKey){ + input.selectKey = ++dojo.doc._count; + } + input.callbacks = [dojo.doc._onDocSearchFn]; + input.name = input.name.toLowerCase(); + input.type = "function_names"; + + dojo.doc._buildCache(input); + }, + + _onDocSearchFn: function(/*String*/ type, /*Array*/ data, /*Object*/ evt){ + dojo.debug("_onDocSearchFn(" + evt.name + ")"); + var packages = []; + var size = 0; + pkgLoop: + for(var pkg in data){ + for(var i = 0, fn; fn = data[pkg][i]; i++){ + if(fn.toLowerCase().indexOf(evt.name) != -1){ + // Build a list of all packages that need to be loaded and their loaded state. + ++size; + packages.push(pkg); + continue pkgLoop; + } + } + } + dojo.doc._keys[evt.selectKey] = {}; + dojo.doc._keys[evt.selectKey].pkgs = packages; + dojo.doc._keys[evt.selectKey].pkg = evt.name; // Remember what we were searching for + dojo.doc._keys[evt.selectKey].loaded = 0; + for(var i = 0, pkg; pkg = packages[i]; i++){ + setTimeout("dojo.doc.getPkgMeta(\"" + evt.selectKey + "\", dojo.doc._onDocResults, \"" + pkg + "\");", i*10); + } + }, + + _onDocResults: function(/*String*/ type, /*Object*/ data, /*Object*/ evt){ + dojo.debug("_onDocResults(" + evt.name + "/" + dojo.doc._keys[evt.selectKey].pkg + ") " + type); + ++dojo.doc._keys[evt.selectKey].loaded; + + if(dojo.doc._keys[evt.selectKey].loaded == dojo.doc._keys[evt.selectKey].pkgs.length){ + var info = dojo.doc._keys[evt.selectKey]; + var pkgs = info.pkgs; + var name = info.pkg; + delete dojo.doc._keys[evt.selectKey]; + var results = {selectKey: evt.selectKey, docResults: []}; + data = dojo.doc._cache; + + for(var i = 0, pkg; pkg = pkgs[i]; i++){ + if(!data[pkg]){ + continue; + } + for(var fn in data[pkg]["meta"]){ + if(fn.toLowerCase().indexOf(name) == -1){ + continue; + } + if(fn != "requires"){ + for(var pId in data[pkg]["meta"][fn]){ + var result = { + pkg: pkg, + name: fn, + summary: "" + } + if(data[pkg]["meta"][fn][pId].summary){ + result.summary = data[pkg]["meta"][fn][pId].summary; + } + results.docResults.push(result); + } + } + } + } + + dojo.debug("Publishing docResults"); + dojo.doc._printResults(results); + } + }, + + _printResults: function(results){ + dojo.debug("_printResults(): called"); + // summary: Call this function to send the /doc/results topic + }, + + _onDocSelectFunction: function(/*Object*/ input){ + // summary: Get doc, meta, and src + var name = input.name; + var selectKey = selectKey; + dojo.debug("_onDocSelectFunction(" + name + ")"); + if(!name){ + return false; + } + if(!selectKey){ + selectKey = ++dojo.doc._count; + } + + dojo.doc._keys[selectKey] = {size: 0}; + dojo.doc._myKeys[++dojo.doc._count] = {selectKey: selectKey, type: "meta"} + dojo.doc.getMeta(dojo.doc._count, dojo.doc._onDocSelectResults, name); + dojo.doc._myKeys[++dojo.doc._count] = {selectKey: selectKey, type: "src"} + dojo.doc.getSrc(dojo.doc._count, dojo.doc._onDocSelectResults, name); + dojo.doc._myKeys[++dojo.doc._count] = {selectKey: selectKey, type: "doc"} + dojo.doc.getDoc(dojo.doc._count, dojo.doc._onDocSelectResults, name); + }, + + _onDocSelectResults: function(/*String*/ type, /*Object*/ data, /*Object*/ evt){ + dojo.debug("dojo.doc._onDocSelectResults(" + evt.type + ", " + evt.name + ")"); + var myKey = dojo.doc._myKeys[evt.selectKey]; + dojo.doc._keys[myKey.selectKey][myKey.type] = data; + dojo.doc._keys[myKey.selectKey].size; + if(++dojo.doc._keys[myKey.selectKey].size == 3){ + var key = dojo.lang.mixin(evt, dojo.doc._keys[myKey.selectKey]); + delete key.size; + dojo.debug("Publishing docFunctionDetail"); + dojo.doc._printFunctionDetail(key); + delete dojo.doc._keys[myKey.selectKey]; + delete dojo.doc._myKeys[evt.selectKey]; + } + }, + + _printFunctionDetail: function(results) { + // summary: Call this function to send the /doc/functionDetail topic event + }, + + _buildCache: function(/*Object*/ input){ + var type = input.type; + var pkg = input.pkg; + var callbacks = input.callbacks; + var id = input.id; + if(!id){ + id = "_"; + } + var name = input.name; + + dojo.debug("_buildCache() type: " + type); + if(type == "function_names"){ + if(!dojo.doc._cache["function_names"]){ + dojo.debug("_buildCache() new cache"); + if(callbacks && callbacks.length){ + dojo.doc._callbacks.function_names.push([input, callbacks.shift()]); + } + dojo.doc._cache["function_names"] = {loading: true}; + dojo.io.bind({ + url: "json/function_names", + mimetype: "text/json", + error: function(type, data, evt){ + dojo.debug("Unable to load function names"); + for(var i = 0, callback; callback = dojo.doc._callbacks.function_names[i]; i++){ + callback[1].call(null, "error", {}, callback[0]); + } + }, + load: function(type, data, evt){ + dojo.doc._cache['function_names'] = data; + for(var i = 0, callback; callback = dojo.doc._callbacks.function_names[i]; i++){ + callback[1].call(null, "load", data, callback[0]); + } + } + }); + }else if(dojo.doc._cache["function_names"].loading){ + dojo.debug("_buildCache() loading cache"); + if(callbacks && callbacks.length){ + dojo.doc._callbacks.function_names.push([input, callbacks.shift()]); + } + }else{ + dojo.debug("_buildCache() from cache"); + if(callbacks && callbacks.length){ + var callback = callbacks.shift(); + callback.call(null, "load", dojo.doc._cache["function_names"], input); + } + } + }else if(type == "meta" || type == "src"){ + if(!pkg){ + if(type == "meta"){ + dojo.doc.functionPackage(dojo.doc._getMeta, input); + }else{ + dojo.doc.functionPackage(dojo.doc._getSrc, input); + } + }else{ + try{ + var cached = dojo.doc._cache[pkg][name][id][type]; + }catch(e){} + + if(cached){ + if(callbacks && callbacks.length){ + var callback = callbacks.shift(); + callback.call(null, "load", cached, input); + return; + } + } + + dojo.debug("Finding " + type + " for: " + pkg + ", function: " + name + ", id: " + id); + + var mimetype = "text/json"; + if(type == "src"){ + mimetype = "text/plain" + } + + var url = "json/" + pkg + "/" + name + "/" + id + "/" + type; + + dojo.io.bind({ + url: url, + input: input, + mimetype: mimetype, + error: function(type, data, evt, args){ + var input = args.input; + var pkg = input.pkg; + var type = input.type; + var callbacks = input.callbacks; + var id = input.id; + var name = input.name; + + if(callbacks && callbacks.length){ + if(!data){ + data = {}; + } + if(!dojo.doc._cache[pkg]){ + dojo.doc._cache[pkg] = {}; + } + if(!dojo.doc._cache[pkg][name]){ + dojo.doc._cache[pkg][name] = {}; + } + if(type == "meta"){ + data.sig = dojo.doc._cache[pkg][name][id].sig; + data.params = dojo.doc._cache[pkg][name][id].params; + } + var callback = callbacks.shift(); + callback.call(null, "error", data, args.input); + } + }, + load: function(type, data, evt, args){ + var input = args.input; + var pkg = input.pkg; + var type = input.type; + var id = input.id; + var name = input.name; + var cache = dojo.doc._cache; + dojo.debug("_buildCache() loaded " + type); + + if(!data){ + data = {}; + } + if(!cache[pkg]){ + dojo.doc._cache[pkg] = {}; + } + if(!cache[pkg][name]){ + dojo.doc._cache[pkg][name] = {}; + } + if(!cache[pkg][name][id]){ + dojo.doc._cache[pkg][name][id] = {}; + } + if(!cache[pkg][name][id].meta){ + dojo.doc._cache[pkg][name][id].meta = {}; + } + dojo.doc._cache[pkg][name][id][type] = data; + if(callbacks && callbacks.length){ + var callback = callbacks.shift(); + callback.call(null, "load", data, args.input); + } + } + }); + } + }else if(type == "pkgmeta"){ + try{ + var cached = dojo.doc._cache[name]["meta"]; + }catch(e){} + + if(cached){ + if(callbacks && callbacks.length){ + var callback = callbacks.shift(); + callback.call(null, "load", cached, input); + return; + } + } + + dojo.debug("Finding package meta for: " + name); + + dojo.io.bind({ + url: "json/" + name + "/meta", + input: input, + mimetype: "text/json", + error: function(type, data, evt, args){ + var callbacks = args.input.callbacks; + if(callbacks && callbacks.length){ + var callback = callbacks.shift(); + callback.call(null, "error", {}, args.input); + } + }, + load: function(type, data, evt, args){ + var pkg = args.input.name; + var cache = dojo.doc._cache; + + dojo.debug("_buildCache() loaded for: " + pkg); + if(!cache[pkg]){ + dojo.doc._cache[pkg] = {}; + } + + if(!cache[pkg]["meta"]){ + dojo.doc._cache[pkg]["meta"] = {}; + } + + var methods = data.methods; + if(methods){ + for(var method in methods){ + if (method == "is") { + continue; + } + for(var pId in methods[method]){ + if(!cache[pkg]["meta"][method]){ + dojo.doc._cache[pkg]["meta"][method] = {}; + } + if(!cache[pkg]["meta"][method][pId]){ + dojo.doc._cache[pkg]["meta"][method][pId] = {}; + } + dojo.doc._cache[pkg]["meta"][method][pId].summary = methods[method][pId]; + } + } + } + + dojo.doc._cache[pkg]["meta"].methods = methods; + var requires = data.requires; + if(requires){ + dojo.doc._cache[pkg]["meta"].requires = requires; + } + if(callbacks && callbacks.length){ + var callback = callbacks.shift(); + callback.call(null, "load", methods, input); + } + } + }); + } + }, + + selectFunction: function(/*String*/ name, /*String?*/ id){ + // summary: The combined information + }, + + savePackage: function(/*String*/ name, /*String*/ description){ + dojo.doc._rpc.callRemote( + "saveForm", + { + form: "DocPkgForm", + path: "/WikiHome/DojoDotDoc/id", + pname1: "main/text", + pvalue1: "Test" + } + ).addCallbacks(dojo.doc._results, dojo.doc._results); + }, + + functionPackage: function(/*Function*/ callback, /*Object*/ input){ + dojo.debug("functionPackage() name: " + input.name + " for type: " + input.type); + input.type = "function_names"; + input.callbacks.unshift(callback); + input.callbacks.unshift(dojo.doc._functionPackage); + dojo.doc._buildCache(input); + }, + + _functionPackage: function(/*String*/ type, /*Array*/ data, /*Object*/ evt){ + dojo.debug("_functionPackage() name: " + evt.name + " for: " + evt.type + " with: " + type); + evt.pkg = ''; + + var data = dojo.doc._cache['function_names']; + for(var key in data){ + if(dojo.lang.inArray(data[key], evt.name)){ + evt.pkg = key; + break; + } + } + + if(evt.callbacks && evt.callbacks.length){ + var callback = evt.callbacks.shift(); + callback.call(null, type, data[key], evt); + } + }, + + _sort: function(a, b){ + if(a[0] < b[0]){ + return -1; + } + if(a[0] > b[0]){ + return 1; + } + return 0; + } +}); + +dojo.event.topic.subscribe("/doc/search", dojo.doc, "_onDocSearch"); +dojo.event.topic.subscribe("/doc/selectFunction", dojo.doc, "_onDocSelectFunction"); + +dojo.event.topic.registerPublisher("/doc/results", dojo.doc, "_printResults"); +dojo.event.topic.registerPublisher("/doc/functionDetail", dojo.doc, "_printFunctionDetail"); \ No newline at end of file diff --git a/source/web/scripts/ajax/src/docs.js b/source/web/scripts/ajax/src/docs.js new file mode 100644 index 0000000000..03b59b9200 --- /dev/null +++ b/source/web/scripts/ajax/src/docs.js @@ -0,0 +1,985 @@ +dojo.provide("dojo.docs"); +dojo.require("dojo.io.*"); +dojo.require("dojo.event.topic"); +dojo.require("dojo.rpc.JotService"); +dojo.require("dojo.dom"); +dojo.require("dojo.uri.Uri"); + +/* + * TODO: + * + * Package summary needs to compensate for "is" + * Handle host environments + * Deal with dojo.widget weirdness + * Parse parameters + * Limit function parameters to only the valid ones (Involves packing parameters onto meta during rewriting) + * + */ + +dojo.docs._count = 0; +dojo.docs._callbacks = {function_names: []}; +dojo.docs._cache = {}; // Saves the JSON objects in cache +dojo.docs._url = dojo.uri.dojoUri("docscripts/json/"); +dojo.docs._rpc = new dojo.rpc.JotService; +dojo.docs._rpc.serviceUrl = dojo.uri.dojoUri("docscripts/jsonrpc.php"); + +dojo.lang.mixin(dojo.docs, { + functionNames: function(/*mixed*/ selectKey, /*Function*/ callback){ + // summary: Returns an ordered list of package and function names. + dojo.debug("functionNames()"); + if(!selectKey){ + selectKey = ++dojo.docs._count; + } + + var input = {}; + if(typeof selectKey == "object" && selectKey.selectKey){ + input = selectKey; + selectKey = selectKey.selectKey; + } + + dojo.docs._buildCache({ + type: "function_names", + callbacks: [dojo.docs._functionNames, callback], + selectKey: selectKey, + input: input + }); + }, + _functionNames: function(/*String*/ type, /*Array*/ data, /*Object*/ evt){ + // summary: Converts the stored JSON object into a sorted list of packages + // and functions + dojo.debug("_functionNames()"); + var searchData = []; + for(var key in data){ + // Add the package if it doesn't exist in its children + if(!dojo.lang.inArray(data[key], key)){ + var aKey = key; + if(aKey.charAt(aKey.length - 1) == "_"){ + aKey = [aKey.substring(0, aKey.length - 1), "*"].join(""); + } + searchData.push([aKey, aKey]); + } + // Add the functions + for(var pkg_key in data[key]){ + var aKey = data[key][pkg_key]; + if(aKey.charAt(aKey.length - 1) == "_"){ + aKey = [aKey.substring(0, aKey.length - 1), "*"].join(""); + } + searchData.push([aKey, aKey]); + } + } + + searchData = searchData.sort(dojo.docs._sort); + + if(evt.callbacks && evt.callbacks.length){ + evt.callbacks.shift()(type, searchData, evt, evt.input); + } + }, + getMeta: function(/*mixed*/ selectKey, /*String*/ pkg, /*String*/ name, /*Function*/ callback, /*String?*/ id){ + // summary: Gets information about a function in regards to its meta data + if(typeof name == "function"){ + // pId: a + // pkg: ignore + id = callback; + callback = name; + name = pkg; + pkg = null; + dojo.debug("getMeta(" + name + ")"); + }else{ + dojo.debug("getMeta(" + pkg + "/" + name + ")"); + } + + if(!id){ + id = "_"; + } + + if(!selectKey){ + selectKey = ++dojo.docs._count; + } + + var input; + if(typeof selectKey == "object" && selectKey.selectKey){ + input = selectKey; + selectKey = selectKey.selectKey; + }else{ + input = {}; + } + + dojo.docs._buildCache({ + type: "meta", + callbacks: [dojo.docs._gotMeta, callback], + pkg: pkg, + name: name, + id: id, + selectKey: selectKey, + input: input + }); + }, + _withPkg: function(/*String*/ type, /*Object*/ data, /*Object*/ evt, /*Object*/ input, /*String*/ newType){ + dojo.debug("_withPkg(" + evt.name + ") has package: " + data[0]); + evt.pkg = data[0]; + if("load" == type && evt.pkg){ + evt.type = newType; + dojo.docs._buildCache(evt); + }else{ + if(evt.callbacks && evt.callbacks.length){ + evt.callbacks.shift()("error", {}, evt, evt.input); + } + } + }, + _gotMeta: function(/*String*/ type, /*Object*/ data, /*Object*/ evt){ + dojo.debug("_gotMeta(" + evt.name + ")"); + + var cached = dojo.docs._getCache(evt.pkg, evt.name, "meta", "methods", evt.id); + if(cached.summary){ + data.summary = cached.summary; + } + if(evt.callbacks && evt.callbacks.length){ + evt.callbacks.shift()(type, data, evt, evt.input); + } + }, + getSrc: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback, /*String?*/ id){ + // summary: Gets src file (created by the doc parser) + dojo.debug("getSrc(" + name + ")"); + if(!id){ + id = "_"; + } + if(!selectKey){ + selectKey = ++dojo.docs._count; + } + + var input; + if(typeof selectKey == "object" && selectKey.selectKey){ + input = selectKey; + selectKey = selectKey.selectKey; + }else{ + input = {}; + } + + dojo.docs._buildCache({ + type: "src", + callbacks: [callback], + name: name, + id: id, + input: input, + selectKey: selectKey + }); + }, + getDoc: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback, /*String?*/ id){ + // summary: Gets external documentation stored on Jot for a given function + dojo.debug("getDoc(" + name + ")"); + + if(!id){ + id = "_"; + } + + if(!selectKey){ + selectKey = ++dojo.docs._count; + } + + var input = {}; + if(typeof selectKey == "object" && selectKey.selectKey){ + input.input = selectKey; + selectKey = selectKey.selectKey; + } + + input.type = "doc"; + input.name = name; + input.selectKey = selectKey; + input.callbacks = [callback]; + input.selectKey = selectKey; + + dojo.docs._buildCache(input); + }, + _gotDoc: function(/*String*/ type, /*Array*/ data, /*Object*/ evt, /*Object*/ input){ + dojo.debug("_gotDoc(" + evt.type + ")"); + + evt[evt.type] = data; + if(evt.expects && evt.expects.doc){ + for(var i = 0, expect; expect = evt.expects.doc[i]; i++){ + if(!(expect in evt)){ + dojo.debug("_gotDoc() waiting for more data"); + return; + } + } + } + + var cache = dojo.docs._getCache(evt.pkg, "meta", "methods", evt.name, evt.id, "meta"); + + var description = evt.fn.description; + cache.description = description; + data = { + returns: evt.fn.returns, + id: evt.id, + variables: [], + selectKey: evt.selectKey + } + if(!cache.parameters){ + cache.parameters = {}; + } + for(var i = 0, param; param = evt.param[i]; i++){ + var fName = param["DocParamForm/name"]; + if(!cache.parameters[fName]){ + cache.parameters[fName] = {}; + } + cache.parameters[fName].description = param["DocParamForm/desc"] + } + + data.description = cache.description; + data.parameters = cache.parameters; + + evt.type = "doc"; + + if(evt.callbacks && evt.callbacks.length){ + evt.callbacks.shift()("load", data, evt, input); + } + }, + getPkgDoc: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback){ + // summary: Gets external documentation stored on Jot for a given package + dojo.debug("getPkgDoc(" + name + ")"); + var input = {}; + if(typeof selectKey == "object" && selectKey.selectKey){ + input = selectKey; + selectKey = selectKey.selectKey; + } + if(!selectKey){ + selectKey = ++dojo.docs._count; + } + dojo.docs._buildCache({ + type: "pkgdoc", + callbacks: [callback], + name: name, + selectKey: selectKey, + input: input + }); + }, + getPkgInfo: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback){ + // summary: Gets a combination of the metadata and external documentation for a given package + dojo.debug("getPkgInfo(" + name + ")"); + if(!selectKey){ + selectKey = ++dojo.docs._count; + } + + var input = { + selectKey: selectKey, + expects: { + pkginfo: ["pkgmeta", "pkgdoc"] + }, + callback: callback + }; + dojo.docs.getPkgMeta(input, name, dojo.docs._getPkgInfo); + dojo.docs.getPkgDoc(input, name, dojo.docs._getPkgInfo); + }, + _getPkgInfo: function(/*String*/ type, /*Object*/ data, /*Object*/ evt){ + dojo.debug("_getPkgInfo() for " + evt.type); + var key = evt.selectKey; + var input = {}; + var results = {}; + if(typeof key == "object"){ + input = key; + key = key.selectKey; + input[evt.type] = data; + if(input.expects && input.expects.pkginfo){ + for(var i = 0, expect; expect = input.expects.pkginfo[i]; i++){ + if(!(expect in input)){ + dojo.debug("_getPkgInfo() waiting for more data"); + return; + } + } + } + results = input.pkgmeta; + results.description = input.pkgdoc; + } + + if(input.callback){ + input.callback("load", results, evt); + } + }, + getInfo: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback){ + dojo.debug("getInfo(" + name + ")"); + var input = { + expects: { + "info": ["meta", "doc"] + }, + selectKey: selectKey, + callback: callback + } + dojo.docs.getMeta(input, name, dojo.docs._getInfo); + dojo.docs.getDoc(input, name, dojo.docs._getInfo); + }, + _getInfo: function(/*String*/ type, /*String*/ data, /*Object*/ evt, /*Object*/ input){ + dojo.debug("_getInfo(" + evt.type + ")"); + if(input && input.expects && input.expects.info){ + input[evt.type] = data; + for(var i = 0, expect; expect = input.expects.info[i]; i++){ + if(!(expect in input)){ + dojo.debug("_getInfo() waiting for more data"); + return; + } + } + } + + if(input.callback){ + input.callback("load", dojo.docs._getCache(evt.pkg, "meta", "methods", evt.name, evt.id, "meta"), evt, input); + } + }, + _getMainText: function(/*String*/ text){ + // summary: Grabs the innerHTML from a Jot Rech Text node + dojo.debug("_getMainText()"); + return text.replace(/^/, "").replace(/<\/html>$/, "").replace(/<\w+\s*\/>/g, ""); + }, + getPkgMeta: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback){ + dojo.debug("getPkgMeta(" + name + ")"); + var input = {}; + if(typeof selectKey == "object" && selectKey.selectKey){ + input = selectKey; + selectKey = selectKey.selectKey; + }else if(!selectKey){ + selectKey = ++dojo.docs._count; + } + dojo.docs._buildCache({ + type: "pkgmeta", + callbacks: [callback], + name: name, + selectKey: selectKey, + input: input + }); + }, + _getPkgMeta: function(/*Object*/ input){ + dojo.debug("_getPkgMeta(" + input.name + ")"); + input.type = "pkgmeta"; + dojo.docs._buildCache(input); + }, + _onDocSearch: function(/*Object*/ input){ + input.name = input.name.replace("*", "_"); + dojo.debug("_onDocSearch(" + input.name + ")"); + if(!input.name){ + return; + } + if(!input.selectKey){ + input.selectKey = ++dojo.docs._count; + } + input.callbacks = [dojo.docs._onDocSearchFn]; + input.name = input.name.toLowerCase(); + input.type = "function_names"; + + dojo.docs._buildCache(input); + }, + _onDocSearchFn: function(/*String*/ type, /*Array*/ data, /*Object*/ evt){ + dojo.debug("_onDocSearchFn(" + evt.name + ")"); + + var packages = []; + pkgLoop: + for(var pkg in data){ + if(pkg.toLowerCase() == evt.name.toLowerCase()){ + evt.name = pkg; + dojo.debug("_onDocSearchFn found a package"); + dojo.docs._onDocSelectPackage(evt); + return; + } + for(var i = 0, fn; fn = data[pkg][i]; i++){ + if(fn.toLowerCase().indexOf(evt.name) != -1){ + // Build a list of all packages that need to be loaded and their loaded state. + packages.push(pkg); + continue pkgLoop; + } + } + } + dojo.debug("_onDocSearchFn found a function"); + + evt.pkgs = packages; + evt.pkg = evt.name; + evt.loaded = 0; + for(var i = 0, pkg; pkg = packages[i]; i++){ + dojo.docs.getPkgMeta(evt, pkg, dojo.docs._onDocResults); + } + }, + _onPkgResults: function(/*String*/ type, /*Object*/ data, /*Object*/ evt, /*Object*/ input){ + dojo.debug("_onPkgResults(" + evt.type + ")"); + var description = ""; + var path = ""; + var methods = {}; + var requires = {}; + if(input){ + input[evt.type] = data; + if(input.expects && input.expects.pkgresults){ + for(var i = 0, expect; expect = input.expects.pkgresults[i]; i++){ + if(!(expect in input)){ + dojo.debug("_onPkgResults() waiting for more data"); + return; + } + } + } + path = input.pkgdoc.path; + description = input.pkgdoc.description; + methods = input.pkgmeta.methods; + requires = input.pkgmeta.requires; + } + var pkg = evt.name.replace("_", "*"); + var results = { + path: path, + description: description, + size: 0, + methods: [], + pkg: pkg, + selectKey: evt.selectKey, + requires: requires + } + var rePrivate = /_[^.]+$/; + for(var method in methods){ + if(!rePrivate.test(method)){ + for(var pId in methods[method]){ + results.methods.push({ + pkg: pkg, + name: method, + id: pId, + summary: methods[method][pId].summary + }) + } + } + } + results.size = results.methods.length; + dojo.docs._printPkgResults(results); + }, + _onDocResults: function(/*String*/ type, /*Object*/ data, /*Object*/ evt, /*Object*/ input){ + dojo.debug("_onDocResults(" + evt.name + "/" + input.pkg + ") " + type); + ++input.loaded; + + if(input.loaded == input.pkgs.length){ + var pkgs = input.pkgs; + var name = input.pkg; + var results = {selectKey: evt.selectKey, docResults: []}; + var rePrivate = /_[^.]+$/; + data = dojo.docs._cache; + + for(var i = 0, pkg; pkg = pkgs[i]; i++){ + var methods = dojo.docs._getCache(pkg, "meta", "methods"); + for(var fn in methods){ + if(fn.toLowerCase().indexOf(name) == -1){ + continue; + } + if(fn != "requires" && !rePrivate.test(fn)){ + for(var pId in methods[fn]){ + var result = { + pkg: pkg, + name: fn, + summary: "" + } + if(methods[fn][pId].summary){ + result.summary = methods[fn][pId].summary; + } + results.docResults.push(result); + } + } + } + } + + dojo.debug("Publishing docResults"); + dojo.docs._printFnResults(results); + } + }, + _printFnResults: function(results){ + dojo.debug("_printFnResults(): called"); + // summary: Call this function to send the /docs/function/results topic + }, + _printPkgResults: function(results){ + dojo.debug("_printPkgResults(): called"); + }, + _onDocSelectFunction: function(/*Object*/ input){ + // summary: Get doc, meta, and src + var name = input.name; + dojo.debug("_onDocSelectFunction(" + name + ")"); + if(!name){ + return false; + } + if(!input.selectKey){ + input.selectKey = ++dojo.docs._count; + } + input.expects = { + "docresults": ["meta", "doc", "pkgmeta"] + } + dojo.docs.getMeta(input, name, dojo.docs._onDocSelectResults); + dojo.docs.getDoc(input, name, dojo.docs._onDocSelectResults); + }, + _onDocSelectPackage: function(/*Object*/ input){ + dojo.debug("_onDocSelectPackage(" + input.name + ")") + input.expects = { + "pkgresults": ["pkgmeta", "pkgdoc"] + }; + if(!input.selectKey){ + input.selectKey = ++dojo.docs._count; + } + dojo.docs.getPkgMeta(input, input.name, dojo.docs._onPkgResults); + dojo.docs.getPkgDoc(input, input.name, dojo.docs._onPkgResults); + }, + _onDocSelectResults: function(/*String*/ type, /*Object*/ data, /*Object*/ evt, /*Object*/ input){ + dojo.debug("_onDocSelectResults(" + evt.type + ", " + evt.name + ")"); + if(evt.type == "meta"){ + dojo.docs.getPkgMeta(input, evt.pkg, dojo.docs._onDocSelectResults); + } + if(input){ + input[evt.type] = data; + if(input.expects && input.expects.docresults){ + for(var i = 0, expect; expect = input.expects.docresults[i]; i++){ + if(!(expect in input)){ + dojo.debug("_onDocSelectResults() waiting for more data"); + return; + } + } + } + } + + dojo.docs._printFunctionDetail(input); + }, + + _printFunctionDetail: function(results) { + // summary: Call this function to send the /docs/function/detail topic event + }, + + _buildCache: function(/*Object*/ input){ + dojo.debug("_buildCache(" + input.type + ", " + input.name + ")"); + // Get stuff from the input object + var type = input.type; + var pkg = input.pkg; + var callbacks = input.callbacks; + var id = input.id; + if(!id){ + id = input.id = "_"; + } + var name = input.name; + var selectKey = input.selectKey; + + var META = "meta"; + var METHODS = "methods"; + var SRC = "src"; + var DESCRIPTION = "description"; + var INPUT = "input"; + var LOAD = "load"; + var ERROR = "error"; + + var docs = dojo.docs; + var getCache = docs._getCache; + + // Stuff to pass to RPC + var search = []; + + if(type == "doc"){ + if(!pkg){ + docs.functionPackages(selectKey, name, function(){ var a = arguments; docs._withPkg.call(this, a[0], a[1], a[2], a[3], "doc"); }, input); + return; + }else{ + var cached = getCache(pkg, META, METHODS, name, id, META); + + if(cached[DESCRIPTION]){ + callbacks.shift()(LOAD, cached[DESCRIPTION], input, input[INPUT]); + return; + } + + var obj = {}; + obj.forFormName = "DocFnForm"; + obj.limit = 1; + + obj.filter = "it/DocFnForm/require = '" + pkg + "' and it/DocFnForm/name = '" + name + "' and "; + if(id == "_"){ + obj.filter += " not(it/DocFnForm/id)"; + }else{ + obj.filter += " it/DocFnForm/id = '" + id + "'"; + } + + obj.load = function(data){ + var cached = getCache(pkg, META, METHODS, name, id, META); + + var description = ""; + var returns = ""; + if(data.list && data.list.length){ + description = docs._getMainText(data.list[0]["main/text"]); + returns = data.list[0]["DocFnForm/returns"]; + } + + cached[DESCRIPTION] = description; + if(!cached.returns){ + cached.returns = {}; + } + cached.returns.summary = returns; + + input.type = "fn"; + docs._gotDoc(LOAD, cached, input, input[INPUT]); + } + obj.error = function(data){ + input.type = "fn"; + docs._gotDoc(ERROR, {}, input, input[INPUT]); + } + search.push(obj); + + obj = {}; + obj.forFormName = "DocParamForm"; + + obj.filter = "it/DocParamForm/fns = '" + pkg + "=>" + name; + if(id != "_"){ + obj.filter += "=>" + id; + } + obj.filter += "'"; + + obj.load = function(data){ + var cache = getCache(pkg, META, METHODS, name, id, META); + for(var i = 0, param; param = data.list[i]; i++){ + var pName = param["DocParamForm/name"]; + if(!cache.parameters[pName]){ + cache.parameters[pName] = {}; + } + cache.parameters[pName].summary = param["DocParamForm/desc"]; + } + input.type = "param"; + docs._gotDoc(LOAD, cache.parameters, input); + } + obj.error = function(data){ + input.type = "param"; + docs._gotDoc(ERROR, {}, input); + } + search.push(obj); + } + }else if(type == "pkgdoc"){ + var cached = getCache(name, META); + + if(cached[DESCRIPTION]){ + callbacks.shift()(LOAD, {description: cached[DESCRIPTION], path: cached.path}, input, input.input); + return; + } + + var obj = {}; + obj.forFormName = "DocPkgForm"; + obj.limit = 1; + obj.filter = "it/DocPkgForm/require = '" + name + "'"; + + obj.load = function(data){ + var description = ""; + var list = data.list; + if(list && list.length && list[0]["main/text"]){ + description = docs._getMainText(list[0]["main/text"]); + cached[DESCRIPTION] = description; + cached.path = list[0].name; + } + + if(callbacks && callbacks.length){ + callbacks.shift()(LOAD, {description: description, path: cached.path}, input, input.input); + } + } + obj.error = function(data){ + if(callbacks && callbacks.length){ + callbacks.shift()(ERROR, "", input, input.input); + } + } + search.push(obj); + }else if(type == "function_names"){ + var cached = getCache(); + if(!cached.function_names){ + dojo.debug("_buildCache() new cache"); + if(callbacks && callbacks.length){ + docs._callbacks.function_names.push([input, callbacks.shift()]); + } + cached.function_names = {loading: true}; + + var obj = {}; + obj.url = "function_names"; + obj.load = function(type, data, evt){ + cached.function_names = data; + while(docs._callbacks.function_names.length){ + var parts = docs._callbacks.function_names.pop(); + parts[1](LOAD, data, parts[0]); + } + } + obj.error = function(type, data, evt){ + while(docs._callbacks.function_names.length){ + var parts = docs._callbacks.function_names.pop(); + parts[1](LOAD, {}, parts[0]); + } + } + search.push(obj); + }else if(cached.function_names.loading){ + dojo.debug("_buildCache() loading cache, adding to callback list"); + if(callbacks && callbacks.length){ + docs._callbacks.function_names.push([input, callbacks.shift()]); + } + return; + }else{ + dojo.debug("_buildCache() loading from cache"); + if(callbacks && callbacks.length){ + callbacks.shift()(LOAD, cached.function_names, input); + } + return; + } + }else if(type == META || type == SRC){ + if(!pkg){ + if(type == META){ + docs.functionPackages(selectKey, name, function(){ var a = arguments; docs._withPkg.call(this, a[0], a[1], a[2], a[3], META); }, input); + return; + }else{ + docs.functionPackages(selectKey, name, function(){ var a = arguments; docs._withPkg.call(this, a[0], a[1], a[2], a[3], SRC); }, input); + return; + } + }else{ + var cached = getCache(pkg, META, METHODS, name, id); + + if(cached[type] && cached[type].returns){ + if(callbacks && callbacks.length){ + callbacks.shift()(LOAD, cached[type], input); + return; + } + } + + dojo.debug("Finding " + type + " for: " + pkg + ", function: " + name + ", id: " + id); + + var obj = {}; + + if(type == SRC){ + obj.mimetype = "text/plain" + } + obj.url = pkg + "/" + name + "/" + id + "/" + type; + obj.load = function(type, data, evt){ + dojo.debug("_buildCache() loaded " + input.type); + + if(input.type == SRC){ + getCache(pkg, META, METHODS, name, id).src = data; + if(callbacks && callbacks.length){ + callbacks.shift()(LOAD, data, input, input[INPUT]); + } + }else{ + var cache = getCache(pkg, META, METHODS, name, id, META); + if(!cache.parameters){ + cache.parameters = {}; + } + for(var i = 0, param; param = data.parameters[i]; i++){ + if(!cache.parameters[param[1]]){ + cache.parameters[param[1]] = {}; + } + cache.parameters[param[1]].type = param[0]; + } + if(!cache.returns){ + cache.returns = {}; + } + cache.returns.type = data.returns; + } + + if(callbacks && callbacks.length){ + callbacks.shift()(LOAD, cache, input, input[INPUT]); + } + } + obj.error = function(type, data, evt){ + if(callbacks && callbacks.length){ + callbacks.shift()(ERROR, {}, input, input[INPUT]); + } + } + } + + search.push(obj); + }else if(type == "pkgmeta"){ + var cached = getCache(name, "meta"); + + if(cached.requires){ + if(callbacks && callbacks.length){ + callbacks.shift()(LOAD, cached, input, input[INPUT]); + return; + } + } + + dojo.debug("Finding package meta for: " + name); + + var obj = {}; + + obj.url = name + "/meta"; + obj.load = function(type, data, evt){ + dojo.debug("_buildCache() loaded for: " + name); + + var methods = data.methods; + if(methods){ + for(var method in methods){ + if (method == "is") { + continue; + } + for(var pId in methods[method]){ + getCache(name, META, METHODS, method, pId, META).summary = methods[method][pId]; + } + } + } + + var requires = data.requires; + var cache = getCache(name, META); + if(requires){ + cache.requires = requires; + } + if(callbacks && callbacks.length){ + callbacks.shift()(LOAD, cache, input, input[INPUT]); + } + } + obj.error = function(type, data, evt){ + if(callbacks && callbacks.length){ + callbacks.shift()(ERROR, {}, input, input[INPUT]); + } + } + search.push(obj); + } + + for(var i = 0, obj; obj = search[i]; i++){ + var load = obj.load; + var error = obj.error; + delete obj.load; + delete obj.error; + var mimetype = obj.mimetype; + if(!mimetype){ + mimetype = "text/json" + } + if(obj.url){ + dojo.io.bind({ + url: new dojo.uri.Uri(docs._url, obj.url), + input: input, + mimetype: mimetype, + error: error, + load: load + }); + }else{ + docs._rpc.callRemote("search", obj).addCallbacks(load, error); + } + } + }, + selectFunction: function(/*String*/ name, /*String?*/ id){ + // summary: The combined information + }, + savePackage: function(/*Object*/ callbackObject, /*String*/ callback, /*Object*/ parameters){ + dojo.event.kwConnect({ + srcObj: dojo.docs, + srcFunc: "_savedPkgRpc", + targetObj: callbackObject, + targetFunc: callback, + once: true + }); + + var props = {}; + var cache = dojo.docs._getCache(parameters.pkg, "meta"); + + var i = 1; + + if(!cache.path){ + var path = "id"; + props[["pname", i].join("")] = "DocPkgForm/require"; + props[["pvalue", i++].join("")] = parameters.pkg; + }else{ + var path = cache.path; + } + + props.form = "//DocPkgForm"; + props.path = ["/WikiHome/DojoDotDoc/", path].join(""); + + if(parameters.description){ + props[["pname", i].join("")] = "main/text"; + props[["pvalue", i++].join("")] = parameters.description; + } + + dojo.docs._rpc.callRemote("saveForm", props).addCallbacks(dojo.docs._pkgRpc, dojo.docs._pkgRpc); + }, + _pkgRpc: function(data){ + if(data.name){ + dojo.docs._getCache(data["DocPkgForm/require"], "meta").path = data.name; + dojo.docs._savedPkgRpc("load"); + }else{ + dojo.docs._savedPkgRpc("error"); + } + }, + _savedPkgRpc: function(type){ + }, + functionPackages: function(/*mixed*/ selectKey, /*String*/ name, /*Function*/ callback, /*Object*/ input){ + // summary: Gets the package associated with a function and stores it in the .pkg value of input + dojo.debug("functionPackages() name: " + name); + + if(!input){ + input = {}; + } + if(!input.callbacks){ + input.callbacks = []; + } + + input.type = "function_names"; + input.name = name; + input.callbacks.unshift(callback); + input.callbacks.unshift(dojo.docs._functionPackages); + dojo.docs._buildCache(input); + }, + _functionPackages: function(/*String*/ type, /*Array*/ data, /*Object*/ evt){ + dojo.debug("_functionPackages() name: " + evt.name); + evt.pkg = ''; + + var results = []; + var data = dojo.docs._cache['function_names']; + for(var key in data){ + if(dojo.lang.inArray(data[key], evt.name)){ + dojo.debug("_functionPackages() package: " + key); + results.push(key); + } + } + + if(evt.callbacks && evt.callbacks.length){ + evt.callbacks.shift()(type, results, evt, evt.input); + } + }, + setUserName: function(/*String*/ name){ + dojo.docs._userName = name; + if(name && dojo.docs._password){ + dojo.docs._logIn(); + } + }, + setPassword: function(/*String*/ password){ + dojo.docs._password = password; + if(password && dojo.docs._userName){ + dojo.docs._logIn(); + } + }, + _logIn: function(){ + dojo.io.bind({ + url: dojo.docs._rpc.serviceUrl.toString(), + method: "post", + mimetype: "text/json", + content: { + username: dojo.docs._userName, + password: dojo.docs._password + }, + load: function(type, data){ + if(data.error){ + dojo.docs.logInSuccess(); + }else{ + dojo.docs.logInFailure(); + } + }, + error: function(){ + dojo.docs.logInFailure(); + } + }); + }, + logInSuccess: function(){}, + logInFailure: function(){}, + _sort: function(a, b){ + if(a[0] < b[0]){ + return -1; + } + if(a[0] > b[0]){ + return 1; + } + return 0; + }, + _getCache: function(/*String...*/ keys){ + var obj = dojo.docs._cache; + for(var i = 0; i < arguments.length; i++){ + var arg = arguments[i]; + if(!obj[arg]){ + obj[arg] = {}; + } + obj = obj[arg]; + } + return obj; + } +}); + +dojo.event.topic.subscribe("/docs/search", dojo.docs, "_onDocSearch"); +dojo.event.topic.subscribe("/docs/function/select", dojo.docs, "_onDocSelectFunction"); +dojo.event.topic.subscribe("/docs/package/select", dojo.docs, "_onDocSelectPackage"); + +dojo.event.topic.registerPublisher("/docs/function/results", dojo.docs, "_printFnResults"); +dojo.event.topic.registerPublisher("/docs/package/results", dojo.docs, "_printPkgResults"); +dojo.event.topic.registerPublisher("/docs/function/detail", dojo.docs, "_printFunctionDetail"); \ No newline at end of file diff --git a/source/web/scripts/ajax/src/dom.js b/source/web/scripts/ajax/src/dom.js new file mode 100644 index 0000000000..5593a0d048 --- /dev/null +++ b/source/web/scripts/ajax/src/dom.js @@ -0,0 +1,464 @@ +dojo.provide("dojo.dom"); + +dojo.dom.ELEMENT_NODE = 1; +dojo.dom.ATTRIBUTE_NODE = 2; +dojo.dom.TEXT_NODE = 3; +dojo.dom.CDATA_SECTION_NODE = 4; +dojo.dom.ENTITY_REFERENCE_NODE = 5; +dojo.dom.ENTITY_NODE = 6; +dojo.dom.PROCESSING_INSTRUCTION_NODE = 7; +dojo.dom.COMMENT_NODE = 8; +dojo.dom.DOCUMENT_NODE = 9; +dojo.dom.DOCUMENT_TYPE_NODE = 10; +dojo.dom.DOCUMENT_FRAGMENT_NODE = 11; +dojo.dom.NOTATION_NODE = 12; + +dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml"; + +/** + * comprehensive list of XML namespaces +**/ +dojo.dom.xmlns = { + svg : "http://www.w3.org/2000/svg", + smil : "http://www.w3.org/2001/SMIL20/", + mml : "http://www.w3.org/1998/Math/MathML", + cml : "http://www.xml-cml.org", + xlink : "http://www.w3.org/1999/xlink", + xhtml : "http://www.w3.org/1999/xhtml", + xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", + xbl : "http://www.mozilla.org/xbl", + fo : "http://www.w3.org/1999/XSL/Format", + xsl : "http://www.w3.org/1999/XSL/Transform", + xslt : "http://www.w3.org/1999/XSL/Transform", + xi : "http://www.w3.org/2001/XInclude", + xforms : "http://www.w3.org/2002/01/xforms", + saxon : "http://icl.com/saxon", + xalan : "http://xml.apache.org/xslt", + xsd : "http://www.w3.org/2001/XMLSchema", + dt: "http://www.w3.org/2001/XMLSchema-datatypes", + xsi : "http://www.w3.org/2001/XMLSchema-instance", + rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + rdfs : "http://www.w3.org/2000/01/rdf-schema#", + dc : "http://purl.org/dc/elements/1.1/", + dcq: "http://purl.org/dc/qualifiers/1.0", + "soap-env" : "http://schemas.xmlsoap.org/soap/envelope/", + wsdl : "http://schemas.xmlsoap.org/wsdl/", + AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" +}; + +dojo.dom.isNode = function(wh){ + if(typeof Element == "function") { + try { + return wh instanceof Element; + } catch(E) {} + } else { + // best-guess + return wh && !isNaN(wh.nodeType); + } +} + +dojo.dom.getUniqueId = function(){ + var _document = dojo.doc(); + do { + var id = "dj_unique_" + (++arguments.callee._idIncrement); + }while(_document.getElementById(id)); + return id; +} +dojo.dom.getUniqueId._idIncrement = 0; + +dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(parentNode, tagName){ + var node = parentNode.firstChild; + while(node && node.nodeType != dojo.dom.ELEMENT_NODE){ + node = node.nextSibling; + } + if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) { + node = dojo.dom.nextElement(node, tagName); + } + return node; +} + +dojo.dom.lastElement = dojo.dom.getLastChildElement = function(parentNode, tagName){ + var node = parentNode.lastChild; + while(node && node.nodeType != dojo.dom.ELEMENT_NODE) { + node = node.previousSibling; + } + if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) { + node = dojo.dom.prevElement(node, tagName); + } + return node; +} + +dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(node, tagName){ + if(!node) { return null; } + do { + node = node.nextSibling; + } while(node && node.nodeType != dojo.dom.ELEMENT_NODE); + + if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) { + return dojo.dom.nextElement(node, tagName); + } + return node; +} + +dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(node, tagName){ + if(!node) { return null; } + if(tagName) { tagName = tagName.toLowerCase(); } + do { + node = node.previousSibling; + } while(node && node.nodeType != dojo.dom.ELEMENT_NODE); + + if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) { + return dojo.dom.prevElement(node, tagName); + } + return node; +} + +// TODO: hmph +/*this.forEachChildTag = function(node, unaryFunc) { + var child = this.getFirstChildTag(node); + while(child) { + if(unaryFunc(child) == "break") { break; } + child = this.getNextSiblingTag(child); + } +}*/ + +dojo.dom.moveChildren = function(srcNode, destNode, trim){ + var count = 0; + if(trim) { + while(srcNode.hasChildNodes() && + srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) { + srcNode.removeChild(srcNode.firstChild); + } + while(srcNode.hasChildNodes() && + srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) { + srcNode.removeChild(srcNode.lastChild); + } + } + while(srcNode.hasChildNodes()){ + destNode.appendChild(srcNode.firstChild); + count++; + } + return count; +} + +dojo.dom.copyChildren = function(srcNode, destNode, trim){ + var clonedNode = srcNode.cloneNode(true); + return this.moveChildren(clonedNode, destNode, trim); +} + +dojo.dom.removeChildren = function(node){ + var count = node.childNodes.length; + while(node.hasChildNodes()){ node.removeChild(node.firstChild); } + return count; +} + +dojo.dom.replaceChildren = function(node, newChild){ + // FIXME: what if newChild is an array-like object? + dojo.dom.removeChildren(node); + node.appendChild(newChild); +} + +dojo.dom.removeNode = function(node){ + if(node && node.parentNode){ + // return a ref to the removed child + return node.parentNode.removeChild(node); + } +} + +dojo.dom.getAncestors = function(node, filterFunction, returnFirstHit) { + var ancestors = []; + var isFunction = (filterFunction && (filterFunction instanceof Function || typeof filterFunction == "function")); + while(node) { + if (!isFunction || filterFunction(node)) { + ancestors.push(node); + } + if (returnFirstHit && ancestors.length > 0) { return ancestors[0]; } + + node = node.parentNode; + } + if (returnFirstHit) { return null; } + return ancestors; +} + +dojo.dom.getAncestorsByTag = function(node, tag, returnFirstHit) { + tag = tag.toLowerCase(); + return dojo.dom.getAncestors(node, function(el){ + return ((el.tagName)&&(el.tagName.toLowerCase() == tag)); + }, returnFirstHit); +} + +dojo.dom.getFirstAncestorByTag = function(node, tag) { + return dojo.dom.getAncestorsByTag(node, tag, true); +} + +dojo.dom.isDescendantOf = function(node, ancestor, guaranteeDescendant){ + // guaranteeDescendant allows us to be a "true" isDescendantOf function + if(guaranteeDescendant && node) { node = node.parentNode; } + while(node) { + if(node == ancestor){ return true; } + node = node.parentNode; + } + return false; +} + +dojo.dom.innerXML = function(node){ + if(node.innerXML){ + return node.innerXML; + }else if (node.xml){ + return node.xml; + }else if(typeof XMLSerializer != "undefined"){ + return (new XMLSerializer()).serializeToString(node); + } +} + +dojo.dom.createDocument = function(){ + var doc = null; + var _document = dojo.doc(); + + if(!dj_undef("ActiveXObject")){ + var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ]; + for(var i = 0; i1) { + var _document = dojo.doc(); + dojo.dom.replaceChildren(node, _document.createTextNode(text)); + return text; + } else { + if(node.textContent != undefined){ //FF 1.5 + return node.textContent; + } + var _result = ""; + if (node == null) { return _result; } + for (var i = 0; i < node.childNodes.length; i++) { + switch (node.childNodes[i].nodeType) { + case 1: // ELEMENT_NODE + case 5: // ENTITY_REFERENCE_NODE + _result += dojo.dom.textContent(node.childNodes[i]); + break; + case 3: // TEXT_NODE + case 2: // ATTRIBUTE_NODE + case 4: // CDATA_SECTION_NODE + _result += node.childNodes[i].nodeValue; + break; + default: + break; + } + } + return _result; + } +} + +dojo.dom.hasParent = function (node) { + return node && node.parentNode && dojo.dom.isNode(node.parentNode); +} + +/** + * Determines if node has any of the provided tag names and + * returns the tag name that matches, empty string otherwise. + * + * Examples: + * + * myFooNode = + * isTag(myFooNode, "foo"); // returns "foo" + * isTag(myFooNode, "bar"); // returns "" + * isTag(myFooNode, "FOO"); // returns "" + * isTag(myFooNode, "hey", "foo", "bar"); // returns "foo" +**/ +dojo.dom.isTag = function(node /* ... */) { + if(node && node.tagName) { + for(var i=1; i2) ? args[0] : "after", + precedence: "last", + once: false, + delay: null, + rate: 0, + adviceMsg: false + }; + + switch(args.length){ + case 0: return; + case 1: return; + case 2: + ao.srcFunc = args[0]; + ao.adviceFunc = args[1]; + break; + case 3: + if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){ + ao.adviceType = "after"; + ao.srcObj = args[0]; + ao.srcFunc = args[1]; + ao.adviceFunc = args[2]; + }else if((dl.isString(args[1]))&&(dl.isString(args[2]))){ + ao.srcFunc = args[1]; + ao.adviceFunc = args[2]; + }else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){ + ao.adviceType = "after"; + ao.srcObj = args[0]; + ao.srcFunc = args[1]; + var tmpName = dl.nameAnonFunc(args[2], ao.adviceObj, searchForNames); + ao.adviceFunc = tmpName; + }else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){ + ao.adviceType = "after"; + ao.srcObj = dj_global; + var tmpName = dl.nameAnonFunc(args[0], ao.srcObj, searchForNames); + ao.srcFunc = tmpName; + ao.adviceObj = args[1]; + ao.adviceFunc = args[2]; + } + break; + case 4: + if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){ + // we can assume that we've got an old-style "connect" from + // the sigslot school of event attachment. We therefore + // assume after-advice. + ao.adviceType = "after"; + ao.srcObj = args[0]; + ao.srcFunc = args[1]; + ao.adviceObj = args[2]; + ao.adviceFunc = args[3]; + }else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){ + ao.adviceType = args[0]; + ao.srcObj = dj_global; + ao.srcFunc = args[1]; + ao.adviceObj = args[2]; + ao.adviceFunc = args[3]; + }else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){ + ao.adviceType = args[0]; + ao.srcObj = dj_global; + var tmpName = dl.nameAnonFunc(args[1], dj_global, searchForNames); + ao.srcFunc = tmpName; + ao.adviceObj = args[2]; + ao.adviceFunc = args[3]; + }else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){ + ao.srcObj = args[1]; + ao.srcFunc = args[2]; + var tmpName = dl.nameAnonFunc(args[3], dj_global, searchForNames); + ao.adviceObj = dj_global; + ao.adviceFunc = tmpName; + }else if(dl.isObject(args[1])){ + ao.srcObj = args[1]; + ao.srcFunc = args[2]; + ao.adviceObj = dj_global; + ao.adviceFunc = args[3]; + }else if(dl.isObject(args[2])){ + ao.srcObj = dj_global; + ao.srcFunc = args[1]; + ao.adviceObj = args[2]; + ao.adviceFunc = args[3]; + }else{ + ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global; + ao.srcFunc = args[1]; + ao.adviceFunc = args[2]; + ao.aroundFunc = args[3]; + } + break; + case 6: + ao.srcObj = args[1]; + ao.srcFunc = args[2]; + ao.adviceObj = args[3] + ao.adviceFunc = args[4]; + ao.aroundFunc = args[5]; + ao.aroundObj = dj_global; + break; + default: + ao.srcObj = args[1]; + ao.srcFunc = args[2]; + ao.adviceObj = args[3] + ao.adviceFunc = args[4]; + ao.aroundObj = args[5]; + ao.aroundFunc = args[6]; + ao.once = args[7]; + ao.delay = args[8]; + ao.rate = args[9]; + ao.adviceMsg = args[10]; + break; + } + + if(dl.isFunction(ao.aroundFunc)){ + var tmpName = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames); + ao.aroundFunc = tmpName; + } + + if(dl.isFunction(ao.srcFunc)){ + ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc); + } + + if(dl.isFunction(ao.adviceFunc)){ + ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc); + } + + if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){ + ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc); + } + + if(!ao.srcObj){ + dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc); + } + if(!ao.adviceObj){ + dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc); + } + + if(!ao.adviceFunc){ + dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc); + dojo.debugShallow(ao); + } + + return ao; + } + + this.connect = function(){ + if(arguments.length == 1){ + var ao = arguments[0]; + }else{ + var ao = interpolateArgs(arguments, true); + } + + if(dojo.lang.isArray(ao.srcObj) && ao.srcObj!=""){ + var tmpAO = {}; + for(var x in ao){ + tmpAO[x] = ao[x]; + } + var mjps = []; + dojo.lang.forEach(ao.srcObj, function(src){ + if((dojo.render.html.capable)&&(dojo.lang.isString(src))){ + src = dojo.byId(src); + // dojo.debug(src); + } + tmpAO.srcObj = src; + // dojo.debug(tmpAO.srcObj, tmpAO.srcFunc); + // dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc); + mjps.push(dojo.event.connect.call(dojo.event, tmpAO)); + }); + return mjps; + } + + // FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!! + var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc); + if(ao.adviceFunc){ + var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc); + } + + mjp.kwAddAdvice(ao); + + return mjp; // advanced users might want to fsck w/ the join point + // manually + } + + this.log = function(a1, a2){ + var kwArgs; + if((arguments.length == 1)&&(typeof a1 == "object")){ + kwArgs = a1; + }else{ + kwArgs = { + srcObj: a1, + srcFunc: a2 + }; + } + kwArgs.adviceFunc = function(){ + var argsStr = []; + for(var x=0; x= this.jp_.around.length){ + return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args); + // return this.jp_.run_before_after(this.object, this.args); + }else{ + var ti = this.jp_.around[this.around_index]; + var mobj = ti[0]||dj_global; + var meth = ti[1]; + return mobj[meth].call(mobj, this); + } +} + + +dojo.event.MethodJoinPoint = function(obj, methname){ + this.object = obj||dj_global; + this.methodname = methname; + this.methodfunc = this.object[methname]; + this.before = []; + this.after = []; + this.around = []; +} + +dojo.event.MethodJoinPoint.getForMethod = function(obj, methname) { + // if(!(methname in obj)){ + if(!obj){ obj = dj_global; } + if(!obj[methname]){ + // supply a do-nothing method implementation + obj[methname] = function(){}; + if(!obj[methname]){ + // e.g. cannot add to inbuilt objects in IE6 + dojo.raise("Cannot set do-nothing method on that object "+methname); + } + }else if((!dojo.lang.isFunction(obj[methname]))&&(!dojo.lang.isAlien(obj[methname]))){ + return null; // FIXME: should we throw an exception here instead? + } + // we hide our joinpoint instance in obj[methname + '$joinpoint'] + var jpname = methname + "$joinpoint"; + var jpfuncname = methname + "$joinpoint$method"; + var joinpoint = obj[jpname]; + if(!joinpoint){ + var isNode = false; + if(dojo.event["browser"]){ + if( (obj["attachEvent"])|| + (obj["nodeType"])|| + (obj["addEventListener"]) ){ + isNode = true; + dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, methname]); + } + } + var origArity = obj[methname].length; + obj[jpfuncname] = obj[methname]; + // joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, methname); + joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname); + obj[methname] = function(){ + var args = []; + + if((isNode)&&(!arguments.length)){ + var evt = null; + try{ + if(obj.ownerDocument){ + evt = obj.ownerDocument.parentWindow.event; + }else if(obj.documentElement){ + evt = obj.documentElement.ownerDocument.parentWindow.event; + }else if(obj.event){ //obj is a window + evt = obj.event; + }else{ + evt = window.event; + } + }catch(e){ + evt = window.event; + } + + if(evt){ + args.push(dojo.event.browser.fixEvent(evt, this)); + } + }else{ + for(var x=0; x0){ + // pass a cloned array, if this event disconnects this event forEach on this.before wont work + dojo.lang.forEach(this.before.concat(new Array()), unrollAdvice); + } + + var result; + if(this.around.length>0){ + var mi = new dojo.event.MethodInvocation(this, obj, args); + result = mi.proceed(); + }else if(this.methodfunc){ + result = this.object[this.methodname].apply(this.object, args); + } + + if(this.after.length>0){ + // see comment on this.before above + dojo.lang.forEach(this.after.concat(new Array()), unrollAdvice); + } + + return (this.methodfunc) ? result : null; + }, + + getArr: function(kind){ + var arr = this.after; + // FIXME: we should be able to do this through props or Array.in() + if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){ + arr = this.before; + }else if(kind=="around"){ + arr = this.around; + } + return arr; + }, + + kwAddAdvice: function(args){ + this.addAdvice( args["adviceObj"], args["adviceFunc"], + args["aroundObj"], args["aroundFunc"], + args["adviceType"], args["precedence"], + args["once"], args["delay"], args["rate"], + args["adviceMsg"]); + }, + + addAdvice: function( thisAdviceObj, thisAdvice, + thisAroundObj, thisAround, + advice_kind, precedence, + once, delay, rate, asMessage){ + var arr = this.getArr(advice_kind); + if(!arr){ + dojo.raise("bad this: " + this); + } + + var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage]; + + if(once){ + if(this.hasAdvice(thisAdviceObj, thisAdvice, advice_kind, arr) >= 0){ + return; + } + } + + if(precedence == "first"){ + arr.unshift(ao); + }else{ + arr.push(ao); + } + }, + + hasAdvice: function(thisAdviceObj, thisAdvice, advice_kind, arr){ + if(!arr){ arr = this.getArr(advice_kind); } + var ind = -1; + for(var x=0; x=0; i=i-1){ + var el = na[i]; + if(el["__clobberAttrs__"]){ + for(var j=0; j 0){ + for(var i = 0;i < dojo.flash._loadedListeners.length; i++){ + dojo.flash._loadedListeners[i].call(null); + } + } + }, + + /** + A callback to know if Flash is currently being installed or + having its version revved. To be notified if Flash is installing, connect + your callback to this method using the following: + + dojo.event.connect(dojo.flash, "installing", myInstance, "myCallback"); + */ + installing: function(){ + //dojo.debug("installing"); + if(dojo.flash._installingListeners.length > 0){ + for(var i = 0; i < dojo.flash._installingListeners.length; i++){ + dojo.flash._installingListeners[i].call(null); + } + } + }, + + /** Initializes dojo.flash. */ + _initialize: function(){ + //dojo.debug("dojo.flash._initialize"); + // see if we need to rev or install Flash on this platform + var installer = new dojo.flash.Install(); + dojo.flash.installer = installer; + + if(installer.needed() == true){ + installer.install(); + }else{ + //dojo.debug("Writing object out"); + // write the flash object into the page + dojo.flash.obj = new dojo.flash.Embed(this._visible); + dojo.flash.obj.write(dojo.flash.info.commVersion); + + // initialize the way we do Flash/JavaScript communication + dojo.flash.comm = new dojo.flash.Communicator(); + } + } +}; + + +/** + A class that helps us determine whether Flash is available, + it's major and minor versions, and what Flash version features should + be used for Flash/JavaScript communication. Parts of this code + are adapted from the automatic Flash plugin detection code autogenerated + by the Macromedia Flash 8 authoring environment. + + An instance of this class can be accessed on dojo.flash.info after + the page is finished loading. + + This constructor must be called before the page is finished loading. +*/ +dojo.flash.Info = function(){ + // Visual basic helper required to detect Flash Player ActiveX control + // version information on Internet Explorer + if(dojo.render.html.ie){ + document.writeln(''); + + // hook for Internet Explorer to receive FSCommands from Flash + if(dojo.render.html.ie){ + document.writeln(', + //which will be treated as an external javascript file in IE + var xscript = dojo.doc().createElement('script'); + xscript.src = "javascript:'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }'"; + dojo.doc().getElementsByTagName("head")[0].appendChild(xscript); + })(); + } +}else{ + //for other browsers, simply use document.createElement + //is enough + dojo.html.createExternalElement = function(doc, tag){ + return doc.createElement(tag); + } +} + +dojo.html._callDeprecated = function(inFunc, replFunc, args, argName, retValue){ + dojo.deprecated("dojo.html." + inFunc, + "replaced by dojo.html." + replFunc + "(" + (argName ? "node, {"+ argName + ": " + argName + "}" : "" ) + ")" + (retValue ? "." + retValue : ""), "0.5"); + var newArgs = []; + if(argName){ var argsIn = {}; argsIn[argName] = args[1]; newArgs.push(args[0]); newArgs.push(argsIn); } + else { newArgs = args } + var ret = dojo.html[replFunc].apply(dojo.html, args); + if(retValue){ return ret[retValue]; } + else { return ret; } +} + +dojo.html.getViewportWidth = function(){ + return dojo.html._callDeprecated("getViewportWidth", "getViewport", arguments, null, "width"); +} +dojo.html.getViewportHeight = function(){ + return dojo.html._callDeprecated("getViewportHeight", "getViewport", arguments, null, "height"); +} +dojo.html.getViewportSize = function(){ + return dojo.html._callDeprecated("getViewportSize", "getViewport", arguments); +} +dojo.html.getScrollTop = function(){ + return dojo.html._callDeprecated("getScrollTop", "getScroll", arguments, null, "top"); +} +dojo.html.getScrollLeft = function(){ + return dojo.html._callDeprecated("getScrollLeft", "getScroll", arguments, null, "left"); +} +dojo.html.getScrollOffset = function(){ + return dojo.html._callDeprecated("getScrollOffset", "getScroll", arguments, null, "offset"); +} diff --git a/source/web/scripts/ajax/src/html/csshack.js b/source/web/scripts/ajax/src/html/csshack.js new file mode 100644 index 0000000000..c9860d7d96 --- /dev/null +++ b/source/web/scripts/ajax/src/html/csshack.js @@ -0,0 +1,34 @@ +dojo.provide("dojo.html.csshack"); + +// provide css classes for browser identification/selection + +// classnames must be reasonably unique - css namespace is global. + +// Need to allow that some servers may choose to pre-process css to remove irrelevant +// rules, in which case these rule names would need to be appended to the GET for the css +// file so no problems with proxy caches serving wrong css. + +(function(){ + var de = document.documentElement; + if (de && !djConfig.disableCssHack) { + with (dojo.render.html) { + var cl = {b_ie: ie, + b_ie55: ie55, + b_ie6: ie60, + b_ie7: ie70, + b_iequirks: ie && quirks, + b_opera: opera, + b_khtml: khtml, + b_safari: safari, + b_gecko: mozilla + }; // no dojo unsupported browsers + } + var cla = [de.className]; + for (var key in cl) { + if (cl[key]) { + cla.push(key); + } + } + de.className = cla.join(' '); + } +})(); \ No newline at end of file diff --git a/source/web/scripts/ajax/src/html/display.js b/source/web/scripts/ajax/src/html/display.js new file mode 100644 index 0000000000..70b57dc301 --- /dev/null +++ b/source/web/scripts/ajax/src/html/display.js @@ -0,0 +1,182 @@ +dojo.provide("dojo.html.display"); + +dojo.require("dojo.html.style"); + +dojo.html._toggle = function(node, tester, setter){ + node = dojo.byId(node); + setter(node, !tester(node)); + return tester(node); +} + +// show/hide are library constructs + +// show() +// if the node.style.display == 'none' then +// set style.display to '' or the value cached by hide() +dojo.html.show = function(node){ + node = dojo.byId(node); + if(dojo.html.getStyleProperty(node, 'display')=='none'){ + dojo.html.setStyle(node, 'display', (node.dojoDisplayCache||'')); + node.dojoDisplayCache = undefined; // cannot use delete on a node in IE6 + } +} + +// if the node.style.display == 'none' then +// set style.display to '' or the value cached by hide() +dojo.html.hide = function(node){ + node = dojo.byId(node); + if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount + var d = dojo.html.getStyleProperty(node, 'display') + if(d!='none'){ + node.dojoDisplayCache = d; + } + } + dojo.html.setStyle(node, 'display', 'none'); +} + +// setShowing() calls show() if showing is true, hide() otherwise +dojo.html.setShowing = function(node, showing){ + dojo.html[(showing ? 'show' : 'hide')](node); +} + +// isShowing() is true if the node.style.display is not 'none' +// FIXME: returns true if node is bad, isHidden would be easier to make correct +dojo.html.isShowing = function(node){ + return (dojo.html.getStyleProperty(node, 'display') != 'none'); +} + +// Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing() +dojo.html.toggleShowing = function(node){ + return dojo.html._toggle(node, dojo.html.isShowing, dojo.html.setShowing); +} + +// display is a CSS concept + +// Simple mapping of tag names to display values +// FIXME: simplistic +dojo.html.displayMap = { tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline' }; + +// Suggest a value for the display property that will show 'node' based on it's tag +dojo.html.suggestDisplayByTagName = function(node) +{ + node = dojo.byId(node); + if(node && node.tagName){ + var tag = node.tagName.toLowerCase(); + return (tag in dojo.html.displayMap ? dojo.html.displayMap[tag] : 'block'); + } +} + +// setDisplay() sets the value of style.display to value of 'display' parameter if it is a string. +// Otherwise, if 'display' is false, set style.display to 'none'. +// Finally, set 'display' to a suggested display value based on the node's tag +dojo.html.setDisplay = function(node, display){ + dojo.html.setStyle(node, 'display', ((display instanceof String || typeof display == "string") ? display : (display ? dojo.html.suggestDisplayByTagName(node) : 'none'))); +} + +// isDisplayed() is true if the the computed display style for node is not 'none' +// FIXME: returns true if node is bad, isNotDisplayed would be easier to make correct +dojo.html.isDisplayed = function(node){ + return (dojo.html.getComputedStyle(node, 'display') != 'none'); +} + +// Call setDisplay() on node with the complement of isDisplayed(), then +// return the new value of isDisplayed() +dojo.html.toggleDisplay = function(node){ + return dojo.html._toggle(node, dojo.html.isDisplayed, dojo.html.setDisplay); +} + +// visibility is a CSS concept + +// setVisibility() sets the value of style.visibility to value of +// 'visibility' parameter if it is a string. +// Otherwise, if 'visibility' is false, set style.visibility to 'hidden'. +// Finally, set style.visibility to 'visible'. +dojo.html.setVisibility = function(node, visibility){ + dojo.html.setStyle(node, 'visibility', ((visibility instanceof String || typeof visibility == "string") ? visibility : (visibility ? 'visible' : 'hidden'))); +} + +// isVisible() is true if the the computed visibility style for node is not 'hidden' +// FIXME: returns true if node is bad, isInvisible would be easier to make correct +dojo.html.isVisible = function(node){ + return (dojo.html.getComputedStyle(node, 'visibility') != 'hidden'); +} + +// Call setVisibility() on node with the complement of isVisible(), then +// return the new value of isVisible() +dojo.html.toggleVisibility = function(node){ + return dojo.html._toggle(node, dojo.html.isVisible, dojo.html.setVisibility); +} + +/* float between 0.0 (transparent) and 1.0 (opaque) */ +dojo.html.setOpacity = function(node, opacity, dontFixOpacity){ + node = dojo.byId(node); + var h = dojo.render.html; + if(!dontFixOpacity){ + if( opacity >= 1.0){ + if(h.ie){ + dojo.html.clearOpacity(node); + return; + }else{ + opacity = 0.999999; + } + }else if( opacity < 0.0){ opacity = 0; } + } + if(h.ie){ + if(node.nodeName.toLowerCase() == "tr"){ + // FIXME: is this too naive? will we get more than we want? + var tds = node.getElementsByTagName("td"); + for(var x=0; x= 0.999999 ? 1.0 : Number(opac); +} diff --git a/source/web/scripts/ajax/src/html/extras.js b/source/web/scripts/ajax/src/html/extras.js new file mode 100644 index 0000000000..182c63affb --- /dev/null +++ b/source/web/scripts/ajax/src/html/extras.js @@ -0,0 +1,428 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.require("dojo.html"); +dojo.provide("dojo.html.extras"); +dojo.require("dojo.string.extras"); + +/** + * Calculates the mouse's direction of gravity relative to the centre + * of the given node. + *

+ * If you wanted to insert a node into a DOM tree based on the mouse + * position you might use the following code: + *

+ * if (gravity(node, e) & gravity.NORTH) { [insert before]; }
+ * else { [insert after]; }
+ * 
+ * + * @param node The node + * @param e The event containing the mouse coordinates + * @return The directions, NORTH or SOUTH and EAST or WEST. These + * are properties of the function. + */ +dojo.html.gravity = function(node, e){ + node = dojo.byId(node); + var mouse = dojo.html.getCursorPosition(e); + + with (dojo.html) { + var nodecenterx = getAbsoluteX(node, true) + (getInnerWidth(node) / 2); + var nodecentery = getAbsoluteY(node, true) + (getInnerHeight(node) / 2); + } + + with (dojo.html.gravity) { + return ((mouse.x < nodecenterx ? WEST : EAST) | + (mouse.y < nodecentery ? NORTH : SOUTH)); + } +} + +dojo.html.gravity.NORTH = 1; +dojo.html.gravity.SOUTH = 1 << 1; +dojo.html.gravity.EAST = 1 << 2; +dojo.html.gravity.WEST = 1 << 3; + + +/** + * Attempts to return the text as it would be rendered, with the line breaks + * sorted out nicely. Unfinished. + */ +dojo.html.renderedTextContent = function(node){ + node = dojo.byId(node); + var result = ""; + if (node == null) { return result; } + for (var i = 0; i < node.childNodes.length; i++) { + switch (node.childNodes[i].nodeType) { + case 1: // ELEMENT_NODE + case 5: // ENTITY_REFERENCE_NODE + var display = "unknown"; + try { + display = dojo.style.getStyle(node.childNodes[i], "display"); + } catch(E) {} + switch (display) { + case "block": case "list-item": case "run-in": + case "table": case "table-row-group": case "table-header-group": + case "table-footer-group": case "table-row": case "table-column-group": + case "table-column": case "table-cell": case "table-caption": + // TODO: this shouldn't insert double spaces on aligning blocks + result += "\n"; + result += dojo.html.renderedTextContent(node.childNodes[i]); + result += "\n"; + break; + + case "none": break; + + default: + if(node.childNodes[i].tagName && node.childNodes[i].tagName.toLowerCase() == "br") { + result += "\n"; + } else { + result += dojo.html.renderedTextContent(node.childNodes[i]); + } + break; + } + break; + case 3: // TEXT_NODE + case 2: // ATTRIBUTE_NODE + case 4: // CDATA_SECTION_NODE + var text = node.childNodes[i].nodeValue; + var textTransform = "unknown"; + try { + textTransform = dojo.style.getStyle(node, "text-transform"); + } catch(E) {} + switch (textTransform){ + case "capitalize": text = dojo.string.capitalize(text); break; + case "uppercase": text = text.toUpperCase(); break; + case "lowercase": text = text.toLowerCase(); break; + default: break; // leave as is + } + // TODO: implement + switch (textTransform){ + case "nowrap": break; + case "pre-wrap": break; + case "pre-line": break; + case "pre": break; // leave as is + default: + // remove whitespace and collapse first space + text = text.replace(/\s+/, " "); + if (/\s$/.test(result)) { text.replace(/^\s/, ""); } + break; + } + result += text; + break; + default: + break; + } + } + return result; +} + +dojo.html.createNodesFromText = function(txt, trim){ + if(trim) { txt = dojo.string.trim(txt); } + + var tn = document.createElement("div"); + // tn.style.display = "none"; + tn.style.visibility= "hidden"; + document.body.appendChild(tn); + var tableType = "none"; + if((/^]/i).test(dojo.string.trimStart(txt))) { + txt = "" + txt + "
"; + tableType = "cell"; + } else if((/^]/i).test(dojo.string.trimStart(txt))) { + txt = "" + txt + "
"; + tableType = "row"; + } else if((/^<(thead|tbody|tfoot)[\s\r\n>]/i).test(dojo.string.trimStart(txt))) { + txt = "" + txt + "
"; + tableType = "section"; + } + tn.innerHTML = txt; + if(tn["normalize"]){ + tn.normalize(); + } + + var parent = null; + switch(tableType) { + case "cell": + parent = tn.getElementsByTagName("tr")[0]; + break; + case "row": + parent = tn.getElementsByTagName("tbody")[0]; + break; + case "section": + parent = tn.getElementsByTagName("table")[0]; + break; + default: + parent = tn; + break; + } + + /* this doesn't make much sense, I'm assuming it just meant trim() so wrap was replaced with trim + if(wrap){ + var ret = []; + // start hack + var fc = tn.firstChild; + ret[0] = ((fc.nodeValue == " ")||(fc.nodeValue == "\t")) ? fc.nextSibling : fc; + // end hack + // tn.style.display = "none"; + document.body.removeChild(tn); + return ret; + } + */ + var nodes = []; + for(var x=0; x view.w) { + x = view.w - w; + } else { + x = desiredX; + } + x = Math.max(padding[0], x) + scroll.x; + + var y = desiredY + h; + if(y > view.h) { + y = view.h - h; + } else { + y = desiredY; + } + y = Math.max(padding[1], y) + scroll.y; + + node.style.left = x + "px"; + node.style.top = y + "px"; + + var ret = [x, y]; + ret.x = x; + ret.y = y; + return ret; +} + +/** + * Like placeOnScreenPoint except that it attempts to keep one of the node's + * corners at desiredX, desiredY. Favors the bottom right position + * + * Examples placing node at mouse position (where e = [Mouse event]): + * placeOnScreenPoint(node, e.clientX, e.clientY); + */ +dojo.html.placeOnScreenPoint = function(node, desiredX, desiredY, padding, hasScroll) { + if(dojo.lang.isArray(desiredX)) { + hasScroll = padding; + padding = desiredY; + desiredY = desiredX[1]; + desiredX = desiredX[0]; + } + + if(!isNaN(padding)) { + padding = [Number(padding), Number(padding)]; + } else if(!dojo.lang.isArray(padding)) { + padding = [0, 0]; + } + + var scroll = dojo.html.getScrollOffset(); + var view = dojo.html.getViewportSize(); + + node = dojo.byId(node); + var oldDisplay = node.style.display; + node.style.display=""; + var w = dojo.style.getInnerWidth(node); + var h = dojo.style.getInnerHeight(node); + node.style.display=oldDisplay; + + if(hasScroll) { + desiredX -= scroll.x; + desiredY -= scroll.y; + } + + var x = -1, y = -1; + //dojo.debug((desiredX+padding[0]) + w, "<=", view.w, "&&", (desiredY+padding[1]) + h, "<=", view.h); + if((desiredX+padding[0]) + w <= view.w && (desiredY+padding[1]) + h <= view.h) { // TL + x = (desiredX+padding[0]); + y = (desiredY+padding[1]); + //dojo.debug("TL", x, y); + } + + //dojo.debug((desiredX-padding[0]), "<=", view.w, "&&", (desiredY+padding[1]) + h, "<=", view.h); + if((x < 0 || y < 0) && (desiredX-padding[0]) <= view.w && (desiredY+padding[1]) + h <= view.h) { // TR + x = (desiredX-padding[0]) - w; + y = (desiredY+padding[1]); + //dojo.debug("TR", x, y); + } + + //dojo.debug((desiredX+padding[0]) + w, "<=", view.w, "&&", (desiredY-padding[1]), "<=", view.h); + if((x < 0 || y < 0) && (desiredX+padding[0]) + w <= view.w && (desiredY-padding[1]) <= view.h) { // BL + x = (desiredX+padding[0]); + y = (desiredY-padding[1]) - h; + //dojo.debug("BL", x, y); + } + + //dojo.debug((desiredX-padding[0]), "<=", view.w, "&&", (desiredY-padding[1]), "<=", view.h); + if((x < 0 || y < 0) && (desiredX-padding[0]) <= view.w && (desiredY-padding[1]) <= view.h) { // BR + x = (desiredX-padding[0]) - w; + y = (desiredY-padding[1]) - h; + //dojo.debug("BR", x, y); + } + + if(x < 0 || y < 0 || (x + w > view.w) || (y + h > view.h)) { + return dojo.html.placeOnScreen(node, desiredX, desiredY, padding, hasScroll); + } + + x += scroll.x; + y += scroll.y; + + node.style.left = x + "px"; + node.style.top = y + "px"; + + var ret = [x, y]; + ret.x = x; + ret.y = y; + return ret; +} + +/** + * For IE z-index schenanigans + * Two possible uses: + * 1. new dojo.html.BackgroundIframe(node) + * Makes a background iframe as a child of node, that fills area (and position) of node + * + * 2. new dojo.html.BackgroundIframe() + * Attaches frame to document.body. User must call size() to set size. + */ +dojo.html.BackgroundIframe = function(node) { + if(dojo.render.html.ie55 || dojo.render.html.ie60) { + var html= + "" + // and we're ready to go! + connected = true; + }else{ + this.rcvNode = dojo.io.createIFrame(this.rcvNodeName); + dojo.io.setIFrameSrc(this.rcvNode, this.topicRoot+"/?tunnelType=iframe"); + // we're still waiting on this one to call back up and advertise + // that it's been initialized + } + } + + this.iframeConnectionInit = function(){ + connected = true; + } + + this.dispatchServerEvent = function(eObj){ + // FIXME: implement basic /meta topic semantics here! + } + + this.init = function(){ + if(initialized){ + return; + } + initialized = true; + + this.widenDomain(); + + // we want to set up a connection to the designated server. Grab the + // server location out of djConfig. + this.topicRoot = djConfig["ShortBusRoot"]; + if(!this.topicRoot){ + dojo.debug("no topic root specified in djConfig.ShortBusRoot"); + return; + } + } + + this.dispatch = function(evt){ + // dipatch events along the specified path + } + + dojo.io.transports.addTransport("ShortBusTransport"); +} diff --git a/source/web/scripts/ajax/src/io/ShortBusInit.html b/source/web/scripts/ajax/src/io/ShortBusInit.html new file mode 100644 index 0000000000..edfb3096eb --- /dev/null +++ b/source/web/scripts/ajax/src/io/ShortBusInit.html @@ -0,0 +1,75 @@ + + + + diff --git a/source/web/scripts/ajax/src/io/XhrIframeProxy.js b/source/web/scripts/ajax/src/io/XhrIframeProxy.js new file mode 100644 index 0000000000..18c4b3b124 --- /dev/null +++ b/source/web/scripts/ajax/src/io/XhrIframeProxy.js @@ -0,0 +1,199 @@ +dojo.provide("dojo.io.XhrIframeProxy"); +dojo.provide("dojo.io.XhrIframeFacade"); + +dojo.require("dojo.experimental"); +dojo.experimental("dojo.io.XhrIframeProxy"); + +dojo.require("dojo.io.IframeIO"); +dojo.require("dojo.html.iframe"); +dojo.require("dojo.dom"); + +/* +TODO: This page might generate a "loading unsecure items on a secure page" +popup in browsers if it is served on a https URL, given that we are not +setting a src on the iframe element. +//TODO: Document that it doesn't work from local disk in Safari. + +*/ + +dojo.io.XhrIframeProxy = new function(){ + this._state = {}; + this._stateIdCounter = 0; + + this.send = function(facade){ + var stateId = "XhrIframeProxy" + (this._stateIdCounter++); + facade._stateId = stateId; + + this._state[stateId] = { + facade: facade, + stateId: stateId, + clientFrame: dojo.io.createIFrame(stateId, + "dojo.io.XhrIframeProxy.clientFrameLoaded('" + stateId + "');", + dojo.uri.dojoUri("src/io/xip_client.html")) + }; + } + + this.receive = function(stateId, urlEncodedData){ + /* urlEncodedData should have the following params: + - responseHeaders + - status + - statusText + - responseText + */ + //Decode response data. + var response = {}; + var nvPairs = urlEncodedData.split("&"); + for(var i = 0; i < nvPairs.length; i++){ + if(nvPairs[i]){ + var nameValue = nvPairs[i].split("="); + response[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]); + } + } + + //Set data on facade object. + var state = this._state[stateId]; + var facade = state.facade; + + facade._setResponseHeaders(response.responseHeaders); + if(response.status == 0 || response.status){ + facade.status = parseInt(response.status, 10); + } + if(response.statusText){ + facade.statusText = response.statusText; + } + if(response.responseText){ + facade.responseText = response.responseText; + + //Fix responseXML. + var contentType = facade.getResponseHeader("Content-Type"); + if(contentType && (contentType == "application/xml" || contentType == "text/xml")){ + facade.responseXML = dojo.dom.createDocumentFromText(response.responseText, contentType); + } + } + facade.readyState = 4; + + this.destroyState(stateId); + } + + this.clientFrameLoaded = function(stateId){ + var state = this._state[stateId]; + var facade = state.facade; + var clientWindow = dojo.html.iframeContentWindow(state.clientFrame); + + var reqHeaders = []; + for(var param in facade._requestHeaders){ + reqHeaders.push(param + ": " + facade._requestHeaders[param]); + } + + var requestData = { + uri: facade._uri + }; + if(reqHeaders.length > 0){ + requestData.requestHeaders = reqHeaders.join("\r\n"); + } + if(facade._method){ + requestData.method = facade._method; + } + if(facade._bodyData){ + requestData.data = facade._bodyData; + } + + clientWindow.send(stateId, facade._ifpServerUrl, dojo.io.argsFromMap(requestData, "utf8")); + } + + this.destroyState = function(stateId){ + var state = this._state[stateId]; + if(state){ + delete this._state[stateId]; + var parentNode = state.clientFrame.parentNode; + parentNode.removeChild(state.clientFrame); + state.clientFrame = null; + state = null; + } + } + + this.createFacade = function(){ + if(arguments && arguments[0] && arguments[0]["iframeProxyUrl"]){ + return new dojo.io.XhrIframeFacade(arguments[0]["iframeProxyUrl"]); + }else{ + return dojo.io.XhrIframeProxy.oldGetXmlhttpObject.apply(dojo.hostenv, arguments); + } + } +} + +//Replace the normal XHR factory with the proxy one. +dojo.io.XhrIframeProxy.oldGetXmlhttpObject = dojo.hostenv.getXmlhttpObject; +dojo.hostenv.getXmlhttpObject = dojo.io.XhrIframeProxy.createFacade; + +/** + Using this a reference: http://www.w3.org/TR/XMLHttpRequest/ + + Does not implement the onreadystate callback since dojo.io.BrowserIO does + not use it. +*/ +dojo.io.XhrIframeFacade = function(ifpServerUrl){ + this._requestHeaders = {}; + this._allResponseHeaders = null; + this._responseHeaders = {}; + this._method = null; + this._uri = null; + this._bodyData = null; + this.responseText = null; + this.responseXML = null; + this.status = null; + this.statusText = null; + this.readyState = 0; + + this._ifpServerUrl = ifpServerUrl; + this._stateId = null; +} + +dojo.lang.extend(dojo.io.XhrIframeFacade, { + //The open method does not properly reset since Dojo does not reuse XHR objects. + open: function(method, uri){ + this._method = method; + this._uri = uri; + + this.readyState = 1; + }, + + setRequestHeader: function(header, value){ + this._requestHeaders[header] = value; + }, + + send: function(stringData){ + this._bodyData = stringData; + + dojo.io.XhrIframeProxy.send(this); + + this.readyState = 2; + }, + abort: function(){ + dojo.io.XhrIframeProxy.destroyState(this._stateId); + }, + + getAllResponseHeaders: function(){ + return this._allResponseHeaders; + + }, + + getResponseHeader: function(header){ + return this._responseHeaders[header]; + }, + + _setResponseHeaders: function(allHeaders){ + if(allHeaders){ + this._allResponseHeaders = allHeaders; + + //Make sure ther are now CR characters in the headers. + allHeaders = allHeaders.replace(/\r/g, ""); + var nvPairs = allHeaders.split("\n"); + for(var i = 0; i < nvPairs.length; i++){ + if(nvPairs[i]){ + var nameValue = nvPairs[i].split(": "); + this._responseHeaders[nameValue[0]] = nameValue[1]; + } + } + } + } +}); diff --git a/source/web/scripts/ajax/src/io/__package__.js b/source/web/scripts/ajax/src/io/__package__.js new file mode 100644 index 0000000000..1421e47763 --- /dev/null +++ b/source/web/scripts/ajax/src/io/__package__.js @@ -0,0 +1,7 @@ +dojo.kwCompoundRequire({ + common: ["dojo.io"], + rhino: ["dojo.io.RhinoIO"], + browser: ["dojo.io.BrowserIO", "dojo.io.cookie"], + dashboard: ["dojo.io.BrowserIO", "dojo.io.cookie"] +}); +dojo.provide("dojo.io.*"); diff --git a/source/web/scripts/ajax/src/io/cometd.js b/source/web/scripts/ajax/src/io/cometd.js new file mode 100644 index 0000000000..d343414038 --- /dev/null +++ b/source/web/scripts/ajax/src/io/cometd.js @@ -0,0 +1,916 @@ +dojo.require("dojo.io"); // io.js provides setIFrameSrc and the IO namespace +dojo.provide("dojo.io.cometd"); +dojo.provide("cometd"); +dojo.require("dojo.AdapterRegistry"); +dojo.require("dojo.json"); +dojo.require("dojo.io.BrowserIO"); // we need XHR for the handshake, etc. +// FIXME: determine if we can use XMLHTTP to make x-domain posts despite not +// being able to hear back about the result +dojo.require("dojo.io.IframeIO"); +dojo.require("dojo.io.ScriptSrcIO"); // for x-domain long polling +dojo.require("dojo.io.cookie"); // for peering +dojo.require("dojo.event.*"); +dojo.require("dojo.lang.common"); +dojo.require("dojo.lang.func"); + +/* + * this file defines Comet protocol client. Actual message transport is + * deferred to one of several connection type implementations. The default is a + * forever-frame implementation. A single global object named "cometd" is + * used to mediate for these connection types in order to provide a stable + * interface. + */ + +// TODO: the auth handling in this file is a *mess*. It should probably live in +// the cometd object with the ability to mix in or call down to an auth-handler +// object, the prototypical variant of which is a no-op + +cometd = new function(){ + + this.initialized = false; + this.connected = false; + + this.connectionTypes = new dojo.AdapterRegistry(true); + + this.version = 0.1; + this.minimumVersion = 0.1; + this.clientId = null; + + this.isXD = false; + this.handshakeReturn = null; + this.currentTransport = null; + this.url = null; + this.lastMessage = null; + this.globalTopicChannels = {}; + this.backlog = []; + + this.tunnelInit = function(childLocation, childDomain){ + // placeholder + } + + this.tunnelCollapse = function(){ + dojo.debug("tunnel collapsed!"); + // placeholder + } + + this.init = function(props, root, bargs){ + // FIXME: if the root isn't from the same host, we should automatically + // try to select an XD-capable transport + props = props||{}; + // go ask the short bus server what we can support + props.version = this.version; + props.minimumVersion = this.minimumVersion; + props.channel = "/meta/handshake"; + // FIXME: do we just assume that the props knows + // everything we care about WRT to auth? Should we be trying to + // call back into it for subsequent auth actions? Should we fire + // local auth functions to ask for/get auth data? + + // FIXME: what about ScriptSrcIO for x-domain comet? + this.url = root||djConfig["cometdRoot"]; + if(!this.url){ + dojo.debug("no cometd root specified in djConfig and no root passed"); + return; + } + + // FIXME: we need to select a way to handle JSONP-style stuff + // generically here. We already know if the server is gonna be on + // another domain (or can know it), so we should select appropriate + // negotiation methods here as well as in final transport type + // selection. + var bindArgs = { + url: this.url, + method: "POST", + mimetype: "text/json", + load: dojo.lang.hitch(this, "finishInit"), + content: { "message": dojo.json.serialize([props]) } + }; + + // borrowed from dojo.uri.Uri in lieu of fixed host and port properties + var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"; + var r = (""+window.location).match(new RegExp(regexp)); + if(r[4]){ + var tmp = r[4].split(":"); + var thisHost = tmp[0]; + var thisPort = tmp[1]||"80"; // FIXME: match 443 + + r = this.url.match(new RegExp(regexp)); + if(r[4]){ + tmp = r[4].split(":"); + var urlHost = tmp[0]; + var urlPort = tmp[1]||"80"; + if( (urlHost != thisHost)|| + (urlPort != thisPort) ){ + dojo.debug(thisHost, urlHost); + dojo.debug(thisPort, urlPort); + + this.isXD = true; + bindArgs.transport = "ScriptSrcTransport"; + bindArgs.jsonParamName = "jsonp"; + } + } + } + if(bargs){ + dojo.lang.mixin(bindArgs, bargs); + } + return dojo.io.bind(bindArgs); + } + + this.finishInit = function(type, data, evt, request){ + this.handshakeReturn = data; + // pick a transport + if(data["authSuccessful"] == false){ + dojo.debug("cometd authentication failed"); + return; + } + if(data.version < this.minimumVersion){ + dojo.debug("cometd protocol version mismatch. We wanted", this.minimumVersion, "but got", data.version); + return; + } + this.currentTransport = this.connectionTypes.match( + data.supportedConnectionTypes, + data.version, + this.isXD + ); + this.currentTransport.version = data.version; + this.clientId = data.clientId; + this.tunnelInit = dojo.lang.hitch(this.currentTransport, "tunnelInit"); + this.tunnelCollapse = dojo.lang.hitch(this.currentTransport, "tunnelCollapse"); + this.initialized = true; + this.currentTransport.startup(data); + while(this.backlog.length != 0){ + var cur = this.backlog.shift(); + var fn = cur.shift(); + this[fn].apply(this, cur); + } + } + + this.getRandStr = function(){ + return Math.random().toString().substring(2, 10); + } + + // public API functions called by cometd or by the transport classes + this.deliver = function(messages){ + dojo.lang.forEach(messages, this._deliver, this); + } + + this._deliver = function(message){ + // dipatch events along the specified path + if(!message["channel"]){ + dojo.debug("cometd error: no channel for message!"); + return; + } + if(!this.currentTransport){ + this.backlog.push(["deliver", message]); + return; + } + this.lastMessage = message; + // check to see if we got a /meta channel message that we care about + if( (message.channel.length > 5)&& + (message.channel.substr(0, 5) == "/meta")){ + // check for various meta topic actions that we need to respond to + switch(message.channel){ + case "/meta/subscribe": + if(!message.successful){ + dojo.debug("cometd subscription error for channel", message.channel, ":", message.error); + return; + } + this.subscribed(message.subscription, message); + break; + case "/meta/unsubscribe": + if(!message.successful){ + dojo.debug("cometd unsubscription error for channel", message.channel, ":", message.error); + return; + } + this.unsubscribed(message.subscription, message); + break; + } + } + // send the message down for processing by the transport + this.currentTransport.deliver(message); + + // dispatch the message to any locally subscribed listeners + var tname = (this.globalTopicChannels[message.channel]) ? message.channel : "/cometd"+message.channel; + dojo.event.topic.publish(tname, message); + } + + this.disconnect = function(){ + if(!this.currentTransport){ + dojo.debug("no current transport to disconnect from"); + return; + } + this.currentTransport.disconnect(); + } + + // public API functions called by end users + this.publish = function(/*string*/channel, /*object*/data, /*object*/properties){ + // summary: + // publishes the passed message to the cometd server for delivery + // on the specified topic + // channel: + // the destination channel for the message + // data: + // a JSON object containing the message "payload" + // properties: + // Optional. Other meta-data to be mixed into the top-level of the + // message + if(!this.currentTransport){ + this.backlog.push(["publish", channel, data, properties]); + return; + } + var message = { + data: data, + channel: channel + }; + if(properties){ + dojo.lang.mixin(message, properties); + } + return this.currentTransport.sendMessage(message); + } + + this.subscribe = function( /*string*/ channel, + /*boolean, optional*/ useLocalTopics, + /*object, optional*/ objOrFunc, + /*string, optional*/ funcName){ // return: boolean + // summary: + // inform the server of this client's interest in channel + // channel: + // name of the cometd channel to subscribe to + // useLocalTopics: + // Determines if up a local event topic subscription to the passed + // function using the channel name that was passed is constructed, + // or if the topic name will be prefixed with some other + // identifier for local message distribution. Setting this to + // "true" is a good way to hook up server-sent message delivery to + // pre-existing local topics. + // objOrFunc: + // an object scope for funcName or the name or reference to a + // function to be called when messages are delivered to the + // channel + // funcName: + // the second half of the objOrFunc/funcName pair for identifying + // a callback function to notifiy upon channel message delivery + if(!this.currentTransport){ + this.backlog.push(["subscribe", channel, useLocalTopics, objOrFunc, funcName]); + return; + } + if(objOrFunc){ + var tname = (useLocalTopics) ? channel : "/cometd"+channel; + if(useLocalTopics){ + this.globalTopicChannels[channel] = true; + } + dojo.event.topic.subscribe(tname, objOrFunc, funcName); + } + // FIXME: would we handle queuing of the subscription if not connected? + // Or should the transport object? + return this.currentTransport.sendMessage({ + channel: "/meta/subscribe", + subscription: channel + }); + } + + this.subscribed = function( /*string*/ channel, + /*obj*/ message){ + dojo.debug(channel); + dojo.debugShallow(message); + } + + this.unsubscribe = function(/*string*/ channel, + /*boolean, optional*/ useLocalTopics, + /*object, optional*/ objOrFunc, + /*string, optional*/ funcName){ // return: boolean + // summary: + // inform the server of this client's disinterest in channel + // channel: + // name of the cometd channel to subscribe to + // useLocalTopics: + // Determines if up a local event topic subscription to the passed + // function using the channel name that was passed is destroyed, + // or if the topic name will be prefixed with some other + // identifier for stopping message distribution. + // objOrFunc: + // an object scope for funcName or the name or reference to a + // function to be called when messages are delivered to the + // channel + // funcName: + // the second half of the objOrFunc/funcName pair for identifying + if(!this.currentTransport){ + this.backlog.push(["unsubscribe", channel, useLocalTopics, objOrFunc, funcName]); + return; + } + // a callback function to notifiy upon channel message delivery + if(objOrFunc){ + // FIXME: should actual local topic unsubscription be delayed for + // successful unsubcribe notices from the other end? (guessing "no") + // FIXME: if useLocalTopics is false, should we go ahead and + // destroy the local topic? + var tname = (useLocalTopics) ? channel : "/cometd"+channel; + dojo.event.topic.unsubscribe(tname, objOrFunc, funcName); + } + return this.currentTransport.sendMessage({ + channel: "/meta/unsubscribe", + subscription: channel + }); + } + + this.unsubscribed = function(/*string*/ channel, + /*obj*/ message){ + dojo.debug(channel); + dojo.debugShallow(message); + } + + // FIXME: add an "addPublisher" function + +} + +/* +transport objects MUST expose the following methods: + - check + - startup + - sendMessage + - deliver + - disconnect +optional, standard but transport dependent methods are: + - tunnelCollapse + - tunnelInit + +Transports SHOULD be namespaced under the cometd object and transports MUST +register themselves with cometd.connectionTypes + +here's a stub transport defintion: + +cometd.blahTransport = new function(){ + this.connected = false; + this.connectionId = null; + this.authToken = null; + this.lastTimestamp = null; + this.lastId = null; + + this.check = function(types, version, xdomain){ + // summary: + // determines whether or not this transport is suitable given a + // list of transport types that the server supports + return dojo.lang.inArray(types, "blah"); + } + + this.startup = function(){ + if(this.connected){ return; } + // FIXME: fill in startup routine here + this.connected = true; + } + + this.sendMessage = function(message){ + // FIXME: fill in message sending logic + } + + this.deliver = function(message){ + if(message["timestamp"]){ + this.lastTimestamp = message.timestamp; + } + if(message["id"]){ + this.lastId = message.id; + } + if( (message.channel.length > 5)&& + (message.channel.substr(0, 5) == "/meta")){ + // check for various meta topic actions that we need to respond to + // switch(message.channel){ + // case "/meta/connect": + // // FIXME: fill in logic here + // break; + // // case ...: ... + // } + } + } + + this.disconnect = function(){ + if(!this.connected){ return; } + // FIXME: fill in shutdown routine here + this.connected = false; + } +} +cometd.connectionTypes.register("blah", cometd.blahTransport.check, cometd.blahTransport); +*/ + +cometd.iframeTransport = new function(){ + this.connected = false; + this.connectionId = null; + + this.rcvNode = null; + this.rcvNodeName = ""; + this.phonyForm = null; + this.authToken = null; + this.lastTimestamp = null; + this.lastId = null; + this.backlog = []; + + this.check = function(types, version, xdomain){ + return ((!xdomain)&& + (!dojo.render.html.safari)&& + (dojo.lang.inArray(types, "iframe"))); + } + + this.tunnelInit = function(){ + // we've gotten our initialization document back in the iframe, so + // now open up a connection and start passing data! + this.postToIframe({ + message: dojo.json.serialize([ + { + channel: "/meta/connect", + clientId: cometd.clientId, + connectionType: "iframe" + // FIXME: auth not passed here! + // "authToken": this.authToken + } + ]) + }); + } + + this.tunnelCollapse = function(){ + if(this.connected){ + // try to restart the tunnel + this.connected = false; + + this.postToIframe({ + message: dojo.json.serialize([ + { + channel: "/meta/reconnect", + clientId: cometd.clientId, + connectionId: this.connectionId, + timestamp: this.lastTimestamp, + id: this.lastId + // FIXME: no authToken provision! + } + ]) + }); + } + } + + this.deliver = function(message){ + // handle delivery details that this transport particularly cares + // about. Most functions of should be handled by the main cometd object + // with only transport-specific details and state being tracked here. + if(message["timestamp"]){ + this.lastTimestamp = message.timestamp; + } + if(message["id"]){ + this.lastId = message.id; + } + // check to see if we got a /meta channel message that we care about + if( (message.channel.length > 5)&& + (message.channel.substr(0, 5) == "/meta")){ + // check for various meta topic actions that we need to respond to + switch(message.channel){ + case "/meta/connect": + if(!message.successful){ + dojo.debug("cometd connection error:", message.error); + return; + } + this.connectionId = message.connectionId; + this.connected = true; + this.processBacklog(); + break; + case "/meta/reconnect": + if(!message.successful){ + dojo.debug("cometd reconnection error:", message.error); + return; + } + this.connected = true; + break; + case "/meta/subscribe": + if(!message.successful){ + dojo.debug("cometd subscription error for channel", message.channel, ":", message.error); + return; + } + // this.subscribed(message.channel); + dojo.debug(message.channel); + break; + } + } + } + + this.widenDomain = function(domainStr){ + // allow us to make reqests to the TLD + var cd = domainStr||document.domain; + if(cd.indexOf(".")==-1){ return; } // probably file:/// or localhost + var dps = cd.split("."); + if(dps.length<=2){ return; } // probably file:/// or an RFC 1918 address + dps = dps.slice(dps.length-2); + document.domain = dps.join("."); + return document.domain; + } + + this.postToIframe = function(content, url){ + if(!this.phonyForm){ + if(dojo.render.html.ie){ + this.phonyForm = document.createElement("
"); + dojo.body().appendChild(this.phonyForm); + }else{ + this.phonyForm = document.createElement("form"); + this.phonyForm.style.display = "none"; // FIXME: will this still work? + dojo.body().appendChild(this.phonyForm); + this.phonyForm.enctype = "application/x-www-form-urlencoded"; + this.phonyForm.method = "POST"; + } + } + + this.phonyForm.action = url||cometd.url; + this.phonyForm.target = this.rcvNodeName; + this.phonyForm.setAttribute("target", this.rcvNodeName); + + while(this.phonyForm.firstChild){ + this.phonyForm.removeChild(this.phonyForm.firstChild); + } + + for(var x in content){ + var tn; + if(dojo.render.html.ie){ + tn = document.createElement(""); + this.phonyForm.appendChild(tn); + }else{ + tn = document.createElement("input"); + this.phonyForm.appendChild(tn); + tn.type = "hidden"; + tn.name = x; + tn.value = content[x]; + } + } + this.phonyForm.submit(); + } + + this.processBacklog = function(){ + while(this.backlog.length > 0){ + this.sendMessage(this.backlog.shift(), true); + } + } + + this.sendMessage = function(message, bypassBacklog){ + // FIXME: what about auth fields? + if((bypassBacklog)||(this.connected)){ + message.connectionId = this.connectionId; + message.clientId = cometd.clientId; + var bindArgs = { + url: cometd.url||djConfig["cometdRoot"], + method: "POST", + mimetype: "text/json", + // FIXME: we should be able to do better than this given that we're sending an array! + content: { message: dojo.json.serialize([ message ]) } + }; + return dojo.io.bind(bindArgs); + }else{ + this.backlog.push(message); + } + } + + this.startup = function(handshakeData){ + dojo.debug("startup!"); + dojo.debug(dojo.json.serialize(handshakeData)); + + if(this.connected){ return; } + + // this.widenDomain(); + + // NOTE: we require the server to cooperate by hosting + // cometdInit.html at the designated endpoint + this.rcvNodeName = "cometdRcv_"+cometd.getRandStr(); + // the "forever frame" approach + + var initUrl = cometd.url+"/?tunnelInit=iframe"; // &domain="+document.domain; + if(false && dojo.render.html.ie){ // FIXME: DISALBED FOR NOW + // use the "htmlfile hack" to prevent the background click junk + this.rcvNode = new ActiveXObject("htmlfile"); + this.rcvNode.open(); + this.rcvNode.write(""); + this.rcvNode.write(" + + +

The Dojo Toolkit -- xip_client.html

+ +

This file is used for Dojo's XMLHttpRequest Iframe Proxy. This is the "client" file used + internally by dojo.io.XhrIframeProxy.

+ + + + diff --git a/source/web/scripts/ajax/src/io/xip_server.html b/source/web/scripts/ajax/src/io/xip_server.html new file mode 100644 index 0000000000..17837e8f70 --- /dev/null +++ b/source/web/scripts/ajax/src/io/xip_server.html @@ -0,0 +1,267 @@ + + + + + + + + + + + + + +

The Dojo Toolkit -- xip_server.html

+ +

This file is used for Dojo's XMLHttpRequest Iframe Proxy. This is the the file + that should go on the server that will actually be doing the XHR request.

+ + diff --git a/source/web/scripts/ajax/src/json.js b/source/web/scripts/ajax/src/json.js new file mode 100644 index 0000000000..8705d86af2 --- /dev/null +++ b/source/web/scripts/ajax/src/json.js @@ -0,0 +1,120 @@ +dojo.provide("dojo.json"); +dojo.require("dojo.lang.func"); +dojo.require("dojo.string.extras"); +dojo.require("dojo.AdapterRegistry"); + +dojo.json = { + jsonRegistry: new dojo.AdapterRegistry(), + + register: function(name, check, wrap, /*optional*/ override){ + /*** + + Register a JSON serialization function. JSON serialization + functions should take one argument and return an object + suitable for JSON serialization: + + - string + - number + - boolean + - undefined + - object + - null + - Array-like (length property that is a number) + - Objects with a "json" method will have this method called + - Any other object will be used as {key:value, ...} pairs + + If override is given, it is used as the highest priority + JSON serialization, otherwise it will be used as the lowest. + ***/ + + dojo.json.jsonRegistry.register(name, check, wrap, override); + }, + + evalJson: function(/* jsonString */ json){ + // FIXME: should this accept mozilla's optional second arg? + try { + return eval("(" + json + ")"); + }catch(e){ + dojo.debug(e); + return json; + } + }, + + serialize: function(o){ + /*** + Create a JSON serialization of an object, note that this doesn't + check for infinite recursion, so don't do that! + ***/ + + var objtype = typeof(o); + if(objtype == "undefined"){ + return "undefined"; + }else if((objtype == "number")||(objtype == "boolean")){ + return o + ""; + }else if(o === null){ + return "null"; + } + if (objtype == "string") { return dojo.string.escapeString(o); } + // recurse + var me = arguments.callee; + // short-circuit for objects that support "json" serialization + // if they return "self" then just pass-through... + var newObj; + if(typeof(o.__json__) == "function"){ + newObj = o.__json__(); + if(o !== newObj){ + return me(newObj); + } + } + if(typeof(o.json) == "function"){ + newObj = o.json(); + if (o !== newObj) { + return me(newObj); + } + } + // array + if(objtype != "function" && typeof(o.length) == "number"){ + var res = []; + for(var i = 0; i < o.length; i++){ + var val = me(o[i]); + if(typeof(val) != "string"){ + val = "undefined"; + } + res.push(val); + } + return "[" + res.join(",") + "]"; + } + // look in the registry + try { + window.o = o; + newObj = dojo.json.jsonRegistry.match(o); + return me(newObj); + }catch(e){ + // dojo.debug(e); + } + // it's a function with no adapter, bad + if(objtype == "function"){ + return null; + } + // generic object code path + res = []; + for (var k in o){ + var useKey; + if (typeof(k) == "number"){ + useKey = '"' + k + '"'; + }else if (typeof(k) == "string"){ + useKey = dojo.string.escapeString(k); + }else{ + // skip non-string or number keys + continue; + } + val = me(o[k]); + if(typeof(val) != "string"){ + // skip non-serializable values + continue; + } + res.push(useKey + ":" + val); + } + return "{" + res.join(",") + "}"; + } +}; diff --git a/source/web/scripts/ajax/src/lang.js b/source/web/scripts/ajax/src/lang.js new file mode 100644 index 0000000000..2888b8bb0d --- /dev/null +++ b/source/web/scripts/ajax/src/lang.js @@ -0,0 +1,4 @@ +dojo.provide("dojo.lang"); +dojo.require("dojo.lang.common"); + +dojo.deprecated("dojo.lang", "replaced by dojo.lang.common", "0.5"); diff --git a/source/web/scripts/ajax/src/lang/Lang.js b/source/web/scripts/ajax/src/lang/Lang.js new file mode 100644 index 0000000000..bca6c4ce60 --- /dev/null +++ b/source/web/scripts/ajax/src/lang/Lang.js @@ -0,0 +1,12 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.require("dojo.lang"); +dojo.deprecated("dojo.lang.Lang", "use dojo.lang instead", "0.4"); diff --git a/source/web/scripts/ajax/src/lang/__package__.js b/source/web/scripts/ajax/src/lang/__package__.js new file mode 100644 index 0000000000..b6ef6b8185 --- /dev/null +++ b/source/web/scripts/ajax/src/lang/__package__.js @@ -0,0 +1,14 @@ +dojo.kwCompoundRequire({ + common: [ + "dojo.lang", + "dojo.lang.common", + "dojo.lang.assert", + "dojo.lang.array", + "dojo.lang.type", + "dojo.lang.func", + "dojo.lang.extras", + "dojo.lang.repr", + "dojo.lang.declare" + ] +}); +dojo.provide("dojo.lang.*"); diff --git a/source/web/scripts/ajax/src/lang/array.js b/source/web/scripts/ajax/src/lang/array.js new file mode 100644 index 0000000000..5e67d28b8d --- /dev/null +++ b/source/web/scripts/ajax/src/lang/array.js @@ -0,0 +1,173 @@ +dojo.provide("dojo.lang.array"); + +dojo.require("dojo.lang.common"); + +// FIXME: Is this worthless since you can do: if(name in obj) +// is this the right place for this? +dojo.lang.has = function(obj, name){ + try{ + return (typeof obj[name] != "undefined"); + }catch(e){ return false; } +} + +dojo.lang.isEmpty = function(obj) { + if(dojo.lang.isObject(obj)) { + var tmp = {}; + var count = 0; + for(var x in obj){ + if(obj[x] && (!tmp[x])){ + count++; + break; + } + } + return (count == 0); + } else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)) { + return obj.length == 0; + } +} + +dojo.lang.map = function(arr, obj, unary_func){ + var isString = dojo.lang.isString(arr); + if(isString){ + arr = arr.split(""); + } + if(dojo.lang.isFunction(obj)&&(!unary_func)){ + unary_func = obj; + obj = dj_global; + }else if(dojo.lang.isFunction(obj) && unary_func){ + // ff 1.5 compat + var tmpObj = obj; + obj = unary_func; + unary_func = tmpObj; + } + if(Array.map){ + var outArr = Array.map(arr, unary_func, obj); + }else{ + var outArr = []; + for(var i=0;i= 3) { dojo.raise("thisObject doesn't exist!"); } + thisObject = dj_global; + } + + var outArr = []; + for(var i = 0; i < arr.length; i++) { + if(callback.call(thisObject, arr[i], i, arr)) { + outArr.push(arr[i]); + } + } + } + if(isString) { + return outArr.join(""); + } else { + return outArr; + } +} + +/** + * Creates a 1-D array out of all the arguments passed, + * unravelling any array-like objects in the process + * + * Ex: + * unnest(1, 2, 3) ==> [1, 2, 3] + * unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4] + */ +dojo.lang.unnest = function(/* ... */) { + var out = []; + for(var i = 0; i < arguments.length; i++) { + if(dojo.lang.isArrayLike(arguments[i])) { + var add = dojo.lang.unnest.apply(this, arguments[i]); + out = out.concat(add); + } else { + out.push(arguments[i]); + } + } + return out; +} + +/** + * Converts an array-like object (i.e. arguments, DOMCollection) + * to an array +**/ +dojo.lang.toArray = function(arrayLike, startOffset) { + var array = []; + for(var i = startOffset||0; i < arrayLike.length; i++) { + array.push(arrayLike[i]); + } + return array; +} diff --git a/source/web/scripts/ajax/src/lang/assert.js b/source/web/scripts/ajax/src/lang/assert.js new file mode 100644 index 0000000000..803a6a9818 --- /dev/null +++ b/source/web/scripts/ajax/src/lang/assert.js @@ -0,0 +1,112 @@ +dojo.provide("dojo.lang.assert"); + +dojo.require("dojo.lang.common"); +dojo.require("dojo.lang.array"); +dojo.require("dojo.lang.type"); + +// ------------------------------------------------------------------- +// Assertion methods +// ------------------------------------------------------------------- + +/** + * Throws an exception if the assertion fails. + * + * If the asserted condition is true, this method does nothing. If the + * condition is false, we throw an error with a error message. + * + * @param booleanValue A boolean value, which needs to be true for the assertion to succeed. + * @param message Optional. A string describing the assertion. + * @throws Throws an Error if 'booleanValue' is false. + */ +dojo.lang.assert = function(booleanValue, message){ + if(!booleanValue){ + var errorMessage = "An assert statement failed.\n" + + "The method dojo.lang.assert() was called with a 'false' value.\n"; + if(message){ + errorMessage += "Here's the assert message:\n" + message + "\n"; + } + // Use throw instead of dojo.raise, until bug #264 is fixed: + // dojo.raise(errorMessage); + throw new Error(errorMessage); + } +} + +/** + * Given a value and a data type, this method checks the type of the value + * to make sure it matches the data type, and throws an exception if there + * is a mismatch. + * + * Examples: + *
+ *   dojo.lang.assertType("foo", String);
+ *   dojo.lang.assertType(12345, Number);
+ *   dojo.lang.assertType(false, Boolean);
+ *   dojo.lang.assertType([6, 8], Array);
+ *   dojo.lang.assertType(dojo.lang.assertType, Function);
+ *   dojo.lang.assertType({foo: "bar"}, Object);
+ *   dojo.lang.assertType(new Date(), Date);
+ *   dojo.lang.assertType(null, Array, {optional: true});
+ * 
+ * + * @scope public function + * @param value Any literal value or object instance. + * @param type A class of object, or a literal type, or the string name of a type, or an array with a list of types. + * @param keywordParameters Optional. A string describing the assertion. + * @throws Throws an Error if 'value' is not of type 'type'. + */ +dojo.lang.assertType = function(value, type, keywordParameters){ + if (dojo.lang.isString(keywordParameters)) { + dojo.deprecated('dojo.lang.assertType(value, type, "message")', 'use dojo.lang.assertType(value, type) instead', "0.5"); + } + if(!dojo.lang.isOfType(value, type, keywordParameters)){ + if(!dojo.lang.assertType._errorMessage){ + dojo.lang.assertType._errorMessage = "Type mismatch: dojo.lang.assertType() failed."; + } + dojo.lang.assert(false, dojo.lang.assertType._errorMessage); + } +} + +/** + * Given an anonymous object and a list of expected property names, this + * method check to make sure the object does not have any properties + * that aren't on the list of expected properties, and throws an Error + * if there are unexpected properties. This is useful for doing error + * checking on keyword arguments, to make sure there aren't typos. + * + * Examples: + *
+ *   dojo.lang.assertValidKeywords({a: 1, b: 2}, ["a", "b"]);
+ *   dojo.lang.assertValidKeywords({a: 1, b: 2}, ["a", "b", "c"]);
+ *   dojo.lang.assertValidKeywords({foo: "iggy"}, ["foo"]);
+ *   dojo.lang.assertValidKeywords({foo: "iggy"}, ["foo", "bar"]);
+ *   dojo.lang.assertValidKeywords({foo: "iggy"}, {foo: null, bar: null});
+ * 
+ * + * @scope public function + * @param object An anonymous object. + * @param expectedProperties An array of strings (or an object with all the expected properties). + * @param message Optional. A string describing the assertion. + * @throws Throws an Error if 'value' is not of type 'type'. + */ +dojo.lang.assertValidKeywords = function(object, expectedProperties, message){ + var key; + if(!message){ + if(!dojo.lang.assertValidKeywords._errorMessage){ + dojo.lang.assertValidKeywords._errorMessage = "In dojo.lang.assertValidKeywords(), found invalid keyword:"; + } + message = dojo.lang.assertValidKeywords._errorMessage; + } + if(dojo.lang.isArray(expectedProperties)){ + for(key in object){ + if(!dojo.lang.inArray(expectedProperties, key)){ + dojo.lang.assert(false, message + " " + key); + } + } + }else{ + for(key in object){ + if(!(key in expectedProperties)){ + dojo.lang.assert(false, message + " " + key); + } + } + } +} diff --git a/source/web/scripts/ajax/src/lang/common.js b/source/web/scripts/ajax/src/lang/common.js new file mode 100644 index 0000000000..f5af0505d7 --- /dev/null +++ b/source/web/scripts/ajax/src/lang/common.js @@ -0,0 +1,170 @@ +dojo.provide("dojo.lang.common"); +dojo.require("dojo.lang"); + +// Backwards compatibility +dojo.lang._mixin = dojo._mixin; +dojo.lang.mixin = dojo.mixin; +dojo.lang.extend = dojo.extend; + +dojo.lang.find = function( /*Array*/ array, + /*Object*/ value, + /*Boolean?*/ identity, + /*Boolean?*/ findLast){ + // summary: Return the index of value in array, returning -1 if not found. + + // param: identity: If true, matches with identity comparison (===). + // If false, uses normal comparison (==). + // param: findLast: If true, returns index of last instance of value. + // usage: + // find(array, value[, identity [findLast]]) // recommended + // usage: + // find(value, array[, identity [findLast]]) // deprecated + + // support both (array, value) and (value, array) + if(!dojo.lang.isArrayLike(array) && dojo.lang.isArrayLike(value)) { + dojo.deprecated('dojo.lang.find(value, array)', 'use dojo.lang.find(array, value) instead', "0.5"); + var temp = array; + array = value; + value = temp; + } + var isString = dojo.lang.isString(array); + if(isString) { array = array.split(""); } + + if(findLast) { + var step = -1; + var i = array.length - 1; + var end = -1; + } else { + var step = 1; + var i = 0; + var end = array.length; + } + if(identity){ + while(i != end) { + if(array[i] === value){ return i; } + i += step; + } + }else{ + while(i != end) { + if(array[i] == value){ return i; } + i += step; + } + } + return -1; // number +} + +dojo.lang.indexOf = dojo.lang.find; + +dojo.lang.findLast = function(/*Array*/ array, /*Object*/ value, /*boolean?*/ identity){ + // summary: Return index of last occurance of value in array, returning -1 if not found. + + // param: identity: If true, matches with identity comparison (===). + // If false, uses normal comparison (==). + return dojo.lang.find(array, value, identity, true); +} + +dojo.lang.lastIndexOf = dojo.lang.findLast; + +dojo.lang.inArray = function(array /*Array*/, value /*Object*/){ + // summary: Return true if value is present in array. + return dojo.lang.find(array, value) > -1; // return: boolean +} + +/** + * Partial implmentation of is* functions from + * http://www.crockford.com/javascript/recommend.html + * NOTE: some of these may not be the best thing to use in all situations + * as they aren't part of core JS and therefore can't work in every case. + * See WARNING messages inline for tips. + * + * The following is* functions are fairly "safe" + */ + +dojo.lang.isObject = function(it){ + // summary: Return true if it is an Object, Array or Function. + if(typeof it == "undefined"){ return false; } + return (typeof it == "object" || it === null || dojo.lang.isArray(it) || dojo.lang.isFunction(it)); +} + +dojo.lang.isArray = function(it){ + // summary: Return true if it is an Array. + return (it instanceof Array || typeof it == "array"); +} + +dojo.lang.isArrayLike = function(it){ + // summary: Return true if it can be used as an array (i.e. is an object with an integer length property). + if((!it)||(dojo.lang.isUndefined(it))){ return false; } + if(dojo.lang.isString(it)){ return false; } + if(dojo.lang.isFunction(it)){ return false; } // keeps out built-in constructors (Number, String, ...) which have length properties + if(dojo.lang.isArray(it)){ return true; } + // form node itself is ArrayLike, but not always iterable. Use form.elements instead. + if((it.tagName)&&(it.tagName.toLowerCase()=='form')){ return false; } + if(dojo.lang.isNumber(it.length) && isFinite(it.length)){ return true; } + return false; +} + +dojo.lang.isFunction = function(it){ + // summary: Return true if it is a Function. + if(!it){ return false; } + return (it instanceof Function || typeof it == "function"); +} + +dojo.lang.isString = function(it){ + // summary: Return true if it is a String. + return (it instanceof String || typeof it == "string"); +} + +dojo.lang.isAlien = function(it){ + // summary: Return true if it is not a built-in function. + if(!it){ return false; } + return !dojo.lang.isFunction() && /\{\s*\[native code\]\s*\}/.test(String(it)); +} + +dojo.lang.isBoolean = function(it){ + // summary: Return true if it is a Boolean. + return (it instanceof Boolean || typeof it == "boolean"); +} + +/** + * The following is***() functions are somewhat "unsafe". Fortunately, + * there are workarounds the the language provides and are mentioned + * in the WARNING messages. + * + */ +dojo.lang.isNumber = function(it){ + // summary: Return true if it is a number. + + // warning: + // In most cases, isNaN(it) is sufficient to determine whether or not + // something is a number or can be used as such. For example, a number or string + // can be used interchangably when accessing array items (array["1"] is the same as + // array[1]) and isNaN will return false for both values ("1" and 1). However, + // isNumber("1") will return false, which is generally not too useful. + // Also, isNumber(NaN) returns true, again, this isn't generally useful, but there + // are corner cases (like when you want to make sure that two things are really + // the same type of thing). That is really where isNumber "shines". + // + // recommendation: Use isNaN(it) when possible + + return (it instanceof Number || typeof it == "number"); +} + +/* + * FIXME: Should isUndefined go away since it is error prone? + */ +dojo.lang.isUndefined = function(it){ + // summary: Return true if it is not defined. + + // warning: In some cases, isUndefined will not behave as you + // might expect. If you do isUndefined(foo) and there is no earlier + // reference to foo, an error will be thrown before isUndefined is + // called. It behaves correctly if you scope yor object first, i.e. + // isUndefined(foo.bar) where foo is an object and bar isn't a + // property of the object. + // + // recommendation: Use typeof foo == "undefined" when possible + + return ((it == undefined)&&(typeof it == "undefined")); +} + +// end Crockford functions diff --git a/source/web/scripts/ajax/src/lang/declare.js b/source/web/scripts/ajax/src/lang/declare.js new file mode 100644 index 0000000000..f8fceff496 --- /dev/null +++ b/source/web/scripts/ajax/src/lang/declare.js @@ -0,0 +1,148 @@ +dojo.provide("dojo.lang.declare"); + +dojo.require("dojo.lang.common"); +dojo.require("dojo.lang.extras"); + +/* + * Creates a constructor: inherit and extend + * + * - inherits from "superclass(es)" + * + * "superclass" argument may be a Function, or an array of + * Functions. + * + * If "superclass" is an array, the first element is used + * as the prototypical ancestor and any following Functions + * become mixin ancestors. + * + * All "superclass(es)" must be Functions (not mere Objects). + * + * Using mixin ancestors provides a type of multiple + * inheritance. Mixin ancestors prototypical + * properties are copied to the subclass, and any + * inializater/constructor is invoked. + * + * - "props" are copied to the constructor prototype + * + * - name of the class ("className" argument) is stored in + * "declaredClass" property + * + * - An initializer function can be specified in the "init" + * argument, or by including a function called "initializer" + * in "props". + * + * - Superclass methods (inherited methods) can be invoked using "inherited" method: + * + * this.inherited([, ]); + * + * - inherited will continue up the prototype chain until it finds an implementation of method + * - nested calls to inherited are supported (i.e. inherited method "A" can succesfully call inherited("A"), and so on) + * + * Aliased as "dojo.declare" + * + * Usage: + * + * dojo.declare("my.classes.bar", my.classes.foo, { + * initializer: function() { + * this.myComplicatedObject = new ReallyComplicatedObject(); + * }, + * someValue: 2, + * aMethod: function() { doStuff(); } + * }); + * + */ +dojo.lang.declare = function(className /*string*/, superclass /*function || array*/, init /*function*/, props /*object*/){ + // FIXME: parameter juggling for backward compat ... deprecate and remove after 0.3.* + // new sig: (className (string)[, superclass (function || array)[, init (function)][, props (object)]]) + // old sig: (className (string)[, superclass (function || array), props (object), init (function)]) + if ((dojo.lang.isFunction(props))||((!props)&&(!dojo.lang.isFunction(init)))){ + var temp = props; + props = init; + init = temp; + } + var mixins = [ ]; + if (dojo.lang.isArray(superclass)) { + mixins = superclass; + superclass = mixins.shift(); + } + if(!init){ + init = dojo.evalObjPath(className, false); + if ((init)&&(!dojo.lang.isFunction(init))){ init = null }; + } + var ctor = dojo.lang.declare._makeConstructor(); + var scp = (superclass ? superclass.prototype : null); + if(scp){ + scp.prototyping = true; + ctor.prototype = new superclass(); + scp.prototyping = false; + } + ctor.superclass = scp; + ctor.mixins = mixins; + for(var i=0,l=mixins.length; i + * dojo.lang.isNumeric(3); // returns true + * dojo.lang.isNumeric("3"); // returns true + * dojo.lang.isNumeric(new Number(3)); // returns true + * dojo.lang.isNumeric(new String("3")); // returns true + * + * dojo.lang.isNumeric(3/0); // returns false + * dojo.lang.isNumeric("foo"); // returns false + * dojo.lang.isNumeric(new Number("foo")); // returns false + * dojo.lang.isNumeric(false); // returns false + * dojo.lang.isNumeric(true); // returns false + * + */ +dojo.lang.isNumeric = function(value){ + return (!isNaN(value) && isFinite(value) && (value != null) && + !dojo.lang.isBoolean(value) && !dojo.lang.isArray(value) && + !/^\s*$/.test(value)); +} + +/** + * Returns true for any literal, and for any object that is an + * instance of a built-in type like String, Number, Boolean, + * Array, Function, or Error. + */ +dojo.lang.isBuiltIn = function(value){ + return (dojo.lang.isArray(value) || + dojo.lang.isFunction(value) || + dojo.lang.isString(value) || + dojo.lang.isNumber(value) || + dojo.lang.isBoolean(value) || + (value == null) || + (value instanceof Error) || + (typeof value == "error") ); +} + +/** + * Returns true for any object where the value of the + * property 'constructor' is 'Object'. + * + * Examples: + *
+ *   dojo.lang.isPureObject(new Object()); // returns true
+ *   dojo.lang.isPureObject({a: 1, b: 2}); // returns true
+ * 
+ *   dojo.lang.isPureObject(new Date());   // returns false
+ *   dojo.lang.isPureObject([11, 2, 3]);   // returns false
+ * 
+ */ +dojo.lang.isPureObject = function(value){ + return ((value != null) && dojo.lang.isObject(value) && value.constructor == Object); +} + +/** + * Given a value and a datatype, this method returns true if the + * type of the value matches the datatype. The datatype parameter + * can be an array of datatypes, in which case the method returns + * true if the type of the value matches any of the datatypes. + * + * Examples: + *
+ *   dojo.lang.isOfType("foo", String);                // returns true
+ *   dojo.lang.isOfType(12345, Number);                // returns true
+ *   dojo.lang.isOfType(false, Boolean);               // returns true
+ *   dojo.lang.isOfType([6, 8], Array);                // returns true
+ *   dojo.lang.isOfType(dojo.lang.isOfType, Function); // returns true
+ *   dojo.lang.isOfType({foo: "bar"}, Object);         // returns true
+ *   dojo.lang.isOfType(new Date(), Date);             // returns true
+ *
+ *   dojo.lang.isOfType("foo", "string");                // returns true
+ *   dojo.lang.isOfType(12345, "number");                // returns true
+ *   dojo.lang.isOfType(false, "boolean");               // returns true
+ *   dojo.lang.isOfType([6, 8], "array");                // returns true
+ *   dojo.lang.isOfType(dojo.lang.isOfType, "function"); // returns true
+ *   dojo.lang.isOfType({foo: "bar"}, "object");         // returns true
+ *   dojo.lang.isOfType(xxxxx, "undefined");             // returns true
+ *   dojo.lang.isOfType(null, "null");                   // returns true
+ *
+ *   dojo.lang.isOfType("foo", [Number, String, Boolean]); // returns true
+ *   dojo.lang.isOfType(12345, [Number, String, Boolean]); // returns true
+ *   dojo.lang.isOfType(false, [Number, String, Boolean]); // returns true
+ *
+ *   dojo.lang.isOfType(null, Date, {optional: true} );    // returns true
+ * 
+ * + * @param value Any literal value or object instance. + * @param type A class of object, or a literal type, or the string name of a type, or an array with a list of types. + * @return Returns a boolean + */ +dojo.lang.isOfType = function(value, type, keywordParameters) { + var optional = false; + if (keywordParameters) { + optional = keywordParameters["optional"]; + } + if (optional && ((value === null) || dojo.lang.isUndefined(value))) { + return true; + } + if(dojo.lang.isArray(type)){ + var arrayOfTypes = type; + for(var i in arrayOfTypes){ + var aType = arrayOfTypes[i]; + if(dojo.lang.isOfType(value, aType)) { + return true; + } + } + return false; + }else{ + if(dojo.lang.isString(type)){ + type = type.toLowerCase(); + } + switch (type) { + case Array: + case "array": + return dojo.lang.isArray(value); + break; + case Function: + case "function": + return dojo.lang.isFunction(value); + break; + case String: + case "string": + return dojo.lang.isString(value); + break; + case Number: + case "number": + return dojo.lang.isNumber(value); + break; + case "numeric": + return dojo.lang.isNumeric(value); + break; + case Boolean: + case "boolean": + return dojo.lang.isBoolean(value); + break; + case Object: + case "object": + return dojo.lang.isObject(value); + break; + case "pureobject": + return dojo.lang.isPureObject(value); + break; + case "builtin": + return dojo.lang.isBuiltIn(value); + break; + case "alien": + return dojo.lang.isAlien(value); + break; + case "undefined": + return dojo.lang.isUndefined(value); + break; + case null: + case "null": + return (value === null); + break; + case "optional": + dojo.deprecated('dojo.lang.isOfType(value, [type, "optional"])', 'use dojo.lang.isOfType(value, type, {optional: true} ) instead', "0.5"); + return ((value === null) || dojo.lang.isUndefined(value)); + break; + default: + if (dojo.lang.isFunction(type)) { + return (value instanceof type); + } else { + dojo.raise("dojo.lang.isOfType() was passed an invalid type"); + } + break; + } + } + dojo.raise("If we get here, it means a bug was introduced above."); +} + +/* + * From reflection code, part of merge. + * TRT 2006-02-01 + */ +dojo.lang.getObject=function(/* String */ str){ + // summary + // Will return an object, if it exists, based on the name in the passed string. + var parts=str.split("."), i=0, obj=dj_global; + do{ + obj=obj[parts[i++]]; + }while(i 0){ this.duration = duration; } + if(repeatCount){ this.repeatCount = repeatCount; } + if(rate){ this.rate = rate; } + if(handlers){ + dojo.lang.forEach([ + "handler", "beforeBegin", "onBegin", + "onEnd", "onPlay", "onStop", "onAnimate" + ], function(item){ + if(handlers[item]){ + this.connect(item, handlers[item]); + } + }, this); + } + if(easing && dojo.lang.isFunction(easing)){ + this.easing=easing; + } +} +dojo.inherits(dojo.lfx.Animation, dojo.lfx.IAnimation); +dojo.lang.extend(dojo.lfx.Animation, { + // "private" properties + _startTime: null, + _endTime: null, + _timer: null, + _percent: 0, + _startRepeatCount: 0, + + // public methods + play: function(delay, gotoStart){ + if(gotoStart){ + clearTimeout(this._timer); + this._active = false; + this._paused = false; + this._percent = 0; + }else if(this._active && !this._paused){ + return this; + } + + this.fire("handler", ["beforeBegin"]); + this.fire("beforeBegin"); + + if(delay > 0){ + setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay); + return this; + } + + this._startTime = new Date().valueOf(); + if(this._paused){ + this._startTime -= (this.duration * this._percent / 100); + } + this._endTime = this._startTime + this.duration; + + this._active = true; + this._paused = false; + + var step = this._percent / 100; + var value = this.curve.getValue(step); + if(this._percent == 0 ){ + if(!this._startRepeatCount){ + this._startRepeatCount = this.repeatCount; + } + this.fire("handler", ["begin", value]); + this.fire("onBegin", [value]); + } + + this.fire("handler", ["play", value]); + this.fire("onPlay", [value]); + + this._cycle(); + return this; + }, + + pause: function(){ + clearTimeout(this._timer); + if(!this._active){ return this; } + this._paused = true; + var value = this.curve.getValue(this._percent / 100); + this.fire("handler", ["pause", value]); + this.fire("onPause", [value]); + return this; + }, + + gotoPercent: function(pct, andPlay){ + clearTimeout(this._timer); + this._active = true; + this._paused = true; + this._percent = pct; + if(andPlay){ this.play(); } + return this; + }, + + stop: function(gotoEnd){ + clearTimeout(this._timer); + var step = this._percent / 100; + if(gotoEnd){ + step = 1; + } + var value = this.curve.getValue(step); + this.fire("handler", ["stop", value]); + this.fire("onStop", [value]); + this._active = false; + this._paused = false; + return this; + }, + + status: function(){ + if(this._active){ + return this._paused ? "paused" : "playing"; + }else{ + return "stopped"; + } + return this; + }, + + // "private" methods + _cycle: function(){ + clearTimeout(this._timer); + if(this._active){ + var curr = new Date().valueOf(); + var step = (curr - this._startTime) / (this._endTime - this._startTime); + + if(step >= 1){ + step = 1; + this._percent = 100; + }else{ + this._percent = step * 100; + } + + // Perform easing + if((this.easing)&&(dojo.lang.isFunction(this.easing))){ + step = this.easing(step); + } + + var value = this.curve.getValue(step); + this.fire("handler", ["animate", value]); + this.fire("onAnimate", [value]); + + if( step < 1 ){ + this._timer = setTimeout(dojo.lang.hitch(this, "_cycle"), this.rate); + }else{ + this._active = false; + this.fire("handler", ["end"]); + this.fire("onEnd"); + + if(this.repeatCount > 0){ + this.repeatCount--; + this.play(null, true); + }else if(this.repeatCount == -1){ + this.play(null, true); + }else{ + if(this._startRepeatCount){ + this.repeatCount = this._startRepeatCount; + this._startRepeatCount = 0; + } + } + } + } + return this; + } +}); + +dojo.lfx.Combine = function(){ + dojo.lfx.IAnimation.call(this); + this._anims = []; + this._animsEnded = 0; + + var anims = arguments; + if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){ + anims = anims[0]; + } + + dojo.lang.forEach(anims, function(anim){ + this._anims.push(anim); + anim.connect("onEnd", dojo.lang.hitch(this, "_onAnimsEnded")); + }, this); +} +dojo.inherits(dojo.lfx.Combine, dojo.lfx.IAnimation); +dojo.lang.extend(dojo.lfx.Combine, { + // private members + _animsEnded: 0, + + // public methods + play: function(delay, gotoStart){ + if( !this._anims.length ){ return this; } + + this.fire("beforeBegin"); + + if(delay > 0){ + setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay); + return this; + } + + if(gotoStart || this._anims[0].percent == 0){ + this.fire("onBegin"); + } + this.fire("onPlay"); + this._animsCall("play", null, gotoStart); + return this; + }, + + pause: function(){ + this.fire("onPause"); + this._animsCall("pause"); + return this; + }, + + stop: function(gotoEnd){ + this.fire("onStop"); + this._animsCall("stop", gotoEnd); + return this; + }, + + // private methods + _onAnimsEnded: function(){ + this._animsEnded++; + if(this._animsEnded >= this._anims.length){ + this.fire("onEnd"); + } + return this; + }, + + _animsCall: function(funcName){ + var args = []; + if(arguments.length > 1){ + for(var i = 1 ; i < arguments.length ; i++){ + args.push(arguments[i]); + } + } + var _this = this; + dojo.lang.forEach(this._anims, function(anim){ + anim[funcName](args); + }, _this); + return this; + } +}); + +dojo.lfx.Chain = function() { + dojo.lfx.IAnimation.call(this); + this._anims = []; + this._currAnim = -1; + + var anims = arguments; + if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){ + anims = anims[0]; + } + + var _this = this; + dojo.lang.forEach(anims, function(anim, i, anims_arr){ + this._anims.push(anim); + if(i < anims_arr.length - 1){ + anim.connect("onEnd", dojo.lang.hitch(this, "_playNext") ); + }else{ + anim.connect("onEnd", dojo.lang.hitch(this, function(){ this.fire("onEnd"); }) ); + } + }, this); +} +dojo.inherits(dojo.lfx.Chain, dojo.lfx.IAnimation); +dojo.lang.extend(dojo.lfx.Chain, { + // private members + _currAnim: -1, + + // public methods + play: function(delay, gotoStart){ + if( !this._anims.length ) { return this; } + if( gotoStart || !this._anims[this._currAnim] ) { + this._currAnim = 0; + } + + var currentAnimation = this._anims[this._currAnim]; + + this.fire("beforeBegin"); + if(delay > 0){ + setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay); + return this; + } + + if(currentAnimation){ + if(this._currAnim == 0){ + this.fire("handler", ["begin", this._currAnim]); + this.fire("onBegin", [this._currAnim]); + } + this.fire("onPlay", [this._currAnim]); + currentAnimation.play(null, gotoStart); + } + return this; + }, + + pause: function(){ + if( this._anims[this._currAnim] ) { + this._anims[this._currAnim].pause(); + this.fire("onPause", [this._currAnim]); + } + return this; + }, + + playPause: function(){ + if(this._anims.length == 0){ return this; } + if(this._currAnim == -1){ this._currAnim = 0; } + var currAnim = this._anims[this._currAnim]; + if( currAnim ) { + if( !currAnim._active || currAnim._paused ) { + this.play(); + } else { + this.pause(); + } + } + return this; + }, + + stop: function(){ + var currAnim = this._anims[this._currAnim]; + if(currAnim){ + currAnim.stop(); + this.fire("onStop", [this._currAnim]); + } + return currAnim; + }, + + // private methods + _playNext: function(){ + if( this._currAnim == -1 || this._anims.length == 0 ) { return this; } + this._currAnim++; + if( this._anims[this._currAnim] ){ + this._anims[this._currAnim].play(null, true); + } + return this; + } +}); + +dojo.lfx.combine = function(){ + var anims = arguments; + if(dojo.lang.isArray(arguments[0])){ + anims = arguments[0]; + } + if(anims.length == 1){ return anims[0]; } + return new dojo.lfx.Combine(anims); +} + +dojo.lfx.chain = function(){ + var anims = arguments; + if(dojo.lang.isArray(arguments[0])){ + anims = arguments[0]; + } + if(anims.length == 1){ return anims[0]; } + return new dojo.lfx.Chain(anims); +} diff --git a/source/web/scripts/ajax/src/lfx/__package__.js b/source/web/scripts/ajax/src/lfx/__package__.js new file mode 100644 index 0000000000..c9816d8cb8 --- /dev/null +++ b/source/web/scripts/ajax/src/lfx/__package__.js @@ -0,0 +1,5 @@ +dojo.kwCompoundRequire({ + browser: ["dojo.lfx.html"], + dashboard: ["dojo.lfx.html"] +}); +dojo.provide("dojo.lfx.*"); \ No newline at end of file diff --git a/source/web/scripts/ajax/src/lfx/extras.js b/source/web/scripts/ajax/src/lfx/extras.js new file mode 100644 index 0000000000..c5035b5fdf --- /dev/null +++ b/source/web/scripts/ajax/src/lfx/extras.js @@ -0,0 +1,109 @@ +dojo.provide("dojo.lfx.extras"); + +dojo.require("dojo.lfx.html"); +dojo.require("dojo.lfx.Animation"); + +dojo.lfx.html.fadeWipeIn = function(nodes, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var anim = dojo.lfx.combine( + dojo.lfx.fadeIn(nodes, duration, easing), + dojo.lfx.wipeIn(nodes, duration, easing) + ); + + if(callback){ + anim.connect("onEnd", function(){ + callback(nodes, anim); + }); + } + + return anim; +} + +dojo.lfx.html.fadeWipeOut = function(nodes, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var anim = dojo.lfx.combine( + dojo.lfx.fadeOut(nodes, duration, easing), + dojo.lfx.wipeOut(nodes, duration, easing) + ); + + if(callback){ + anim.connect("onEnd", function(){ + callback(nodes, anim); + }); + } + + return anim; +} + +dojo.lfx.html.scale = function(nodes, percentage, scaleContent, fromCenter, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var anims = []; + + dojo.lang.forEach(nodes, function(node){ + var outer = dojo.html.getMarginBox(node); + + var actualPct = percentage/100.0; + var props = [ + { property: "width", + start: outer.width, + end: outer.width * actualPct + }, + { property: "height", + start: outer.height, + end: outer.height * actualPct + }]; + + if(scaleContent){ + var fontSize = dojo.html.getStyle(node, 'font-size'); + var fontSizeType = null; + if(!fontSize){ + fontSize = parseFloat('100%'); + fontSizeType = '%'; + }else{ + dojo.lang.some(['em','px','%'], function(item, index, arr){ + if(fontSize.indexOf(item)>0){ + fontSize = parseFloat(fontSize); + fontSizeType = item; + return true; + } + }); + } + props.push({ + property: "font-size", + start: fontSize, + end: fontSize * actualPct, + units: fontSizeType }); + } + + if(fromCenter){ + var positioning = dojo.html.getStyle(node, "position"); + var originalTop = node.offsetTop; + var originalLeft = node.offsetLeft; + var endTop = ((outer.height * actualPct) - outer.height)/2; + var endLeft = ((outer.width * actualPct) - outer.width)/2; + props.push({ + property: "top", + start: originalTop, + end: (positioning == "absolute" ? originalTop - endTop : (-1*endTop)) + }); + props.push({ + property: "left", + start: originalLeft, + end: (positioning == "absolute" ? originalLeft - endLeft : (-1*endLeft)) + }); + } + + var anim = dojo.lfx.propertyAnimation(node, props, duration, easing); + if(callback){ + anim.connect("onEnd", function(){ + callback(node, anim); + }); + } + + anims.push(anim); + }); + + return dojo.lfx.combine(anims); +} + +dojo.lang.mixin(dojo.lfx, dojo.lfx.html); diff --git a/source/web/scripts/ajax/src/lfx/html.js b/source/web/scripts/ajax/src/lfx/html.js new file mode 100644 index 0000000000..b0a726aa4c --- /dev/null +++ b/source/web/scripts/ajax/src/lfx/html.js @@ -0,0 +1,608 @@ +dojo.provide("dojo.lfx.html"); + +dojo.require("dojo.lfx.Animation"); +dojo.require("dojo.lang.array"); +dojo.require("dojo.html.display"); +dojo.require("dojo.html.color"); +dojo.require("dojo.html.layout"); + +dojo.lfx.html._byId = function(nodes){ + if(!nodes){ return []; } + if(dojo.lang.isArrayLike(nodes)){ + if(!nodes.alreadyChecked){ + var n = []; + dojo.lang.forEach(nodes, function(node){ + n.push(dojo.byId(node)); + }); + n.alreadyChecked = true; + return n; + }else{ + return nodes; + } + }else{ + var n = []; + n.push(dojo.byId(nodes)); + n.alreadyChecked = true; + return n; + } +} + +dojo.lfx.html.propertyAnimation = function( /*DOMNode*/ nodes, + /*Array*/ propertyMap, + /*int*/ duration, + /*function*/ easing, + /*Object*/ handlers){ + nodes = dojo.lfx.html._byId(nodes); + + var targs = { + "propertyMap": propertyMap, + "nodes": nodes, + "duration": duration, + "easing": easing||dojo.lfx.easeDefault + }; + + var setEmUp = function(args){ + if(args.nodes.length==1){ + // FIXME: we're only supporting start-value filling when one node is + // passed + + var pm = args.propertyMap; + if(!dojo.lang.isArray(args.propertyMap)){ + // it's stupid to have to pack an array with a set of objects + // when you can just pass in an object list + var parr = []; + for(var pname in pm){ + pm[pname].property = pname; + parr.push(pm[pname]); + } + pm = args.propertyMap = parr; + } + dojo.lang.forEach(pm, function(prop){ + if(dj_undef("start", prop)){ + if(prop.property != "opacity"){ + prop.start = parseInt(dojo.html.getComputedStyle(args.nodes[0], prop.property)); + }else{ + prop.start = dojo.html.getOpacity(args.nodes[0]); + } + } + }); + } + } + + var coordsAsInts = function(coords){ + var cints = []; + dojo.lang.forEach(coords, function(c){ + cints.push(Math.round(c)); + }); + return cints; + } + + var setStyle = function(n, style){ + n = dojo.byId(n); + if(!n || !n.style){ return; } + for(var s in style){ + if(s == "opacity"){ + dojo.html.setOpacity(n, style[s]); + }else{ + n.style[s] = style[s]; + } + } + } + + var propLine = function(properties){ + this._properties = properties; + this.diffs = new Array(properties.length); + dojo.lang.forEach(properties, function(prop, i){ + // calculate the end - start to optimize a bit + if(dojo.lang.isFunction(prop.start)){ + prop.start = prop.start(prop, i); + } + if(dojo.lang.isFunction(prop.end)){ + prop.end = prop.end(prop, i); + } + if(dojo.lang.isArray(prop.start)){ + // don't loop through the arrays + this.diffs[i] = null; + }else if(prop.start instanceof dojo.graphics.color.Color){ + // save these so we don't have to call toRgb() every getValue() call + prop.startRgb = prop.start.toRgb(); + prop.endRgb = prop.end.toRgb(); + }else{ + this.diffs[i] = prop.end - prop.start; + } + }, this); + + this.getValue = function(n){ + var ret = {}; + dojo.lang.forEach(this._properties, function(prop, i){ + var value = null; + if(dojo.lang.isArray(prop.start)){ + // FIXME: what to do here? + }else if(prop.start instanceof dojo.graphics.color.Color){ + value = (prop.units||"rgb") + "("; + for(var j = 0 ; j < prop.startRgb.length ; j++){ + value += Math.round(((prop.endRgb[j] - prop.startRgb[j]) * n) + prop.startRgb[j]) + (j < prop.startRgb.length - 1 ? "," : ""); + } + value += ")"; + }else{ + value = ((this.diffs[i]) * n) + prop.start + (prop.property != "opacity" ? prop.units||"px" : ""); + } + ret[dojo.html.toCamelCase(prop.property)] = value; + }, this); + return ret; + } + } + + var anim = new dojo.lfx.Animation({ + beforeBegin: function(){ + setEmUp(targs); + anim.curve = new propLine(targs.propertyMap); + }, + onAnimate: function(propValues){ + dojo.lang.forEach(targs.nodes, function(node){ + setStyle(node, propValues); + }); + } + }, + targs.duration, + null, + targs.easing + ); + if(handlers){ + for(var x in handlers){ + if(dojo.lang.isFunction(handlers[x])){ + anim.connect(x, anim, handlers[x]); + } + } + } + + return anim; +} + +dojo.lfx.html._makeFadeable = function(nodes){ + var makeFade = function(node){ + if(dojo.render.html.ie){ + // only set the zoom if the "tickle" value would be the same as the + // default + if( (node.style.zoom.length == 0) && + (dojo.html.getStyle(node, "zoom") == "normal") ){ + // make sure the node "hasLayout" + // NOTE: this has been tested with larger and smaller user-set text + // sizes and works fine + node.style.zoom = "1"; + // node.style.zoom = "normal"; + } + // don't set the width to auto if it didn't already cascade that way. + // We don't want to f anyones designs + if( (node.style.width.length == 0) && + (dojo.html.getStyle(node, "width") == "auto") ){ + node.style.width = "auto"; + } + } + } + if(dojo.lang.isArrayLike(nodes)){ + dojo.lang.forEach(nodes, makeFade); + }else{ + makeFade(nodes); + } +} + +dojo.lfx.html.fade = function(nodes, values, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var props = { property: "opacity" }; + if(!dj_undef("start", values)){ + props.start = values.start; + }else{ + props.start = function(){ return dojo.html.getOpacity(nodes[0]); }; + } + + if(!dj_undef("end", values)){ + props.end = values.end; + }else{ + dojo.raise("dojo.lfx.html.fade needs an end value"); + } + + var anim = dojo.lfx.propertyAnimation(nodes, [ props ], duration, easing); + anim.connect("beforeBegin", function(){ + dojo.lfx.html._makeFadeable(nodes); + }); + if(callback){ + anim.connect("onEnd", function(){ callback(nodes, anim); }); + } + + return anim; +} + +dojo.lfx.html.fadeIn = function(nodes, duration, easing, callback){ + return dojo.lfx.html.fade(nodes, { end: 1 }, duration, easing, callback); +} + +dojo.lfx.html.fadeOut = function(nodes, duration, easing, callback){ + return dojo.lfx.html.fade(nodes, { end: 0 }, duration, easing, callback); +} + +dojo.lfx.html.fadeShow = function(nodes, duration, easing, callback){ + nodes=dojo.lfx.html._byId(nodes); + dojo.lang.forEach(nodes, function(node){ + dojo.html.setOpacity(node, 0.0); + }); + + var anim = dojo.lfx.html.fadeIn(nodes, duration, easing, callback); + anim.connect("beforeBegin", function(){ + if(dojo.lang.isArrayLike(nodes)){ + dojo.lang.forEach(nodes, dojo.html.show); + }else{ + dojo.html.show(nodes); + } + }); + + return anim; +} + +dojo.lfx.html.fadeHide = function(nodes, duration, easing, callback){ + var anim = dojo.lfx.html.fadeOut(nodes, duration, easing, function(){ + if(dojo.lang.isArrayLike(nodes)){ + dojo.lang.forEach(nodes, dojo.html.hide); + }else{ + dojo.html.hide(nodes); + } + if(callback){ callback(nodes, anim); } + }); + + return anim; +} + +dojo.lfx.html.wipeIn = function(nodes, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var anims = []; + + dojo.lang.forEach(nodes, function(node){ + var oprop = { overflow: null }; + + var anim = dojo.lfx.propertyAnimation(node, + { "height": { + start: 0, + end: function(){ return node.scrollHeight; } + } + }, + duration, + easing); + + anim.connect("beforeBegin", function(){ + oprop.overflow = dojo.html.getStyle(node, "overflow"); + with(node.style){ + if(oprop.overflow == "visible") { + overflow = "hidden"; + } + visibility = "visible"; + height = "0px"; + } + dojo.html.show(node); + }); + + anim.connect("onEnd", function(){ + with(node.style){ + overflow = oprop.overflow; + // height = "auto"; + height = ""; + visibility = "visible"; + } + if(callback){ callback(node, anim); } + }); + anims.push(anim); + }); + + return dojo.lfx.combine(anims); +} + +dojo.lfx.html.wipeOut = function(nodes, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var anims = []; + + dojo.lang.forEach(nodes, function(node){ + var oprop = { overflow: null }; + var anim = dojo.lfx.propertyAnimation(node, + { "height": { + start: function(){ return dojo.html.getContentBox(node).height; }, + end: 0 + } + }, + duration, + easing, + { + "beforeBegin": function(){ + oprop.overflow = dojo.html.getStyle(node, "overflow"); + if(oprop.overflow == "visible") { + node.style.overflow = "hidden"; + } + node.style.visibility = "visible"; + dojo.html.show(node); + }, + + "onEnd": function(){ + // dojo.html.hide(node); + with(node.style){ + overflow = oprop.overflow; + visibility = "hidden"; + height = ""; + } + if(callback){ callback(node, anim); } + } + } + ); + anims.push(anim); + }); + + return dojo.lfx.combine(anims); +} + +dojo.lfx.html.slideTo = function(nodes, coords, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var anims = []; + var compute = dojo.html.getComputedStyle; + + if(dojo.lang.isArray(coords)){ + dojo.deprecated('dojo.lfx.html.slideTo(node, array)', 'use dojo.lfx.html.slideTo(node, {top: value, left: value});', '0.5'); + coords = { top: coords[0], left: coords[1] }; + } + dojo.lang.forEach(nodes, function(node){ + var top = null; + var left = null; + + var init = (function(){ + var innerNode = node; + return function(){ + var pos = compute(innerNode, 'position'); + top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0); + left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0); + + if (!dojo.lang.inArray(['absolute', 'relative'], pos)) { + var ret = dojo.html.abs(innerNode, true); + dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;"); + top = ret.y; + left = ret.x; + } + } + })(); + init(); + + var anim = dojo.lfx.propertyAnimation(node, + { "top": { start: top, end: (coords.top||0) }, + "left": { start: left, end: (coords.left||0) } + }, + duration, + easing, + { "beforeBegin": init } + ); + + if(callback){ + anim.connect("onEnd", function(){ callback(nodes, anim); }); + } + + anims.push(anim); + }); + + return dojo.lfx.combine(anims); +} + +dojo.lfx.html.slideBy = function(nodes, coords, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var anims = []; + var compute = dojo.html.getComputedStyle; + + if(dojo.lang.isArray(coords)){ + dojo.deprecated('dojo.lfx.html.slideBy(node, array)', 'use dojo.lfx.html.slideBy(node, {top: value, left: value});', '0.5'); + coords = { top: coords[0], left: coords[1] }; + } + + dojo.lang.forEach(nodes, function(node){ + var top = null; + var left = null; + + var init = (function(){ + var innerNode = node; + return function(){ + var pos = compute(innerNode, 'position'); + top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0); + left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0); + + if (!dojo.lang.inArray(['absolute', 'relative'], pos)) { + var ret = dojo.html.abs(innerNode, true); + dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;"); + top = ret.y; + left = ret.x; + } + } + })(); + init(); + + var anim = dojo.lfx.propertyAnimation(node, + { + "top": { start: top, end: top+(coords.top||0) }, + "left": { start: left, end: left+(coords.left||0) } + }, + duration, + easing).connect("beforeBegin", init); + + if(callback){ + anim.connect("onEnd", function(){ callback(nodes, anim); }); + } + + anims.push(anim); + }); + + return dojo.lfx.combine(anims); +} + +dojo.lfx.html.explode = function(start, endNode, duration, easing, callback){ + var h = dojo.html; + start = dojo.byId(start); + endNode = dojo.byId(endNode); + var startCoords = h.toCoordinateObject(start, true); + var outline = document.createElement("div"); + h.copyStyle(outline, endNode); + with(outline.style){ + position = "absolute"; + display = "none"; + // border = "1px solid black"; + } + dojo.body().appendChild(outline); + + with(endNode.style){ + visibility = "hidden"; + display = "block"; + } + var endCoords = h.toCoordinateObject(endNode, true); + outline.style.backgroundColor = h.getStyle(endNode, "background-color").toLowerCase(); + with(endNode.style){ + display = "none"; + visibility = "visible"; + } + + var props = { opacity: { start: 0.5, end: 1.0 } }; + dojo.lang.forEach(["height", "width", "top", "left"], function(type){ + props[type] = { start: startCoords[type], end: endCoords[type] } + }); + + var anim = new dojo.lfx.propertyAnimation(outline, + props, + duration, + easing, + { + "beforeBegin": function(){ + h.setDisplay(outline, "block"); + }, + "onEnd": function(){ + h.setDisplay(endNode, "block"); + outline.parentNode.removeChild(outline); + } + } + ); + + if(callback){ + anim.connect("onEnd", function(){ callback(endNode, anim); }); + } + return anim; +} + +dojo.lfx.html.implode = function(startNode, end, duration, easing, callback){ + var h = dojo.html; + startNode = dojo.byId(startNode); + end = dojo.byId(end); + var startCoords = dojo.html.toCoordinateObject(startNode, true); + var endCoords = dojo.html.toCoordinateObject(end, true); + + var outline = document.createElement("div"); + dojo.html.copyStyle(outline, startNode); + dojo.html.setOpacity(outline, 0.3); + with(outline.style){ + position = "absolute"; + display = "none"; + backgroundColor = h.getStyle(startNode, "background-color").toLowerCase(); + } + dojo.body().appendChild(outline); + + var props = { opacity: { start: 1.0, end: 0.5 } }; + dojo.lang.forEach(["height", "width", "top", "left"], function(type){ + props[type] = { start: startCoords[type], end: endCoords[type] } + }); + + var anim = new dojo.lfx.propertyAnimation(outline, + props, + duration, + easing, + { + "beforeBegin": function(){ + dojo.html.hide(startNode); + dojo.html.show(outline); + }, + "onEnd": function(){ + outline.parentNode.removeChild(outline); + } + } + ); + + if(callback){ + anim.connect("onEnd", function(){ callback(startNode, anim); }); + } + return anim; +} + +dojo.lfx.html.highlight = function(nodes, startColor, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var anims = []; + + dojo.lang.forEach(nodes, function(node){ + var color = dojo.html.getBackgroundColor(node); + var bg = dojo.html.getStyle(node, "background-color").toLowerCase(); + var bgImage = dojo.html.getStyle(node, "background-image"); + var wasTransparent = (bg == "transparent" || bg == "rgba(0, 0, 0, 0)"); + while(color.length > 3) { color.pop(); } + + var rgb = new dojo.graphics.color.Color(startColor); + var endRgb = new dojo.graphics.color.Color(color); + + var anim = dojo.lfx.propertyAnimation(node, + { "background-color": { start: rgb, end: endRgb } }, + duration, + easing, + { + "beforeBegin": function(){ + if(bgImage){ + node.style.backgroundImage = "none"; + } + node.style.backgroundColor = "rgb(" + rgb.toRgb().join(",") + ")"; + }, + "onEnd": function(){ + if(bgImage){ + node.style.backgroundImage = bgImage; + } + if(wasTransparent){ + node.style.backgroundColor = "transparent"; + } + if(callback){ + callback(node, anim); + } + } + } + ); + + anims.push(anim); + }); + return dojo.lfx.combine(anims); +} + +dojo.lfx.html.unhighlight = function(nodes, endColor, duration, easing, callback){ + nodes = dojo.lfx.html._byId(nodes); + var anims = []; + + dojo.lang.forEach(nodes, function(node){ + var color = new dojo.graphics.color.Color(dojo.html.getBackgroundColor(node)); + var rgb = new dojo.graphics.color.Color(endColor); + + var bgImage = dojo.html.getStyle(node, "background-image"); + + var anim = dojo.lfx.propertyAnimation(node, + { "background-color": { start: color, end: rgb } }, + duration, + easing, + { + "beforeBegin": function(){ + if(bgImage){ + node.style.backgroundImage = "none"; + } + node.style.backgroundColor = "rgb(" + color.toRgb().join(",") + ")"; + }, + "onEnd": function(){ + if(callback){ + callback(node, anim); + } + } + } + ); + anims.push(anim); + }); + return dojo.lfx.combine(anims); +} + +dojo.lang.mixin(dojo.lfx, dojo.lfx.html); diff --git a/source/web/scripts/ajax/src/lfx/toggle.js b/source/web/scripts/ajax/src/lfx/toggle.js new file mode 100644 index 0000000000..b3fd32de34 --- /dev/null +++ b/source/web/scripts/ajax/src/lfx/toggle.js @@ -0,0 +1,44 @@ +dojo.provide("dojo.lfx.toggle"); +dojo.require("dojo.lfx.*"); + +dojo.lfx.toggle.plain = { + show: function(node, duration, easing, callback){ + dojo.html.show(node); + if(dojo.lang.isFunction(callback)){ callback(); } + }, + + hide: function(node, duration, easing, callback){ + dojo.html.hide(node); + if(dojo.lang.isFunction(callback)){ callback(); } + } +} + +dojo.lfx.toggle.fade = { + show: function(node, duration, easing, callback){ + dojo.lfx.fadeShow(node, duration, easing, callback).play(); + }, + + hide: function(node, duration, easing, callback){ + dojo.lfx.fadeHide(node, duration, easing, callback).play(); + } +} + +dojo.lfx.toggle.wipe = { + show: function(node, duration, easing, callback){ + dojo.lfx.wipeIn(node, duration, easing, callback).play(); + }, + + hide: function(node, duration, easing, callback){ + dojo.lfx.wipeOut(node, duration, easing, callback).play(); + } +} + +dojo.lfx.toggle.explode = { + show: function(node, duration, easing, callback, explodeSrc){ + dojo.lfx.explode(explodeSrc||{x:0,y:0,width:0,height:0}, node, duration, easing, callback).play(); + }, + + hide: function(node, duration, easing, callback, explodeSrc){ + dojo.lfx.implode(node, explodeSrc||{x:0,y:0,width:0,height:0}, duration, easing, callback).play(); + } +} diff --git a/source/web/scripts/ajax/src/loader.js b/source/web/scripts/ajax/src/loader.js new file mode 100644 index 0000000000..ba574c88e7 --- /dev/null +++ b/source/web/scripts/ajax/src/loader.js @@ -0,0 +1,464 @@ +/* + * loader.js - runs before the hostenv_*.js file. Contains all of the package loading methods. + */ + +//A semi-colon is at the start of the line because after doing a build, this function definition +//get compressed onto the same line as the last line in bootstrap1.js. That list line is just a +//curly bracket, and the browser complains about that syntax. The semicolon fixes it. Putting it +//here instead of at the end of bootstrap1.js, since it is more of an issue for this file, (using +//the closure), and bootstrap1.js could change in the future. +;(function(){ + //Additional properties for dojo.hostenv + var _addHostEnv = { + pkgFileName: "__package__", + + // for recursion protection + loading_modules_: {}, + loaded_modules_: {}, + addedToLoadingCount: [], + removedFromLoadingCount: [], + + inFlightCount: 0, + + // FIXME: it should be possible to pull module prefixes in from djConfig + modulePrefixes_: { + dojo: {name: "dojo", value: "src"} + }, + + + setModulePrefix: function(module, prefix){ + this.modulePrefixes_[module] = {name: module, value: prefix}; + }, + + getModulePrefix: function(module){ + var mp = this.modulePrefixes_; + if((mp[module])&&(mp[module]["name"])){ + return mp[module].value; + } + return module; + }, + + getTextStack: [], + loadUriStack: [], + loadedUris: [], + + //WARNING: This variable is referenced by packages outside of bootstrap: FloatingPane.js and undo/browser.js + post_load_: false, + + //Egad! Lots of test files push on this directly instead of using dojo.addOnLoad. + modulesLoadedListeners: [], + unloadListeners: [], + loadNotifying: false + }; + + //Add all of these properties to dojo.hostenv + for(var param in _addHostEnv){ + dojo.hostenv[param] = _addHostEnv[param]; + } +})(); + +/** + * Loads and interprets the script located at relpath, which is relative to the + * script root directory. If the script is found but its interpretation causes + * a runtime exception, that exception is not caught by us, so the caller will + * see it. We return a true value if and only if the script is found. + * + * For now, we do not have an implementation of a true search path. We + * consider only the single base script uri, as returned by getBaseScriptUri(). + * + * @param relpath A relative path to a script (no leading '/', and typically + * ending in '.js'). + * @param module A module whose existance to check for after loading a path. + * Can be used to determine success or failure of the load. + * @param cb a function to pass the result of evaluating the script (optional) + */ +dojo.hostenv.loadPath = function(relpath, module /*optional*/, cb /*optional*/){ + var uri; + if((relpath.charAt(0) == '/')||(relpath.match(/^\w+:/))){ + // dojo.raise("relpath '" + relpath + "'; must be relative"); + uri = relpath; + }else{ + uri = this.getBaseScriptUri() + relpath; + } + if(djConfig.cacheBust && dojo.render.html.capable){ + uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,""); + } + try{ + return ((!module) ? this.loadUri(uri, cb) : this.loadUriAndCheck(uri, module, cb)); + }catch(e){ + dojo.debug(e); + return false; + } +} + +/** + * Reads the contents of the URI, and evaluates the contents. + * Returns true if it succeeded. Returns false if the URI reading failed. + * Throws if the evaluation throws. + * The result of the eval is not available to the caller TODO: now it is; was this a deliberate restriction? + * + * @param uri a uri which points at the script to be loaded + * @param cb a function to process the result of evaluating the script as an expression (optional) + */ +dojo.hostenv.loadUri = function(uri, cb /*optional*/){ + if(this.loadedUris[uri]){ + return 1; + } + var contents = this.getText(uri, null, true); + if(contents == null){ return 0; } + this.loadedUris[uri] = true; + if(cb){ contents = '('+contents+')'; } + var value = dj_eval(contents); + if(cb){ + cb(value); + } + return 1; +} + +// FIXME: probably need to add logging to this method +dojo.hostenv.loadUriAndCheck = function(uri, module, cb){ + var ok = true; + try{ + ok = this.loadUri(uri, cb); + }catch(e){ + dojo.debug("failed loading ", uri, " with error: ", e); + } + return ((ok)&&(this.findModule(module, false))) ? true : false; +} + +dojo.loaded = function(){ } +dojo.unloaded = function(){ } + +dojo.hostenv.loaded = function(){ + this.loadNotifying = true; + this.post_load_ = true; + var mll = this.modulesLoadedListeners; + for(var x=0; x 1) { + dh.modulesLoadedListeners.push(function() { + obj[fcnName](); + }); + } + + //Added for xdomain loading. dojo.addOnLoad is used to + //indicate callbacks after doing some dojo.require() statements. + //In the xdomain case, if all the requires are loaded (after initial + //page load), then immediately call any listeners. + if(dh.post_load_ && dh.inFlightCount == 0 && !dh.loadNotifying){ + dh.callLoaded(); + } +} + +dojo.addOnUnload = function(obj, fcnName){ + var dh = dojo.hostenv; + if(arguments.length == 1){ + dh.unloadListeners.push(obj); + } else if(arguments.length > 1) { + dh.unloadListeners.push(function() { + obj[fcnName](); + }); + } +} + +dojo.hostenv.modulesLoaded = function(){ + if(this.post_load_){ return; } + if((this.loadUriStack.length==0)&&(this.getTextStack.length==0)){ + if(this.inFlightCount > 0){ + dojo.debug("files still in flight!"); + return; + } + dojo.hostenv.callLoaded(); + } +} + +dojo.hostenv.callLoaded = function(){ + if(typeof setTimeout == "object"){ + setTimeout("dojo.hostenv.loaded();", 0); + }else{ + dojo.hostenv.loaded(); + } +} + +dojo.hostenv.getModuleSymbols = function(modulename) { + var syms = modulename.split("."); + for(var i = syms.length - 1; i > 0; i--){ + var parentModule = syms.slice(0, i).join("."); + var parentModulePath = this.getModulePrefix(parentModule); + if(parentModulePath != parentModule){ + syms.splice(0, i, parentModulePath); + break; + } + } + return syms; +} + +//list of all defined namespaces +dojo._namespaces = {}; + +(function(){ +//list of namespaces being loaded, to prevent recursion +var loadingNamespaces = {}; + +//list of all namespaces that were needed, but didn't have the required file in the dojo/src/namespaces folder. +//This list ensures that a namespace will only be looked for once, rather than repeatedly trying to load the namespace descriptor file +var failedNamespaces = {}; + +//This returns a namespace with the given short name. If the namespace has not been loaded already, it tries to load it. +dojo.getNamespace = function(nsPrefix){ + if(!dojo._namespaces[nsPrefix] && !failedNamespaces[nsPrefix]){ + var req = dojo.require; + var nsFile = "dojo.namespaces."+nsPrefix; + if(!loadingNamespaces[nsFile]){ + loadingNamespaces[nsFile]=true; + req(nsFile, false, true); + loadingNamespaces[nsFile]=false; + if(!dojo._namespaces[nsPrefix]){ + failedNamespaces[nsPrefix] = true; //only look for a namespace once + } + } + } + + return dojo._namespaces[nsPrefix]; +}; +})(); + +/** +* loadModule("A.B") first checks to see if symbol A.B is defined. +* If it is, it is simply returned (nothing to do). +* +* If it is not defined, it will look for "A/B.js" in the script root directory, +* followed by "A.js". +* +* It throws if it cannot find a file to load, or if the symbol A.B is not +* defined after loading. +* +* It returns the object A.B. +* +* This does nothing about importing symbols into the current package. +* It is presumed that the caller will take care of that. For example, to import +* all symbols: +* +* with (dojo.hostenv.loadModule("A.B")) { +* ... +* } +* +* And to import just the leaf symbol: +* +* var B = dojo.hostenv.loadModule("A.B"); +* ... +* +* dj_load is an alias for dojo.hostenv.loadModule +*/ +dojo.hostenv._global_omit_module_check = false; +dojo.hostenv.loadModule = function(modulename, exact_only, omit_module_check){ + if(!modulename){ return; } + omit_module_check = this._global_omit_module_check || omit_module_check; + var module = this.findModule(modulename, false); + if(module){ + return module; + } + + // protect against infinite recursion from mutual dependencies + if(dj_undef(modulename, this.loading_modules_)){ + this.addedToLoadingCount.push(modulename); + } + this.loading_modules_[modulename] = 1; + + // convert periods to slashes + var relpath = modulename.replace(/\./g, '/') + '.js'; + + var nsyms = modulename.split("."); + if(djConfig.autoLoadNamespace){ dojo.getNamespace(nsyms[0]); } + + var syms = this.getModuleSymbols(modulename); + var startedRelative = ((syms[0].charAt(0) != '/')&&(!syms[0].match(/^\w+:/))); + var last = syms[syms.length - 1]; + // figure out if we're looking for a full package, if so, we want to do + // things slightly diffrently + if(last=="*"){ + modulename = (nsyms.slice(0, -1)).join('.'); + + while(syms.length){ + syms.pop(); + syms.push(this.pkgFileName); + relpath = syms.join("/") + '.js'; + if(startedRelative && (relpath.charAt(0)=="/")){ + relpath = relpath.slice(1); + } + ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null)); + if(ok){ break; } + syms.pop(); + } + }else{ + relpath = syms.join("/") + '.js'; + modulename = nsyms.join('.'); + var ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null)); + if((!ok)&&(!exact_only)){ + syms.pop(); + while(syms.length){ + relpath = syms.join('/') + '.js'; + ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null)); + if(ok){ break; } + syms.pop(); + relpath = syms.join('/') + '/'+this.pkgFileName+'.js'; + if(startedRelative && (relpath.charAt(0)=="/")){ + relpath = relpath.slice(1); + } + ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null)); + if(ok){ break; } + } + } + + if((!ok)&&(!omit_module_check)){ + dojo.raise("Could not load '" + modulename + "'; last tried '" + relpath + "'"); + } + } + + // check that the symbol was defined + //Don't bother if we're doing xdomain (asynchronous) loading. + if(!omit_module_check && !this["isXDomain"]){ + // pass in false so we can give better error + module = this.findModule(modulename, false); + if(!module){ + dojo.raise("symbol '" + modulename + "' is not defined after loading '" + relpath + "'"); + } + } + + return module; +} + +/** +* startPackage("A.B") follows the path, and at each level creates a new empty +* object or uses what already exists. It returns the result. +*/ +dojo.hostenv.startPackage = function(packname){ + //Make sure we have a string. + var fullPkgName = (new String(packname)).toString(); + var strippedPkgName = fullPkgName; + + var syms = packname.split(/\./); + if(syms[syms.length-1]=="*"){ + syms.pop(); + strippedPkgName = syms.join("."); + } + var evaledPkg = dojo.evalObjPath(strippedPkgName.toString(), true); + this.loaded_modules_[fullPkgName] = evaledPkg; + this.loaded_modules_[strippedPkgName] = evaledPkg; + + return evaledPkg; +} + +/** + * findModule("A.B") returns the object A.B if it exists, otherwise null. + * @param modulename A string like 'A.B'. + * @param must_exist Optional, defualt false. throw instead of returning null + * if the module does not currently exist. + */ +dojo.hostenv.findModule = function(modulename, must_exist){ + var lmn = new String(modulename).toString(); + + if(this.loaded_modules_[lmn]){ + return this.loaded_modules_[lmn]; + } + + if(must_exist){ + dojo.raise("no loaded module named '" + modulename + "'"); + } + return null; +} + +//Start of old bootstrap2: + +/* + * This method taks a "map" of arrays which one can use to optionally load dojo + * modules. The map is indexed by the possible dojo.hostenv.name_ values, with + * two additional values: "default" and "common". The items in the "default" + * array will be loaded if none of the other items have been choosen based on + * the hostenv.name_ item. The items in the "common" array will _always_ be + * loaded, regardless of which list is chosen. Here's how it's normally + * called: + * + * dojo.kwCompoundRequire({ + * browser: [ + * ["foo.bar.baz", true, true], // an example that passes multiple args to loadModule() + * "foo.sample.*", + * "foo.test, + * ], + * default: [ "foo.sample.*" ], + * common: [ "really.important.module.*" ] + * }); + */ +dojo.kwCompoundRequire = function(modMap){ + var common = modMap["common"]||[]; + var result = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]); + + for(var x=0; x 0){ + output.push("depends: ["); + for(var i = 0; i < deps.length; i++){ + if(i > 0){ + output.push(",\n"); + } + output.push("[" + deps[i] + "]"); + } + output.push("],"); + } + + //Add the contents of the file inside a function. + //Pass in dojo as an argument to the function to help with + //allowing multiple versions of dojo in a page. + output.push("\ndefinePackage: function(dojo){"); + output.push(contents); + output.push("\n}});"); + + return output.join(""); +} + +dojo.hostenv.loadPath = function(relpath, module /*optional*/, cb /*optional*/){ + //Only do getBaseScriptUri if path does not start with a URL with a protocol. + //If there is a colon before the first / then, we have a URL with a protocol. + var colonIndex = relpath.indexOf(":"); + var slashIndex = relpath.indexOf("/"); + var uri; + var currentIsXDomain = false; + if(colonIndex > 0 && colonIndex < slashIndex){ + uri = relpath; + this.isXDomain = currentIsXDomain = true; + }else{ + uri = this.getBaseScriptUri() + relpath; + + //Is ithe base script URI-based URL a cross domain URL? + colonIndex = uri.indexOf(":"); + slashIndex = uri.indexOf("/"); + if(colonIndex > 0 && colonIndex < slashIndex && (!location.host || uri.indexOf("http://" + location.host) != 0)){ + this.isXDomain = currentIsXDomain = true; + } + } + + if(djConfig.cacheBust && dojo.render.html.capable) { uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,""); } + try{ + return ((!module || this.isXDomain) ? this.loadUri(uri, cb, currentIsXDomain, module) : this.loadUriAndCheck(uri, module, cb)); + }catch(e){ + dojo.debug(e); + return false; + } +} + +//Overriding loadUri for now. Wanted to override getText(), but it is used by +//the widget code in too many, synchronous ways right now. This means the xd stuff +//is not suitable for widgets yet. +dojo.hostenv.loadUri = function(uri, cb, currentIsXDomain, module){ + if(this.loadedUris[uri]){ + return 1; + } + + //Add the module (package) to the list of modules. + if(this.isXDomain){ + //Curious: is this array going to get whacked with multiple access since scripts + //load asynchronously and may be accessing the array at the same time? + //JS is single-threaded supposedly, so it should be ok. And we don't need + //a precise ordering. + this.xdOrderedReqs.push(module); + + //Add to waiting packages. + //If this is a __package__.js file, then this must be + //a package.* request (since xdomain can only work with the first + //path in a package search list. However, .* module names are not + //passed to this function, so do an adjustment here. + if(uri.indexOf("__package__") != -1){ + module += ".*"; + } + + this.xdInFlight[module] = true; + + //Increment inFlightCount + //This will stop the modulesLoaded from firing all the way. + this.inFlightCount++; + + //Start timer + if(!this.xdTimer){ + this.xdTimer = setInterval("dojo.hostenv.watchInFlightXDomain();", 100); + } + this.xdStartTime = (new Date()).getTime(); + } + + if (currentIsXDomain){ + //Fix name to be a .xd.fileextension name. + var lastIndex = uri.lastIndexOf('.'); + if(lastIndex <= 0){ + lastIndex = uri.length - 1; + } + + var xdUri = uri.substring(0, lastIndex) + ".xd"; + if(lastIndex != uri.length - 1){ + xdUri += uri.substring(lastIndex, uri.length); + } + + //Add to script src + var element = document.createElement("script"); + element.type = "text/javascript"; + element.src = xdUri; + if(!this.headElement){ + this.headElement = document.getElementsByTagName("head")[0]; + } + this.headElement.appendChild(element); + }else{ + var contents = this.getText(uri, null, true); + if(contents == null){ return 0; } + + if(this.isXDomain){ + var pkg = this.createXdPackage(contents); + dj_eval(pkg); + }else{ + if(cb){ contents = '('+contents+')'; } + var value = dj_eval(contents); + if(cb){ + cb(value); + } + } + } + + //These steps are done in the non-xd loader version of this function. + //Maintain these steps to fit in with the existing system. + this.loadedUris[uri] = true; + return 1; +} + +dojo.hostenv.packageLoaded = function(pkg){ + var deps = pkg.depends; + var requireList = null; + var requireAfterList = null; + var provideList = []; + if(deps && deps.length > 0){ + var dep = null; + var insertHint = 0; + var attachedPackage = false; + for(var i = 0; i < deps.length; i++){ + dep = deps[i]; + + //Look for specific dependency indicators. + if (dep[0] == "provide" || dep[0] == "hostenv.moduleLoaded"){ + provideList.push(dep[1]); + }else{ + if(!requireList){ + requireList = []; + } + if(!requireAfterList){ + requireAfterList = []; + } + + var unpackedDeps = this.unpackXdDependency(dep); + if(unpackedDeps.requires){ + requireList = requireList.concat(unpackedDeps.requires); + } + if(unpackedDeps.requiresAfter){ + requireAfterList = requireAfterList.concat(unpackedDeps.requiresAfter); + } + } + + //Call the dependency indicator to allow for the normal dojo setup. + //Only allow for one dot reference, for the hostenv.* type calls. + var depType = dep[0]; + var objPath = depType.split("."); + if(objPath.length == 2){ + dojo[objPath[0]][objPath[1]].apply(dojo[objPath[0]], dep.slice(1)); + }else{ + dojo[depType].apply(dojo, dep.slice(1)); + } + } + + //Save off the package contents for definition later. + var contentIndex = this.xdContents.push({content: pkg.definePackage, isDefined: false}) - 1; + + //Add provide/requires to dependency map. + for(var i = 0; i < provideList.length; i++){ + this.xdDepMap[provideList[i]] = { requires: requireList, requiresAfter: requireAfterList, contentIndex: contentIndex }; + } + + //Now update the inflight status for any provided packages in this loaded package. + //Do this at the very end (in a *separate* for loop) to avoid shutting down the + //inflight timer check too soon. + for(var i = 0; i < provideList.length; i++){ + this.xdInFlight[provideList[i]] = false; + } + } +} + +//This is a bit brittle: it has to know about the dojo methods that deal with dependencies +//It would be ideal to intercept the actual methods and do something fancy at that point, +//but I have concern about knowing which provide to match to the dependency in that case, +//since scripts can load whenever they want, and trigger new calls to dojo.hostenv.packageLoaded(). +dojo.hostenv.unpackXdDependency = function(dep){ + //Extract the dependency(ies). + var newDeps = null; + var newAfterDeps = null; + switch(dep[0]){ + case "requireIf": + case "requireAfterIf": + case "conditionalRequire": + //First arg (dep[1]) is the test. Depedency is dep[2]. + if((dep[1] === true)||(dep[1]=="common")||(dep[1] && dojo.render[dep[1]].capable)){ + newDeps = [{name: dep[2], content: null}]; + } + break; + case "requireAll": + //the arguments are an array, each element a call to require. + //Get rid of first item, which is "requireAll". + dep.shift(); + newDeps = dep; + dojo.hostenv.flattenRequireArray(newDeps); + break; + case "kwCompoundRequire": + case "hostenv.conditionalLoadModule": + var modMap = dep[1]; + var common = modMap["common"]||[]; + var newDeps = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]); + dojo.hostenv.flattenRequireArray(newDeps); + break; + case "require": + case "requireAfter": + case "hostenv.loadModule": + //Just worry about dep[1] + newDeps = [{name: dep[1], content: null}]; + break; + } + + //The requireAfterIf or requireAfter needs to be evaluated after the current package is evaluated. + if(dep[0] == "requireAfterIf"){ + newAfterDeps = newDeps; + newDeps = null; + } + return {requires: newDeps, requiresAfter: newAfterDeps}; +} + +//Walks the requires and evaluates package contents in +//the right order. +dojo.hostenv.xdWalkReqs = function(){ + var reqChain = null; + var req; + for(var i = 0; i < this.xdOrderedReqs.length; i++){ + req = this.xdOrderedReqs[i]; + if(this.xdDepMap[req]){ + reqChain = [req]; + reqChain[req] = true; //Allow for fast lookup of the req in the array + this.xdEvalReqs(reqChain); + } + } +} + +//Trace down any requires. +dojo.hostenv.xdTraceReqs = function(reqs, reqChain){ + if(reqs && reqs.length > 0){ + var nextReq; + for(var i = 0; i < reqs.length; i++){ + nextReq = reqs[i].name; + if(nextReq && !reqChain[nextReq]){ + //New req depedency. Follow it down. + reqChain.push(nextReq); + reqChain[nextReq] = true; + this.xdEvalReqs(reqChain); + } + } + } +} + +//Do a depth first, breadth second search and eval or reqs. +dojo.hostenv.xdEvalReqs = function(reqChain){ + if(reqChain.length > 0){ + var req = reqChain[reqChain.length - 1]; + var pkg = this.xdDepMap[req]; + if(pkg){ + //Trace down any requires for this package. + this.xdTraceReqs(pkg.requires, reqChain); + + //Evaluate the package. + var contents = this.xdContents[pkg.contentIndex]; + if(!contents.isDefined){ + //Evaluate the package to bring it into being. + //Pass dojo in so that later, to support multiple versions of dojo + //in a page, we can pass which version of dojo to use. + contents.content(dojo); + contents.isDefined = true; + } + this.xdDepMap[req] = null; + + //Trace down any requireAfters for this package.. + this.xdTraceReqs(pkg.requiresAfter, reqChain); + } + + //Done with that require. Remove it and go to the next one. + reqChain.pop(); + this.xdEvalReqs(reqChain); + } +} + +dojo.hostenv.clearXdInterval = function(){ + clearInterval(this.xdTimer); + this.xdTimer = 0; +} + +dojo.hostenv.watchInFlightXDomain = function(){ + //Make sure we haven't waited timed out. + var waitInterval = (djConfig.xdWaitSeconds || 30) * 1000; + + if(this.xdStartTime + waitInterval < (new Date()).getTime()){ + this.clearXdInterval(); + var noLoads = ""; + for(var param in this.xdInFlight){ + if(this.xdInFlight[param]){ + noLoads += param + " "; + } + } + dojo.raise("Could not load cross-domain packages: " + noLoads); + } + + //If any are true, then still waiting. + //Come back later. + for(var param in this.xdInFlight){ + if(this.xdInFlight[param]){ + return; + } + } + + //All done loading. Clean up and notify that we are loaded. + this.clearXdInterval(); + + this.xdWalkReqs(); + + //Evaluate any packages that were not evaled before. + //This normally shouldn't happen with proper dojo.provide and dojo.require + //usage, but providing it just in case. Note that these may not be executed + //in the original order that the developer intended. + //Pass dojo in so that later, to support multiple versions of dojo + //in a page, we can pass which version of dojo to use. + for(var i = 0; i < this.xdContents.length; i++){ + var current = this.xdContents[i]; + if(current.content && !current.isDefined){ + current.content(dojo); + } + } + + //Clean up for the next round of xd loading. + this.resetXd(); + + //Clear inflight count so we will finally do finish work. + this.inFlightCount = 0; + this.callLoaded(); +} + +dojo.hostenv.flattenRequireArray = function(target){ + //Each result could be an array of 3 elements (the 3 arguments to dojo.require). + //We only need the first one. + if(target){ + for(var i = 0; i < target.length; i++){ + if(target[i] instanceof Array){ + target[i] = {name: target[i][0], content: null}; + }else{ + target[i] = {name: target[i], content: null}; + } + } + } +} diff --git a/source/web/scripts/ajax/src/logging/Logger.js b/source/web/scripts/ajax/src/logging/Logger.js new file mode 100644 index 0000000000..6e96f8ce7c --- /dev/null +++ b/source/web/scripts/ajax/src/logging/Logger.js @@ -0,0 +1,397 @@ +/* This is the dojo logging facility, which is imported from nWidgets + (written by Alex Russell, CLA on file), which is patterned on the + Python logging module, which in turn has been heavily influenced by + log4j (execpt with some more pythonic choices, which we adopt as well). + + While the dojo logging facilities do provide a set of familiar + interfaces, many of the details are changed to reflect the constraints + of the browser environment. Mainly, file and syslog-style logging + facilites are not provided, with HTTP POST and GET requests being the + only ways of getting data from the browser back to a server. Minimal + support for this (and XML serialization of logs) is provided, but may + not be of practical use in a deployment environment. + + The Dojo logging classes are agnostic of any environment, and while + default loggers are provided for browser-based interpreter + environments, this file and the classes it define are explicitly + designed to be portable to command-line interpreters and other + ECMA-262v3 envrionments. + + the logger needs to accomidate: + log "levels" + type identifiers + file? + message + tic/toc? + + The logger should ALWAYS record: + time/date logged + message + type + level +*/ +// TODO: conver documentation to javadoc style once we confirm that is our choice +// TODO: define DTD for XML-formatted log messages +// TODO: write XML Formatter class +// TODO: write HTTP Handler which uses POST to send log lines/sections + +// Filename: LogCore.js +// Purpose: a common logging infrastructure for dojo +// Classes: dojo.logging, dojo.logging.Logger, dojo.logging.Record, dojo.logging.LogFilter +// Global Objects: dojo.logging +// Dependencies: none + +dojo.provide("dojo.logging.Logger"); +dojo.provide("dojo.log"); +dojo.require("dojo.lang"); + +/* + A simple data structure class that stores information for and about + a logged event. Objects of this type are created automatically when + an event is logged and are the internal format in which information + about log events is kept. +*/ + +dojo.logging.Record = function(lvl, msg){ + this.level = lvl; + this.message = msg; + this.time = new Date(); + // FIXME: what other information can we receive/discover here? +} + +// an empty parent (abstract) class which concrete filters should inherit from. +dojo.logging.LogFilter = function(loggerChain){ + this.passChain = loggerChain || ""; + this.filter = function(record){ + // FIXME: need to figure out a way to enforce the loggerChain + // restriction + return true; // pass all records + } +} + +dojo.logging.Logger = function(){ + this.cutOffLevel = 0; + this.propagate = true; + this.parent = null; + // storage for dojo.logging.Record objects seen and accepted by this logger + this.data = []; + this.filters = []; + this.handlers = []; +} + +dojo.lang.extend(dojo.logging.Logger, { + argsToArr: function(args){ + // utility function, reproduced from __util__ here to remove dependency + var ret = []; + for(var x=0; x= this.cutOffLevel; + }, + + getEffectiveLevel: function(){ + if((this.cutOffLevel==0)&&(this.parent)){ + return this.parent.getEffectiveLevel(); + } + return this.cutOffLevel; + }, + + addFilter: function(flt){ + this.filters.push(flt); + return this.filters.length-1; + }, + + removeFilterByIndex: function(fltIndex){ + if(this.filters[fltIndex]){ + delete this.filters[fltIndex]; + return true; + } + return false; + }, + + removeFilter: function(fltRef){ + for(var x=0; x=this.cutOffLevel)){ + this.parent.log(lvl, msg); + return false; + } + // FIXME: need to call logging providers here! + this.handle(new dojo.logging.Record(lvl, msg)); + return true; + }, + + // logger helpers + debug:function(msg){ + return this.logType("DEBUG", this.argsToArr(arguments)); + }, + + info: function(msg){ + return this.logType("INFO", this.argsToArr(arguments)); + }, + + warning: function(msg){ + return this.logType("WARNING", this.argsToArr(arguments)); + }, + + error: function(msg){ + return this.logType("ERROR", this.argsToArr(arguments)); + }, + + critical: function(msg){ + return this.logType("CRITICAL", this.argsToArr(arguments)); + }, + + exception: function(msg, e, squelch){ + // FIXME: this needs to be modified to put the exception in the msg + // if we're on Moz, we can get the following from the exception object: + // lineNumber + // message + // fileName + // stack + // name + // on IE, we get: + // name + // message (from MDA?) + // number + // description (same as message!) + if(e){ + var eparts = [e.name, (e.description||e.message)]; + if(e.fileName){ + eparts.push(e.fileName); + eparts.push("line "+e.lineNumber); + // eparts.push(e.stack); + } + msg += " "+eparts.join(" : "); + } + + this.logType("ERROR", msg); + if(!squelch){ + throw e; + } + }, + + logType: function(type, args){ + var na = [dojo.logging.log.getLevel(type)]; + if(dojo.lang.isArray(args)){ + na = na.concat(args); + }else if((typeof args == "object")&&(args["length"])){ + na = na.concat(this.argsToArr(args)); + /* for(var x=0; x=this.cutOffLevel)){ + this.emit(record); + } +} + +dojo.logging.LogHandler.prototype.emit = function(record){ + // do whatever is necessaray to actually log the record + dojo.unimplemented("emit"); +} + +// set aliases since we don't want to inherit from dojo.logging.Logger +void(function(){ // begin globals protection closure + var names = [ + "setLevel", "addFilter", "removeFilterByIndex", "removeFilter", + "removeAllFilters", "filter" + ]; + var tgt = dojo.logging.LogHandler.prototype; + var src = dojo.logging.Logger.prototype; + for(var x=0; xthis.numRecords){ + this.data.shift(); + } + } +} + +dojo.logging.logQueueHandler = new dojo.logging.MemoryLogHandler(0,50,0,10000); +// actual logging event handler +dojo.logging.logQueueHandler.emit = function(record){ + if (!djConfig.isDebug) { return; } + // we should probably abstract this in the future + var logStr = String(dojo.log.getLevelName(record.level)+": "+record.time.toLocaleTimeString())+": "+record.message; + if(!dj_undef("println", dojo.hostenv)){ + dojo.hostenv.println(logStr); + } + this.data.push(record); + if(this.numRecords != -1){ + while(this.data.length>this.numRecords){ + this.data.shift(); + } + } +} + +dojo.logging.log.addHandler(dojo.logging.logQueueHandler); +dojo.log = dojo.logging.log; diff --git a/source/web/scripts/ajax/src/logging/__package__.js b/source/web/scripts/ajax/src/logging/__package__.js new file mode 100644 index 0000000000..fab96c697c --- /dev/null +++ b/source/web/scripts/ajax/src/logging/__package__.js @@ -0,0 +1,5 @@ +dojo.kwCompoundRequire({ + common: ["dojo.logging.Logger", false, false], + rhino: ["dojo.logging.RhinoLogger"] +}); +dojo.provide("dojo.logging.*"); diff --git a/source/web/scripts/ajax/src/math.js b/source/web/scripts/ajax/src/math.js new file mode 100644 index 0000000000..1a660fe004 --- /dev/null +++ b/source/web/scripts/ajax/src/math.js @@ -0,0 +1,124 @@ +dojo.provide("dojo.math"); + +dojo.math.degToRad = function (x) { return (x*Math.PI) / 180; } +dojo.math.radToDeg = function (x) { return (x*180) / Math.PI; } + +dojo.math.factorial = function (n) { + if(n<1){ return 0; } + var retVal = 1; + for(var i=1;i<=n;i++){ retVal *= i; } + return retVal; +} + +//The number of ways of obtaining an ordered subset of k elements from a set of n elements +dojo.math.permutations = function (n,k) { + if(n==0 || k==0) return 1; + return (dojo.math.factorial(n) / dojo.math.factorial(n-k)); +} + +//The number of ways of picking n unordered outcomes from r possibilities +dojo.math.combinations = function (n,r) { + if(n==0 || r==0) return 1; + return (dojo.math.factorial(n) / (dojo.math.factorial(n-r) * dojo.math.factorial(r))); +} + +dojo.math.bernstein = function (t,n,i) { + return (dojo.math.combinations(n,i) * Math.pow(t,i) * Math.pow(1-t,n-i)); +} + +/** + * Returns random numbers with a Gaussian distribution, with the mean set at + * 0 and the variance set at 1. + * + * @return A random number from a Gaussian distribution + */ +dojo.math.gaussianRandom = function () { + var k = 2; + do { + var i = 2 * Math.random() - 1; + var j = 2 * Math.random() - 1; + k = i * i + j * j; + } while (k >= 1); + k = Math.sqrt((-2 * Math.log(k)) / k); + return i * k; +} + +/** + * Calculates the mean of an Array of numbers. + * + * @return The mean of the numbers in the Array + */ +dojo.math.mean = function () { + var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments; + var mean = 0; + for (var i = 0; i < array.length; i++) { mean += array[i]; } + return mean / array.length; +} + +/** + * Extends Math.round by adding a second argument specifying the number of + * decimal places to round to. + * + * @param number The number to round + * @param places The number of decimal places to round to + * @return The rounded number + */ +// TODO: add support for significant figures +dojo.math.round = function (number, places) { + if (!places) { var shift = 1; } + else { var shift = Math.pow(10, places); } + return Math.round(number * shift) / shift; +} + +/** + * Calculates the standard deviation of an Array of numbers + * + * @return The standard deviation of the numbers + */ +dojo.math.sd = function () { + var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments; + return Math.sqrt(dojo.math.variance(array)); +} + +/** + * Calculates the variance of an Array of numbers + * + * @return The variance of the numbers + */ +dojo.math.variance = function () { + var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments; + var mean = 0, squares = 0; + for (var i = 0; i < array.length; i++) { + mean += array[i]; + squares += Math.pow(array[i], 2); + } + return (squares / array.length) + - Math.pow(mean / array.length, 2); +} + +/** + * Like range() in python +**/ +dojo.math.range = function(a, b, step) { + if(arguments.length < 2) { + b = a; + a = 0; + } + if(arguments.length < 3) { + step = 1; + } + + var range = []; + if(step > 0) { + for(var i = a; i < b; i += step) { + range.push(i); + } + } else if(step < 0) { + for(var i = a; i > b; i += step) { + range.push(i); + } + } else { + throw new Error("dojo.math.range: step must be non-zero"); + } + return range; +} diff --git a/source/web/scripts/ajax/src/math/Math.js b/source/web/scripts/ajax/src/math/Math.js new file mode 100644 index 0000000000..f1e87f5e07 --- /dev/null +++ b/source/web/scripts/ajax/src/math/Math.js @@ -0,0 +1,12 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.deprecated("dojo.math.Math", "include dojo.math instead", "0.4"); +dojo.require("dojo.math"); diff --git a/source/web/scripts/ajax/src/math/__package__.js b/source/web/scripts/ajax/src/math/__package__.js new file mode 100644 index 0000000000..42c1548be7 --- /dev/null +++ b/source/web/scripts/ajax/src/math/__package__.js @@ -0,0 +1,8 @@ +dojo.kwCompoundRequire({ + common: [ + ["dojo.math", false, false], + ["dojo.math.curves", false, false], + ["dojo.math.points", false, false] + ] +}); +dojo.provide("dojo.math.*"); diff --git a/source/web/scripts/ajax/src/math/curves.js b/source/web/scripts/ajax/src/math/curves.js new file mode 100644 index 0000000000..80f3d2ee58 --- /dev/null +++ b/source/web/scripts/ajax/src/math/curves.js @@ -0,0 +1,212 @@ +dojo.provide("dojo.math.curves"); + +dojo.require("dojo.math"); + +/* Curves from Dan's 13th lib stuff. + * See: http://pupius.co.uk/js/Toolkit.Drawing.js + * http://pupius.co.uk/dump/dojo/Dojo.Math.js + */ + +dojo.math.curves = { + //Creates a straight line object + Line: function(start, end) { + this.start = start; + this.end = end; + this.dimensions = start.length; + + for(var i = 0; i < start.length; i++) { + start[i] = Number(start[i]); + } + + for(var i = 0; i < end.length; i++) { + end[i] = Number(end[i]); + } + + //simple function to find point on an n-dimensional, straight line + this.getValue = function(n) { + var retVal = new Array(this.dimensions); + for(var i=0;i= 1) return this.p[this.p.length-1]; // if step>=1 we must be at the end of the curve + if(step <= 0) return this.p[0]; // if step<=0 we must be at the start of the curve + var retVal = new Array(this.p[0].length); + for(var k=0;j= this.p.length) i1 = this.p.length-1; + var i2 = node+2; if(i2 >= this.p.length) i2 = this.p.length-1; + + var u = progress; + var u2 = progress*progress; + var u3 = progress*progress*progress; + + var retVal = new Array(this.p[0].length); + for(var k=0;k 2D point for center of arc + // radius => scalar quantity for radius of arc + // start => to define an arc specify start angle (default: 0) + // end => to define an arc specify start angle + CenteredArc : function(center, radius, start, end) { + this.center = center; + this.radius = radius; + this.start = start || 0; + this.end = end; + + this.getValue = function(n) { + var retVal = new Array(2); + var theta = dojo.math.degToRad(this.start+((this.end-this.start)*n)); + + retVal[0] = this.center[0] + this.radius*Math.sin(theta); + retVal[1] = this.center[1] - this.radius*Math.cos(theta); + + return retVal; + } + + return this; + }, + + // Special case of Arc (start = 0, end = 360) + Circle : function(center, radius) { + dojo.math.curves.CenteredArc.call(this, center, radius, 0, 360); + return this; + }, + + Path : function() { + var curves = []; + var weights = []; + var ranges = []; + var totalWeight = 0; + + this.add = function(curve, weight) { + if( weight < 0 ) { dojo.raise("dojo.math.curves.Path.add: weight cannot be less than 0"); } + curves.push(curve); + weights.push(weight); + totalWeight += weight; + computeRanges(); + } + + this.remove = function(curve) { + for(var i = 0; i < curves.length; i++) { + if( curves[i] == curve ) { + curves.splice(i, 1); + totalWeight -= weights.splice(i, 1)[0]; + break; + } + } + computeRanges(); + } + + this.removeAll = function() { + curves = []; + weights = []; + totalWeight = 0; + } + + this.getValue = function(n) { + var found = false, value = 0; + for(var i = 0; i < ranges.length; i++) { + var r = ranges[i]; + //w(r.join(" ... ")); + if( n >= r[0] && n < r[1] ) { + var subN = (n - r[0]) / r[2]; + value = curves[i].getValue(subN); + found = true; + break; + } + } + + // FIXME: Do we want to assume we're at the end? + if( !found ) { + value = curves[curves.length-1].getValue(1); + } + + for(var j = 0; j < i; j++) { + value = dojo.math.points.translate(value, curves[j].getValue(1)); + } + return value; + } + + function computeRanges() { + var start = 0; + for(var i = 0; i < weights.length; i++) { + var end = start + weights[i] / totalWeight; + var len = end - start; + ranges[i] = [start, end, len]; + start = end; + } + } + + return this; + } +}; diff --git a/source/web/scripts/ajax/src/math/matrix.js b/source/web/scripts/ajax/src/math/matrix.js new file mode 100644 index 0000000000..b72f800daf --- /dev/null +++ b/source/web/scripts/ajax/src/math/matrix.js @@ -0,0 +1,295 @@ +dojo.provide("dojo.math.matrix"); + +// +// some of this code is based on +// http://www.mkaz.com/math/MatrixCalculator.java +// (published under a BSD Open Source License) +// +// the rest is from my vague memory of matricies in school [cal] +// +// the copying of arguments is a little excessive, and could be trimmed back in +// the case where a function doesn't modify them at all (but some do!) +// + +dojo.math.matrix.iDF = 0; + +dojo.math.matrix.multiply = function(a, b){ + + a = dojo.math.matrix.copy(a); + b = dojo.math.matrix.copy(b); + + var ax = a[0].length; + var ay = a.length; + var bx = b[0].length; + var by = b.length; + + if (ax != by){ + dojo.debug("Can't multiply matricies of sizes "+ax+','+ay+' and '+bx+','+by); + return [[0]]; + } + + var c = []; + + for(var k=0; k= tms){ + + // check if switched all rows + dojo.math.matrix.iDF = 0; + stop_loop = 1; + }else{ + for (var c = 0; c < tms; c++) { + temp = m[col][c]; + m[col][c] = m[col + v][c]; // switch rows + m[col + v][c] = temp; + } + v++; // count row switchs + dojo.math.matrix.iDF *= -1; // each switch changes determinant factor + } + } + + if (m[col][col] != 0) { + f1 = (-1) * m[row][col] / m[col][col]; + for (var i = col; i < tms; i++) { + m[row][i] = f1 * m[col][i] + m[row][i]; + } + } + } + } + + return m; +} + +dojo.math.matrix.create = function(a, b){ + var m = []; + for(var i=0; iidx){ + var operand=new dojo.math.Matrix(); + var current=this.transformations[idx++]; + operand.a=matrix.a*current.a + matrix.b*current.d + matrix.c*current.g; + operand.b=matrix.a*current.b + matrix.b*current.e + matrix.c*current.h; + operand.c=matrix.a*current.c + matrix.b*current.f + matrix.c*current.i; + operand.d=matrix.d*current.a + matrix.e*current.d + matrix.f*current.g; + operand.e=matrix.d*current.b + matrix.e*current.e + matrix.f*current.h; + operand.f=matrix.d*current.c + matrix.e*current.f + matrix.f*current.i; + operand.g=matrix.g*current.a + matrix.h*current.d + matrix.i*current.g; + operand.h=matrix.g*current.b + matrix.h*current.e + matrix.i*current.h; + operand.i=matrix.g*current.c + matrix.h*current.f + matrix.i*current.i; + matrix=operand; + } + return matrix; + }, + peek:function(){ + return this.transformations[0]; + }, + rotate:function(angle){ + angle=dojo.math.degToRad(angle); + var matrix=new dojo.math.Matrix(); + matrix.a=matrix.e=Math.cos(angle); + matrix.d=Math.sin(angle); + matrix.b=-1*matrix.d; + this.add(matrix); + }, + rotateAt:function(angle, cx, cy){ + this.translate(cx, cy); + this.rotate(angle); + this.translate(-cx, -cy); + }, + scale:function(sx, sy){ + var matrix=new dojo.math.Matrix(); + matrix.a=sx; + matrix.e=sy; + this.add(matrix); + }, + skewX:function(angle){ + angle=dojo.math.degToRad(angle); + var matrix=new dojo.math.Matrix(); + matrix.b=Math.tan(angle); + this.add(matrix); + }, + skewY:function(angle){ + angle=dojo.math.degToRad(angle); + var matrix=new dojo.math.Matrix(); + matrix.d=Math.tan(angle); + this.add(matrix); + }, + translate:function(tx, ty){ + var matrix=new dojo.math.Matrix(); + matrix.c=tx; + matrix.f=ty; + this.add(matrix); + } +}); diff --git a/source/web/scripts/ajax/src/namespace.js b/source/web/scripts/ajax/src/namespace.js new file mode 100644 index 0000000000..b90b00c023 --- /dev/null +++ b/source/web/scripts/ajax/src/namespace.js @@ -0,0 +1,68 @@ +dojo.provide("dojo.namespace"); + +//Every namespace that is defined using the dojo.defineNamespace method has one of these Namespace objects created. +//It stores the fully qualified namespace name, it's location relative to the dojo root, the short namespace name, and a +//resolver function that maps a widget's short name to it's fully qualified name +dojo.Namespace = function(objRoot, location, nsPrefix, resolver){ + this.root = objRoot; + this.location = location; + this.nsPrefix = nsPrefix; + this.resolver = resolver; + + dojo.setModulePrefix(nsPrefix, location); +}; + +dojo.Namespace.prototype._loaded = {}; +dojo.Namespace.prototype.load = function(name, domain){ + if(this.resolver){ + var fullName = this.resolver(name,domain); + //only load a widget once. This is a quicker check than dojo.require does + if(fullName && !this._loaded[fullName]){ + //workaround so we don't break the build system + var req = dojo.require; + req(fullName); + + this._loaded[fullName] = true; + } + if(this._loaded[fullName]){ + return true; + } + } + return false; +}; + +//This function is used to define a new namespace. +//objRoot is the fully qualified namespace name +//location is the file system location relative to the dojo root, e.g. "../myNewNamespace" +//nsPrefix is the short name of the namespace. e.g. for the namespace " my.new.namespace", the nsPrefix could be "mnn" +//resolver is a function that takes two parameters: +// 1. a short name of a widget and returns it's fully qualified name. For example if passed "checkbox", it could return " dojo.widget.CheckBox" +// 2. the widget domain, e.g. "html", "svg", "vml" etc. This is optional, and depends on what the particular widget set supports. Dojo defaults to "html" +// resolver is optional, as it only applies to widgets, and a namespace may have no widgets +//widgetPackage the name of a widget package. e.g. if you had a namespace with nsPrefix = "mnn", and your widgets were in a +// "widget" folder in that namespace, your widget package would be "mnn.widget". This is optional, like the resolver +dojo.defineNamespace = function(objRoot, location, nsPrefix, resolver /*optional*/, widgetPackage /*optional*/){ +// dojo.debug("dojo.defineNamespace('"+objRoot+"','"+location+"','"+nsPrefix+"',resolver) called"); + if(dojo._namespaces[objRoot]){ + return; + } + var ns = new dojo.Namespace(objRoot, location, nsPrefix, resolver); + dojo._namespaces[objRoot] = ns; + if(nsPrefix){ + dojo._namespaces[nsPrefix] = ns; + } + if(widgetPackage){ + dojo.widget.manager.registerWidgetPackage(widgetPackage); + } +}; + +dojo.findNamespaceForWidget = function(widgetName){ + dojo.deprecated('dojo.findNamespaceForWidget', 'Widget [' + widgetName + '] not defined for a namespace'+ + ', so searching all namespaces. Developers should specify namespaces for all non-Dojo widgets', "0.5"); + widgetName = widgetName.toLowerCase(); + for(x in dojo._namespaces){ + if(dojo._namespaces[x].load(widgetName)){ + return dojo._namespaces[x]; + } + } +}; diff --git a/source/web/scripts/ajax/src/namespaces/dojo.js b/source/web/scripts/ajax/src/namespaces/dojo.js new file mode 100644 index 0000000000..e0d02c0a7e --- /dev/null +++ b/source/web/scripts/ajax/src/namespaces/dojo.js @@ -0,0 +1,103 @@ +dojo.provide("dojo.namespaces.dojo"); +dojo.require("dojo.namespace"); + +(function(){ + //mapping of all widget short names to their full package names + // This is used for widget autoloading - no dojo.require() is necessary. + // If you use a widget in markup or create one dynamically, then this + // mapping is used to find and load any dependencies not already loaded. + // You should use your own namespace for any custom widgets. + // For extra widgets you use, dojo.declare() may be used to explicitly load them. + var map = { + html: { + "accordioncontainer": "dojo.widget.AccordionContainer", + "treerpccontroller": "dojo.widget.TreeRPCController", + "accordionpane": "dojo.widget.AccordionPane", + "button": "dojo.widget.Button", + "chart": "dojo.widget.Chart", + "checkbox": "dojo.widget.Checkbox", + "civicrmdatepicker": "dojo.widget.CiviCrmDatePicker", + "colorpalette": "dojo.widget.ColorPalette", + "combobox": "dojo.widget.ComboBox", + "combobutton": "dojo.widget.Button", + "contentpane": "dojo.widget.ContentPane", + "contextmenu": "dojo.widget.ContextMenu", + "datepicker": "dojo.widget.DatePicker", + "debugconsole": "dojo.widget.DebugConsole", + "dialog": "dojo.widget.Dialog", + "docpane": "dojo.widget.DocPane", + "dropdownbutton": "dojo.widget.Button", + "dropdowndatepicker": "dojo.widget.DropdownDatePicker", + "editor2": "dojo.widget.Editor2", + "editor2toolbar": "dojo.widget.Editor2Toolbar", + "editor": "dojo.widget.Editor", + "editortree": "dojo.widget.EditorTree", + "editortreecontextmenu": "dojo.widget.EditorTreeContextMenu", + "editortreenode": "dojo.widget.EditorTreeNode", + "fisheyelist": "dojo.widget.FisheyeList", + "editortreecontroller": "dojo.widget.EditorTreeController", + "googlemap": "dojo.widget.GoogleMap", + "editortreeselector": "dojo.widget.EditorTreeSelector", + "floatingpane": "dojo.widget.FloatingPane", + "hslcolorpicker": "dojo.widget.HslColorPicker", + "inlineeditbox": "dojo.widget.InlineEditBox", + "layoutcontainer": "dojo.widget.LayoutContainer", + "linkpane": "dojo.widget.LinkPane", + "manager": "dojo.widget.Manager", + "popupcontainer": "dojo.widget.Menu2", + "popupmenu2": "dojo.widget.Menu2", + "menuitem2": "dojo.widget.Menu2", + "menuseparator2": "dojo.widget.Menu2", + "menubar2": "dojo.widget.Menu2", + "menubaritem2": "dojo.widget.Menu2", + "monthlyCalendar": "dojo.widget.MonthlyCalendar", + "richtext": "dojo.widget.RichText", + "remotetabcontroller": "dojo.widget.RemoteTabController", + "resizehandle": "dojo.widget.ResizeHandle", + "resizabletextarea": "dojo.widget.ResizableTextarea", + "select": "dojo.widget.Select", + "slideshow": "dojo.widget.SlideShow", + "sortabletable": "dojo.widget.SortableTable", + "splitcontainer": "dojo.widget.SplitContainer", + "svgbutton": "dojo.widget.SvgButton", + "tabcontainer": "dojo.widget.TabContainer", + "taskbar": "dojo.widget.TaskBar", + "timepicker": "dojo.widget.TimePicker", + "titlepane": "dojo.widget.TitlePane", + "toaster": "dojo.widget.Toaster", + "toggler": "dojo.widget.Toggler", + "toolbar": "dojo.widget.Toolbar", + "tooltip": "dojo.widget.Tooltip", + "tree": "dojo.widget.Tree", + "treebasiccontroller": "dojo.widget.TreeBasicController", + "treecontextmenu": "dojo.widget.TreeContextMenu", + "treeselector": "dojo.widget.TreeSelector", + "treecontrollerextension": "dojo.widget.TreeControllerExtension", + "treenode": "dojo.widget.TreeNode", + "validate": "dojo.widget.validate", + "treeloadingcontroller": "dojo.widget.TreeLoadingController", + "widget": "dojo.widget.Widget", + "wizard": "dojo.widget.Wizard", + "yahoomap": "dojo.widget.YahooMap" + }, + svg: { + "chart": "dojo.widget.svg.Chart", + "hslcolorpicker": "dojo.widget.svg.HslColorPicker" + }, + vml: { + "chart": "dojo.widget.vml.Chart" + } + }; + + function dojoNamespaceResolver(name, domain){ + if(!domain){ domain="html"; } + if(!map[domain]){ return null; } + return map[domain][name]; + } + + dojo.defineNamespace("dojo", "src", "dojo", dojoNamespaceResolver); + + dojo.addDojoNamespaceMapping = function(shortName, fullName){ + map[shortName]=fullName; + }; +})(); diff --git a/source/web/scripts/ajax/src/profile.js b/source/web/scripts/ajax/src/profile.js new file mode 100644 index 0000000000..18a208dff6 --- /dev/null +++ b/source/web/scripts/ajax/src/profile.js @@ -0,0 +1,110 @@ +dojo.provide("dojo.profile"); + + + + +dojo.profile = new function(){ + var profiles = {}; + var pns = []; + + this.start = function(name){ + if(!profiles[name]){ + profiles[name] = {iters: 0, total: 0}; + pns[pns.length] = name; + }else{ + if(profiles[name]["start"]){ + this.end(name); + } + } + profiles[name].end = null; + profiles[name].start = new Date(); + } + + this.end = function(name){ + var ed = new Date(); + if((profiles[name])&&(profiles[name]["start"])){ + with(profiles[name]){ + end = ed; + total += (end - start); + start = null; + iters++; + } + }else{ + // oops! bad call to end(), what should we do here? + return true; + } + } + + this.stop = this.end; + + this.dump = function(appendToDoc){ + var tbl = document.createElement("table"); + with(tbl.style){ + border = "1px solid black"; + borderCollapse = "collapse"; + } + var hdr = tbl.createTHead(); + var hdrtr = hdr.insertRow(0); + // document.createElement("tr"); + var cols = ["Identifier","Calls","Total","Avg"]; + for(var x=0; x0){ + var bdytr = tbl.insertRow(true); + var vals = [pns[x], prf.iters, prf.total, parseInt(prf.total/prf.iters)]; + for(var y=0; y0){ + textAlign = "right"; + borderRight = "1px solid gray"; + }else{ + borderRight = "1px solid black"; + } + } + } + } + } + + if(appendToDoc){ + var ne = document.createElement("div"); + ne.id = "profileOutputTable"; + with(ne.style){ + fontFamily = "Courier New, monospace"; + fontSize = "12px"; + lineHeight = "16px"; + borderTop = "1px solid black"; + padding = "10px"; + } + if(document.getElementById("profileOutputTable")){ + dojo.body().replaceChild(ne, document.getElementById("profileOutputTable")); + }else{ + dojo.body().appendChild(ne); + } + ne.appendChild(tbl); + } + + return tbl; + } +} diff --git a/source/web/scripts/ajax/src/reflect/__package__.js b/source/web/scripts/ajax/src/reflect/__package__.js new file mode 100644 index 0000000000..21f2d5d271 --- /dev/null +++ b/source/web/scripts/ajax/src/reflect/__package__.js @@ -0,0 +1,15 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.deprecated("dojo.reflect", "merged into dojo.lang (dojo.lang[type]).", "0.4"); +dojo.kwCompoundRequire({ + common: ["dojo.reflect.reflection"] +}); +dojo.provide("dojo.reflect.*"); diff --git a/source/web/scripts/ajax/src/reflect/reflection.js b/source/web/scripts/ajax/src/reflect/reflection.js new file mode 100644 index 0000000000..caed8244e6 --- /dev/null +++ b/source/web/scripts/ajax/src/reflect/reflection.js @@ -0,0 +1,198 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.deprecated("dojo.reflect", "merged into dojo.lang (dojo.lang[type])", "0.4"); +dojo.provide("dojo.reflect"); + +/***************************************************************** + reflect.js + v.1.5.0 + (c) 2003-2004 Thomas R. Trenka, Ph.D. + + Derived from the reflection functions of f(m). + http://dojotoolkit.org + http://fm.dept-z.com + + There is a dependency on the variable dJ_global, which + should always refer to the global object. +******************************************************************/ +if(!dj_global){ var dj_global = this; } + +dojo.reflect = {} ; +dojo.reflect.$unknownType = function(){ } ; +dojo.reflect.ParameterInfo = function(name, type){ + this.name = name ; + this.type = (type) ? type : dojo.reflect.$unknownType ; +} ; +dojo.reflect.PropertyInfo = function(name, type) { + this.name = name ; + this.type = (type) ? type : dojo.reflect.$unknownType ; +} ; +dojo.reflect.MethodInfo = function(name, fn){ + var parse = function(f) { + var o = {} ; + var s = f.toString() ; + var param = ((s.substring(s.indexOf('(')+1, s.indexOf(')'))).replace(/\s+/g, "")).split(",") ; + o.parameters = [] ; + for (var i = 0; i < param.length; i++) { + o.parameters.push(new dojo.reflect.ParameterInfo(param[i])) ; + } + o.body = (s.substring(s.indexOf('{')+1, s.lastIndexOf('}'))).replace(/(^\s*)|(\s*$)/g, "") ; + return o ; + } ; + + var tmp = parse(fn) ; + var p = tmp.parameters ; + var body = tmp.body ; + + this.name = (name) ? name : "anonymous" ; + this.getParameters = function(){ return p ; } ; + this.getNullArgumentsObject = function() { + var a = [] ; + for (var i = 0; i < p.length; i++){ + a.push(null); + } + return a ; + } ; + this.getBody = function() { return body ; } ; + this.type = Function ; + this.invoke = function(src, args){ return fn.apply(src, args) ; } ; +} ; + +// Static object that can activate instances of the passed type. +dojo.reflect.Activator = new (function(){ + this.createInstance = function(type, args) { + switch (typeof(type)) { + case "function" : { + var o = {} ; + type.apply(o, args) ; + return o ; + } ; + case "string" : { + var o = {} ; + (dojo.reflect.Reflector.getTypeFromString(type)).apply(o, args) ; + return o ; + } ; + } + throw new Error("dojo.reflect.Activator.createInstance(): no such type exists."); + } +})() ; + +dojo.reflect.Reflector = new (function(){ + this.getTypeFromString = function(s) { + var parts = s.split("."), i = 0, obj = dj_global ; + do { obj = obj[parts[i++]] ; } while (i < parts.length && obj) ; + return (obj != dj_global) ? obj : null ; + }; + + this.typeExists = function(s) { + var parts = s.split("."), i = 0, obj = dj_global ; + do { obj = obj[parts[i++]] ; } while (i < parts.length && obj) ; + return (obj && obj != dj_global) ; + }; + + this.getFieldsFromType = function(s) { + var type = s ; + if (typeof(s) == "string") { + type = this.getTypeFromString(s) ; + } + var nullArgs = (new dojo.reflect.MethodInfo(type)).getNullArgumentsObject() ; + return this.getFields(dojo.reflect.Activator.createInstance(s, nullArgs)) ; + }; + + this.getPropertiesFromType = function(s) { + var type = s ; + if (typeof(s) == "string") { + type = this.getTypeFromString(s); + } + var nullArgs = (new dojo.reflect.MethodInfo(type)).getNullArgumentsObject() ; + return this.getProperties(dojo.reflect.Activator.createInstance(s, nullArgs)) ; + }; + + this.getMethodsFromType = function(s) { + var type = s ; + if (typeof(s) == "string") { + type = this.getTypeFromString(s) ; + } + var nullArgs = (new dojo.reflect.MethodInfo(type)).getNullArgumentsObject() ; + return this.getMethods(dojo.reflect.Activator.createInstance(s, nullArgs)) ; + }; + + this.getType = function(o) { return o.constructor ; } ; + + this.getFields = function(obj) { + var arr = [] ; + for (var p in obj) { + if(this.getType(obj[p]) != Function){ + arr.push(new dojo.reflect.PropertyInfo(p, this.getType(obj[p]))) ; + }else{ + arr.push(new dojo.reflect.MethodInfo(p, obj[p])); + } + } + return arr ; + }; + + this.getProperties = function(obj) { + var arr = [] ; + var fi = this.getFields(obj) ; + for (var i = 0; i < fi.length; i++){ + if (this.isInstanceOf(fi[i], dojo.reflect.PropertyInfo)){ + arr.push(fi[i]) ; + } + } + return arr ; + }; + + this.getMethods = function(obj) { + var arr = [] ; + var fi = this.getFields(obj) ; + for (var i = 0; i < fi.length; i++){ + if (this.isInstanceOf(fi[i], dojo.reflect.MethodInfo)){ + arr.push(fi[i]) ; + } + } + return arr ; + }; + + /* + this.implements = function(o, type) { + if (this.isSubTypeOf(o, type)) return false ; + var f = this.getFieldsFromType(type) ; + for (var i = 0; i < f.length; i++) { + if (typeof(o[(f[i].name)]) == "undefined"){ + return false; + } + } + return true ; + }; + */ + + this.getBaseClass = function(o) { + if (o.getType().prototype.prototype.constructor){ + return (o.getType()).prototype.prototype.constructor ; + } + return Object ; + } ; + + this.isInstanceOf = function(o, type) { + return (this.getType(o) == type) ; + }; + + this.isSubTypeOf = function(o, type) { + return (o instanceof type) ; + }; + + this.isBaseTypeOf = function(o, type) { + return (type instanceof o); + }; +})(); + +// back-compat +dojo.provide("dojo.reflect.reflection"); diff --git a/source/web/scripts/ajax/src/regexp.js b/source/web/scripts/ajax/src/regexp.js new file mode 100644 index 0000000000..4c04634c35 --- /dev/null +++ b/source/web/scripts/ajax/src/regexp.js @@ -0,0 +1,596 @@ +dojo.provide("dojo.regexp"); +dojo.provide("dojo.regexp.us"); + +// *** Regular Expression Generators *** + +/** + Builds a RE that matches a top-level domain. + + @param flags An object. + flags.allowCC Include 2 letter country code domains. Default is true. + flags.allowGeneric Include the generic domains. Default is true. + flags.allowInfra Include infrastructure domains. Default is true. + + @return A string for a regular expression for a top-level domain. +*/ +dojo.regexp.tld = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.allowCC != "boolean") { flags.allowCC = true; } + if (typeof flags.allowInfra != "boolean") { flags.allowInfra = true; } + if (typeof flags.allowGeneric != "boolean") { flags.allowGeneric = true; } + + // Infrastructure top-level domain - only one at present + var infraRE = "arpa"; + + // Generic top-level domains RE. + var genericRE = + "aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|xxx|jobs|mobi|post"; + + // Country Code top-level domains RE + var ccRE = + "ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|" + + "bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|" + + "ec|ee|eg|er|es|et|fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|" + + "hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kr|kw|ky|kz|la|" + + "lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|" + + "mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|" + + "ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sk|sl|sm|sn|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|" + + "to|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw"; + + // Build top-level domain RE + var a = []; + if (flags.allowInfra) { a.push(infraRE); } + if (flags.allowGeneric) { a.push(genericRE); } + if (flags.allowCC) { a.push(ccRE); } + + var tldRE = ""; + if (a.length > 0) { + tldRE = "(" + a.join("|") + ")"; + } + + return tldRE; +} + +/** + Builds a RE that matches an IP Address. + Supports 5 formats for IPv4: dotted decimal, dotted hex, dotted octal, decimal and hexadecimal. + Supports 2 formats for Ipv6. + + @param flags An object. All flags are boolean with default = true. + flags.allowDottedDecimal Example, 207.142.131.235. No zero padding. + flags.allowDottedHex Example, 0x18.0x11.0x9b.0x28. Case insensitive. Zero padding allowed. + flags.allowDottedOctal Example, 0030.0021.0233.0050. Zero padding allowed. + flags.allowDecimal Example, 3482223595. A decimal number between 0-4294967295. + flags.allowHex Example, 0xCF8E83EB. Hexadecimal number between 0x0-0xFFFFFFFF. + Case insensitive. Zero padding allowed. + flags.allowIPv6 IPv6 address written as eight groups of four hexadecimal digits. + flags.allowHybrid IPv6 address written as six groups of four hexadecimal digits + followed by the usual 4 dotted decimal digit notation of IPv4. x:x:x:x:x:x:d.d.d.d + + @return A string for a regular expression for an IP address. +*/ +dojo.regexp.ipAddress = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.allowDottedDecimal != "boolean") { flags.allowDottedDecimal = true; } + if (typeof flags.allowDottedHex != "boolean") { flags.allowDottedHex = true; } + if (typeof flags.allowDottedOctal != "boolean") { flags.allowDottedOctal = true; } + if (typeof flags.allowDecimal != "boolean") { flags.allowDecimal = true; } + if (typeof flags.allowHex != "boolean") { flags.allowHex = true; } + if (typeof flags.allowIPv6 != "boolean") { flags.allowIPv6 = true; } + if (typeof flags.allowHybrid != "boolean") { flags.allowHybrid = true; } + + // decimal-dotted IP address RE. + var dottedDecimalRE = + // Each number is between 0-255. Zero padding is not allowed. + "((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])"; + + // dotted hex IP address RE. Each number is between 0x0-0xff. Zero padding is allowed, e.g. 0x00. + var dottedHexRE = "(0[xX]0*[\\da-fA-F]?[\\da-fA-F]\\.){3}0[xX]0*[\\da-fA-F]?[\\da-fA-F]"; + + // dotted octal IP address RE. Each number is between 0000-0377. + // Zero padding is allowed, but each number must have at least 4 characters. + var dottedOctalRE = "(0+[0-3][0-7][0-7]\\.){3}0+[0-3][0-7][0-7]"; + + // decimal IP address RE. A decimal number between 0-4294967295. + var decimalRE = "(0|[1-9]\\d{0,8}|[1-3]\\d{9}|4[01]\\d{8}|42[0-8]\\d{7}|429[0-3]\\d{6}|" + + "4294[0-8]\\d{5}|42949[0-5]\\d{4}|429496[0-6]\\d{3}|4294967[01]\\d{2}|42949672[0-8]\\d|429496729[0-5])"; + + // hexadecimal IP address RE. + // A hexadecimal number between 0x0-0xFFFFFFFF. Case insensitive. Zero padding is allowed. + var hexRE = "0[xX]0*[\\da-fA-F]{1,8}"; + + // IPv6 address RE. + // The format is written as eight groups of four hexadecimal digits, x:x:x:x:x:x:x:x, + // where x is between 0000-ffff. Zero padding is optional. Case insensitive. + var ipv6RE = "([\\da-fA-F]{1,4}\\:){7}[\\da-fA-F]{1,4}"; + + // IPv6/IPv4 Hybrid address RE. + // The format is written as six groups of four hexadecimal digits, + // followed by the 4 dotted decimal IPv4 format. x:x:x:x:x:x:d.d.d.d + var hybridRE = "([\\da-fA-F]{1,4}\\:){6}" + + "((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])"; + + // Build IP Address RE + var a = []; + if (flags.allowDottedDecimal) { a.push(dottedDecimalRE); } + if (flags.allowDottedHex) { a.push(dottedHexRE); } + if (flags.allowDottedOctal) { a.push(dottedOctalRE); } + if (flags.allowDecimal) { a.push(decimalRE); } + if (flags.allowHex) { a.push(hexRE); } + if (flags.allowIPv6) { a.push(ipv6RE); } + if (flags.allowHybrid) { a.push(hybridRE); } + + var ipAddressRE = ""; + if (a.length > 0) { + ipAddressRE = "(" + a.join("|") + ")"; + } + + return ipAddressRE; +} + +/** + Builds a RE that matches a host. + A host is a domain name or an IP address, possibly followed by a port number. + + @param flags An object. + flags.allowIP Allow an IP address for hostname. Default is true. + flags.allowLocal Allow the host to be "localhost". Default is false. + flags.allowPort Allow a port number to be present. Default is true. + flags in regexp.ipAddress can be applied. + flags in regexp.tld can be applied. + + @return A string for a regular expression for a host. +*/ +dojo.regexp.host = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.allowIP != "boolean") { flags.allowIP = true; } + if (typeof flags.allowLocal != "boolean") { flags.allowLocal = false; } + if (typeof flags.allowPort != "boolean") { flags.allowPort = true; } + + // Domain names can not end with a dash. + var domainNameRE = "([0-9a-zA-Z]([-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?\\.)+" + dojo.regexp.tld(flags); + + // port number RE + var portRE = ( flags.allowPort ) ? "(\\:" + dojo.regexp.integer({signed: false}) + ")?" : ""; + + // build host RE + var hostNameRE = domainNameRE; + if (flags.allowIP) { hostNameRE += "|" + dojo.regexp.ipAddress(flags); } + if (flags.allowLocal) { hostNameRE += "|localhost"; } + + return "(" + hostNameRE + ")" + portRE; +} + +/** + Builds a regular expression that matches a URL. + + @param flags An object. + flags.scheme Can be true, false, or [true, false]. + This means: required, not allowed, or match either one. + flags in regexp.host can be applied. + flags in regexp.ipAddress can be applied. + flags in regexp.tld can be applied. + + @return A string for a regular expression for a URL. +*/ +dojo.regexp.url = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.scheme == "undefined") { flags.scheme = [true, false]; } + + // Scheme RE + var protocalRE = dojo.regexp.buildGroupRE(flags.scheme, + function(q) { if (q) { return "(https?|ftps?)\\://"; } return ""; } + ); + + // Path and query and anchor RE + var pathRE = "(/([^?#\\s/]+/)*)?([^?#\\s/]+(\\?[^?#\\s/]*)?(#[A-Za-z][\\w.:-]*)?)?"; + + return (protocalRE + dojo.regexp.host(flags) + pathRE); +} + +/** + Builds a regular expression that matches an email address. + + @param flags An object. + flags.allowCruft Allow address like . Default is false. + flags in regexp.host can be applied. + flags in regexp.ipAddress can be applied. + flags in regexp.tld can be applied. + + @return A string for a regular expression for an email address. +*/ +dojo.regexp.emailAddress = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.allowCruft != "boolean") { flags.allowCruft = false; } + flags.allowPort = false; // invalid in email addresses + + // user name RE - apostrophes are valid if there's not 2 in a row + var usernameRE = "([\\da-z]+[-._+&'])*[\\da-z]+"; + + // build emailAddress RE + var emailAddressRE = usernameRE + "@" + dojo.regexp.host(flags); + + // Allow email addresses with cruft + if ( flags.allowCruft ) { + emailAddressRE = "?"; + } + + return emailAddressRE; +} + +/** + Builds a regular expression that matches a list of email addresses. + + @param flags An object. + flags.listSeparator The character used to separate email addresses. Default is ";", ",", "\n" or " ". + flags in regexp.emailAddress can be applied. + flags in regexp.host can be applied. + flags in regexp.ipAddress can be applied. + flags in regexp.tld can be applied. + + @return A string for a regular expression for an email address list. +*/ +dojo.regexp.emailAddressList = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.listSeparator != "string") { flags.listSeparator = "\\s;,"; } + + // build a RE for an Email Address List + var emailAddressRE = dojo.regexp.emailAddress(flags); + var emailAddressListRE = "(" + emailAddressRE + "\\s*[" + flags.listSeparator + "]\\s*)*" + + emailAddressRE + "\\s*[" + flags.listSeparator + "]?\\s*"; + + return emailAddressListRE; +} + +/** + Builds a regular expression that matches an integer. + + @param flags An object. + flags.signed The leading plus-or-minus sign. Can be true, false, or [true, false]. + Default is [true, false], (i.e. will match if it is signed or unsigned). + flags.separator The character used as the thousands separator. Default is no separator. + For more than one symbol use an array, e.g. [",", ""], makes ',' optional. + flags.groupSize group size between separators + flags.groupSize2 second grouping (for India) + + @return A string for a regular expression for an integer. +*/ +dojo.regexp.integer = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.signed == "undefined") { flags.signed = [true, false]; } + if (typeof flags.separator == "undefined") { + flags.separator = ""; + } else if (typeof flags.groupSize == "undefined") { + flags.groupSize = 3; + } + // build sign RE + var signRE = dojo.regexp.buildGroupRE(flags.signed, + function(q) { return q ? "[-+]" : ""; } + ); + + // number RE + var numberRE = dojo.regexp.buildGroupRE(flags.separator, + function(sep) { + if ( sep == "" ) { + return "(0|[1-9]\\d*)"; + } + var grp = flags.groupSize, grp2 = flags.groupSize2; + if ( typeof grp2 != "undefined" ) { + var grp2RE = "(0|[1-9]\\d{0," + (grp2-1) + "}([" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})"; + return ((grp-grp2) > 0) ? "(" + grp2RE + "|(0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE; + } + return "(0|[1-9]\\d{0," + (grp-1) + "}([" + sep + "]\\d{" + grp + "})*)"; + } + ); + + // integer RE + return (signRE + numberRE); +} + +/** + Builds a regular expression to match a real number in exponential notation. + + @param flags An object. + flags.places The integer number of decimal places. + If not given, the decimal part is optional and the number of places is unlimited. + flags.decimal A string for the character used as the decimal point. Default is ".". + flags.fractional Whether decimal places are allowed. + Can be true, false, or [true, false]. Default is [true, false] + flags.exponent Express in exponential notation. Can be true, false, or [true, false]. + Default is [true, false], (i.e. will match if the exponential part is present are not). + flags.eSigned The leading plus-or-minus sign on the exponent. Can be true, false, + or [true, false]. Default is [true, false], (i.e. will match if it is signed or unsigned). + flags in regexp.integer can be applied. + + @return A string for a regular expression for a real number. +*/ +dojo.regexp.realNumber = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.places != "number") { flags.places = Infinity; } + if (typeof flags.decimal != "string") { flags.decimal = "."; } + if (typeof flags.fractional == "undefined") { flags.fractional = [true, false]; } + if (typeof flags.exponent == "undefined") { flags.exponent = [true, false]; } + if (typeof flags.eSigned == "undefined") { flags.eSigned = [true, false]; } + + // integer RE + var integerRE = dojo.regexp.integer(flags); + + // decimal RE + var decimalRE = dojo.regexp.buildGroupRE(flags.fractional, + function(q) { + var re = ""; + if (q && (flags.places > 0)) { + re = "\\" + flags.decimal; + if ( flags.places == Infinity) { + re = "(" + re + "\\d+)?"; + } + else { + re = re + "\\d{" + flags.places + "}"; + } + } + + return re; + } + ); + + + // exponent RE + var exponentRE = dojo.regexp.buildGroupRE(flags.exponent, + function(q) { + if (q) { return "([eE]" + dojo.regexp.integer({ signed: flags.eSigned}) + ")"; } + return ""; + } + ); + + // real number RE + return (integerRE + decimalRE + exponentRE); +} + +/** + Builds a regular expression to match a monetary value. + + @param flags An object. + flags.symbol A currency symbol such as Yen "�", Pound "�", or the Euro sign "�". + Default is "$". For more than one symbol use an array, e.g. ["$", ""], makes $ optional. + flags.placement The symbol can come "before" the number or "after" the number. Default is "before". + flags.signPlacement The sign can come "before" the number or "after" the sign, + "around" to put parentheses around negative values, or "end" for the final char. Default is "before". + flags.cents deprecated, in favor of flags.fractional + flags in regexp.realNumber can be applied except exponent, eSigned. + + @return A string for a regular expression for a monetary value. +*/ +dojo.regexp.currency = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.signed == "undefined") { flags.signed = [true, false]; } + if (typeof flags.symbol == "undefined") { flags.symbol = "$"; } + if (typeof flags.placement != "string") { flags.placement = "before"; } + if (typeof flags.signPlacement != "string") { flags.signPlacement = "before"; } + if (typeof flags.separator == "undefined") { flags.separator = ","; } + if (typeof flags.fractional == "undefined" && typeof flags.cents != "undefined") { + dojo.deprecated("dojo.regexp.currency: flags.cents", "use flags.fractional instead", "0.5"); + flags.fractional = flags.cents; + } + if (typeof flags.decimal != "string") { flags.decimal = "."; } + + // build sign RE + var signRE = dojo.regexp.buildGroupRE(flags.signed, + function(q) { if (q) { return "[-+]"; } return ""; } + ); + + // build symbol RE + var symbolRE = dojo.regexp.buildGroupRE(flags.symbol, + function(symbol) { + // escape all special characters + return "\\s?" + symbol.replace( /([.$?*!=:|\\\/^])/g, "\\$1") + "\\s?"; + } + ); + + switch (flags.signPlacement){ + case "before": + symbolRE = signRE + symbolRE; + break; + case "after": + symbolRE = symbolRE + signRE; + break; + } + + // number RE + var flagsCopy = flags; //TODO: copy by value? + flagsCopy.signed = false; flagsCopy.exponent = false; + var numberRE = dojo.regexp.realNumber(flagsCopy); + + // build currency RE + var currencyRE; + switch (flags.placement){ + case "before": + currencyRE = symbolRE + numberRE; + break; + case "after": + currencyRE = numberRE + symbolRE; + break; + } + + switch (flags.signPlacement){ + case "around": + currencyRE = "(" + currencyRE + "|" + "\\(" + currencyRE + "\\)" + ")"; + break; + case "end": + currencyRE = currencyRE + signRE; + break; + } + return currencyRE; +} + +/** + A regular expression to match US state and territory abbreviations. + + @param flags An object. + flags.allowTerritories Allow Guam, Puerto Rico, etc. Default is true. + flags.allowMilitary Allow military 'states', e.g. Armed Forces Europe (AE). Default is true. + + @return A string for a regular expression for a US state. +*/ +dojo.regexp.us.state = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.allowTerritories != "boolean") { flags.allowTerritories = true; } + if (typeof flags.allowMilitary != "boolean") { flags.allowMilitary = true; } + + // state RE + var statesRE = + "AL|AK|AZ|AR|CA|CO|CT|DE|DC|FL|GA|HI|ID|IL|IN|IA|KS|KY|LA|ME|MD|MA|MI|MN|MS|MO|MT|" + + "NE|NV|NH|NJ|NM|NY|NC|ND|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VT|VA|WA|WV|WI|WY"; + + // territories RE + var territoriesRE = "AS|FM|GU|MH|MP|PW|PR|VI"; + + // military states RE + var militaryRE = "AA|AE|AP"; + + // Build states and territories RE + if (flags.allowTerritories) { statesRE += "|" + territoriesRE; } + if (flags.allowMilitary) { statesRE += "|" + militaryRE; } + + return "(" + statesRE + ")"; +} + +/** + Builds a regular expression to match any International format for time. + The RE can match one format or one of multiple formats. + + Format + h 12 hour, no zero padding. + hh 12 hour, has leading zero. + H 24 hour, no zero padding. + HH 24 hour, has leading zero. + m minutes, no zero padding. + mm minutes, has leading zero. + s seconds, no zero padding. + ss seconds, has leading zero. + t am or pm, case insensitive. + All other characters must appear literally in the expression. + + Example + "h:m:s t" -> 2:5:33 PM + "HH:mm:ss" -> 14:05:33 + + @param flags An object. + flags.format A string or an array of strings. Default is "h:mm:ss t". + flags.amSymbol The symbol used for AM. Default is "AM". + flags.pmSymbol The symbol used for PM. Default is "PM". + + @return A string for a regular expression for a time value. +*/ +dojo.regexp.time = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.format == "undefined") { flags.format = "h:mm:ss t"; } + if (typeof flags.amSymbol != "string") { flags.amSymbol = "AM"; } + if (typeof flags.pmSymbol != "string") { flags.pmSymbol = "PM"; } + + // Converts a time format to a RE + var timeRE = function(format) { + // escape all special characters + format = format.replace( /([.$?*!=:|{}\(\)\[\]\\\/^])/g, "\\$1"); + var amRE = flags.amSymbol.replace( /([.$?*!=:|{}\(\)\[\]\\\/^])/g, "\\$1"); + var pmRE = flags.pmSymbol.replace( /([.$?*!=:|{}\(\)\[\]\\\/^])/g, "\\$1"); + + // replace tokens with Regular Expressions + format = format.replace("hh", "(0[1-9]|1[0-2])"); + format = format.replace("h", "([1-9]|1[0-2])"); + format = format.replace("HH", "([01][0-9]|2[0-3])"); + format = format.replace("H", "([0-9]|1[0-9]|2[0-3])"); + format = format.replace("mm", "([0-5][0-9])"); + format = format.replace("m", "([1-5][0-9]|[0-9])"); + format = format.replace("ss", "([0-5][0-9])"); + format = format.replace("s", "([1-5][0-9]|[0-9])"); + format = format.replace("t", "\\s?(" + amRE + "|" + pmRE + ")\\s?" ); + + return format; + }; + + // build RE for multiple time formats + return dojo.regexp.buildGroupRE(flags.format, timeRE); +} + +/** + Builds a regular expression to match any sort of number based format. + Use it for phone numbers, social security numbers, zip-codes, etc. + The RE can match one format or one of multiple formats. + + Format + # Stands for a digit, 0-9. + ? Stands for an optional digit, 0-9 or nothing. + All other characters must appear literally in the expression. + + Example + "(###) ###-####" -> (510) 542-9742 + "(###) ###-#### x#???" -> (510) 542-9742 x153 + "###-##-####" -> 506-82-1089 i.e. social security number + "#####-####" -> 98225-1649 i.e. zip code + + @param flags An object. + flags.format A string or an Array of strings for multiple formats. + @return A string for a regular expression for the number format(s). +*/ +dojo.regexp.numberFormat = function(flags) { + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + if (typeof flags.format == "undefined") { flags.format = "###-###-####"; } + + // Converts a number format to RE. + var digitRE = function(format) { + // escape all special characters, except '?' + format = format.replace( /([.$*!=:|{}\(\)\[\]\\\/^])/g, "\\$1"); + + // Now replace '?' with Regular Expression + format = format.replace(/\?/g, "\\d?"); + + // replace # with Regular Expression + format = format.replace(/#/g, "\\d"); + + return format; + }; + + // build RE for multiple number formats + return dojo.regexp.buildGroupRE(flags.format, digitRE); +} + + +/** + This is basically a utility function used by some of the RE generators. + Builds a regular expression that groups subexpressions. + The subexpressions are constructed by the function, re, in the second parameter. + re builds one subexpression for each elem in the array a, in the first parameter. + + @param a A single value or an array of values. + @param re A function. Takes one parameter and converts it to a regular expression. + @return A string for a regular expression that groups all the subexpressions. +*/ +dojo.regexp.buildGroupRE = function(a, re) { + + // case 1: a is a single value. + if ( !( a instanceof Array ) ) { + return re(a); + } + + // case 2: a is an array + var b = []; + for (var i = 0; i < a.length; i++) { + // convert each elem to a RE + b.push(re(a[i])); + } + + // join the REs as alternatives in a RE group. + return "(" + b.join("|") + ")"; +} diff --git a/source/web/scripts/ajax/src/rpc/Deferred.js b/source/web/scripts/ajax/src/rpc/Deferred.js new file mode 100644 index 0000000000..aeb5e6b43a --- /dev/null +++ b/source/web/scripts/ajax/src/rpc/Deferred.js @@ -0,0 +1,5 @@ +dojo.provide("dojo.rpc.Deferred"); +dojo.require("dojo.Deferred"); + +dojo.rpc.Deferred = dojo.Deferred; +dojo.rpc.Deferred.prototype = dojo.Deferred.prototype; diff --git a/source/web/scripts/ajax/src/rpc/JotService.js b/source/web/scripts/ajax/src/rpc/JotService.js new file mode 100644 index 0000000000..7dfc358bcf --- /dev/null +++ b/source/web/scripts/ajax/src/rpc/JotService.js @@ -0,0 +1,31 @@ +dojo.provide("dojo.rpc.JotService"); +dojo.require("dojo.rpc.RpcService"); +dojo.require("dojo.rpc.JsonService"); +dojo.require("dojo.json"); + +dojo.rpc.JotService = function(){ + this.serviceUrl = "/_/jsonrpc"; +} + +dojo.inherits(dojo.rpc.JotService, dojo.rpc.JsonService); + +dojo.lang.extend(dojo.rpc.JotService, { + bind: function(method, parameters, deferredRequestHandler, url){ + dojo.io.bind({ + url: url||this.serviceUrl, + content: { + json: this.createRequest(method, parameters) + }, + method: "POST", + mimetype: "text/json", + load: this.resultCallback(deferredRequestHandler), + error: this.errorCallback(deferredRequestHandler), + preventCache: true + }); + }, + + createRequest: function(method, params){ + var req = { "params": params, "method": method, "id": this.lastSubmissionId++ }; + return dojo.json.serialize(req); + } +}); diff --git a/source/web/scripts/ajax/src/rpc/JsonService.js b/source/web/scripts/ajax/src/rpc/JsonService.js new file mode 100644 index 0000000000..f39c389a9c --- /dev/null +++ b/source/web/scripts/ajax/src/rpc/JsonService.js @@ -0,0 +1,95 @@ +dojo.provide("dojo.rpc.JsonService"); +dojo.require("dojo.rpc.RpcService"); +dojo.require("dojo.io.*"); +dojo.require("dojo.json"); +dojo.require("dojo.lang"); + +dojo.rpc.JsonService = function(args){ + // passing just the URL isn't terribly useful. It's expected that at + // various times folks will want to specify: + // - just the serviceUrl (for use w/ remoteCall()) + // - the text of the SMD to evaluate + // - a raw SMD object + // - the SMD URL + if(args){ + if(dojo.lang.isString(args)){ + // we assume it's an SMD file to be processed, since this was the + // earlier function signature + + // FIXME: also accept dojo.uri.Uri objects? + this.connect(args); + }else{ + // otherwise we assume it's an arguments object with the following + // (optional) properties: + // - serviceUrl + // - strictArgChecks + // - smdUrl + // - smdStr + // - smdObj + if(args["smdUrl"]){ + this.connect(args.smdUrl); + } + if(args["smdStr"]){ + this.processSmd(dj_eval("("+args.smdStr+")")); + } + if(args["smdObj"]){ + this.processSmd(args.smdObj); + } + if(args["serviceUrl"]){ + this.serviceUrl = args.serviceUrl; + } + if(typeof args["strictArgChecks"] != "undefined"){ + this.strictArgChecks = args.strictArgChecks; + } + } + } +} + +dojo.inherits(dojo.rpc.JsonService, dojo.rpc.RpcService); + +dojo.lang.extend(dojo.rpc.JsonService, { + + bustCache: false, + + contentType: "application/json-rpc", + + lastSubmissionId: 0, + + callRemote: function(method, params){ + var deferred = new dojo.rpc.Deferred(); + this.bind(method, params, deferred); + return deferred; + }, + + bind: function(method, parameters, deferredRequestHandler, url){ + dojo.io.bind({ + url: url||this.serviceUrl, + postContent: this.createRequest(method, parameters), + method: "POST", + contentType: this.contentType, + mimetype: "text/json", + load: this.resultCallback(deferredRequestHandler), + preventCache:this.bustCache + }); + }, + + createRequest: function(method, params){ + var req = { "params": params, "method": method, "id": ++this.lastSubmissionId }; + var data = dojo.json.serialize(req); + dojo.debug("JsonService: JSON-RPC Request: " + data); + return data; + }, + + parseResults: function(obj){ + if(!obj){ return; } + if (obj["Result"]!=null){ + return obj["Result"]; + }else if(obj["result"]!=null){ + return obj["result"]; + }else if(obj["ResultSet"]){ + return obj["ResultSet"]; + }else{ + return obj; + } + } +}); diff --git a/source/web/scripts/ajax/src/rpc/RpcService.js b/source/web/scripts/ajax/src/rpc/RpcService.js new file mode 100644 index 0000000000..f043da95ed --- /dev/null +++ b/source/web/scripts/ajax/src/rpc/RpcService.js @@ -0,0 +1,106 @@ +dojo.provide("dojo.rpc.RpcService"); +dojo.require("dojo.io.*"); +dojo.require("dojo.json"); +dojo.require("dojo.lang.func"); +dojo.require("dojo.rpc.Deferred"); + +dojo.rpc.RpcService = function(url){ + // summary + // constructor for rpc base class + if(url){ + this.connect(url); + } +} + +dojo.lang.extend(dojo.rpc.RpcService, { + + strictArgChecks: true, + serviceUrl: "", + + parseResults: function(obj){ + // summary + // parse the results coming back from an rpc request. + // this base implementation, just returns the full object + // subclasses should parse and only return the actual results + return obj; + }, + + errorCallback: function(/* dojo.rpc.Deferred */ deferredRequestHandler){ + // summary + // create callback that calls the Deferres errback method + return function(type, obj, e){ + deferredRequestHandler.errback(e); + } + }, + + resultCallback: function(/* dojo.rpc.Deferred */ deferredRequestHandler){ + // summary + // create callback that calls the Deferred's callback method + var tf = dojo.lang.hitch(this, + function(type, obj, e){ + var results = this.parseResults(obj||e); + deferredRequestHandler.callback(results); + } + ); + return tf; + }, + + + generateMethod: function(/*string*/ method, /*array*/ parameters, /*string*/ url){ + // summary + // generate the local bind methods for the remote object + return dojo.lang.hitch(this, function(){ + var deferredRequestHandler = new dojo.rpc.Deferred(); + + // if params weren't specified, then we can assume it's varargs + if( (this.strictArgChecks) && + (parameters != null) && + (arguments.length != parameters.length) + ){ + // put error stuff here, no enough params + dojo.raise("Invalid number of parameters for remote method."); + } else { + this.bind(method, arguments, deferredRequestHandler, url); + } + + return deferredRequestHandler; + }); + }, + + processSmd: function(/*json*/ object){ + // summary + // callback method for reciept of a smd object. Parse the smd and + // generate functions based on the description + dojo.debug("RpcService: Processing returned SMD."); + if(object.methods){ + dojo.lang.forEach(object.methods, function(m){ + if(m && m["name"]){ + dojo.debug("RpcService: Creating Method: this.", m.name, "()"); + this[m.name] = this.generateMethod( m.name, + m.parameters, + m["url"]||m["serviceUrl"]||m["serviceURL"]); + if(dojo.lang.isFunction(this[m.name])){ + dojo.debug("RpcService: Successfully created", m.name, "()"); + }else{ + dojo.debug("RpcService: Failed to create", m.name, "()"); + } + } + }, this); + } + + this.serviceUrl = object.serviceUrl||object.serviceURL; + dojo.debug("RpcService: Dojo RpcService is ready for use."); + }, + + connect: function(/*String*/ smdUrl){ + // summary + // connect to a remote url and retrieve a smd object + dojo.debug("RpcService: Attempting to load SMD document from:", smdUrl); + dojo.io.bind({ + url: smdUrl, + mimetype: "text/json", + load: dojo.lang.hitch(this, function(type, object, e){ return this.processSmd(object); }), + sync: true + }); + } +}); diff --git a/source/web/scripts/ajax/src/rpc/YahooService.js b/source/web/scripts/ajax/src/rpc/YahooService.js new file mode 100644 index 0000000000..922744ace1 --- /dev/null +++ b/source/web/scripts/ajax/src/rpc/YahooService.js @@ -0,0 +1,45 @@ +dojo.provide("dojo.rpc.YahooService"); +dojo.require("dojo.rpc.RpcService"); +dojo.require("dojo.rpc.JsonService"); +dojo.require("dojo.json"); +dojo.require("dojo.uri.*"); +dojo.require("dojo.io.ScriptSrcIO"); + +dojo.rpc.YahooService = function(appId){ + this.appId = appId; + if(!appId){ + this.appId = "dojotoolkit"; + dojo.debug( "please initialize the YahooService class with your own", + "application ID. Using the default may cause problems during", + "deployment of your application"); + } + this.connect(dojo.uri.dojoUri("src/rpc/yahoo.smd")); + this.strictArgChecks = false; +} + +dojo.inherits(dojo.rpc.YahooService, dojo.rpc.JsonService); + +dojo.lang.extend(dojo.rpc.YahooService, { + strictArgChecks: false, + + bind: function(method, parameters, deferredRequestHandler, url){ + var params = parameters; + if( (dojo.lang.isArrayLike(parameters))&& + (parameters.length == 1)){ + params = parameters[0]; + } + params.output = "json"; + params.appid= this.appId; + dojo.io.bind({ + url: url||this.serviceUrl, + transport: "ScriptSrcTransport", + // FIXME: need to get content interpolation fixed + content: params, + jsonParamName: "callback", + mimetype: "text/json", + load: this.resultCallback(deferredRequestHandler), + error: this.errorCallback(deferredRequestHandler), + preventCache: true + }); + } +}); diff --git a/source/web/scripts/ajax/src/rpc/__package__.js b/source/web/scripts/ajax/src/rpc/__package__.js new file mode 100644 index 0000000000..1d026252ea --- /dev/null +++ b/source/web/scripts/ajax/src/rpc/__package__.js @@ -0,0 +1,4 @@ +dojo.kwCompoundRequire({ + common: ["dojo.rpc.JsonService", false, false] +}); +dojo.provide("dojo.rpc.*"); diff --git a/source/web/scripts/ajax/src/rpc/yahoo.smd b/source/web/scripts/ajax/src/rpc/yahoo.smd new file mode 100644 index 0000000000..85648fa4dd --- /dev/null +++ b/source/web/scripts/ajax/src/rpc/yahoo.smd @@ -0,0 +1,263 @@ +{ + "SMDVersion":".1", + "objectName":"yahoo", + "serviceType":"JSON-P", + "methods":[ + // + // MAPS + // + { + // http://developer.yahoo.com/maps/rest/V1/mapImage.html + "name":"mapImage", + "serviceURL": "http://api.local.yahoo.com/MapsService/V1/mapImage", + "parameters":[ + { "name":"street", "type":"STRING" }, + { "name":"city", "type":"STRING" }, + { "name":"zip", "type":"INTEGER" }, + { "name":"location", "type":"STRING" }, + { "name":"longitude", "type":"FLOAT" }, + { "name":"latitude", "type":"FLOAT" }, + { "name":"image_type", "type":"STRING" }, + { "name":"image_width", "type":"INTEGER" }, + { "name":"image_height", "type":"INTEGER" }, + { "name":"zoom", "type":"INTEGER" }, + { "name":"radius", "type":"INTEGER" } + ] + }, + { + // http://developer.yahoo.com/traffic/rest/V1/index.html + "name":"trafficData", + "serviceURL": "http://api.local.yahoo.com/MapsService/V1/trafficData", + "parameters":[ + { "name":"street", "type":"STRING" }, + { "name":"city", "type":"STRING" }, + { "name":"zip", "type":"INTEGER" }, + { "name":"location", "type":"STRING" }, + { "name":"longitude", "type":"FLOAT" }, + { "name":"latitude", "type":"FLOAT" }, + { "name":"severity", "type":"INTEGER" }, + { "name":"include_map", "type":"INTEGER" }, + { "name":"image_type", "type":"STRING" }, + { "name":"image_width", "type":"INTEGER" }, + { "name":"image_height", "type":"INTEGER" }, + { "name":"zoom", "type":"INTEGER" }, + { "name":"radius", "type":"INTEGER" } + ] + }, + /* + // Yahoo's geocoding service is f'd for JSON and Y! advises that it + // may not be returning + { + // http://developer.yahoo.com/maps/rest/V1/geocode.html + "name":"geocode", + "serviceURL": "http://api.local.yahoo.com/MapsService/V1/geocode", + "parameters":[ + { "name":"street", "type":"STRING" }, + { "name":"city", "type":"STRING" }, + { "name":"zip", "type":"INTEGER" }, + { "name":"location", "type":"STRING" } + ] + }, + */ + // + // LOCAL SEARCH + // + { + // http://developer.yahoo.com/search/local/V3/localSearch.html + "name":"localSearch", + "serviceURL": "http://api.local.yahoo.com/LocalSearchService/V3/localSearch", + "parameters":[ + { "name":"street", "type":"STRING" }, + { "name":"city", "type":"STRING" }, + { "name":"zip", "type":"INTEGER" }, + { "name":"location", "type":"STRING" }, + { "name":"listing_id", "type":"STRING" }, + { "name":"sort", "type":"STRING" }, // "relevence", "title", "distance", or "rating" + { "name":"start", "type":"INTEGER" }, + { "name":"radius", "type":"FLOAT" }, + { "name":"results", "type":"INTEGER" }, // 1-50, defaults to 10 + { "name":"longitude", "type":"FLOAT" }, + { "name":"latitude", "type":"FLOAT" }, + { "name":"category", "type":"INTEGER" }, + { "name":"omit_category", "type":"INTEGER" }, + { "name":"minimum_rating", "type":"INTEGER" } + ] + }, + // + // WEB SEARCH + // + + // NOTE: contextual search and term extraction are not stubbed out + // becaues I'm not sure if we can POST via script src inclusion method + { + // http://developer.yahoo.com/search/web/V1/webSearch.html + "name":"webSearch", + "serviceURL": "http://api.search.yahoo.com/WebSearchService/V1/webSearch", + "parameters":[ + { "name":"query", "type":"STRING" }, + { "name":"type", "type":"STRING" }, // defaults to "all" + { "name":"region", "type":"STRING" }, // defaults to "us" + { "name":"results", "type":"INTEGER" }, // defaults to 10 + { "name":"start", "type":"INTEGER" }, // defaults to 1 + { "name":"format", "type":"STRING" }, // defaults to "any", can be "html", "msword", "pdf", "ppt", "rst", "txt", or "xls" + { "name":"adult_ok", "type":"INTEGER" }, // defaults to null + { "name":"similar_ok", "type":"INTEGER" }, // defaults to null + { "name":"language", "type":"STRING" }, // defaults to null + { "name":"country", "type":"STRING" }, // defaults to null + { "name":"site", "type":"STRING" }, // defaults to null + { "name":"subscription", "type":"STRING" }, // defaults to null + { "name":"license", "type":"STRING" } // defaults to "any" + ] + }, + { + // http://developer.yahoo.com/search/web/V1/spellingSuggestion.html + "name":"spellingSuggestion", + "serviceURL": "http://api.search.yahoo.com/WebSearchService/V1/spellingSuggestion", + "parameters":[ { "name":"query", "type":"STRING" } ] + }, + { + // http://developer.yahoo.com/search/web/V1/relatedSuggestion.html + "name":"spellingSuggestion", + "serviceURL": "http://api.search.yahoo.com/WebSearchService/V1/relatedSuggestion", + "parameters":[ + { "name":"query", "type":"STRING" }, + { "name":"results", "type":"INTEGER" } // 1-50, defaults to 10 + ] + }, + // + // IMAGE SEARCH + // + { + // http://developer.yahoo.com/search/image/V1/imageSearch.html + "name":"imageSearch", + "serviceURL": "http://api.search.yahoo.com/ImageSearchService/V1/imageSearch", + "parameters":[ + { "name":"query", "type":"STRING" }, + { "name":"type", "type":"STRING" }, // defaults to "all", can by "any" or "phrase" + { "name":"results", "type":"INTEGER" }, // defaults to 10 + { "name":"start", "type":"INTEGER" }, // defaults to 1 + { "name":"format", "type":"STRING" }, // defaults to "any", can be "bmp", "gif", "jpeg", or "png" + { "name":"adult_ok", "type":"INTEGER" }, // defaults to null + { "name":"coloration", "type":"STRING" }, // "any", "color", or "bw" + { "name":"site", "type":"STRING" } // defaults to null + ] + }, + // + // SITE EXPLORER + // + { + // http://developer.yahoo.com/search/siteexplorer/V1/inlinkData.html + "name":"inlinkData", + "serviceURL": "http://api.search.yahoo.com/SiteExplorerService/V1/inlinkData", + "parameters":[ + { "name":"query", "type":"STRING" }, + { "name":"type", "type":"STRING" }, // defaults to "all", can by "any" or "phrase" + { "name":"entire_site", "type":"INTEGER" }, // defaults to null + { "name":"omit_inlinks", "type":"STRING" }, // "domain" or "subdomain", defaults to null + { "name":"results", "type":"INTEGER" }, // defaults to 50 + { "name":"start", "type":"INTEGER" }, // defaults to 1 + { "name":"site", "type":"STRING" } // defaults to null + ] + }, + { + // http://developer.yahoo.com/search/siteexplorer/V1/pageData.html + "name":"pageData", + "serviceURL": "http://api.search.yahoo.com/SiteExplorerService/V1/pageData", + "parameters":[ + { "name":"query", "type":"STRING" }, + { "name":"type", "type":"STRING" }, // defaults to "all", can by "any" or "phrase" + { "name":"domain_only", "type":"INTEGER" }, // defaults to null + { "name":"results", "type":"INTEGER" }, // defaults to 50 + { "name":"start", "type":"INTEGER" }, // defaults to 1 + { "name":"site", "type":"STRING" } // defaults to null + ] + }, + // + // MUSIC SEARCH + // + { + // http://developer.yahoo.com/search/audio/V1/artistSearch.html + "name":"artistSearch", + "serviceURL": "http://api.search.yahoo.com/AudioSearchService/V1/artistSearch", + "parameters":[ + { "name":"artist", "type":"STRING" }, + { "name":"artistid", "type":"STRING" }, + { "name":"type", "type":"STRING" }, // "all", "any", or "phrase" + { "name":"results", "type":"INTEGER" }, // 1-50, defaults to 10 + { "name":"start", "type":"INTEGER" } // defaults to 1 + ] + }, + { + // http://developer.yahoo.com/search/audio/V1/albumSearch.html + "name":"albumSearch", + "serviceURL": "http://api.search.yahoo.com/AudioSearchService/V1/albumSearch", + "parameters":[ + { "name":"artist", "type":"STRING" }, + { "name":"artistid", "type":"STRING" }, + { "name":"album", "type":"STRING" }, + { "name":"type", "type":"STRING" }, // "all", "any", or "phrase" + { "name":"results", "type":"INTEGER" }, // 1-50, defaults to 10 + { "name":"start", "type":"INTEGER" } // defaults to 1 + ] + }, + { + // http://developer.yahoo.com/search/audio/V1/songSearch.html + "name":"songSearch", + "serviceURL": "http://api.search.yahoo.com/AudioSearchService/V1/songSearch", + "parameters":[ + { "name":"artist", "type":"STRING" }, + { "name":"artistid", "type":"STRING" }, + { "name":"album", "type":"STRING" }, + { "name":"albumid", "type":"STRING" }, + { "name":"song", "type":"STRING" }, + { "name":"songid", "type":"STRING" }, + { "name":"type", "type":"STRING" }, // "all", "any", or "phrase" + { "name":"results", "type":"INTEGER" }, // 1-50, defaults to 10 + { "name":"start", "type":"INTEGER" } // defaults to 1 + ] + }, + { + // http://developer.yahoo.com/search/audio/V1/songDownloadLocation.html + "name":"songDownloadLocation", + "serviceURL": "http://api.search.yahoo.com/AudioSearchService/V1/songDownloadLocation", + "parameters":[ + { "name":"songid", "type":"STRING" }, + // "source" can contain: + // audiolunchbox artistdirect buymusic dmusic + // emusic epitonic garageband itunes yahoo + // livedownloads mp34u msn musicmatch mapster passalong + // rhapsody soundclick theweb + { "name":"source", "type":"STRING" }, + { "name":"results", "type":"INTEGER" }, // 1-50, defaults to 10 + { "name":"start", "type":"INTEGER" } // defaults to 1 + ] + }, + // + // NEWS SEARCH + // + { + // http://developer.yahoo.com/search/news/V1/newsSearch.html + "name":"newsSearch", + "serviceURL": "http://api.search.yahoo.com/NewsSearchService/V1/newsSearch", + "parameters":[ + { "name":"query", "type":"STRING" }, + { "name":"type", "type":"STRING" }, // defaults to "all" + { "name":"results", "type":"INTEGER" }, // defaults to 10 + { "name":"start", "type":"INTEGER" }, // defaults to 1 + { "name":"sort", "type":"STRING" }, // "rank" or "date" + { "name":"language", "type":"STRING" }, // defaults to null + { "name":"site", "type":"STRING" } // defaults to null + ] + } + /* + { + // + "name":"", + "serviceURL": "", + "parameters":[ + { "name":"street", "type":"STRING" }, + ] + } + */ + ] +} diff --git a/source/web/scripts/ajax/src/selection/Selection.js b/source/web/scripts/ajax/src/selection/Selection.js new file mode 100644 index 0000000000..a2e945c591 --- /dev/null +++ b/source/web/scripts/ajax/src/selection/Selection.js @@ -0,0 +1,415 @@ +dojo.provide("dojo.selection.Selection"); +dojo.require("dojo.lang.array"); +dojo.require("dojo.lang.func"); +dojo.require("dojo.math"); + +dojo.selection.Selection = function(items, isCollection) { + this.items = []; + this.selection = []; + this._pivotItems = []; + this.clearItems(); + + if(items) { + if(isCollection) { + this.setItemsCollection(items); + } else { + this.setItems(items); + } + } +} +dojo.lang.extend(dojo.selection.Selection, { + items: null, // items to select from, order matters for growable selections + + selection: null, // items selected, aren't stored in order (see sorted()) + lastSelected: null, // last item selected + + allowImplicit: true, // if true, grow selection will start from 0th item when nothing is selected + length: 0, // number of *selected* items + + // if true, the selection is treated as an in-order and can grow by ranges, not just by single item + isGrowable: true, + + _pivotItems: null, // stack of pivot items + _pivotItem: null, // item we grow selections from, top of stack + + // event handlers + onSelect: function(item) {}, + onDeselect: function(item) {}, + onSelectChange: function(item, selected) {}, + + _find: function(item, inSelection) { + if(inSelection) { + return dojo.lang.find(this.selection, item); + } else { + return dojo.lang.find(this.items, item); + } + }, + + isSelectable: function(item) { + // user-customizable, will filter items through this + return true; + }, + + setItems: function(/* ... */) { + this.clearItems(); + this.addItems.call(this, arguments); + }, + + // this is in case you have an active collection array-like object + // (i.e. getElementsByTagName collection) that manages its own order + // and item list + setItemsCollection: function(collection) { + this.items = collection; + }, + + addItems: function(/* ... */) { + var args = dojo.lang.unnest(arguments); + for(var i = 0; i < args.length; i++) { + this.items.push(args[i]); + } + }, + + addItemsAt: function(item, before /* ... */) { + if(this.items.length == 0) { // work for empy case + return this.addItems(dojo.lang.toArray(arguments, 2)); + } + + if(!this.isItem(item)) { + item = this.items[item]; + } + if(!item) { throw new Error("addItemsAt: item doesn't exist"); } + var idx = this._find(item); + if(idx > 0 && before) { idx--; } + for(var i = 2; i < arguments.length; i++) { + if(!this.isItem(arguments[i])) { + this.items.splice(idx++, 0, arguments[i]); + } + } + }, + + removeItem: function(item) { + // remove item + var idx = this._find(item); + if(idx > -1) { + this.items.splice(idx, 1); + } + // remove from selection + // FIXME: do we call deselect? I don't think so because this isn't how + // you usually want to deselect an item. For example, if you deleted an + // item, you don't really want to deselect it -- you want it gone. -DS + idx = this._find(item, true); + if(idx > -1) { + this.selection.splice(idx, 1); + } + }, + + clearItems: function() { + this.items = []; + this.deselectAll(); + }, + + isItem: function(item) { + return this._find(item) > -1; + }, + + isSelected: function(item) { + return this._find(item, true) > -1; + }, + + /** + * allows you to filter item in or out of the selection + * depending on the current selection and action to be taken + **/ + selectFilter: function(item, selection, add, grow) { + return true; + }, + + /** + * update -- manages selections, most selecting should be done here + * item => item which may be added/grown to/only selected/deselected + * add => behaves like ctrl in windows selection world + * grow => behaves like shift + * noToggle => if true, don't toggle selection on item + **/ + update: function(item, add, grow, noToggle) { + if(!this.isItem(item)) { return false; } + + if(this.isGrowable && grow) { + if(!this.isSelected(item) + && this.selectFilter(item, this.selection, false, true)) { + this.grow(item); + this.lastSelected = item; + } + } else if(add) { + if(this.selectFilter(item, this.selection, true, false)) { + if(noToggle) { + if(this.select(item)) { + this.lastSelected = item; + } + } else if(this.toggleSelected(item)) { + this.lastSelected = item; + } + } + } else { + this.deselectAll(); + this.select(item); + } + + this.length = this.selection.length; + }, + + /** + * Grow a selection. + * toItem => which item to grow selection to + * fromItem => which item to start the growth from (it won't be selected) + * + * Any items in (fromItem, lastSelected] that aren't part of + * (fromItem, toItem] will be deselected + **/ + grow: function(toItem, fromItem) { + if(!this.isGrowable) { return; } + + if(arguments.length == 1) { + fromItem = this._pivotItem; + if(!fromItem && this.allowImplicit) { + fromItem = this.items[0]; + } + } + if(!toItem || !fromItem) { return false; } + + var fromIdx = this._find(fromItem); + + // get items to deselect (fromItem, lastSelected] + var toDeselect = {}; + var lastIdx = -1; + if(this.lastSelected) { + lastIdx = this._find(this.lastSelected); + var step = fromIdx < lastIdx ? -1 : 1; + var range = dojo.math.range(lastIdx, fromIdx, step); + for(var i = 0; i < range.length; i++) { + toDeselect[range[i]] = true; + } + } + + // add selection (fromItem, toItem] + var toIdx = this._find(toItem); + var step = fromIdx < toIdx ? -1 : 1; + var shrink = lastIdx >= 0 && step == 1 ? lastIdx < toIdx : lastIdx > toIdx; + var range = dojo.math.range(toIdx, fromIdx, step); + if(range.length) { + for(var i = range.length-1; i >= 0; i--) { + var item = this.items[range[i]]; + if(this.selectFilter(item, this.selection, false, true)) { + if(this.select(item, true) || shrink) { + this.lastSelected = item; + } + if(range[i] in toDeselect) { + delete toDeselect[range[i]]; + } + } + } + } else { + this.lastSelected = fromItem; + } + + // now deselect... + for(var i in toDeselect) { + if(this.items[i] == this.lastSelected) { + //dojo.debug("oops!"); + } + this.deselect(this.items[i]); + } + + // make sure everything is all kosher after selections+deselections + this._updatePivot(); + }, + + /** + * Grow selection upwards one item from lastSelected + **/ + growUp: function() { + if(!this.isGrowable) { return; } + + var idx = this._find(this.lastSelected) - 1; + while(idx >= 0) { + if(this.selectFilter(this.items[idx], this.selection, false, true)) { + this.grow(this.items[idx]); + break; + } + idx--; + } + }, + + /** + * Grow selection downwards one item from lastSelected + **/ + growDown: function() { + if(!this.isGrowable) { return; } + + var idx = this._find(this.lastSelected); + if(idx < 0 && this.allowImplicit) { + this.select(this.items[0]); + idx = 0; + } + idx++; + while(idx > 0 && idx < this.items.length) { + if(this.selectFilter(this.items[idx], this.selection, false, true)) { + this.grow(this.items[idx]); + break; + } + idx++; + } + }, + + toggleSelected: function(item, noPivot) { + if(this.isItem(item)) { + if(this.select(item, noPivot)) { return 1; } + if(this.deselect(item)) { return -1; } + } + return 0; + }, + + select: function(item, noPivot) { + if(this.isItem(item) && !this.isSelected(item) + && this.isSelectable(item)) { + this.selection.push(item); + this.lastSelected = item; + this.onSelect(item); + this.onSelectChange(item, true); + if(!noPivot) { + this._addPivot(item); + } + return true; + } + return false; + }, + + deselect: function(item) { + var idx = this._find(item, true); + if(idx > -1) { + this.selection.splice(idx, 1); + this.onDeselect(item); + this.onSelectChange(item, false); + if(item == this.lastSelected) { + this.lastSelected = null; + } + + this._removePivot(item); + + return true; + } + return false; + }, + + selectAll: function() { + for(var i = 0; i < this.items.length; i++) { + this.select(this.items[i]); + } + }, + + deselectAll: function() { + while(this.selection && this.selection.length) { + this.deselect(this.selection[0]); + } + }, + + selectNext: function() { + var idx = this._find(this.lastSelected); + while(idx > -1 && ++idx < this.items.length) { + if(this.isSelectable(this.items[idx])) { + this.deselectAll(); + this.select(this.items[idx]); + return true; + } + } + return false; + }, + + selectPrevious: function() { + //debugger; + var idx = this._find(this.lastSelected); + while(idx-- > 0) { + if(this.isSelectable(this.items[idx])) { + this.deselectAll(); + this.select(this.items[idx]); + return true; + } + } + return false; + }, + + // select first selectable item + selectFirst: function() { + this.deselectAll(); + var idx = 0; + while(this.items[idx] && !this.select(this.items[idx])) { + idx++; + } + return this.items[idx] ? true : false; + }, + + // select last selectable item + selectLast: function() { + this.deselectAll(); + var idx = this.items.length-1; + while(this.items[idx] && !this.select(this.items[idx])) { + idx--; + } + return this.items[idx] ? true : false; + }, + + _addPivot: function(item, andClear) { + this._pivotItem = item; + if(andClear) { + this._pivotItems = [item]; + } else { + this._pivotItems.push(item); + } + }, + + _removePivot: function(item) { + var i = dojo.lang.find(this._pivotItems, item); + if(i > -1) { + this._pivotItems.splice(i, 1); + this._pivotItem = this._pivotItems[this._pivotItems.length-1]; + } + + this._updatePivot(); + }, + + _updatePivot: function() { + if(this._pivotItems.length == 0) { + if(this.lastSelected) { + this._addPivot(this.lastSelected); + } + } + }, + + sorted: function() { + return dojo.lang.toArray(this.selection).sort( + dojo.lang.hitch(this, function(a, b) { + var A = this._find(a), B = this._find(b); + if(A > B) { + return 1; + } else if(A < B) { + return -1; + } else { + return 0; + } + }) + ); + }, + + // remove any items from the selection that are no longer in this.items + updateSelected: function() { + for(var i = 0; i < this.selection.length; i++) { + if(this._find(this.selection[i]) < 0) { + var removed = this.selection.splice(i, 1); + + this._removePivot(removed[0]); + } + } + + this.length = this.selection.length; + } +}); diff --git a/source/web/scripts/ajax/src/storage.js b/source/web/scripts/ajax/src/storage.js new file mode 100644 index 0000000000..f615757952 --- /dev/null +++ b/source/web/scripts/ajax/src/storage.js @@ -0,0 +1,353 @@ +/** + FIXME: Write better docs. + + @author Brad Neuberg, bkn3@columbia.edu +*/ +dojo.provide("dojo.storage"); +dojo.provide("dojo.storage.StorageProvider"); + +dojo.require("dojo.lang.*"); +dojo.require("dojo.event.*"); + + +/** The base class for all storage providers. */ + +/** + The constructor for a storage provider. You should avoid initialization + in the constructor; instead, define initialization in your initialize() + method. +*/ +dojo.declare("dojo.storage", null, { + /** A put() call to a storage provider was succesful. */ + SUCCESS: "success", + + /** A put() call to a storage provider failed. */ + FAILED: "failed", + + /** A put() call to a storage provider is pending user approval. */ + PENDING: "pending", + + /** + Returned by getMaximumSize() if this storage provider can not determine + the maximum amount of data it can support. + */ + SIZE_NOT_AVAILABLE: "Size not available", + + /** + Returned by getMaximumSize() if this storage provider has no theoretical + limit on the amount of data it can store. + */ + SIZE_NO_LIMIT: "No size limit", + + /** + The namespace for all storage operations. This is useful if several + applications want access to the storage system from the same domain but + want different storage silos. + */ + namespace: "dojoStorage", + + /** + If a function is assigned to this property, then when the settings + provider's UI is closed this function is called. Useful, for example, + if the user has just cleared out all storage for this provider using + the settings UI, and you want to update your UI. + */ + onHideSettingsUI: null, + + initialize: function(){ + // summary: + // Allows this storage provider to initialize itself. This is + // called after the page has finished loading, so you can not do + // document.writes(). + dojo.unimplemented("dojo.storage.initialize"); + }, + + isAvailable: function(){ /*Boolean*/ + // summary: + // Returns whether this storage provider is available on this + // platform. + dojo.unimplemented("dojo.storage.isAvailable"); + }, + + /** + + */ + put: function( /*string*/ key, + /*object*/ value, + /*function*/ resultsHandler){ + // summary: + // Puts a key and value into this storage system. + // key: + // A string key to use when retrieving this value in the future. + // value: + // A value to store; this can be any JavaScript type. + // resultsHandler: + // A callback function that will receive three arguments. The + // first argument is one of three values: dojo.storage.SUCCESS, + // dojo.storage.FAILED, or dojo.storage.PENDING; these values + // determine how the put request went. In some storage systems + // users can deny a storage request, resulting in a + // dojo.storage.FAILED, while in other storage systems a storage + // request must wait for user approval, resulting in a + // dojo.storage.PENDING status until the request is either + // approved or denied, resulting in another call back with + // dojo.storage.SUCCESS. + // The second argument in the call back is the key name that was being stored. + // The third argument in the call back is an optional message that + // details possible error messages that might have occurred during + // the storage process. + + Example: + var resultsHandler = function(status, key, message){ + alert("status="+status+", key="+key+", message="+message); + }; + dojo.storage.put("test", "hello world", + resultsHandler); + dojo.unimplemented("dojo.storage.put"); + }, + + get: function(/*string*/ key){ /*Object*/ + // summary: + // Gets the value with the given key. Returns null if this key is + // not in the storage system. + // key: + // A string key to get the value of. + // return: Returns any JavaScript object type; null if the key is not present + dojo.unimplemented("dojo.storage.get"); + }, + + hasKey: function(/*string*/ key){ /*Boolean*/ + // summary: Determines whether the storage has the given key. + return (this.get(key) != null); + }, + + /** + + getKeys: function(){ /*Array*/ + // summary: Enumerates all of the available keys in this storage system. + dojo.unimplemented("dojo.storage.getKeys"); + }, + + /** + */ + clear: function(){ + // summary: + // Completely clears this storage system of all of it's values and + // keys. + dojo.unimplemented("dojo.storage.clear"); + }, + + /** Removes the given key from the storage system. */ + remove: function(key){ + dojo.unimplemented("dojo.storage.remove"); + }, + + isPermanent: function(){ /*Boolean*/ + // summary: + // Returns whether this storage provider's values are persisted + // when this platform is shutdown. + dojo.unimplemented("dojo.storage.isPermanent"); + }, + + /** + The maximum storage allowed by this provider. + + @returns Returns the maximum storage size + supported by this provider, in + thousands of bytes (i.e., if it + returns 60 then this means that 60K + of storage is supported). + + If this provider can not determine + it's maximum size, then + dojo.storage.SIZE_NOT_AVAILABLE is + returned; if there is no theoretical + limit on the amount of storage + this provider can return, then + dojo.storage.SIZE_NO_LIMIT is + returned + */ + getMaximumSize: function(){ + dojo.unimplemented("dojo.storage.getMaximumSize"); + }, + + hasSettingsUI: function(){ /*Boolean*/ + // summary: Determines whether this provider has a settings UI. + return false; + }, + + showSettingsUI: function(){ + // summary: If this provider has a settings UI, it is shown. + dojo.unimplemented("dojo.storage.showSettingsUI"); + }, + + hideSettingsUI: function(){ + // summary: If this provider has a settings UI, hides it. + dojo.unimplemented("dojo.storage.hideSettingsUI"); + }, + + getType: function(){ /*String*/ + // summary: + // The provider name as a string, such as + // "dojo.storage.FlashStorageProvider". + dojo.unimplemented("dojo.storage.getType"); + }, + + isValidKey: function(/*string*/ keyName){ /*Boolean*/ + // summary: + // Subclasses can call this to ensure that the key given is valid + // in a consistent way across different storage providers. We use + // the lowest common denominator for key values allowed: only + // letters, numbers, and underscores are allowed. No spaces. + if((keyName == null)||(typeof keyName == "undefined")){ + return false; + } + + return /^[0-9A-Za-z_]*$/.test(keyName); + } +}); + + + + +/** + Initializes the storage systems and figures out the best available + storage options on this platform. +*/ +dojo.storage.manager = new function(){ + this.currentProvider = null; + this.available = false; + this.initialized = false; + this.providers = new Array(); + + // TODO: Provide a way for applications to override the default namespace + this.namespace = "dojo.storage"; + + this.initialize = function(){ + // summary: + // Initializes the storage system and autodetects the best storage + // provider we can provide on this platform + this.autodetect(); + } + + /** + + */ + this.register = function(/*string*/ name, /*Object*/ instance) { + // summary: + // Registers the existence of a new storage provider; used by + // subclasses to inform the manager of their existence. + // name: + // The full class name of this provider, such as + // "dojo.storage.browser.Flash6StorageProvider". + // instance: + // An instance of this provider, which we will use to call + // isAvailable() on. + this.providers[this.providers.length] = instance; + this.providers[name] = instance; + } + + /** + + */ + this.setProvider = function(storageClass){ + // summary: + // Instructs the storageManager to use the given storage class for + // all storage requests. + // description: + // Example: + // dojo.storage.setProvider( + // dojo.storage.browser.IEStorageProvider) + + } + + this.autodetect = function(){ + // summary: + // Autodetects the best possible persistent storage provider + // available on this platform. + if(this.initialized == true) // already finished + return; + + // go through each provider, seeing if it can be used + var providerToUse = null; + for(var i = 0; i < this.providers.length; i++) { + providerToUse = this.providers[i]; + if(providerToUse.isAvailable()){ + break; + } + } + + if(providerToUse == null){ // no provider available + this.initialized = true; + this.available = false; + this.currentProvider = null; + dojo.raise("No storage provider found for this platform"); + } + + // create this provider and copy over it's properties + this.currentProvider = providerToUse; + for(var i in providerToUse){ + dojo.storage[i] = providerToUse[i]; + } + dojo.storage.manager = this; + + // have the provider initialize itself + dojo.storage.initialize(); + + this.initialized = true; + this.available = true; + } + + this.isAvailable = function(){ /*Boolean*/ + // summary: Returns whether any storage options are available. + return this.available; + } + + this.isInitialized = function(){ /*Boolean*/ + // summary: + // Returns whether the storage system is initialized and ready to + // be used. + + // FIXME: This should _really_ not be in here, but it fixes a bug + if(dojo.flash.ready == false){ + return false; + }else{ + return this.initialized; + } + } + + this.supportsProvider = function(/*string*/ storageClass){ + // summary: Determines if this platform supports the given storage provider. + // description: + // Example: + // dojo.storage.manager.supportsProvider( + // "dojo.storage.browser.InternetExplorerStorageProvider"); + + // construct this class dynamically + try{ + // dynamically call the given providers class level isAvailable() + // method + var provider = eval("new " + storageClass + "()"); + var results = provider.isAvailable(); + if(results == null || typeof results == "undefined") + return false; + return results; + }catch (exception){ + dojo.debug("exception="+exception); + return false; + } + } + + /** Gets the current provider. */ + this.getProvider = function(){ + return this.currentProvider; + } + + this.loaded = function(){ + // summary: + // The storage provider should call this method when it is loaded + // and ready to be used. Clients who will use the provider will + // connect to this method to know when they can use the storage + // system: + } +} diff --git a/source/web/scripts/ajax/src/storage/Storage.as b/source/web/scripts/ajax/src/storage/Storage.as new file mode 100644 index 0000000000..e1ed9f850f --- /dev/null +++ b/source/web/scripts/ajax/src/storage/Storage.as @@ -0,0 +1,136 @@ +import DojoExternalInterface; + +class Storage { + public static var SUCCESS = "success"; + public static var FAILED = "failed"; + public static var PENDING = "pending"; + + public var so; + + public function Storage(){ + //getURL("javascript:dojo.debug('FLASH:Storage constructor')"); + DojoExternalInterface.initialize(); + DojoExternalInterface.addCallback("put", this, put); + DojoExternalInterface.addCallback("get", this, get); + DojoExternalInterface.addCallback("showSettings", this, showSettings); + DojoExternalInterface.addCallback("clear", this, clear); + DojoExternalInterface.addCallback("getKeys", this, getKeys); + DojoExternalInterface.addCallback("remove", this, remove); + DojoExternalInterface.loaded(); + + // preload the System Settings finished button movie for offline + // access so it is in the cache + _root.createEmptyMovieClip("_settingsBackground", 1); + // getURL("javascript:alert('"+DojoExternalInterface.dojoPath+"');"); + _root._settingsBackground.loadMovie(DojoExternalInterface.dojoPath + "storage_dialog.swf"); + } + + public function put(keyName, keyValue, namespace){ + // Get the SharedObject for these values and save it + so = SharedObject.getLocal(namespace); + + // prepare a storage status handler + var self = this; + so.onStatus = function(infoObject:Object){ + //getURL("javascript:dojo.debug('FLASH: onStatus, infoObject="+infoObject.code+"')"); + + // delete the data value if the request was denied + if (infoObject.code == "SharedObject.Flush.Failed"){ + delete self.so.data[keyName]; + } + + var statusResults; + if(infoObject.code == "SharedObject.Flush.Failed"){ + statusResults = Storage.FAILED; + }else if(infoObject.code == "SharedObject.Flush.Pending"){ + statusResults = Storage.PENDING; + }else if(infoObject.code == "SharedObject.Flush.Success"){ + statusResults = Storage.SUCCESS; + } + //getURL("javascript:dojo.debug('FLASH: onStatus, statusResults="+statusResults+"')"); + + // give the status results to JavaScript + DojoExternalInterface.call("dojo.storage._onStatus", null, statusResults, + keyName); + } + + // save the key and value + so.data[keyName] = keyValue; + var flushResults = so.flush(); + + // return results of this command to JavaScript + var statusResults; + if(flushResults == true){ + statusResults = Storage.SUCCESS; + }else if(flushResults == "pending"){ + statusResults = Storage.PENDING; + }else{ + statusResults = Storage.FAILED; + } + + DojoExternalInterface.call("dojo.storage._onStatus", null, statusResults, + keyName); + } + + public function get(keyName, namespace){ + // Get the SharedObject for these values and save it + so = SharedObject.getLocal(namespace); + var results = so.data[keyName]; + + return results; + } + + public function showSettings(){ + // Show the configuration options for the Flash player, opened to the + // section for local storage controls (pane 1) + System.showSettings(1); + + // there is no way we can intercept when the Close button is pressed, allowing us + // to hide the Flash dialog. Instead, we need to load a movie in the + // background that we can show a close button on. + _root.createEmptyMovieClip("_settingsBackground", 1); + _root._settingsBackground.loadMovie(DojoExternalInterface.dojoPath + "storage_dialog.swf"); + } + + public function clear(namespace){ + so = SharedObject.getLocal(namespace); + so.clear(); + so.flush(); + } + + public function getKeys(namespace){ + // Returns a list of the available keys in this namespace + + // get the storage object + so = SharedObject.getLocal(namespace); + + // get all of the keys + var results = new Array(); + for(var i in so.data) + results.push(i); + + // join the keys together in a comma seperated string + results = results.join(","); + + return results; + } + + public function remove(keyName, namespace){ + // Removes a key + + // get the storage object + so = SharedObject.getLocal(namespace); + + // delete this value + delete so.data[keyName]; + + // save the changes + so.flush(); + } + + static function main(mc){ + //getURL("javascript:dojo.debug('FLASH: storage loaded')"); + _root.app = new Storage(); + } +} + diff --git a/source/web/scripts/ajax/src/storage/__package__.js b/source/web/scripts/ajax/src/storage/__package__.js new file mode 100644 index 0000000000..afe714f21e --- /dev/null +++ b/source/web/scripts/ajax/src/storage/__package__.js @@ -0,0 +1,7 @@ +dojo.kwCompoundRequire({ + common: ["dojo.storage"], + browser: ["dojo.storage.browser"], + dashboard: ["dojo.storage.dashboard"] +}); +dojo.provide("dojo.storage.*"); + diff --git a/source/web/scripts/ajax/src/storage/browser.js b/source/web/scripts/ajax/src/storage/browser.js new file mode 100644 index 0000000000..1a82be0eae --- /dev/null +++ b/source/web/scripts/ajax/src/storage/browser.js @@ -0,0 +1,188 @@ +dojo.provide("dojo.storage.browser"); +dojo.provide("dojo.storage.browser.FlashStorageProvider"); + +dojo.require("dojo.storage"); +dojo.require("dojo.flash"); +dojo.require("dojo.json"); +dojo.require("dojo.uri.*"); + +/** + Storage provider that uses features in Flash to achieve permanent storage. + + @author Brad Neuberg, bkn3@columbia.edu +*/ +dojo.storage.browser.FlashStorageProvider = function(){ +} + +dojo.inherits(dojo.storage.browser.FlashStorageProvider, dojo.storage); + +// instance methods and properties +dojo.lang.extend(dojo.storage.browser.FlashStorageProvider, { + namespace: "default", + initialized: false, + _available: null, + _statusHandler: null, + + initialize: function(){ + if(djConfig["disableFlashStorage"] == true){ + return; + } + + // initialize our Flash + var loadedListener = function(){ + dojo.storage._flashLoaded(); + } + dojo.flash.addLoadedListener(loadedListener); + var swfloc6 = dojo.uri.dojoUri("Storage_version6.swf").toString(); + var swfloc8 = dojo.uri.dojoUri("Storage_version8.swf").toString(); + dojo.flash.setSwf({flash6: swfloc6, flash8: swfloc8, visible: false}); + }, + + isAvailable: function(){ + if(djConfig["disableFlashStorage"] == true){ + this._available = false; + } + + return this._available; + }, + + setNamespace: function(namespace){ + this.namespace = namespace; + }, + + put: function(key, value, resultsHandler){ + if(this.isValidKey(key) == false){ + dojo.raise("Invalid key given: " + key); + } + + this._statusHandler = resultsHandler; + + // serialize the value + // Handle strings differently so they have better performance + if(dojo.lang.isString(value)){ + value = "string:" + value; + }else{ + value = dojo.json.serialize(value); + } + + dojo.flash.comm.put(key, value, this.namespace); + }, + + get: function(key){ + if(this.isValidKey(key) == false){ + dojo.raise("Invalid key given: " + key); + } + + var results = dojo.flash.comm.get(key, this.namespace); + + if(results == ""){ + return null; + } + + // destringify the content back into a + // real JavaScript object + // Handle strings differently so they have better performance + if(!dojo.lang.isUndefined(results) && results != null + && /^string:/.test(results)){ + results = results.substring("string:".length); + }else{ + results = dojo.json.evalJson(results); + } + + return results; + }, + + getKeys: function(){ + var results = dojo.flash.comm.getKeys(this.namespace); + + if(results == ""){ + return new Array(); + } + + // the results are returned comma seperated; split them + results = results.split(","); + + return results; + }, + + clear: function(){ + dojo.flash.comm.clear(this.namespace); + }, + + remove: function(key){ + }, + + isPermanent: function(){ + return true; + }, + + getMaximumSize: function(){ + return dojo.storage.SIZE_NO_LIMIT; + }, + + hasSettingsUI: function(){ + return true; + }, + + showSettingsUI: function(){ + dojo.flash.comm.showSettings(); + dojo.flash.obj.setVisible(true); + dojo.flash.obj.center(); + }, + + hideSettingsUI: function(){ + // hide the dialog + dojo.flash.obj.setVisible(false); + + // call anyone who wants to know the dialog is + // now hidden + if(dojo.storage.onHideSettingsUI != null && + !dojo.lang.isUndefined(dojo.storage.onHideSettingsUI)){ + dojo.storage.onHideSettingsUI.call(null); + } + }, + + /** + The provider name as a string, such as + "dojo.storage.FlashStorageProvider". + */ + getType: function(){ + return "dojo.storage.FlashStorageProvider"; + }, + + /** Called when the Flash is finished loading. */ + _flashLoaded: function(){ + this.initialized = true; + + // indicate that this storage provider is now loaded + dojo.storage.manager.loaded(); + }, + + /** + Called if the storage system needs to tell us about the status + of a put() request. + */ + _onStatus: function(statusResult, key){ + var ds = dojo.storage; + var dfo = dojo.flash.obj; + //dojo.debug("_onStatus, statusResult="+statusResult+", key="+key); + if(statusResult == ds.PENDING){ + dfo.center(); + dfo.setVisible(true); + }else{ + dfo.setVisible(false); + } + + if((!dj_undef("_statusHandler", ds))&&(ds._statusHandler != null)){ + ds._statusHandler.call(null, statusResult, key); + } + } +}); + +// register the existence of our storage providers +dojo.storage.manager.register("dojo.storage.browser.FlashStorageProvider", + new dojo.storage.browser.FlashStorageProvider()); + +// now that we are loaded and registered tell the storage manager to initialize +// itself +dojo.storage.manager.initialize(); diff --git a/source/web/scripts/ajax/src/storage/dashboard.js b/source/web/scripts/ajax/src/storage/dashboard.js new file mode 100644 index 0000000000..0c431d12f6 --- /dev/null +++ b/source/web/scripts/ajax/src/storage/dashboard.js @@ -0,0 +1,42 @@ +dojo.require("dojo.storage"); +dojo.require("dojo.json"); +dojo.provide("dojo.storage.dashboard"); + +dojo.storage.dashboard.StorageProvider = function(){ + this.initialized = false; +} + +dojo.inherits(dojo.storage.dashboard.StorageProvider, dojo.storage.StorageProvider); + +dojo.lang.extend(dojo.storage.dashboard.StorageProvider, { + storageOnLoad: function(){ + this.initialized = true; + }, + + set: function(key, value, ns){ + if (ns && widget.system){ + widget.system("/bin/mkdir " + ns); + var system = widget.system("/bin/echo " + value + " >" + ns + "/" + key); + if(system.errorString){ + return false; + } + return true; + } + + return widget.setPreferenceForKey(dojo.json.serialize(value), key); + }, + + get: function(key, ns){ + if (ns && widget.system) { + var system = widget.system("/bin/cat " + ns + "/" + key); + if(system.errorString){ + return ""; + } + return system.outputString; + } + + return dojo.json.evalJson(widget.preferenceForKey(key)); + } +}); + +dojo.storage.setProvider(new dojo.storage.dashboard.StorageProvider()); diff --git a/source/web/scripts/ajax/src/storage/storage_dialog.fla b/source/web/scripts/ajax/src/storage/storage_dialog.fla new file mode 100644 index 0000000000000000000000000000000000000000..1273c10566d2646702e58620ed458dcbd54708be GIT binary patch literal 344064 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

oQ3v@ZGCbGd&00OM!V`@SX zT{8-sI=}om_+^E>@6(-M&a&R+W4)`6T4L$?H)g4&HSO;u8;1uq4F7d~D_uIt<<;X$s8{dlrkP5;2m z>tC-Jk8f4?M~6bY=0#a*?V}$&`f#i4rURC6WCTz8|W-?!>_3 z?WQfergHz;ylsP;y^b+Xsbz0?!sqfs^Nvpv%~Y>=e=^ZquWy}s?asZJAEOKA?;TRv z|B=0ZzuE0A?p_(vCh75#&$lPL)iOO=*UR)*W!nIUyt;#Ow^X_EU}^sFwA?Lq*O$(| zvu=Izyk`TRS39t|)6h$wUw2-wKWNa2JxfQ$j9A#!HDC8()Xqqo1Yh$b+JhSPPs=?! zdZeko?A5;e#HP&NSaxXWGnXmPOT$7N5G&;a%5HD?Zv47hkFJE9`+= zx7nAgylr7{BcgBddF5GUl{<9K**&u53$qq8uQWJ6aPXT!>+kBGTr%s5^VkvVrXB6~ zYjSMaw+=r)*8l!=NA4(%Ziyl0=^gri2v(as;ArdYo_?eAMt-?|;Fa0MvQDc9oV$=T z;96|{h~RyL#!oQyoo1Z+UgKif9FIwHw;bx_XTR|cdirYDil3JboNi=XYI^rh@_{eb z7t1COeXX>wplENS_NAte_9h#bdRX7D-)Bk(_uQwMiCeGuj(hD_v0p9yK5cAcE-zbC z8lpDlUdhT5i=s_WH=KHs{$Tor(#&YBHaW_JLafHy_ue}zzL&u^6^$1olNKDFe`<(I z&FjV=H{KY#f5@s>7ppnG?q644t2LnA*}_-rDjF{CbKTq4Irnlu!|vz$eQ^F%QY&In z!cLvd!Ijl&pRL=%?uNmbn4WdQnjUPk+a)+f~TW=+ZS{%_jdXB?e=zMmGw7 z*!o!Q#OP#~hrVrwuI}Rzcjt*mWOC5SfTO4GJuw)X_Otk4sE*~ay^TKBUVGW%RHGiT zu8!SS+vbOV@@*Htpy6|!z)>;!2llGAeA(o2%gwgVR?d+#n_NjfW<7Xzv*yuhU366v zr#X4*+&t@bH=t?$G@H2-!hHN)-&gma7jUkB?S!4D3$C|p_fe^7W`6tF9y_XP_EYQT zzvIbCYuCG%tnXE@4XaSrIJe)AqZ9Vrd-dk(hQk*kd$0D}k~-pRWcE$3)q$<%#=H$^ zzS5(G`oe?5->-NXysGx;Ec5gepx#Z2tt%sj<%OBQ% za)eRtu$QZc{_1?q-s9ExOrry30gf;4&v`U7L_cS*Ns~7HtG9U_vG=gg`+G(k-KG;oduv^1>u9LDNcD$U@ zdc@m;cN6b7oRl^9WUHr*j;@~h!nDju$8u@gZw>>d+&o-ig{7;_$d$)qBA+fYZ$EOJ zeoEqzS;KC3i5`~TWsK$0Z$^tU5lt}pAIU|Om9a#hE|)OZv9<_o%?+&ObonT|?Qx1rv%W7;e+ zxT4*1K>Gm|A9{`}v^gBDk*t2}UaJ&OlWi|_t)?BiyllZH$93->j5RvH__I#Gjz;tI z)-AYI*(UjUpI2vAgq3bPlU+N;NN2{z4Kr)3Z#v8Fv0sS$_Al-I+~($V?pdqzgHd~o zzPI1g^Xj)bSG?C3&ewFRb9!wBxB5T7T{ttg-#g0DADvp?RR8ox&jZKK|J>KH!UlDd@Uj6PtLk`6 zJ2coKO0{X50ro20H#)U>_$n>-oK20c?%y6SZs7R($Q#G)I$vfwDWzzA(KWyKaEV=C ztJr!Qo}6mnP!@0hI^*=iCu>$4F1s*cbA$b@8)@_#-G5{B*b0FoXYDodn!m4ozzloq zGvDqQ+LcF5oo$G1gxX4ojZ5Eeo(RdsPm25*)99N zr`l|LbK`2YE~;;CtFdu+)5-mdCS8eaw(_mTRNJD^^pUo&UXEV>Ud2W8ief?J8ET=U8d}sJ#Zx`nsUw6*iusbGu$cEasw)48I*f?hQ!risQd^PGU z8XX>JTyg98st$)&DChQ3S@*`*w6$Z)o9BvKv@h{f-tT>PhS_UjFqKDh<;u6j(_V;NMudgP>QH7EGwRO+**TTS03vwGx5oh@ix zRyVe^&xVrG*#?Gcb-bIdoThnu-1ln>_ebXB?EHG=Y1TNSPT6%<_ENvw+AM3K{`fAh zEB8J5c5Ah!^|BuoMOgf>pO;>kzCB8HOyR;~LymXysOC`ZfOAov{?siER{Si=YrWaE zZ^n+pg-spuN+%pG%q#kQw1<1wn|rohy*jk!(m-#gEk&Q5!=t98wch3Gq1Va1g{!~X zm|+9lEX=y9Y9_F(JBns}8=_n}(0| zf6;sVdZ+0Ar^fnSo%`vlL(!liMjBBI?Gid0?(f*%^yStiGe78MIIlF*Kclmz*@SL` z501F&n6%P+`=4sB?c^ALfVsV#%i+h^gP6^%D z(aNvM86ABOHB+Pf{Ucqjm}_hAvdlGY)T6=TXPe$vG4Vf_?)-Lt!RK$|o@q_}8L!&0 zW9a3pAI{G)v8&J`HL2#~n_G{#U%&Uh+u{J-%8xyUDt~luQE^B_X#JSXnZXtNO{;Mv zHZ`l*<$aB4mD}4bKkFJ^ta5IQ?x|m!EnjEay^oz(lsVmQ>)k`T?{lhjTH|XmM8zU@ zot5SGrL{8)tNuz4%c&S?x6^iY-}i0z)Q!L5nIDim(<#|MYsMXa=bvetlUDp{&|T^ytHG^R645 znC$+-ZQucrnRxoUzH(vQ(2a>{*2y=&^lD}s!Gru4J= zF1tO^b#>Y<^}eIEZr5J*iCvcDYc)ttKK9VQ+N+;K_E@$zaam*4rb*1VX7BuzFZCWZ z%D<{trMi9XD$HNraop`n^KO*1f7vYa*r~LZWytXR`x#&G3DZWDISQaXFmEIzr4ZH)xIYb~AEP1*a=c=#dD_*rL6 zyBQZfpE2iv{-MOqCm%Enc(<Rx- zdhvjt&!V&@Th`5M)b9Fq_3K?P4bqsp>%<-Hoj+T(xp-+=mAD&47wX-p|F+Jx!?$~? zzBRJfNKMkXy2r;hI>>UFb>Ch;Ck(6q*!}Ja?Unm__N>y(>&n%a6BZRt|Ki~=b?3V| z4G;FwxYzD@+=M{)%d_isspRA8(JZT0m8E?u+}P$5tYY+Z(fX!)ALw~)uC6n;_F3cf z23-#)-}l`Ac>9KL`H8C&FX*(bx-e%^wBENhO_k5|`83^hZK-SafQ(43sqZFPmu`7* z;dn)d^w?jXeP8q|Hu*N=_}nVF72nrcme90G%2}toO&a^}k9NIjWw3AB`8RP#hrAkC z+0|-aH?uR_`kSTuG_}6?W79Ur`YtUlSsUjJZuD{SuAcg3_RqT=DQI%f)a?GAzNQ`x zO`G0RuK7J;&$?eX+D`PZQUCnYQ(5j#S_aq8Z?UL-!PKTf(ZrjJv{Tv{AFASbF0r{u zSnUNZUa71&3XitGA4~YHJiUu;g$;`kKVA zo31Q6vu~f1amuSZ>iH>OPS{>wT*s_??&2$R!isa$kFV3u7#uTsQN&$PnVwc z*u1Ul>EUIX%_nUz&Dys-q2`;%&0jy>ZfcKpvE8lqgeaSLv3y}uYwo0HHKz5iRr+Dw_>BQiKAX+%qxoiXgm;U}NBYM1 zxfqeP<$DMFvU^t(+iq){H|+A`?=66(^#c3zK%o=3=^r+Q*oo1(NcIgkMM!jC+zP0DN%2V8?^y_LgFm~$w z_76TzFX>^RH~QpT-3I5JTdOxv&ED7H!=5^060es1+OInNhUcSBmlH1=#XL{Hy<$Pf zl#fpmm#uW3cyiVCjVSxSXxPKo{`r6pliX8p4sQ~X7rJ|I-OTR0+l)@WxG_~V?R)j` z*zN}x#TreyeZMuAMedLGtkG8L^Y5cR+wy8BH^jgp&dy9o?<6Cnc&w2E^(r~TZDuJys zdNmxAy|tj->C9f6?_?i1`{hh{>7$$Jy~botb5M5Z(IqTs;iKMJF4=Y=b^CYFDBAtP zYVF9lZjFc6FiY@!{8ddY;7ycIyx)f7Yo^^fxOh$YmMEKs`CYZO$D6)v>v?xTmF1T4 zhYh3VCaTsLJ0y3CS>%p3#uYn9&)j>g@uCAgCk>vO>odBa=8;z27VlYctL*XsoOKMiu&NO%66P zR_-~z(Z^P=tlvET7Thi^?%m~6?JDF&M%?bXbolK9>$CLtJE@x=yk673#kT6#XE;o9 zTwVKZ%0A^a#lf9_Y-{D)U9-}H+s&H(aGI-Mcj)pFPF087RhZWJz@@Uau0?V0W~R^W zSMfm3vg@Cc9#wtYv2MOfivQQCpGH)_+a>m-$Lb>>_? zKcGgF>~*^ml)I}{pZU4vikP35m$n&!TRx&E8N%9j%twI zZNX%xudgbvJzi(J-dgKR?H5e0va4_KZoeB9EVI=nFRg%Q& zQyd>pwMm&WB>Il)v(~LXxz_)_vc_ts^}Bbdrq-EbHnZJ$KmT__RC;JkPO@BBDf{^P z(B7@*UatCDd!qBZ`YJsd{%AG#`^?=TTTM&4&%In_#G|XTax^y`ylL+KrAy;gMOQ6z zG`pH;O)tGGRjkp6t!K>Si3g zqvF8y2OV>^%oHUgFdJZJ)r7#hv52wynGF*_j^goYv0Gy_7S$Q`^t2 z+qCR^y-BuhzdKLUCcmrv!)#_k$*@WzPPS`SvLy9_^`JZ3-VECMX3(}bd0XEYZZ8f0 zkf5U1%P^>B&xBrn$u8fjG-`S9Z2qJDH|r%0d+=&h!Hd|7O&-455b(LU(arN~er#US zBR|%;$DKW=XIX}gUiB(=iCNOH3gLk!TkbpBylCD$$TN2Rx}DYcKGK~%G)vcT_xh;w z3(RBHiyhSJ*t%6rial!(UvK!;CF&>K@0y;x+G5(+$|0XEosPNBt2}q?o)?d-I&9Ka zS2uIGUaeX1M9(h=RwU#&`A*pP=$TRC$bKDAQa?fYoJOAEV~2SLp%$?Le#$ESQ!Q<_ zC{0>@*WElOBc|Gfg{RI}Z1ARSlL`jItE43tsgJp^{z8{!8V83SnAfEJ#r(0uk9M_O zc*;9{>!HN?#$C(KH>h{El0kK4zd8FgF20EGo%6KG)zURB`g)%)*7{)4-M&pP`?1wL z9Y?k6Wzhd&ZOwM}cB@yeSe2_1H_v(FijiAeoQ$dJH){HxbxPejcDPyEe3kV%%@?hT zCMNHk`hNgGK)=7kIit@s@B3Bx~5z}`+9IQhE0ZKrC zAQmamZ@Wp067M%f4(>MLg6}rIVZsHG68-dw?2IFCW)+4WC?_)aFDQWk6IrSSFZG+G zMwL`g=GujZV5$WyP_#}JttaMRx|2BT7AIQSQzv5!DNr7lmv6nI;59Hixx-WJ zwU^<#wh!9o#@kh0?O4Ve@g8>BxuIw74z#T)#UXq&7>5s1>}EM}IrWS5ccpRSF6IJ> zA<_-bmVa@8Xv8HoV2x0-?pKga0wo6_E(l=J5gV8G6xa}GkX*d(Fk1=dkd<>-1#iN1 zf{My!B&RVItWrI%W`oaCy5`@6m)O|8YXTeycVflQLJ(W+ou#y;FdHoti=+L zLa?r%MbhKPkLEH((N)ZFR?P61=A937wVLO8n;yM`-^?4r(iIy+M1oD4NU$kHS=o@+ zS_IKd16^XXJ&3M~gHUq&Hnnjqyg*uE0t^hweR)8AX#lHj{n4eGBR&UHHDWt$>Xsg_ znOU1tJ``xK#KOepn_MvV0QsxV-6B~xA>$_G+=ReQNV^G=>%YwOg0e$1R#W6Br;#JO zAkxsTHX8oBJ722;*U^Ztwey|qS~~E8J@7;{bStC|%3e0RijjS6&{ah0g8leuvtUbI zZ~zzTBMH;Q$9X#Mg1vqt(E_1pji0Sm3IO-u1$Pi2oTEb~*ylHJ;hg8!hKdCPW`F%C zE08sWe-23v8#HTLXJAe!wjLB^r;Kzc*t;;TcSVa z5_>?Ecu=(=(VH`QQ*SUQ^#&g>JRmkp%!u(&?+>lIYpey4C=FhYIWlW>+bR)qfbo!A znl@3$5eH17SEMG1c!d)EsKm6BNaV^&YcQZKn94~yo3tjhqaM_obS+sc1yf8{ywte& zX$H_)z0H<*Vr-~27o3$i*TWJcI~(cLXHaA}cv=fhB%KpU-H7$8f*N2xl{p`B)$@p@ z;F=5`Rh)ldX0QJjTD@VF}QMDQeQ!D*3AF7)Q$EgX6(7?PTF!2u3k2nrk;R-q9U z8dag93XQ4IMHL!Xp^4xGhn`lUODZ&}LYGx&N`*=)G!3Dx5PEAcLsZr>{WHr9Qu>Fu z?6{?*&fk?-oxitM)cJd>tMhjilds|5X9sy;hX4sdrp<0x0dBO^)@EQ z9_8%gnEi~Ky*{rvZz6lf$gYlB`*uaG-GvK(v;kbhCq&Gq))ql4r@S4Na}3ws^9`LD zsCuI=(HTLsrCOd_%do5$WwAnf2A`gs7%6Hp8kl)-TMAjJh-%NVcEPcS=ZbScuzTwe zDMFQ)GNUM{tov+R-gGfzEX`UAE&*JSF$>VxwR1pE)nrOO|e~!Q2+Bh>w z=nD#q)WsI5lSS%di`2^^4Y5TUlxSEDU;~i&iO7r09^@i`;MB0V?5QsfP(g80|Gat{!=X8C5Ew# z+|Po)3T#0h(h-*aiL92EQ=q&KgC@C5s=Ux>eL30#Jla&B!h_MX4~TYD>~BCI#9$07 zRjom%W$J3#%~#77yjpfgUoDUHhcB1^R0EZ7oKEhGzg)ug^z{;PT|8L4c0X0TRJdNA-evfr!MsPNJ@FS>aqb)4D8*Ga6fG4-IkPkc5%JF>QG4C~36<|4R%&PR>KH%n@`ZOIOZ_HJ;>Y<4s)&U$~?eVNwI@ zZeygh20uMU3iykl=lN#xmi;L{6a#%Y&EVlAlFT{CUVXm7`N=0$BWohQ`&z!IDqMxN z>ME3jkRprqSXQ33VB4?WxY(Immk8HrQZ<_G8XwheVdO-gXR>Zf?8sW=;9FyhwC3HH zX4_!MqKpFN<&38eC0pQySqDl<#&g#{8iZE>OSd=(|P%sdaW)} zuu@&KHAI)mO1jK*=oWibuqIfm0Zw~$9c+|fy_>vY@K%EtY(()Mb8BzH^qy>UIGyQI zrr0w1y<6e3Btzrc{6BbHEe_j6n$g|@<)mFIR52EV-5Qe4ULi?7TeNartJ9>cy)CLX z*Xbne2#k#rc9fCW5emTzR9UG{5@Ru9M{YOtbuFy9Y+}t(RedBHSWeIm@w+H(DK2SC z-)xXA+*A@V;pa57a~W%D^2D-oVm|0;20I8z%^=#BMKm)LjrI}oW2bTjjnQ3Uy{fOo zytuX-Dxem4-u4>6bAnA_rI9F`V9=y0fW|G3V7fBT-qm)q{kCAUrahkIygj_{cmv5i zv~Rv$)NBm0m!#`TcPsu_;x-|Vh z`=~T?L;*I3WLeQQJDD=9v)d3Di7-Nc5ncnuvEqzaP%VW>Av5o^Hj>Xl$J)=373am$ z&?STFIO<5}A|v9NR2bE+11y(#@L;1Motp)Y1;lw2)(33vZNnXiu~CiGIOidut$!Cg z7Fp*-WQ#WZ>e`@#7hBqzi@vwg5oFSgNhZw#(UFXxi;b9=n*dkOX)ozZPmeiC%>l*| zWz=FuT{xpYZ+YL+8RT-ZDV#z6AX~y2t)`nvGh>PAmeb5=$BboW1|kt1mPgLzMn1!% zi(aga6Nza;n?#LtWHJgGp_;>AKsFQVhDIjB4TaUSD9u=jN0156=<01KgbkHM))I-YQ>0q;nY?hHN zBCqw zk?9ReibL%Ui$m=bC0)U*z2xj^Kkb?f?y3L+2mBy7Km*JK_lU?nLbb+($sB%@qI~b59lwu;l?V=toqd@1DI?KGY-<~m3nxHx z%rcsE6wAs=hxlo7T#x;P*7>KaNY3vhd$<&3cEfT>u*l`b+P!)eo>4WAN{a%S;QkQo zkLHgIn3Wu=datouAsN;C2XWm_nd^q#?4&yB9w0wpH)Nybbd{dR`;UcJV~0FyP`NNT zt;_)I#g(9!#p6oGK|SZ!x}$z@Pzk$$9DpC3$%AN5@R00jx9P3?3<)jlbsA>LOr!#w z(Vf+^T+$gVYPvbk1bGF>b?%T3$+->S>jPN#eX`owDyltfRTF^jH%zb%(((oqUbgK0 zOCIw%+xwIl`f2ahRuW0b{$=TYYmk}AKJfGS-SAxHY_Jt zb7kcf%Nve!NhKSIOP=FrgLr=Hh^=r2QotkXH6=~N>i3oHnhX6iS)D!8H#0GMW@1V_ zM#gnerLyNPCu+F=BIi*Bc0x8O(g(y~QIqEUOtE-MC2{V_$x@j$ZFD{*N}Z!K#ln~> zkS~pr&Xid55m9Vzrf7B0Yb6_{oQ`^(3~#zLJvXg@ie9~>qcQRdhwjM;pulsAe9sL# zF33!$X(vmUhf60)SH1k)<>Cw&L@rKG7iSC)Y^2KOfkGEK4Y4$_Oy_5=>$Hmg#zL03 zLtaVFqwBZnxmgUM3gKg7Axl@}5_nfI%Eb-$ap*Q|v@lzM9l_bhCyO)S`x^cd)KK*g z|DeNb@szv>R)0?vX1wDgVtAthcH~3O+qFw4dF9gN#3*mC%8te;e7-czp%X+c5*vMX zVx%zHSD5zDjof%iEVft+F}kVI!pxb%wbGoVH$7G?Ug|AeV+sju01DE`dih~bI(M+d zsCdUBC&S>&&6^efi}R;2J2P?hJepN50oCi8cf}j`#EBrD`rGh#XVhPj5KO=K97?nzXj`PqMik=Zvl&>gXqV6WPhyre_5t%;|go2}C&9`a^1%x@}~BC6DXai$OA|XA#0=(7+;uSfkOXfw@iS zHjE?9*|j>SCHvnKs^Qk4jgyz4uP%-(&Hmo}zm}2q?4Qj4xva2E_1}^G{`bEx%XFHN z3OUOR&sK8_P#x}vdpUNo*`9XwaD2bBWrjXSbMF4!n(99us%CBHYOt* z%*e*<4}S0i8QFyFDz4oVZ069GU@M2V1=~5agAGF@gI&RHuD&OGOV4O8_kt_B4>#3* z7oFROJLJGQi;pfB{vLB}4rX6@*a`Y2tE;Bz>}TFQ1e9(M&y_o zIc`Qyn30nlQ6FN+7B7+K^V3Qc`S-qIe=zv`u~d6_F-dfZFSc-avzC`?X| z7ra*S!uFP2WJJ_!M)a>NaGG1nV)Ix(>*gT==ptY)EleaDXFXDkOlXbGWbiaZYS?Tf zn|iK+e5gPzfcl})!8WvN8^{#aBFcddAW`y9TnANC=`Gz4FbbruW}*~zdAC|U^oMHe z7eT#dl9A1fjs54N;?f?gyN>##qM25+!^coK<`S4s)D*dfT3DMYzac0w!MqOSN~ZlZ z(&p+Q_pN?w=G33Pbo9{6gP*Mfau!rSc^%YF)qqh?sLC8EUbkFljUzwlCo`Y?^nX73?R%das{`%B zdX;?b-QPR9>%acwHYV4x)3kISE?Of@WwNio99C3Z0 zJ=VIuP`?>AG0`VOp?Qdr)x4IGaeHd@A|P7d=j;uuPH1iATxA_Or^pE3Bm@~|W8Dc#s)lwLJdagXHTa*$ouG3*M$z+p(W`GjpSZPueBT2D zivdwunFGg%VR#{B1A{cLM2;BZ1pjN^RYL|z5%xpmDVbxDoz%R4CMZv9YxOZ#%1cQ1teVP8LY0^L=t8-g%1h14pd**rv*(YF?h*ghK~y1! zV25cn+}F_>=8R`9Bk`Cd>)+Rsc?t@)QaAL+DDT)=9&C3(pQsM>P^>Qm`UNYug(_>% zFIag=sImtAf|Zw=l@|a#12w6Env_6ADPyII&Om+bAPn!`MSw88dlv!1@a|m%2x|vk z|Hcyy|IZ1n&k7PJhOj!@YCx4z1U{CEGPXo@@5q;wetlok0>g-y;rh@FwGwUZbv7@x zCSt}RdsBo1KMfwIh zq1qwEz@$i^hEY9whXWSeb#49MSfNxq7_CVn>S0c2mln^WI=hX89B=Rx=rU6F9(_M6 z5jT|}-=yqi9v)_0JR1T0weg!QA1YiQ50rd7Qm3j%84llXG_uTjbRJmv(YZ=KI$f9T z)HY`xT)bpbjecDoQm@PXI2h^Ye82O$l<%JH*1KoE;bWap^TxZAha#Sr(T^pq)?s)& z*SZ_l11jQ8CEriyOU_eD#!eI`OIN(HiK)>%NMt-yn3#ko7}p-38yn+DSv|^5Ttk(N z;W^O{HI8;J%cYswQ*%=&K%^gVv&Hf(yflk5&lE?kVQSSAIoM0=e_cEfOOvx=%J5RW zToMh8qqL6sajliT&il;BgwV)yBODvf&H|>o=83ap&+6(9iVIP)yjC|gfuN0JgZk>; zrF=;aLmPxCMeE&w&1nfxZ)m1evQ&5Uei<4XH8;}T_Hcb)SRGG^tNt5oIV~ko#al89 z7ciYHW^#aH4x!|npFoLAC%MK?pvt(DTK&F=lyJb01qtT85g1OmV=JHj2bkQg{TiE#sXyIBA^ ze89El@p7s~WeE?w2uZyOZmaF)CNv@sf%hEk$RQ^)T&ja56VMd+8h1&DTpNg{|vWzL8*V%?oKDMEb#Wj?MVd2bt1*FcK zng9-6IhX#3j89nl8UOA9q|JU@%=T5-Izu%Y8muU&9>0$ zxX&8kmZ2>Gmz29^-pmfayMJqBbl#3|Mif+I&717|6u8zhYJD(hFuaQZr$gdaZ%`2t=|FJP(vv-*&>OR-E&hPVm+Q{~yhlsNc? z64druR`OfR&PSznPjk9H+;)=0C1)H1jg+$+&JYUY)({QC29Zxfhcpp)*safBwazvdmO!7`&Iz)W) zD<|nrtXtVbM*^6hMVE6KHY`ZO7QQ;lM7pu)D*-H(7d;cT=rMI@(KF#iue-Sw&hqSL z`PtEXBsHoZWtq{58Pe(Sz8|xlD??kE;xp!*zAo)Ur7&H;C@UTQ6{&b#jhwvvtCAH@TDydA>*WcVHpybsHjJiBW52zXwI2i*tbs9Jw zVFQ*`04Ikt%w0!)U~^MuDsIXQZOUxB`ZD7-WtKN(F0?6gyeY+HRY&08AF-c7rs5(W z;e4UiDPbYJitB74g3ZSiZfeirSoL$D{hF2gbqj9dU!RW#WRvL_W$Pf@zh8+ndNQHK zV<}LpJ)#=~WSrr4_wL+o-4kq6TH>a^H6Pp1e+7H#iR+>-{0Gcc-HensT9?$HR@}K-LQ$y5@vR>DbM+2r^UTle-ghbpuHIYJIJ167p*e z*rF0DgYbD}no`|`Bk7i=RCo8Eq2mJMIiM%2@A}*J!sY0IiNT+s!Y_H$$ZDeaY>%j) zlguMN+x8C1bNvZ&Mj^ROZcPP?1j6EW=;)Nqr$a|4a^e2%tAq`QqDC_Jl??BHdj9lYQ1!gxel9#MY846dsgXx~q|o2s`%(%KH&JcZ{&hy8p7G=f;R zf`G(~kk@z#(Z}==_9fh!ou6-%j)m7}U)7Nf*~IUHP69?YW}iza|HDhNMNdVRX2*1- zBm0LsvLgG6jNK3cK;n0;W7S!4Iau*S|+WNjHDlLr-3 z>i{+*nOXtTtHji@DjBRSSHh6(tg$-q*}3vKds>Z7E5{Ya zjN)Z*nmX|;sdyXtqK}6PSH->EKQ&sss<=I~GC>5Cx7LebXYA^%Dm+%2xh#h{WoFD2 zCrcyX+4S7C@@(<4oGceDkIWQBYaoz_qL%16))0Ag1V+if=nyGTjG4e2pBRmhgq$fp zQxuRF=@z76B8BPMxfx|u7DLT@0?Wqx2#PoY1V?sdj_V4_NX}m{wG;* z)f>(ii`26>QGTd2N8U1lPcCvKU&M{vNGMsWCR0r%jkIc>$CS}_Lb2Ai# zzC~e*i~y-)^7!OrFZBVoug?`{RT~r-FQbyU$Ny*@dfKMR&Fa+&D063YjBd12mwBJnWvii=j zP@YYDo2AqOGJK&HA1`7;(m42VuaZq>)87P#VM;TO0&RDFLexApgKm*SXNVz+e3C#l zHQF00q=l+SCMQNNIVR2Fn1OF$RGn|ujK?Qt&li#9E6N;~-vkW%ajb^HH&GBc^f(h^ z_Cuv9{i$iuY%uJuckRWZ-q1r=u`6-1(7#;&5!?9)oeT!V*>kfrPzie;EC+)5j6&Iw zSFpI`VQzbjui&tUV9g?`Xd58LBtiH_Fs+GNr}o{(7yrnb5H5J9EVh$EN7j^|4nkJF za)!e$!!Fwp$z)*xWkgu}f|9nEYX?K-+v^}yt9+>@v5fuJ53~oYdg?UWYGDqHO*taww}j$^mT0Bm7t4Qy?V;!+|I){-1xB3KtruMECUDTUXf zJqLtKt(T=XpnPPbJC}`^yh(Q6GichK-i*y{Q83?la}}@vV2K69z0%|;S+Pw{6|YdJ ziJ5m>;WIQ}aNC@%_cqVyXS?hN*r$nL2Q+}B7PS!A#!ln=xtBZI6=7DBX1+VGHQ<6h z+Q9u@HJyDlCSQ)}rSO>c+heMrsBs{|FylQ;RWuo(2hn$K8xQ}GI=FU*9sS*_MtV>m zjsm;mV`8=O>{J#=4U2{Sx|{-AhJ|m`A;lZkZiF)>UE+)LREY<3ABhnG*h*e%3T(oI zb-tt&PR2qz@|qz{t8^PjWY6$$1keaTD^*QT0Z4-CkN_2rWsgG`c!j$^gj?W9*o@-_ ztH9Cxb<$sXInMV`mQYDg7EIja%=9hjP839MQgX5an*5QRt1u7f5 z500yj@8LZQ?peJ5)iX9#0s)g?!2BwA*Jo_gCnM7Oh{~t#jD*{m!DhB9zpRaSiV=T~ zc?HYmy=vy$J*eS|YG>xLWuDss(w#+OS8&=OL?LJn58elB*dLtk>begS`nzPs!_FnS z!G&M+F(Ru!aC1LMY}faA!L*l|K$Ozg&#v6)#Ee%rSt?zcoAzq(K}3S6|FrfN6R*<- za|=ukSUAl$Ya~uXtgpbY4dXOHzv6&kDvIBOSyMZCtCjqRGFgKC4AN+=L|OJ_nf#z+ z_L~H_Gf{~TMJ48=5(lCZ&qgIaY$Vpm#KErfFvNeGrRX_6K+okhBj>BHlBUo(OdpVl z?0H6HHzuj-BS!PUYh%B4Hns5)gt)I8Kl`d0hh6ef2|`(N!>fpZLi4>wbGGz^OvOn& zh9}1(d57ZpCIbQO#_y!Yyulu$n;x%5>9G89qskY&YWjYna^EnE0k2(^g_h3Hv%sF( z89d^nrrQ*Psd+&?!RN2lP`%eq77iel%8&BF^dyhB(wv^O=Cni(pOQ_e;(E!EJV}(| z)oh3P%~*pkzPG5$@Nq^q?MKCb_svH|Mw-jTZl5B*A;C~yAB$T#?=5+AMK7p}R`*w6 zd@g6N4|hb&PZr=SXaJ7MITuKhCogLei@7gi@!ISvMoxzdUs-~_p%)`7T1#W(aYM8; zOYLm6G-rs&AF&sg#ZJuJR0Uef{m358wM2prnd@x~x(gAYyWsS2dSTFASP*mx$p@YgnmfCb*H zkL8lOWayotWiSjE#XF=xrxcnsr4w4%e{n-zQi6UT(cUa$2=%eIi+RJEfPS1_aPM~B zl;+jOFH#0#l7{(k1vtFtM>ZT@%gP=75zVB?GXV=b(8M>e-v9=quE&>gE&)GyA>{*} z1$?vf9D|iXgR|e_g)xwkP!9K#^^geXfmA!8NVcuF~dz>G+(eMJoD|i zLkfo*tE>dZ^ZX9fkGS8r9&zK2AHgsCw0INgL?m%1ZFwao;*O-gruz*620vW*;vmy% zpj6tf=W#;A9egRX`7KS&iu{rg6=#XdYQ^t)7)%d|d9-4!vEVh8Efofw= zS|?%kAtGSIo;Ca=5>@Q!K|a0>A-;2a2lTgMdU_=HA zGH@ZqO`nI6v2ZXt{1e|*N{srEyaltDB~cwx1W4;F5krWUj{FqB?jFy=XT5IU>gg;2c_^O5Z?--#-gFX=8{l9vzh&$G^+sd7(LXw*0+-cL6T z-z)dn2c$LcCqnanOr`WX=kUw0RCtJ`@sqH=kYcEzH-}CyNeIHta*23CnxJxAx&)at z#wo{8XQXXWY4k~J7849Dm`qFS#*=hGj5}@M4bdvU&pN&X4W5?OdO)owr2h`?Ky$MU zz89~|WrsNU?vOwjdB~b_>HZ}|m(JNd8v44YkpWciu@lT4E{Q4_v=$d_MMhB9-|CRM zR-^CInV^hwF@2ZT;Xr3q%{eeLkplISjJW!)!9BPI-%!3qt!hzXr&&IQv*w4iHE+r> z1D(po>Kd31V6Kskr!e`#!ciLGq!D+Y^n>yYQmZ9eRFPr@1)FK~5d0CV5}=D}-6WX0 zq+``U_CnRf)^^(w+wVWVYTj>Gocrw@zcP2_EtMQ~dh_)||6b*PuOX6Rj?9)yWG-+1 zyxFArtvzd);P4Yt)L53(Uu=5>pGFNZNu+!mD4h)Sqj|-IO%nM&-5&bfCD~reEE3t? zk0sm7PFAvYkfVNvKlLMiibKc5CB$HR8?%xo7}j6&BrXxfyjp5Avx&Lz_OLVF@V9Gl zAJ7ZXNtgEgjzRVhbi&Pf=OCk`*Tm4D%X8$ah`dXAo_^jrPFC>=wKcCl_A5~z`@2oM zHbq{T8Jxtre)A-)$hFvpJcX(l9Q%~kE-{ChnlV*6g~>Uw0^hhZkc>hJ*McXtCOi}IT93w#c}-zm%gsx6yLdQ*nzm>FUx+C-P8u$kAP<}cQeavMI z7SO4vGH5k3@~pGZ*#MIX)vSqh5iiTvVsc%@GW8Y8h!HhJ%%m~Ws*@<(7^%}2Q@A;z za7$d_mPkSP0cDv)ks!vv!bxNkOZ*VsU@2y|tGjC1$FtDRn5R~`gKyl;eB&y!ID*$zl+P7O#*bMv=@sltL-J_Ovs4c#ej>>n% zOBb@vN#ucJ!*0z&jVzCEL^{5dmJPjNmw~5a)^6n!bC2$1@38?BPXgsjz?1e7o&*wS zRpLnp6rOaD@uY2tCmoE#lR%t*vM@ajzTv^`9r&+&r2(qn$R@W|!j-}`4V{|t;>e|b zaW6XWDO<&rS9j zNJs`7Q&YMsQmS65{43PTLbx163d0Y9VvZJyHxyM|1P*PvMWAZs>B)%^scREnBom3X z7UwRuFn+LZ@wkz5g}xX%5fL*2v_{-e;9Lh;h&z!Ul?!Sr96nh|ZX;GnxLMJC5ZTpP zi8|B~G0PXILlGq5Qj=^!OFX3T5V{aE+$sfdkyMsucCY5wKQg+^hV9&B!B9YvMWX(y>@VmX%*gK(Ht`|>wEloPJ+4IM|i%}o7GwGA@Wi*L@adZA}F<)al)0fQ| zq$|$Z{)ClIQNmz1+TU`5HPlo1-R!P(EcgkYA(Z?anvNh=QD3N?k&0&m-NZ^JnqDy# z%PU4{;;dw^|6<7Mj$U$Pj@?F%(;GdE-+guQOQ$#AI`gT2%9MMLTi5lL&P?P%#BFb& zwtf7X|F+W1!keYi9{JkBKxH|h*@cb=b*?7krUSdYLGmsL$B3K6M#;jl)~9!#!Lf-j zyT6a2XY~+9_@^*5MGRY!lK!mZIHrRc&T>VFc$(ZCv4X5rx;q72XhOiDZDE$~}tpu-U1*kj-@Y9Nway!-w!A zIohzxgOBZy^Be{n5ZjAaXSW-6APimJ`GyN=XyV2f600Jf#Arc+)PSRE>u0f%IE>*@ z@d(oz^vVQ)spGF~2-v=G@OH&U{OtNtT)4WB62YlR{u}seH!GXGTaYaQ3 z=cdk2TrP?X+qy=y{U1E3+{pIW)4n_|1Cfykh;|Aw*_z{@9Z}-e1R@rsQ5A zFs@dm?iW`c9=CkR<_vaVjq0n-ydH@cP{Y0+%nEfkTO?PxG|6^5PVlR;9tY$pgE}@Y z*?{(y8uKK0Ye~X1MRe30(b4*-j+$%+e5kF^QQwl+z3_o=7H)_w3{szA8e=Qwwc+0- zk($0s^|g!+W#ZakY(X)I14VOOUWu?Noi%c;E@%9lyu#^r*T^iUf9mB$wx$(%4PPIvI_H&M

go&G6hU1Oq*rKtzQQQ(MdgM2xj03~S%^dXlLLIX3< zCL2c)7Xj5%qn%+xESwB!I3ya1?$j8IHZ?<)rb@FDW7n`0+2b+FD%TUrq6>C0QWzN* z^BD6crk*KGqG1#oj&yuTU>V^-%}h*P)IJf*3_8QRgfs`l0Pdd}FV0NNidr#OjN=+o z`QquK6kTLsrfB!#` z!8YH|WPfk|U&~04E&cI4My!fyc2Sni{=+}~2Pz8^g?}>t=Q0vxzxh|}oZa`6n%kSE zNDWH8<&@F~Q{~7Vd5xP~gGMWIL7S8WTS8_mYJ#O^3P?_Z>1DXb6I z2&o*6lwe{dv?jt3YxGN3B@iS4{5miBhL zDMR0+QyEOgMqviUgA092zHfa?`qSD#AC>PwG>iI34AD*v(KJBxuO&p6&<)(slQ6)T zQee#aYKJ(ZO4y@H7^A}D%m+c^oE%o{eK?skE=}{$jJ-!|ne(E1(LglrL%;g_ux8== zFt_l1SR1+zGk76n{2F~9X8fGK4>Nvk^nIAcidj)n-G^CGQQe1GQBmE88SynAmL;rP4RBanQIU{Gc5h`j}LSBx~i` zp^q$3pq1l=P)nJ<4RfM1A;rhEV*UnXmo06%>=7>n@c0uf6~8vrMpNdY8~|z|y-gCQ z=Xei0_T21!3ttZ;uAf${jmSs!b#|O!Wp0r#?hHyJBGDVQSEL&`j4XrYl#xytT3&fZ z26i_-6t?O?q!5uyvESqYifG4l+HdBzN)(44No&~d47pkY?+!&bxR2=u-AFgM&!rn& znwTnYKT(<)MQQLgNvLq+Ltb+f#Q@5BTsCAUm2$KNM#yqhplDb*rR3lmeP@-0S}1^4^Xd%I4os*I8tABK zJXY<-YEm2PFmJ52xUmjLZmdDECDi8nOd2ZP_;F%;?B+7c>kY;UEZokvnAN|SC@nL~ z0PaE{`FkiO4`x@fk%x-2GZQ0auQWDRE<*WXD|xawHtWp_jARB|874QS+=pEh8s~v2 z3eIb=ph}HuB9Rg2u%@dSaj!gH7%g4Vc}5)VyiURdbhe%34Wr>zqh%d;>0y-b(8z31 znA!sje_EWo?HgW|dc(e9%FLMGa5iMWHOOlhb8cRZ%Bx-I+pjgeOxYS}p`!JwXoI8Q z_(ATsQNKYFfL=;zthL^)4onJ~#ZA^Cey!ry<|ok&)=5C>k9n#VC=e)vD->^W;;l}+ z&4?dSuY*|AsSV1k+fqYnNNMQSF9eCD3KH#A6=j3}@@(yhI$OP2>~95a&IuqLw2)`Ab@fZ?R?K%TNu}hJkX5; zKg0&8Xw~~}JJatRJNY|HW1bJM<&K~aON=8EeN#kQ`tFRu^PDSVr(3AT^+a7eXjp-+ zv#*OJnwz=eCC9x#Zi928UcMe1$kPm3KDN?)eLYATM%oo*Y#AiabV%_68pRk%YvZG> z4I>8r6anVv2)ypIpUOzxTa=&jVX<|FlvW`eU?O z)rz%JyYy$V>(EM5bt?<#{+Gr(aW+K2`a5*3?S4HVjoA7>i`N>%@aNZBwYzt=MqO*)`Odeiz1HOL ze)z*5R(X3xRQ~RF|3j6RR9w%Cyrjf*U(6XF>+PT4Ge5s)?%>CYnB5!svHr`<&v)<7 zcke$+EUC`_IhKU5<}LC6@2#)r`EyLH+Arh>-p>vu#*!l4OfcP?I1**KW)%`M$q4%z z{JT~P9j!wKRhqPUP+|j_J?n!shoaQ;(s{+rNpomZ(8IzWo88{SN*~=)!|9LCays<9zU}a8DB)=ZnH^h-aTX)LmXDlDy<$@~~PGPb* zQ!Sg`@YX}jbn2@x9pcWLNscsw$>t)?6swB} zP#0%OF$O*Q!YucU@;pm(vyf{rx+1wuv*Q05Z&>_4DGHRP$p!Dk6m)aBI5o$ujg&4A zm-3~l`$u8@z)kI`iOET75?U)xjbg%UV_MCqtwOG8t6^e=B`0j)I)Dv{IT!*HCdbPZ zmME7dCn8i~enxpKqDFcNu2N-4YGh-yZMQdPr*wn&?b!CQ+*vinY?M zMZy$P6yu09!5Y_J?2;Z9eE5?b30^K?^Ri3xtVWaa{0vRF)l)^1ES#8$9<)}-06B6e zha9P7sZH`&n!$p3t8brEUcb?_Ooqd{?`?NN~N>3tK#k<>1UCk_AJ9Ujfe$Z>g+SGtrh?m*-%->`v zX`OV0hT~W(e(S{umWUDi4Sv0Nchvc{(k00T#5;e7Yi^9MSug6=p^Ujz^98QiAoXI< z^R5Z3Mnn_AL|OK!r=DV*mL6}lAiR{qUQZ^c^uWigY{GjnoY+NDKIH#0d85B5tS`8Aje@T9@Dps;Pypq5D+rI&Cr$4ZICuEzYrxxmc zQLDYjSTE60k~&IO)zKo>b!CdybqB8NpL@5Kd%@1^UyEL;`>R*4df8WAc_jkodKBh> z2%a<;)WJ+K9bvhitR8gygj1FJ&{t;&vI#HlO-d8qmZS;q!U=yM`dF!A^EDZ#ClMkd zHaO7xfGjKI@!AJgf5)w7|nUjty!DE8dz2;Jl9G?TMIUpafX&O zhgSVQFILC)FM2_dMF}Dx0%Cw+k<2znL0D4zTO&+|R+%)B%6zVj~Ur1<-iOwM_~@1196o_(HqX66H*J$Bh=KY90; zpK!^Ky<^4WUa{gW??3ImD_{GDcYgIn7ajWG-EUj5{wM!=_7#8dt<#_Vu3axX`kC90 zy!gm3{oWgX{dM0w?@xC8^T*$N;>CZq_b1--(_i}YUEg}x)_2@}#lp6mXMXm`&;QZK zm!J9Mw>yH1< z>G%Ju|MCMDKW^C{{pL$=yX*@)-+a%)>3?8ZQ{ zk+=JuwrsF$^frI+CHL#WH($KrpYC1w@TONi@T)&J_xtDk3SZzK0y#~ttKWs|!9p-A zuEkAP^Z3hF{Feu3um8}F+fUoM;WZCif6pCH`;C{mK<{sX%3ZO%6IJhoJnhuMsc2k# zjC!Z?;^3iNICa{rxqgw?`r(^X*z_U(wqgf`)_>XZ~plY z{LL5E{URz9BR*pet+7R3(7Fp3d7XB8(HXvkcTXGQMy-9gQPJGT7*rVi{Ux7x%2!T# z*3)194=;J~4}I&ayIt@r*yY{$%O>hTdoK@mf8?!SJNKEdUH9QHzx%}N-}Ai>bHSd@ ztMRtul1ze)_4!oZ1a??@TzYx%-531YPv7&-El>O4>glV#`sE$_ImDYV`aO7k)(oDk za5`@mOkK6w4xfAsKEU-Q^Myc3O_s+XpcpE!y$qzQX}SgL{G#lZz< zU-_!FtA1(Yt3UMX-K3%D6BU>DBsL2E%LTR zwn7n^$NN_gF2Ua;=I=`MX*(eKcb~2t!M+5o>K&+;0Ll@3E_bMs+sU<;46X#^bdM%5 zjv3Rss~I@bog>K7xBbA5w90X?HLNuS= zjTpWwH*fM?$|Kw7O|gffeV;=Qcbac<`n?huCbbEhcWaWiLllaoQ2$cgg!z5ofGf>y z%~#=fi&MFclW$*(y1boo2GAx9H<3pOL3oGM=1rUYL~g2;&yV4A|4n|PZ(csQd{qCE z5gjK0oqw+}wMZx4UdTJDEiJVt!lmrlQM8&5fkLR1bZOQE23wsqJ)3RRHt*h~DsNlg z?eFy9E`z05zj@cY-toD{m@6tzi^Xk&Z6%2%$LDaAZH-z$B$Pa%^vQ!UQKGld+`Uus z5V(YQ!0v~JB3H33O442UeIlT?)ZdEtcMonfwa7DN7%ukGP&T3!>NSnvD-#ZHFKvtYYe0$eDBV6?1N!kM?LFxCj5B`Ki5#K(zK+3C&TKDOnjDDARy?6VW8khAYn~jW= zDpOnTOBF2__D(U?W~|k6K$^N#Yc}rChi-z2=NX#xiwXKCa?87WOFHF=l0;YjV#q1) zQKy%3F$!h&Vn~m5;@P7r?y>blO2zTXPp$qG95>UbX*(8|skSn%oK~k@tNPL+I z6ZG_*=ou|NTW!i|o{yhluXCs?v!u4QXMnx!;H`FTb0)kUmZ@EB&gjw*Qd&9; zi?H>SUc1rQ-W|X$XG$^ZwBb;hT{huOTBkUTM$PP<{{P_fbB9@=26GK=~BPAEJC3L&s-ka~n+V+0 z;0pX)UFb-*X);ar2>w2#isjiP>x z*D-!W`@Ises&K4bFm?kVa|wr$7TWD9{#CqPX)qV(8%jegnp@THJj|2(eIHQA?bq*u zb4IK!Hi&uK?=q&e^?W`pk08$fRTNOky%N#%RfPuWZ~TmhhLY=IJ>KW$xL5~%vk|&F zjYZCzLRp29lGrYEkIP!v$L8ZK)4W>3EPbcU>?>wLQxcZ$t6#?Ln*A+>z6z@?8jP2ufJA%a%dR#qYWifQAUlfr0n|@ z&=UN++UUqD5?V4vk87d-I8(nC_LeJ9D)D(#(ApbdN0?pGw*E9M{)~wgv}^x5c!K*z zaqhN#2*;3b=@busKdLMnH9M^uSE!kRpnhtXR*Ew>!gr|BG(Kl? zUe`p^lt*~-IHN0f_Q6>eMir$m15aInvKrn#qnT~F%8t0IHRe>%Zm)*+Y13MYuiTh) zj@D~4s}Zed$Jvg%ELF6AzRpOEzDlAvkCwSyn>k5p4zGRB)A^}>TdMT~Pqa%vu*8No zgBABMQr4xbN9bp0rEqLp&*w8z-DdQp}sP@R?id?_23!dCri$5x*Y(7>r za^l0u!w&J&3_E@++FKv9ePPDNPU2(K*7I@YW>whStUO%NLd0>^|9Ip5Y(Czq95R;& z>Gv~A#R$U8Kryo(pV>@GYV&etV^bJ8xsH_H)sS5Ne}#kdd@5Isx@yiEjxYUomyxKn zq+^v;BXDt^rj+tZ*S5HtO)g+WZ7n#1fAurQEhIn2&DtTKEJo|L-Eut7w$~4j0nFex{|x zw%5&qGv-^f3;r6V$g4KrDB)K8RSUm2iOeFb%;JQE1$cjb|R9kE6Zj5*`$6RRTtCe_j}9v zeb*7!!{{>Ve)!2Bb^VC@*9~pfqvJZbIi!B@pIU3mou5$)=gg}W_m=YQS`Ixi>)WLL z-mvv9{-AG%QhLmev`brgGqrxlH&@Jm>*rAYw(LuEy$YLMX*~HJeMV zogrI}Y9l*7*H(K^wxuq->$kYra&q6&48)se7rG-~Z$1w*B2|w1@mu4(X!AKV+8py> zbL*ZwP7X8SD4&Ob?>&RXjs^HUy%bX9(qnp*cNxa5m4Et?T(`|?EvW7G!?(>STlU%W zYd7cJScIUv7Irm5|F$f|ht&X~oYZ48RXZ1(T&cVQ*$&L4DX%lNTPdJ4cA3q#p*q>QY~|3>xea$&Y}4aj zTNzjD){^hvFZGN)q~=Ydouxc6;Sm0EZJ6gE@#G5bwT-V6wSn`UM%^xce~6zn*Y|Jr zEH}HlBjoCoCtjA}F7Qqs)njw`Y4EqZ3hLtC%5@jYTU<5KBwbnzc`fCo^gr6L?L2*= z*0~>_IhWF9zRs!FoPYa@R`|V}2H#4S#tq&ssQzi{x7$1)q%WGvsalM``T1zoaVPiP z=Cq5=ak|K+uMHM=uH(*FHM-$qgPamdfW>mGPIC;#xW5Yq-BFcC)3+9}xUe&RcePVv z4|SNVuH77`i^r9uOq&>8Kep1PZH)U|-PXTWu~bU6T_r8mwVTs8CO2(nGF`QIKa{n4 z?>n8tcsf`)BggJj3BTKH_Lk}Cc=9ICfL(^N;`AHL{Aw@WnLus`PoCu|f%Ax9aQ4N| zmhscGT&<%g8-C!^?_RrN1c?y&hfw+Xx#npwIE_C6peQ`j!aj`NVlDo`F((TK#3{>G zna@r+?YhBv_{%@{AHBt&%a$!499_0|@U>U|!I@9J;EeU3`@66G^^Hd!cjWQEJn^qL zZ~x)ndcdQvf70m4Qi$FvLf;M(NfjLznW0Dd7M7o_AFNhZG8Mcb_C1 zh<=f3f_S5}|cMS~;zWY>#m3$RO9PB9knQ@!3=$P;`f}Ms) zn{sfjeAQ5|y7<=LwT}w&!se`MQPzd<+7bLbOX0ZIiRg-c!OvYypr^F|i=$|CbTIMt z)>%>G{?y(nph~WCXChWx#Riq?yW6z>u3d0`o|DQoC9;xVrW$pnnyw##^vGcZcW$b~ zx`8&;H${T`6Ymx(+DAG`_D@ju&_%Cuuqw2FK(iGV2 z!k-*i0jMik7Kd5743C_s9EhI@HA^HUzQQ+Ak^Ab^4=ZLxr=Sso$38q%0&47fy8@@b z15P2u4~mSW+XNJ^TAD!xyHukNGHjab8(+`Wn8{pXL$OUth31UM)QLY`IR0vt=HIZ0pTp(LpYSa9Qa4%@uN~IzF*eQ?+Us zw%t&LN=;s3ce}GuZxTtd?gdj94W5mW$XoukCw$Xo!3ziOyXC8+%5UqEid&0|Z9K}F zKj-lOPPA+%fXLZeytQnIo2OEBc4F}yz=U>xjstzGc?h9 z9-%BpNl$nUH^@|L0RiF=+K`J>SK7My$mlbBUr{Q%Fp* zZ*!$lrYRD`C-KA$^1BRQ^SD+XJIj&#QdiF{A#}ZJCQn}v4QLQK9g?Mujy;tgC2Zuj+(&*^#E zEd_lOxR{p@?JLPop5?5E8d_z)_j&E8UfKgaIzL!rBz`;mr2UYztFg)GAT~?z8s4=S z2QLP8bxNv_ydXs6l=7aItlPm4vFkD?p?TcH_-{4(z+RUres9>i*c7+*vaNG_?`w(> z8Z9$%;$yX5`zc1XzVdw2wh4#*r`JKPOnOQ>^rWGUOW%||YlmUizt{Ofh#$qa^%E!2 zUtTBbQ{4B^LpUOf6R2n9rHX5Ox+%78ZKk7fQ?PIGUF@f^LTlIk!pCsO9|%{eKNyhuV&30Yo^c?q(kVRaBaJtCriI; z3)+!Oh{=6TB{Sqb;bHo8GB**)a`k>nvV1N~L0*qOTkmDk&`zJrzGJtRn6PE__axE) zLr<78;g8Lgio6kRGUE}eX8E(sg*kJI6wYi5G2V=GeV6vsn@G+tcU8c45X>ubi4f~VZ#bG$pQy8d;WE!ci_n=2?B_3zb7X*({3(-l8mW(hi` zyg5Rg|Kws%Mnh?DacTd582J(9X1xV3V=#Q6{ zFP32mgUz0dh1iEQjYFfK3(FszS{+~{Bz4WSNPoA;ekwYy{x_l1zL3ZHlN`DeHpj-3 z(((Ef?5fy()p(mm>{IRMMS~c!gkPy z5I9TWu@amoHSY)gr*MZ#UPLC&TRYGXKhEXP1M0n(9cE-4+kxUj`s*-0u8EMNW*bo~ zw3cBszs>{oM;e1@*JjkW>E_{wHafQESltu#j+UY)s`adeXserK#bGCoA?ugsv@sIf z;!0YV6$c$FBQK0k5LQzjpTT@;Z^lf6@6Bbs$3us8w(qW8xGPchvNwZXxp0(+{P|@L zobt@0X+O#{j=cJzMA+4!W>RjcOxv9^VjcwTz8CgIqmMC*E79w;6$qheOf(Kn>|5C) z`0-J-V19fp2G9B&OnVb4cP}h)#~p{7UKML9J84L&dvj+}O5=Kn>!Us)v67z?kZ#3F zS>F(w^}C*OB9&bzR@MvcN9;ptHd5cB$M~SEf4cTlY22)zbrjJ^NPCJXgzmCahSY4N zbrezPipTacUWU}HpLG;rJg7Z2xo&e4U5l<2(&j*7V9NYH80W5AIgX z7kA??d+cK&7qVIbdPAIh+8Ioq?`iAL6m_e(BgBxd>G=IuzqOtpwLT9(;X2auQJ67U z_5$-DNuC!+wXZ@J!cp7>3LGnD?p%lTXAf=XwgQ(Ls4YyTkgl<9Lf?oVS;2Q#E7KSX z$18CPdc(Vs_s6JAQ~K4xly!oBpk5E2*oVJ%kNPBVz*Ro`Nk8-=UN*x&=Ea9|J>kT{ z<2;0G!pC}t>_*i3!8svPFKq`N%G#-3y5rp8!xX+_yqZyozIx5I)qTD4^cHAVf!U6y zRf07>UzJJi+YMV&I8ri#o7x7}9k$f&Qde5v(*unNi5H?1lj^-w&ipxk91F7}I!Nu= zqP{QrX)UPOEW@$hdCF#n+^lg7{`CC~w!+w&PbGhR>HI260)67cfH9A}bHb9mKI7s9ML`co1&!8S*fwQzNwDW^-J4&1Hv`at#~G{ z=gQ07Ft}8?I>f2OwZYz0v zpjVGz$6dIBEIw;r?d0Z8`B)(yxkqhobjeHG8IGm8La3VO*j9DjdJ103MtFxJ!|7;= zw!;~j&Hr4y%&(@_kJ7N)T#n)As>iWDk5T7D75JWy&y;SRfSkLt-W)kuTzl#Fr-`jp zE|G|O+NQei`B;i`i{@#SbM?|(m!jUR@gB->)Xp}IN2dCv@M1couQt@O^$o9CpX=x) z5%~6W^4AC2kJX3MY>aihB(S^F!-vzXkJWV0Xn9*YXnp1Dwsa`NX*R}cIw8ZBTryhcQOW|9SH%wR5a% z@sp~X_7KD;;JnxF;wgKfuC)yz>0H=_RBhSgoZNS|vtqPqwsT%4RkNIGca|qK@_os0 z-sR+6cgk2^w3O#Z{pu~=@i3ZSy|m}EJiumFGv1ZTie^6bf2^p_rw-*OR%dQBKjP&m zDtqfqY)W^;iNwURu!r!-cN)FG8IGG#T|SA$zA4e$s(1Z0mHIcS5#6`A4y3s%j1qG` zWpnl1Z0;&7=jCv_IBuG~9X}o|FQzG(gMFV>z&YC-u*ulUPGk<=ir*xT=$^uEL%u^fGJ?TlYPv^qW{AJ>y+ox}HJ*mJHe%s3P(x6gNE zt;?zDYyYR;{_M4RB%6CXdm1+nqVqylbR>Ya&6zH@~72UegwOW z!*uUMNdBSX&Gs#Z(6)j{c|z0%(1EK7+_}*fuj2PuN=p7iU(ozSs)`3b%T+ z?MTg)Kia6wx%J8SlG-#)yrAB>u{eV3%i*1ck3J|K4ba|H^-ZLyzJ4B(tDxCyG@HgI zXkm0uvl|^KM^AmZn}~%=&oEXIo`}*WI^niKos`z1 zp;oi|uI)U9{?UG5iQeu!@|CV0#J;1g+kq3)m^&AbX=eg$ z_e6`{wlI0sz3A`m5a&!}g5JP0fP)Pcagm%cTWHVh0UD#l)>~=YJ9jP?vvnFfqHj$b z%_xn{iy+pSb9;*;QEhu-?QT^++_RI_XV?ZRaV9~&ZYCQ$jW;c$3Zob5QKvjI`r>h{ z(BGAO6V6VXU?e8N!r?I)^|NkcZ}|8~^_LQpp2?@jNiA};B*me>RL4UC#g*GOFhYIO z7D~6C$P8@=Ihi%wHPabCA;fMyOzV7~TjH|De49hi-;Ch4Isbvy*^OOqGJ1@9Mb5&i zmX=UH)Nf}@c^-cm`Am84%89X&@eByrqfaOF5FqV=UDU{Ob+3HwANG;)ZAR2N+ZKz; zyVjVe%5n2#V_%Y0^VHLW(Iq{j+n)1h;~pE)ZdtQ68H?&zw;k0pi_?mGPQqFvL4u_= zC*s)kruulLPeVIy?%e6N!ee2nmCChnF;1errQ|U;j3?dr-l1|%+GfRg-?f8l0F|7| z7!zY5qzA3oG0e)DV@hvG*Ca_9JxOdsWADgq)$-^Xz_b$BgjZwO;`~GS$F{)FnxFb@ zsp!JwejsAXLSMZ8Yg<0WV?17_1%3I@#`9A~Swz?QJSjCKx_%H`a3}l&w_WmA-TXGK zy{aiC`84D>x5d;7TxtZ}dKjbjdb4`TRf7Jw(%q}T$QdQe_r&U`L<_?8CyrM&vt5g7 zO9LPCx_eP#NO4`dI6M~4pxcf}BNy?#72UWc&3m9{)YLDEwZVO|JW*CL+`hL0X$xpe%gYsl z%YX@Yo9 z7Fl~a1TV2~8;ph{++!sUs^@xzF%3(`6B2#AH>t0&RiB6RA>4-6b^SUQM#z^kZ&_<_ zOb^0()5LWc1o!z@WB582#)+H6d1Eu7R3C4^X#H57-xrtv(^#yAZfCT8!d9KX5gw&l zt+f6p1sEaW{`}_Su=JBXYO?OU84x*N_W7h)3|BbC!nY%FHCkTpRU&29D5o3d+K&_k zCol)K647SvP2W^UMg6q~Sfj2zRzU|H2_^9Wa7?c}Uz&Vxwh6V#AChz8Vqbr614;9> z*!$F6)0c|+)+~0Ux5|gKshqRXJL%@d5)W*JebzfXa(Wq$LB^nMFuJkHSt22a#^72> zs_$YEI)0CjS3GKRY)(v|8iv)KO>H70ue{fSSlToiPZt$?(op7r zGl?7QG`^&tYs$P}@+h%fEe{7x=I9|a?`i7qunord4uW&t7++lPw8?qB)buVt+f}_9 zUAIj0UAS^q&4D2#$(tdjNq2V*ZW1{0DVOp_?~6FGa?lMUE}Zf`BPo3N(Zdkuy1p%0 zMXtvrGX@{){ERluwah+`=k-Ia0AW$<4`ETQ_G8RSW5*lOo{eEigshu=APilxpTr!6 zaqlB&?^tAp(rh$@toXDT+Jx~@w{%@cpsvlyO$}+Rmcg#`KBLAugfLV#eHi6Ds1&|J zcwwKO*+ZWQgEKgnk5hfBlMETLwS&B)C~gzGwGf-dfie_A-+c|M2}ixj=pD3nm367s>ahk>ioEyE)!_?jr>sc z{qv;3;zKbJr$c()5<=i==1w!`^J(S6@Vz_>W3g^fO83NT`EJMihe8e`?3vx~VZptO!P+B5c+Pu*W`{N&Y`*a!)^TEzV^YT@#p7+2_DlJ z2;qpHV}6<NcZNPidbJrqcF^p-xp^wZtUT5RNwXT(oVT*vKA-{Osg%U(9T_RJJsI z68fDhQMlTc+7-Osd`tP^>bb1dGuwO7fTt3a-F)z6NJwTUCaOkAdC&v(K~H9Z__YGTzMfFqaW1&J8GDW)k6Ym-V;O7P9rTXSf3@8g%FN-CcThWMbr52^j#Al8~ zL+lifopAnSp@Rz`;g;~`y)36!`$|4kEirMeQ=2|abtvpMB%gI;;fxAF%<4`ES-lym z=Y)1};}q%>mc=N?JCiY1ZW^LWRUBy@M>StmrMN8rx%}cLFj)A$QeFhGDFJoW@p4Eh zmD6LZM&)kLvCbhes*76w@erwoU5%i><6WZ}*ojIN6}Jg$C&)G;zOLulsv{$eoL_|z zOJ{PiOW#&aqS8s)A9Z2XC}Sw!3Pe*vrVB&d&Jxrz-L4|TiPn&4w(+%Cc19b-qs}9$ z%#lmjnBb!R!oOu0A{UM*+?er-q1&Q4&f!(i@^(tadeRj4{g_o3OEGL$8&oxZl6eqg z(`Ke+F%5KFZ8qzlj<;Si+QW4z(hn3%*O?lV9O7TH^`C}%%&X^YfakO5zA?A2jN~zc z-k^s)?dqn{@=`PjY^5ywnbQ9Ql zLcR^boobY7N^q05)4p)XjN8l% z9_($H9B&B zOdHpmcxvSHPK~*8>6uE}b@ZvWzHr22Zg*4H&xc(gQ7o97e0gQjDKxHOB|`ipKDuujb`h&v%U4SPPZLuQ=!lG7{@WE z_uRZF8Z}Rs`XfxW5@M-oJ3b}CcFOk3(!9$x%$rW*cvWG$;6+FrT%IHeYKuI0aA1+$@S9I zLtDbQEPXU|?IGfEgll9e%+MPT_*^DcoGYp{5C!*kfCbEF^CCqr6GNNt;x!ZD_mQkrbhp zsXhe4*TnH;aUWx03ptzKC$m%MEY6{ODBq289?Any9*FWFln0}H56VMOz8B@8DCeU*4CUb{k3e}O$^^;NqU3gyu# zk3o4X%HvQbQ7%GxJjxSLE=GAG%1V?=P%cHe3}qF{6w2i&t5L2%xf10nl8+Hlf^rawE!3D4S7kM%jWg zjj|Qx7L;32ZbNwn$}>@(g>pN}9VpL6`2mz|D9=IJjHmB67roJJJ-Ud^kMND|T=kH>B}EZ1JS3 zIm(BqJ-chbbWc47D^0mn?X|GPOod}Cta)uF-Ijf^JdD`>Zv_lufxbZ1^;4-4uzve` z`%#;an~t6FlOwJ9K}UZ8*q7rRW|7}AL?n_|yOS$q(=HcaH$k5tGaSMwPc!V&&t?>I z)P)iCG}q&Bih%`UOg6h>34%l=GrEhL??PkEQ(Bn;vl!-yaZX9y#Tmg2=vTD@RS!kz&<=(~r)M&@C8lO&+dv~8^4V=P3Zwm46awe+ z>bbSSiNfKb0xpN=(U#K4T?jl-XczqaxW}Sgn4&GAFr$4V@OtCu_im87aGLg}o;L2` zxuL%*hvC!TraaRx&tI`lVTxQyJa-wN!l)l#suvs$xsK~TvijL8&!(;WYq@dnKyY2< z8LS5{A5H*%eU@agApfURCryqkUjprOFZD1FuyXyRwmO%X>5@wwQj%7mYl}PtEqC@> z?bi_^brOBgU7oH`E7NxLoX^lwuEWpj*_5k?`)IYA!m07ZHS@{2;y4}&nFrN(z&qs1 z;Jnf8v|6EcPHU@>qXg!4BM*qaR?f8-aa`pdUsgA(;o9du5^ZL@y4T)_p74-dl54Q!5WY3D;`sPvzmdYVSZ(%YCs-{Uu-CTQ*u(JgJMDz|g~G zn%IVDsJ$?GN^GGmgqB{E6_JlYsax2Rnq8LK>KU`VcotV5>Cw@I@IW$|c!f({KD0Zu5^sQ7U_UfzJkO}adq%@chmY#hnnFFvlA{)-Ol3cf7BwKd z6#WZ>xV*7BRK;z_FI2IvJ3}!H3({q!A!2L!AsBTYTmx9g8(dUifZ;AI2g!{j^?~X- zm<$grHs;6|8a}O;z;p=JAB#@%nomf5I3cIg87AK}U7TtLS2#0gS*ID?yeUW>Ojptu zhvyylqBomJx1u*-Iqt+s%@(R0Dc3?Nc)aLrrK7B4=Om;?T9qTj?@(`aLQfM10JRog z9z74DNuT?0BW`!_GC8==L{&Mj(91>W43yuIS*TpC=DtTYqy{g2W@i&T<+&m`4nyb! zq|J`1Rl;?ZK-%25h0d!R*aErKk7A^Ims;W8W@cB6Yv#u#eHCcOeYE4r7HPO!FxGna zsSQ5MCcGVtF6VjpqYjPgQoBz|&(LXVgPWL})D8|047XQzTB&LSZ3riY>##95H!VYo z8LgeZNY1CJ*5ld}wIo+!u_?`MBH^O#Rpd}cAHEJmDiZ1a&$oD*@Z4gG6#n%0MvP@Q_razdIgxP zGe*+7!Oy7HJaoku$`rlo%zhcNi)XrFu1R2Q-13)w(^uJHPaIWIxtQh39Njmr1h{5}s-Y8r zlqz`iuijE@Y;@g+UVRh-F8@J?%vMJHd_c&NF&yB{g^xv7wp{GFSQDO&tzwZk!ux^r zGp-MXcR>iWc6u&0o|I#f8cnqx3?(PlW}bUO$JTXGZZI;)b)ub^Soo4ruNQVepK(1z zlNq_{W;KqwAHF2zK($VF6KElx)csG7I5iolO22lYS$)xdv^+dPHViizt*>2GQ@3{< zL+rJ+pJ_<01lL9z2D`kk18`1j2QrBR~$(X>< zg4yjbilN+;sgOph1?z7%bbOkQcMj33=?DT4qNgX6{TjL-c=v&!t1%7JL@`-!;-$Jr zh|b1aTTW`?5JPh>al^e3uC>PDKQw4+;%TTwxcFQ7qD%j|UFN?smaFF4D*|0t$=hcV zy3={_yjfs$H)u)`;Z($@>?CL{5pmz;5Eoyv(YcRT-`P-_&ZT;lrv#_bFNw?YLRWV; z3)^pZg}2g(?m^XQQGO4#RyCK@D`l%N{P9RlM_E)JSmHXpRPvnct@O?iP^qHQ=m}Ix zd6Z+Snm}+LIr(GJCWGZU+DK{YJZ*H`NIt2kL|?ExKL4_Ji^b+C!aT8>JHZ$f@Rca` z?poJxiydx}hUbn(S3`e-`W zVtPFlr1Hij{9#}Db~h&4))utG+|-$(wQlh|J0odtNY#XXjx;;H00NxaFq}FTm6P`{_gZvGczaN) z$s8AB$$c9ldXXZXRpVu|i|961HI0_Pr^<~8nGrP>e!n+I!kyP!5hx2;+7ZcT#54@I z4?6FT#TKd^p}2=Ft#)-%LMr6MB;sSqb8yNO5b+kp&i{GrXe>vij)t+d*wA=~i9Rk$uuqAO7nk^lOd&U2GynK0~fe}>3#mJZCbo?-?;Xj|HKTQwyatubkJ#_;@eFhQSyf7YZBRYZX79H47XcHziB*ZuR_#^oZ-YMXm1?u;)q@GkPzJ*r=U@RnbqU zqMu$K{d9Ho(<@4!h88d8MTGpXBl1V1{zx9w#r945$$zV(tzFe=H+ny_tEtrbvZfea z+V!5;8m0E6J}b-duqLWki(+<$*?H3_vQk0csxy!#UR6zmLHi|_;ONs0#%I=(SyIha zMHn=mbf0kQFn|YXxI>4?0c$+pClTIW*b#MQ$jWxX$r9B^*U5H7+&;!fSav1iBC2&2 z9w*dd7#Se>a=>Hgm4{y?c-n4_R_g z^v*K|Vf)+}&&eg#xOHS~Z#LpPd%Eu|-r_0FRHr<%fwz2U9$UGG*0ft_OEZqG=owRY zY`dvZRO2~?(>cJlsj)GfYL*`{da@Hm>Uv9x>cLD~Oh9NW98j(pi1ftP31aSx8Y7x& z{vO|A)@|+R0Psnv9YTj$>XJW|hf|cU=U3uMG-KaVrWv42(-1h z+^02P2!Ut9%DEgdm)~(%dbhuHV0s6d@-SklFDKcdsacUz5?l8oRH$>z*^Gw8syr&x z8J#?7h0V5N1v+k~QG4MrOXk$X#w_gJWKAT*W!)&7I5x39Hgje|j+IpV(Y8awC%xdz=!U3 z%Y_k2f=Gdt*Lnk+lVUiTOlucU7*WVp-PRdJA|}J z_pm9QbbTi+-}SBcK`p-@YFJK#{iRtro9q-OPo-47T5b0y-ZdK^3GN*F%-rF^3?Fwl zU0Z-F*gsuRZVTkB*!p{GVD7Lq!LJQ4U6y%x)jAeAo&F!w!}md3`CEP4G>bat$pTC* z?1kr^U7vC3W+w=*r|S6QNO4Lv49aKy<@M!buDMjgGkNYR9frLX+GBSS`sbn9E^*xh zv2VxjEy0&0)$_nm|Lv9FdRiv87HyTzJm*XgsJk?%J^@KNiqTZlA(f*N)=FWt>;?I= z-cSpijguZ0%T&Xm9dk7VaWmy7iFO8Y5Hg_D`H?+6SdPb6^fY+GN!_?#sBnyd84K8Z(C8 zv>nfemA~CA*y)0Zn=g8yks`KcB|3#?!nGa`<4dws$yS)z*=&Du41=*>?Rm6(6Jjyl&m!Wo#y(((T15`x;M`<)sZaFAsFN8>buB@&QBxMF zDe2>{9a%h$d!+r{I1a3St>?QmguT$_JhGT^9BK*aTh)xohZXXPTo1t69Ty~dn$fR?Fir&3HgwnJxAwNod;979IuaA6AcV}#_J*bl?|az1I|%61c_ zokc$h{;PVhVe4Y)+}6vst_c-sCG#$C_W40Es`ZuUo3>3j>_5E@YGqP=U3O&r^{Htiy_x_yi9Vn2-)TD!x> zqPi<2pVHuScI%tIfa;kiYb~ewihf{*@ZTbw*dI{?0u~s5!2z{h_q0#QOY_(nfhWvn|AU zGtTwh_T$MWd*)okXAr&>iTmxGX{g3O2z_v8N&gTh;X#qj@qa5?VM;2O@lx}s_W0Mi zl*&>HQd|oxUZ#E;Pik!$jW`U<^fhBh!E5S!hW+a{Td@7=HdjzM>ffuE;!AQq-W5() z{B)Tm=$P{62yq^+-{X;|m3bT|e(3!?J+(yci}w+anGnl4Zo|AK;IrfMw65qI!N2lf4MjDARMNZz@YRs z#K+B`LaIF)$zy;vi?G%qK-h~G4sZl46Mp}z;H&|b6;4pPy9{jV%F>+MJ z`wdnv zZqYeK`=_%#>z1=eKEyh%*@T{x$5C^c8PUW2`Z;`Zr;(A}28Q9>hx#z8aBMfMq6ep} zf=sR^oJac*HbaV@mc)lrnHXl+)VjVVb4kYaSRZ1(TrwC-I+7wAq59ULFT|D^X=A7s z))vEEyP%;C8|gV{p?*)9t8@$UtZ}vX8J?n}D$;`9ux?4h>nKk>M{`T9xEnf(x`WqN z&*6#VGu-enA8U0mZvg~yx!~Pk=<=ozxM)Xw`J=W1O(4N0N| zaB^LYx`fF(UZE_C{v+od0#)pU7%a)5ASx(Pkb~ zM!^9foqE{_E`EP-nQz!1rUozoHiy3xjwZwVg0BaaYx(yEQh%|vJwjDtsl@d;I>L@X z>E>9IhpdFnxC4d%S6h}jaO{wMc$|;HoIX$^4@-RI^g^&vqJ0ir;`bmXN7fS1a_ltx zWMrUZbE-L#7KYeP*ue|@ao2GUlWY&6ck~OULH&(L94;t7(F~ULRi*}4!GB8oDouLj z>p!D)zN^fUmlKw>awESiQ z+E=vGgh|{NFB^PQ>SuK;QJdRZN0QEzz!|Ym!oiR9X;ZCnHyXN`iHn62_TE(YF%D>Uo)S z+e2oI$6yJ_iH8j|qjMkcW2_F^b-+4h?fE0ApkA%egsYlC7adDakTORZ(b3B%8)3)S znBW6vN*a7FFmrC%eryE_m4CVWvFCV^5JbY`jm4^ zKw&9qt~yB8CtC$=BOaNN=h6%#hf2={Pd4XeIdvF4FX^Ld8&KormU>@JW6n=(JTBsc zG=i29^(pyv`7~NL@<4+)LPLm>(6A3FOyceEx{X4!t+)(!ME`;=B@dv^h9&J%2~G}2 z*;ya@A^u2-7S&wRl(N;(np3t8R~Zl=R<=du*)?N3IzyX){Lwe07hGVhq+7%r7=rRu zF~a8o!Yu545knR1mvncXmA4k1y=m+9hVx@;Wj#sXi=fxcayXkcm?_OFa(Gc@NHwgBzdy*R)iUgDTHnmW+>$j&GfX{qeb^`JBzwzES_Vq|58%KzNd;=u4bQ)>{gm zd1zS%h?_7<&IlJhBeAWhO&JL4-UR88xyV){EA-bn8=-G3-0sZxO0(fQe!mW|G!X`E zpK{rNkq-RUP;L@lM~m2!`ee=RGHaSh?nTXc+Uo5z7aOiCc`McWw_=`E!f&;`Pg1Gm z8P;(vBl}!00DVQbJs`%XI{&xvgyQ{RPPY}jdkCJnEMph%m9$kI7nWx$5!(OATC6qQ zVOrw-8X9?GgjbGxX8c1|C?%Rchqread6|Lc-Gw^bBDJ7jLzSqhvm`3+t8SXs!!MRM@dfC9PSl?MUULTs^XXxa8?rSxX4N6uCO8z{_W`?ETAnnRr&8x!{*4G}8t* z!P7WkVpfbT$;v8!Q+aY^mSZW&N}aBu=bQz%w3b!}w^qNfuO;s-M{>f*bb;5>+_aj* zl$R>k;!FLjP0n_Jdd@S_Luv^fFr& zuc*eJm-<>|N!1#N-&ypv6lt%5K;LTDfNxlzyNpHg6AS1iPq3QT{iu@TyHElfL5kbc zqE{u|UM)RwShOR>DjVjbjPe40DHc}^c@69wv(_X+=wzQY)Wg=h=bAm&2dOjB8ZcWP0VvPJ>dbkNzxl4|fPdGVau_&Pn{O=H5g~)L*NC_SW1@OZ7*fFc7wBqh0o;3Qq3Gh>Y>e&jO4UQ zTUn#8O4O~=Hq-{1ExQJgiF3)LEpS|#15`m%|BSO?9%e~TM17)BR)o;4B*l!5 zIq4!nCZl{X_;;kK4+dur)~-Fc@8ImZT|?%&;}iLF+6(I*r}*;b!Pf^58SrH!yvgIG z=JB{bUS}Rp=;L$DBP3Z~KFY_bQM)5hQwH*+LY^{^ufd~%JY^uS#iM~dWgu_Bqk%kS zAYX?^19@0LHg$&v@&gsr&_HHG@~wfq8jl9@&_HIp2J+BAekvXfm%1@!Aa4eh z1Xl*~X4Dj1onYO~gIA0|Ey&~7@d1c39Psh;fGo%v4R8c%Xdq82i9^+j!?(P=iqUK zIzGNn>rNTSZ_*J>jUX4O6Ho=}`1ouc;M4*iNh32rY!AaDiDG&+G?4FA$ffZPO`kX8 zRnx2C3DzZz1eAeHDSNvS93MHyj~~a&{9|6m&(Y&RUgoz%onT{%kFP2ohkSfV@n}F1 zlOxpe+XHwUp^lGx@Hj#pAK$4jPZ`K>!guE7DFgXs#p4LFetUv-@3N@lBWW~39UniW zkcS5H`xMmBKz>t!s}bY^bponD9Um!2G1Q$naRT4+&pi>uV$To0CF*4OQ+)iX;&I4F zGNF8XoR2Rm9t~t-Qodc_w}RvJKAJvLj$){zB@xtpBZ@kHOVpY1LW|(>wZ-F* zkFO{mkMr@R#p4M+zOZ;S4H1(g)bZQ57mo%q8!|(Hkl|a>NFEJj(nuZ+WYS0;3&`dX zb^vwXg>Owm&~11mjpSPcnGG3ifCup{LO1kRMSz z8pv!&zBQ09#iN0YR`737JQ~Pn6pse-xy7S_{E*_&K%OWb4P-WCAOom@Oz3nl%(t5p z-yS#C>I?XGggP3p0RWjkA2-(OfyJY-R=kZ}zBSfrHHdB6J#MTO?Y^P@2?P0W@F>F{ z*}>7vCk*8KY26bB^7+N1fjn6}8px!PfxKWKe+iF*$q{7z)<7nWX*%;22_$8*B9$d^LBWXoZ`vj1Dcq7QTI9#Sb0>wGS=$e0$0AV zK5pzFeKXVU@def;d*36dcjZf4Dh(IRv*#< z9yivCcHa!}xUp9B%>-8?Yo(9ITG53t-8f-vH;G~(pD>W`Rmdj{YiY3nhwgKd9eAG2oGQUvOot6@4?om9bWoebWsL7cbK+%40zz^9X&6Z|}jkrd{Y5 zJd#GjW(G2SGr^UC%yy-&fy{=?i138Eq`rA9jR@)r-*SK>)am00b^2%^168A!1(bnI z8hwLAagj4PT|eQD?PYvt^vC|boP0@usv<`)&ci46E%*0UH2}U@8?BQT@=9Z; zXO|VdFu_IXlyqp%G7Xlb2EelbScGrfVGs$d&CmF=_+z5tr4_HbDC{`@XPo~7CX#sU zIRD3615d^K_aHhx&i_eaCvye76#K7Uy{n1+kMn<2g2(wk#w0?cl`MSDs>wM2XPo~t z1Kn=+{MvI^O6zacnkrNKP57&o`I!Ra{GW0D53}#MOJtn?lYd^>IRA%7D30@gcs|)U z|A!el+{HpYqNn+d^MB&G>c#B1%|=NqOa~g^ag@ZuN9sEBSVA2WDaQFf7Ilh^5$gEJ zKW*}V#_@lyAu*;j-v4_D5jLKVQbdr(`+sY06&dgU)xEmo{lBV5#`}MHo>na4 zN4;v#02=TAb=Gsn`+woW6?=J>a{n)G^)mUna34orq?sTO7RLGC#-|=(<3DZwH;B&t zE3)8YcBhTkeEW%iI!C`}CMKk53<$;|&Z0yh5dg~n7ybeLCz0VG02f(oFgyKTt3n5vL)}*tk7f_y zON&Rd)9;1FqaQ)W)^gN+yMBx9(s<-Tqp?@x{r|wmc>lld#2)YeADP*RNETBcBhKUX zKL`#d0ps;Q=yr2v!Fd1wXixii|NnUZf4hhgbS%_LgBTNDZCZnote}l_ 0){ + a.push(b); + } + b = ""; + } + + this.toString = this.valueOf = function(){ + return (this.arrConcat) ? a.join("") : b; + }; + + this.append = function(){ + for(var x=0; x0){ + s = b.substring(0, (f-1)); + } + b = s + b.substring(f + l); + length = this.length = b.length; + if(this.arrConcat){ + a.push(b); + b=""; + } + return this; + }; + + this.replace = function(o,n){ + if(this.arrConcat){ + b = a.join(""); + } + a = []; + b = b.replace(o,n); + length = this.length = b.length; + if(this.arrConcat){ + a.push(b); + b=""; + } + return this; + }; + + this.insert = function(idx,s){ + if(this.arrConcat){ + b = a.join(""); + } + a=[]; + if(idx == 0){ + b = s + b; + }else{ + var t = b.split(""); + t.splice(idx,0,s); + b = t.join("") + } + length = this.length = b.length; + if(this.arrConcat){ + a.push(b); + b=""; + } + return this; + }; + + this.append.apply(this, arguments); +}; diff --git a/source/web/scripts/ajax/src/string/__package__.js b/source/web/scripts/ajax/src/string/__package__.js new file mode 100644 index 0000000000..3763fb560c --- /dev/null +++ b/source/web/scripts/ajax/src/string/__package__.js @@ -0,0 +1,9 @@ +dojo.kwCompoundRequire({ + common: [ + "dojo.string", + "dojo.string.common", + "dojo.string.extras", + "dojo.string.Builder" + ] +}); +dojo.provide("dojo.string.*"); diff --git a/source/web/scripts/ajax/src/string/common.js b/source/web/scripts/ajax/src/string/common.js new file mode 100644 index 0000000000..48eec3c2b4 --- /dev/null +++ b/source/web/scripts/ajax/src/string/common.js @@ -0,0 +1,77 @@ +dojo.provide("dojo.string.common"); + +dojo.require("dojo.string"); + +/** + * Trim whitespace from 'str'. If 'wh' > 0, + * only trim from start, if 'wh' < 0, only trim + * from end, otherwise trim both ends + */ +dojo.string.trim = function(str, wh){ + if(!str.replace){ return str; } + if(!str.length){ return str; } + var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g); + return str.replace(re, ""); +} + +/** + * Trim whitespace at the beginning of 'str' + */ +dojo.string.trimStart = function(str) { + return dojo.string.trim(str, 1); +} + +/** + * Trim whitespace at the end of 'str' + */ +dojo.string.trimEnd = function(str) { + return dojo.string.trim(str, -1); +} + +/** + * Return 'str' repeated 'count' times, optionally + * placing 'separator' between each rep + */ +dojo.string.repeat = function(str, count, separator) { + var out = ""; + for(var i = 0; i < count; i++) { + out += str; + if(separator && i < count - 1) { + out += separator; + } + } + return out; +} + +/** + * Pad 'str' to guarantee that it is at least 'len' length + * with the character 'c' at either the start (dir=1) or + * end (dir=-1) of the string + */ +dojo.string.pad = function(str, len/*=2*/, c/*='0'*/, dir/*=1*/) { + var out = String(str); + if(!c) { + c = '0'; + } + if(!dir) { + dir = 1; + } + while(out.length < len) { + if(dir > 0) { + out = c + out; + } else { + out += c; + } + } + return out; +} + +/** same as dojo.string.pad(str, len, c, 1) */ +dojo.string.padLeft = function(str, len, c) { + return dojo.string.pad(str, len, c, 1); +} + +/** same as dojo.string.pad(str, len, c, -1) */ +dojo.string.padRight = function(str, len, c) { + return dojo.string.pad(str, len, c, -1); +} diff --git a/source/web/scripts/ajax/src/string/extras.js b/source/web/scripts/ajax/src/string/extras.js new file mode 100644 index 0000000000..197582ac5b --- /dev/null +++ b/source/web/scripts/ajax/src/string/extras.js @@ -0,0 +1,209 @@ +dojo.provide("dojo.string.extras"); + +dojo.require("dojo.string.common"); +dojo.require("dojo.lang.common"); +dojo.require("dojo.lang.array"); + +/** + * Performs parameterized substitutions on a string. For example, + * dojo.string.substituteParams("File '%{0}' is not found in directory '%{1}'.","foo.html","/temp"); + * returns + * "File 'foo.html' is not found in directory '/temp'." + * + * @param template the original string template with %{values} to be replaced + * @param hash name/value pairs (type object) to provide substitutions. Alternatively, substitutions may be + * included as arguments 1..n to this function, corresponding to template parameters 0..n-1 + * @return the completed string. Throws an exception if any parameter is unmatched + */ +//TODO: use ${} substitution syntax instead, like widgets do? +dojo.string.substituteParams = function(template /*string */, hash /* object - optional or ... */) { + var map = (typeof hash == 'object') ? hash : dojo.lang.toArray(arguments, 1); + + return template.replace(/\%\{(\w+)\}/g, function(match, key){ + return map[key] || dojo.raise("Substitution not found: " + key); + }); +}; + +/** Uppercases the first letter of each word */ +dojo.string.capitalize = function (str) { + if (!dojo.lang.isString(str)) { return ""; } + if (arguments.length == 0) { str = this; } + + var words = str.split(' '); + for(var i=0; i/gm, ">").replace(/"/gm, """); + if(!noSingleQuotes) { str = str.replace(/'/gm, "'"); } + return str; +} + +dojo.string.escapeSql = function(str) { + return str.replace(/'/gm, "''"); +} + +dojo.string.escapeRegExp = function(str) { + return str.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1"); +} + +dojo.string.escapeJavaScript = function(str) { + return str.replace(/(["'\f\b\n\t\r])/gm, "\\$1"); +} + +dojo.string.escapeString = function(str){ + return ('"' + str.replace(/(["\\])/g, '\\$1') + '"' + ).replace(/[\f]/g, "\\f" + ).replace(/[\b]/g, "\\b" + ).replace(/[\n]/g, "\\n" + ).replace(/[\t]/g, "\\t" + ).replace(/[\r]/g, "\\r"); +} + +// TODO: make an HTML version +dojo.string.summary = function(str, len) { + if(!len || str.length <= len) { + return str; + } else { + return str.substring(0, len).replace(/\.+$/, "") + "..."; + } +} + +/** + * Returns true if 'str' ends with 'end' + */ +dojo.string.endsWith = function(str, end, ignoreCase) { + if(ignoreCase) { + str = str.toLowerCase(); + end = end.toLowerCase(); + } + if((str.length - end.length) < 0){ + return false; + } + return str.lastIndexOf(end) == str.length - end.length; +} + +/** + * Returns true if 'str' ends with any of the arguments[2 -> n] + */ +dojo.string.endsWithAny = function(str /* , ... */) { + for(var i = 1; i < arguments.length; i++) { + if(dojo.string.endsWith(str, arguments[i])) { + return true; + } + } + return false; +} + +/** + * Returns true if 'str' starts with 'start' + */ +dojo.string.startsWith = function(str, start, ignoreCase) { + if(ignoreCase) { + str = str.toLowerCase(); + start = start.toLowerCase(); + } + return str.indexOf(start) == 0; +} + +/** + * Returns true if 'str' starts with any of the arguments[2 -> n] + */ +dojo.string.startsWithAny = function(str /* , ... */) { + for(var i = 1; i < arguments.length; i++) { + if(dojo.string.startsWith(str, arguments[i])) { + return true; + } + } + return false; +} + +/** + * Returns true if 'str' contains any of the arguments 2 -> n + */ +dojo.string.has = function(str /* , ... */) { + for(var i = 1; i < arguments.length; i++) { + if(str.indexOf(arguments[i]) > -1){ + return true; + } + } + return false; +} + +dojo.string.normalizeNewlines = function (text,newlineChar) { + if (newlineChar == "\n") { + text = text.replace(/\r\n/g, "\n"); + text = text.replace(/\r/g, "\n"); + } else if (newlineChar == "\r") { + text = text.replace(/\r\n/g, "\r"); + text = text.replace(/\n/g, "\r"); + } else { + text = text.replace(/([^\r])\n/g, "$1\r\n"); + text = text.replace(/\r([^\n])/g, "\r\n$1"); + } + return text; +} + +dojo.string.splitEscaped = function (str,charac) { + var components = []; + for (var i = 0, prevcomma = 0; i < str.length; i++) { + if (str.charAt(i) == '\\') { i++; continue; } + if (str.charAt(i) == charac) { + components.push(str.substring(prevcomma, i)); + prevcomma = i + 1; + } + } + components.push(str.substr(prevcomma)); + return components; +} diff --git a/source/web/scripts/ajax/src/style.js b/source/web/scripts/ajax/src/style.js new file mode 100644 index 0000000000..dcfd5752d3 --- /dev/null +++ b/source/web/scripts/ajax/src/style.js @@ -0,0 +1,5 @@ +dojo.provide("dojo.style"); +dojo.kwCompoundRequire({ + browser: ["dojo.html.style"] +}); +dojo.deprecated("dojo.style", "replaced by dojo.html.style", "0.5"); diff --git a/source/web/scripts/ajax/src/svg.js b/source/web/scripts/ajax/src/svg.js new file mode 100644 index 0000000000..60fd339bcb --- /dev/null +++ b/source/web/scripts/ajax/src/svg.js @@ -0,0 +1,269 @@ +dojo.provide("dojo.svg"); +dojo.require("dojo.lang"); +dojo.require("dojo.dom"); + +dojo.lang.mixin(dojo.svg, dojo.dom); + +dojo.svg.graphics=dojo.svg.g=new function(/* DomDocument */ d){ + // summary + // Singleton to encapsulate SVG rendering functions. + this.suspend=function(){ + // summary + // Suspend the rendering engine + try { d.documentElement.suspendRedraw(0); } catch(e){ } + }; + this.resume=function(){ + // summary + // Resume the rendering engine + try { d.documentElement.unsuspendRedraw(0); } catch(e){ } + }; + this.force=function(){ + // summary + // Force the render engine to redraw + try { d.documentElement.forceRedraw(); } catch(e){ } + }; +}(document); + +dojo.svg.animations=dojo.svg.anim=new function(/* DOMDocument */ d){ + // summary + // Singleton to encapsulate SVG animation functionality. + this.arePaused=function(){ + // summary + // check to see if all animations are paused + try { + return d.documentElement.animationsPaused(); // bool + } catch(e){ + return false; // bool + } + } ; + this.pause=function(){ + // summary + // pause all animations + try { d.documentElement.pauseAnimations(); } catch(e){ } + }; + this.resume=function(){ + // summary + // resume all animations + try { d.documentElement.unpauseAnimations(); } catch(e){ } + }; +}(document); + +// fixme: these functions should be mixed in from dojo.style, but dojo.style is HTML-centric and needs to change. +dojo.svg.toCamelCase=function(/* string */ selector){ + // summary + // converts a CSS-style selector to a camelCased one + var arr=selector.split('-'), cc=arr[0]; + for(var i=1; i < arr.length; i++) { + cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1); + } + return cc; // string +}; +dojo.svg.toSelectorCase=function(/* string */ selector) { + // summary + // converts a camelCased selector to a CSS style one + return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase(); // string +}; +dojo.svg.getStyle=function(/* SVGElement */ node, /* string */ cssSelector){ + // summary + // get the computed style of selector for node. + return document.defaultView.getComputedStyle(node, cssSelector); // object +}; +dojo.svg.getNumericStyle=function(/* SVGElement */ node, /* string */ cssSelector){ + // summary + // return the numeric version of the computed style of selector on node. + return parseFloat(dojo.svg.getStyle(node, cssSelector)); +}; + +// fixme: there are different ways of doing the following, need to take into account +dojo.svg.getOpacity=function(/* SVGElement */node){ + // summary + // Return the opacity of the passed element + return Math.min(1.0, dojo.svg.getNumericStyle(node, "fill-opacity")); // float +}; +dojo.svg.setOpacity=function(/* SVGElement */ node, /* float */ opacity){ + // summary + // set the opacity of node using attributes. + node.setAttributeNS(this.xmlns.svg, "fill-opacity", opacity); + node.setAttributeNS(this.xmlns.svg, "stroke-opacity", opacity); +}; +dojo.svg.clearOpacity=function(/* SVGElement */ node){ + // summary + // Set any attributes setting opacity to opaque (1.0) + node.setAttributeNS(this.xmlns.svg, "fill-opacity", "1.0"); + node.setAttributeNS(this.xmlns.svg, "stroke-opacity", "1.0"); +}; + +/** + * Coordinates and dimensions. + */ + +// TODO ////////////////////////////////////////////////////////// TODO +dojo.svg.getCoords=function(/* SVGElement */ node){ + if (node.getBBox) { + var box=node.getBBox(); + return { x: box.x, y: box.y }; + } + return null; +}; +dojo.svg.setCoords=function(node, coords){ + var p=dojo.svg.getCoords(); + if (!p) return; + var dx=p.x - coords.x; + var dy=p.y - coords.y; + dojo.svg.translate(node, dx, dy); +}; +dojo.svg.getDimensions=function(node){ + if (node.getBBox){ + var box=node.getBBox(); + return { width: box.width, height : box.height }; + } + return null; +}; +dojo.svg.setDimensions=function(node, dim){ + // will only support shape-based and container elements; path-based elements are ignored. + if (node.width){ + node.width.baseVal.value=dim.width; + node.height.baseVal.value=dim.height; + } + else if (node.r){ + node.r.baseVal.value=Math.min(dim.width, dim.height)/2; + } + else if (node.rx){ + node.rx.baseVal.value=dim.width/2; + node.ry.baseVal.value=dim.height/2; + } +}; + +/** + * Transformations. + */ +dojo.svg.translate=function(node, dx, dy){ + if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){ + var t=node.ownerSVGElement.createSVGTransform(); + t.setTranslate(dx, dy); + node.transform.baseVal.appendItem(t); + } +}; +dojo.svg.scale=function(node, scaleX, scaleY){ + if (!scaleY) var scaleY=scaleX; + if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){ + var t=node.ownerSVGElement.createSVGTransform(); + t.setScale(scaleX, scaleY); + node.transform.baseVal.appendItem(t); + } +}; +dojo.svg.rotate=function(node, ang, cx, cy){ + if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){ + var t=node.ownerSVGElement.createSVGTransform(); + if (!cx) t.setMatrix(t.matrix.rotate(ang)); + else t.setRotate(ang, cx, cy); + node.transform.baseVal.appendItem(t); + } +}; +dojo.svg.skew=function(node, ang, axis){ + var dir=axis || "x"; + if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){ + var t=node.ownerSVGElement.createSVGTransform(); + if (dir != "x") t.setSkewY(ang); + else t.setSkewX(ang); + node.transform.baseVal.appendItem(t); + } +}; +dojo.svg.flip=function(node, axis){ + var dir=axis || "x"; + if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){ + var t=node.ownerSVGElement.createSVGTransform(); + t.setMatrix((dir != "x") ? t.matrix.flipY() : t.matrix.flipX()); + node.transform.baseVal.appendItem(t); + } +}; +dojo.svg.invert=function(node){ + if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){ + var t=node.ownerSVGElement.createSVGTransform(); + t.setMatrix(t.matrix.inverse()); + node.transform.baseVal.appendItem(t); + } +}; +dojo.svg.applyMatrix=function(node, a, b, c, d, e, f){ + if (node.transform && node.ownerSVGElement && node.ownerSVGElement.createSVGTransform){ + var m; + if (b){ + var m=node.ownerSVGElement.createSVGMatrix(); + m.a=a; + m.b=b; + m.c=c; + m.d=d; + m.e=e; + m.f=f; + } else m=a; + var t=node.ownerSVGElement.createSVGTransform(); + t.setMatrix(m); + node.transform.baseVal.appendItem(t); + } +}; + +/** + * Grouping and z-index operations. + */ +dojo.svg.group=function(nodes){ + // expect an array of nodes, attaches the group to the parent of the first node. + var p=nodes.item(0).parentNode; + var g=document.createElementNS(this.xmlns.svg, "g"); + for (var i=0; i < nodes.length; i++) g.appendChild(nodes.item(i)); + p.appendChild(g); + return g; +}; +dojo.svg.ungroup=function(g){ + // puts the children of the group on the same level as group was. + var p=g.parentNode; + while (g.childNodes.length > 0) p.appendChild(g.childNodes.item(0)); + p.removeChild(g); +}; +// if the node is part of a group, return the group, else return null. +dojo.svg.getGroup=function(node){ + // if the node is part of a group, return the group, else return null. + var a=this.getAncestors(node); + for (var i=0; i < a.length; i++){ + if (a[i].nodeType == this.ELEMENT_NODE && a[i].nodeName.toLowerCase() == "g") + return a[i]; + } + return null; +}; +dojo.svg.bringToFront=function(node){ + var n=this.getGroup(node) || node; + n.ownerSVGElement.appendChild(n); +}; +dojo.svg.sendToBack=function(node){ + var n=this.getGroup(node) || node; + n.ownerSVGElement.insertBefore(n, n.ownerSVGElement.firstChild); +}; + +// TODO: possibly push node up a level in the DOM if it's at the beginning or end of the childNodes list. +dojo.svg.bringForward=function(node){ + var n=this.getGroup(node) || node; + if (this.getLastChildElement(n.parentNode) != n){ + this.insertAfter(n, this.getNextSiblingElement(n), true); + } +}; +dojo.svg.sendBackward=function(node){ + var n=this.getGroup(node) || node; + if (this.getFirstChildElement(n.parentNode) != n){ + this.insertBefore(n, this.getPreviousSiblingElement(n), true); + } +}; +// END TODO ////////////////////////////////////////////////////// TODO + +dojo.svg.createNodesFromText=function(/* string */ txt, /* bool? */ wrap){ + // summary + // Create a list of nodes from text + var docFrag=(new DOMParser()).parseFromString(txt, "text/xml").normalize(); + if(wrap){ + return [docFrag.firstChild.cloneNode(true)]; // array + } + var nodes=[]; + for(var x=0; x 0) { + var arr = this.name.split('.'); + this.group = arr[0]; + this.name = arr[1]; + } + + // don't do any parsing, leave to implementation + this.value = right; +} + + +// tokeniser, parses into an array of properties. +dojo.textDirectoryTokeniser.tokenise = function (text) { + // normlize to one propterty per line and parse + var nText = dojo.string.normalizeNewlines(text,"\n"); + nText = nText.replace(/\n[ \t]/g, ''); + nText = nText.replace(/\x00/g, ''); + + var lines = nText.split("\n"); + var properties = [] + + for (var i = 0; i < lines.length; i++) { + if (dojo.string.trim(lines[i]) == '') { continue; } + var prop = new dojo.textDirectoryTokeniser.Property(lines[i]); + properties.push(prop); + } + return properties; +} diff --git a/source/web/scripts/ajax/src/undo/Manager.js b/source/web/scripts/ajax/src/undo/Manager.js new file mode 100644 index 0000000000..7940f69f8c --- /dev/null +++ b/source/web/scripts/ajax/src/undo/Manager.js @@ -0,0 +1,190 @@ +dojo.provide("dojo.undo.Manager"); +dojo.require("dojo.lang"); + +dojo.undo.Manager = function(parent) { + this.clear(); + this._parent = parent; +}; +dojo.lang.extend(dojo.undo.Manager, { + _parent: null, + _undoStack: null, + _redoStack: null, + _currentManager: null, + + canUndo: false, + canRedo: false, + + isUndoing: false, + isRedoing: false, + + // these events allow you to hook in and update your code (UI?) as necessary + onUndo: function(manager, item) {}, + onRedo: function(manager, item) {}, + + // fired when you do *any* undo action, which means you'll have one for every item + // in a transaction. this is usually only useful for debugging + onUndoAny: function(manager, item) {}, + onRedoAny: function(manager, item) {}, + + _updateStatus: function() { + this.canUndo = this._undoStack.length > 0; + this.canRedo = this._redoStack.length > 0; + }, + + clear: function() { + this._undoStack = []; + this._redoStack = []; + this._currentManager = this; + + this.isUndoing = false; + this.isRedoing = false; + + this._updateStatus(); + }, + + undo: function() { + if(!this.canUndo) { return false; } + + this.endAllTransactions(); + + this.isUndoing = true; + var top = this._undoStack.pop(); + if(top instanceof dojo.undo.Manager){ + top.undoAll(); + }else{ + top.undo(); + } + if(top.redo){ + this._redoStack.push(top); + } + this.isUndoing = false; + + this._updateStatus(); + this.onUndo(this, top); + if(!(top instanceof dojo.undo.Manager)) { + this.getTop().onUndoAny(this, top); + } + return true; + }, + + redo: function() { + if(!this.canRedo){ return false; } + + this.isRedoing = true; + var top = this._redoStack.pop(); + if(top instanceof dojo.undo.Manager) { + top.redoAll(); + }else{ + top.redo(); + } + this._undoStack.push(top); + this.isRedoing = false; + + this._updateStatus(); + this.onRedo(this, top); + if(!(top instanceof dojo.undo.Manager)){ + this.getTop().onRedoAny(this, top); + } + return true; + }, + + undoAll: function() { + while(this._undoStack.length > 0) { + this.undo(); + } + }, + + redoAll: function() { + while(this._redoStack.length > 0) { + this.redo(); + } + }, + + push: function(undo, redo /* optional */, description /* optional */) { + if(!undo) { return; } + + if(this._currentManager == this) { + this._undoStack.push({ + undo: undo, + redo: redo, + description: description + }); + } else { + this._currentManager.push.apply(this._currentManager, arguments); + } + // adding a new undo-able item clears out the redo stack + this._redoStack = []; + this._updateStatus(); + }, + + concat: function(manager) { + if ( !manager ) { return; } + + if (this._currentManager == this ) { + for(var x=0; x < manager._undoStack.length; x++) { + this._undoStack.push(manager._undoStack[x]); + } + // adding a new undo-able item clears out the redo stack + if (manager._undoStack.length > 0) { + this._redoStack = []; + } + this._updateStatus(); + } else { + this._currentManager.concat.apply(this._currentManager, arguments); + } + }, + + beginTransaction: function(description /* optional */) { + if(this._currentManager == this) { + var mgr = new dojo.undo.Manager(this); + mgr.description = description ? description : ""; + this._undoStack.push(mgr); + this._currentManager = mgr; + return mgr; + } else { + //for nested transactions need to make sure the top level _currentManager is set + this._currentManager = this._currentManager.beginTransaction.apply(this._currentManager, arguments); + } + }, + + endTransaction: function(flatten /* optional */) { + if(this._currentManager == this) { + if(this._parent) { + this._parent._currentManager = this._parent; + // don't leave empty transactions hangin' around + if(this._undoStack.length == 0 || flatten) { + var idx = dojo.lang.find(this._parent._undoStack, this); + if (idx >= 0) { + this._parent._undoStack.splice(idx, 1); + //add the current transaction to parents undo stack + if (flatten) { + for(var x=0; x < this._undoStack.length; x++){ + this._parent._undoStack.splice(idx++, 0, this._undoStack[x]); + } + this._updateStatus(); + } + } + } + return this._parent; + } + } else { + //for nested transactions need to make sure the top level _currentManager is set + this._currentManager = this._currentManager.endTransaction.apply(this._currentManager, arguments); + } + }, + + endAllTransactions: function() { + while(this._currentManager != this) { + this.endTransaction(); + } + }, + + // find the top parent of an undo manager + getTop: function() { + if(this._parent) { + return this._parent.getTop(); + } else { + return this; + } + } +}); diff --git a/source/web/scripts/ajax/src/undo/__package__.js b/source/web/scripts/ajax/src/undo/__package__.js new file mode 100644 index 0000000000..c5f6e36d72 --- /dev/null +++ b/source/web/scripts/ajax/src/undo/__package__.js @@ -0,0 +1,2 @@ +dojo.require("dojo.undo.Manager"); +dojo.provide("dojo.undo.*"); diff --git a/source/web/scripts/ajax/src/undo/browser.js b/source/web/scripts/ajax/src/undo/browser.js new file mode 100644 index 0000000000..2d2af2ee81 --- /dev/null +++ b/source/web/scripts/ajax/src/undo/browser.js @@ -0,0 +1,269 @@ +dojo.provide("dojo.undo.browser"); +dojo.require("dojo.io"); + +try{ + if((!djConfig["preventBackButtonFix"])&&(!dojo.hostenv.post_load_)){ + document.write(""); + } +}catch(e){/* squelch */} + +if(dojo.render.html.opera){ + dojo.debug("Opera is not supported with dojo.undo.browser, so back/forward detection will not work."); +} + +/* NOTES: + * Safari 1.2: + * back button "works" fine, however it's not possible to actually + * DETECT that you've moved backwards by inspecting window.location. + * Unless there is some other means of locating. + * FIXME: perhaps we can poll on history.length? + * Safari 2.0.3+ (and probably 1.3.2+): + * works fine, except when changeUrl is used. When changeUrl is used, + * Safari jumps all the way back to whatever page was shown before + * the page that uses dojo.undo.browser support. + * IE 5.5 SP2: + * back button behavior is macro. It does not move back to the + * previous hash value, but to the last full page load. This suggests + * that the iframe is the correct way to capture the back button in + * these cases. + * Don't test this page using local disk for MSIE. MSIE will not create + * a history list for iframe_history.html if served from a file: URL. + * The XML served back from the XHR tests will also not be properly + * created if served from local disk. Serve the test pages from a web + * server to test in that browser. + * IE 6.0: + * same behavior as IE 5.5 SP2 + * Firefox 1.0: + * the back button will return us to the previous hash on the same + * page, thereby not requiring an iframe hack, although we do then + * need to run a timer to detect inter-page movement. + */ +dojo.undo.browser = { + initialHref: window.location.href, + initialHash: window.location.hash, + + moveForward: false, + historyStack: [], + forwardStack: [], + historyIframe: null, + bookmarkAnchor: null, + locationTimer: null, + + /** + * setInitialState sets the state object and back callback for the very first page that is loaded. + * It is recommended that you call this method as part of an event listener that is registered via + * dojo.addOnLoad(). + */ + setInitialState: function(args){ + this.initialState = {"url": this.initialHref, "kwArgs": args, "urlHash": this.initialHash}; + }, + + //FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things. + //FIXME: is there a slight race condition in moz using change URL with the timer check and when + // the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent. + /** + * addToHistory takes one argument, and it is an object that defines the following functions: + * - To support getting back button notifications, the object argument should implement a + * function called either "back", "backButton", or "handle". The string "back" will be + * passed as the first and only argument to this callback. + * - To support getting forward button notifications, the object argument should implement a + * function called either "forward", "forwardButton", or "handle". The string "forward" will be + * passed as the first and only argument to this callback. + * - If you want the browser location string to change, define "changeUrl" on the object. If the + * value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment + * identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does + * not evaluate to false, that value will be used as the fragment identifier. For example, + * if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1 + * + * Full example: + * + * dojo.undo.browser.addToHistory({ + * back: function() { alert('back pressed'); }, + * forward: function() { alert('forward pressed'); }, + * changeUrl: true + * }); + */ + addToHistory: function(args){ + var hash = null; + if(!this.historyIframe){ + this.historyIframe = window.frames["djhistory"]; + } + if(!this.bookmarkAnchor){ + this.bookmarkAnchor = document.createElement("a"); + dojo.body().appendChild(this.bookmarkAnchor); + this.bookmarkAnchor.style.display = "none"; + } + if((!args["changeUrl"])||(dojo.render.html.ie)){ + var url = dojo.hostenv.getBaseScriptUri()+"iframe_history.html?"+(new Date()).getTime(); + this.moveForward = true; + dojo.io.setIFrameSrc(this.historyIframe, url, false); + } + if(args["changeUrl"]){ + this.changingUrl = true; + hash = "#"+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime()); + setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;", 1); + this.bookmarkAnchor.href = hash; + + if(dojo.render.html.ie){ + var oldCB = args["back"]||args["backButton"]||args["handle"]; + + //The function takes handleName as a parameter, in case the + //callback we are overriding was "handle". In that case, + //we will need to pass the handle name to handle. + var tcb = function(handleName){ + if(window.location.hash != ""){ + setTimeout("window.location.href = '"+hash+"';", 1); + } + //Use apply to set "this" to args, and to try to avoid memory leaks. + oldCB.apply(this, [handleName]); + } + + //Set interceptor function in the right place. + if(args["back"]){ + args.back = tcb; + }else if(args["backButton"]){ + args.backButton = tcb; + }else if(args["handle"]){ + args.handle = tcb; + } + + //If addToHistory is called, then that means we prune the + //forward stack -- the user went back, then wanted to + //start a new forward path. + this.forwardStack = []; + var oldFW = args["forward"]||args["forwardButton"]||args["handle"]; + + //The function takes handleName as a parameter, in case the + //callback we are overriding was "handle". In that case, + //we will need to pass the handle name to handle. + var tfw = function(handleName){ + if(window.location.hash != ""){ + window.location.href = hash; + } + if(oldFW){ // we might not actually have one + //Use apply to set "this" to args, and to try to avoid memory leaks. + oldFW.apply(this, [handleName]); + } + } + + //Set interceptor function in the right place. + if(args["forward"]){ + args.forward = tfw; + }else if(args["forwardButton"]){ + args.forwardButton = tfw; + }else if(args["handle"]){ + args.handle = tfw; + } + + }else if(dojo.render.html.moz){ + // start the timer + if(!this.locationTimer){ + this.locationTimer = setInterval("dojo.undo.browser.checkLocation();", 200); + } + } + } + + this.historyStack.push({"url": url, "kwArgs": args, "urlHash": hash}); + }, + + checkLocation: function(){ + if (!this.changingUrl){ + var hsl = this.historyStack.length; + + if((window.location.hash == this.initialHash||window.location.href == this.initialHref)&&(hsl == 1)){ + // FIXME: could this ever be a forward button? + // we can't clear it because we still need to check for forwards. Ugg. + // clearInterval(this.locationTimer); + this.handleBackButton(); + return; + } + // first check to see if we could have gone forward. We always halt on + // a no-hash item. + if(this.forwardStack.length > 0){ + if(this.forwardStack[this.forwardStack.length-1].urlHash == window.location.hash){ + this.handleForwardButton(); + return; + } + } + + // ok, that didn't work, try someplace back in the history stack + if((hsl >= 2)&&(this.historyStack[hsl-2])){ + if(this.historyStack[hsl-2].urlHash==window.location.hash){ + this.handleBackButton(); + return; + } + } + } + }, + + iframeLoaded: function(evt, ifrLoc){ + if(!dojo.render.html.opera){ + var query = this._getUrlQuery(ifrLoc.href); + if(query == null){ + // alert("iframeLoaded"); + // we hit the end of the history, so we should go back + if(this.historyStack.length == 1){ + this.handleBackButton(); + } + return; + } + if(this.moveForward){ + // we were expecting it, so it's not either a forward or backward movement + this.moveForward = false; + return; + } + + //Check the back stack first, since it is more likely. + //Note that only one step back or forward is supported. + if(this.historyStack.length >= 2 && query == this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){ + this.handleBackButton(); + } + else if(this.forwardStack.length > 0 && query == this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){ + this.handleForwardButton(); + } + } + }, + + handleBackButton: function(){ + //The "current" page is always at the top of the history stack. + var current = this.historyStack.pop(); + if(!current){ return; } + var last = this.historyStack[this.historyStack.length-1]; + if(!last && this.historyStack.length == 0){ + last = this.initialState; + } + if (last){ + if(last.kwArgs["back"]){ + last.kwArgs["back"](); + }else if(last.kwArgs["backButton"]){ + last.kwArgs["backButton"](); + }else if(last.kwArgs["handle"]){ + last.kwArgs.handle("back"); + } + } + this.forwardStack.push(current); + }, + + handleForwardButton: function(){ + var last = this.forwardStack.pop(); + if(!last){ return; } + if(last.kwArgs["forward"]){ + last.kwArgs.forward(); + }else if(last.kwArgs["forwardButton"]){ + last.kwArgs.forwardButton(); + }else if(last.kwArgs["handle"]){ + last.kwArgs.handle("forward"); + } + this.historyStack.push(last); + }, + + _getUrlQuery: function(url){ + var segments = url.split("?"); + if (segments.length < 2){ + return null; + } + else{ + return segments[1]; + } + } +} diff --git a/source/web/scripts/ajax/src/uri/Uri.js b/source/web/scripts/ajax/src/uri/Uri.js new file mode 100644 index 0000000000..1eeb187c65 --- /dev/null +++ b/source/web/scripts/ajax/src/uri/Uri.js @@ -0,0 +1,104 @@ +dojo.provide("dojo.uri.Uri"); + +dojo.uri = new function() { + this.dojoUri = function (uri) { + // returns a Uri object resolved relative to the dojo root + return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), uri); + } + + //returns a URI of a widget in a namespace, for example dojo.uri.nsUri("dojo","Editor"), or dojo.uri.nsUri("customNS","someWidget") + this.nsUri = function(namespace,uri){ + var ns = dojo.getNamespace(namespace); + if(!ns){ + return null; + } + var loc = ns.location; + if(loc.lastIndexOf("/") != loc.length - 1){ + loc += "/"; + } + return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri()+loc,uri); + } + + this.Uri = function (/*uri1, uri2, [...]*/) { + // An object representing a Uri. + // Each argument is evaluated in order relative to the next until + // a conanical uri is producued. To get an absolute Uri relative + // to the current document use + // new dojo.uri.Uri(document.baseURI, uri) + + // TODO: support for IPv6, see RFC 2732 + + // resolve uri components relative to each other + var uri = arguments[0]; + for (var i = 1; i < arguments.length; i++) { + if(!arguments[i]) { continue; } + + // Safari doesn't support this.constructor so we have to be explicit + var relobj = new dojo.uri.Uri(arguments[i].toString()); + var uriobj = new dojo.uri.Uri(uri.toString()); + + if (relobj.path == "" && relobj.scheme == null && + relobj.authority == null && relobj.query == null) { + if (relobj.fragment != null) { uriobj.fragment = relobj.fragment; } + relobj = uriobj; + } else if (relobj.scheme == null) { + relobj.scheme = uriobj.scheme; + + if (relobj.authority == null) { + relobj.authority = uriobj.authority; + + if (relobj.path.charAt(0) != "/") { + var path = uriobj.path.substring(0, + uriobj.path.lastIndexOf("/") + 1) + relobj.path; + + var segs = path.split("/"); + for (var j = 0; j < segs.length; j++) { + if (segs[j] == ".") { + if (j == segs.length - 1) { segs[j] = ""; } + else { segs.splice(j, 1); j--; } + } else if (j > 0 && !(j == 1 && segs[0] == "") && + segs[j] == ".." && segs[j-1] != "..") { + + if (j == segs.length - 1) { segs.splice(j, 1); segs[j - 1] = ""; } + else { segs.splice(j - 1, 2); j -= 2; } + } + } + relobj.path = segs.join("/"); + } + } + } + + uri = ""; + if (relobj.scheme != null) { uri += relobj.scheme + ":"; } + if (relobj.authority != null) { uri += "//" + relobj.authority; } + uri += relobj.path; + if (relobj.query != null) { uri += "?" + relobj.query; } + if (relobj.fragment != null) { uri += "#" + relobj.fragment; } + } + + this.uri = uri.toString(); + + // break the uri into its main components + var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"; + var r = this.uri.match(new RegExp(regexp)); + + this.scheme = r[2] || (r[1] ? "" : null); + this.authority = r[4] || (r[3] ? "" : null); + this.path = r[5]; // can never be undefined + this.query = r[7] || (r[6] ? "" : null); + this.fragment = r[9] || (r[8] ? "" : null); + + if (this.authority != null) { + // server based naming authority + regexp = "^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$"; + r = this.authority.match(new RegExp(regexp)); + + this.user = r[3] || null; + this.password = r[4] || null; + this.host = r[5]; + this.port = r[7] || null; + } + + this.toString = function(){ return this.uri; } + } +}; diff --git a/source/web/scripts/ajax/src/uri/__package__.js b/source/web/scripts/ajax/src/uri/__package__.js new file mode 100644 index 0000000000..0184acb58c --- /dev/null +++ b/source/web/scripts/ajax/src/uri/__package__.js @@ -0,0 +1,4 @@ +dojo.kwCompoundRequire({ + common: ["dojo.uri.Uri", false, false] +}); +dojo.provide("dojo.uri.*"); diff --git a/source/web/scripts/ajax/src/uuid/LightweightGenerator.js b/source/web/scripts/ajax/src/uuid/LightweightGenerator.js new file mode 100644 index 0000000000..b0e1c08ab2 --- /dev/null +++ b/source/web/scripts/ajax/src/uuid/LightweightGenerator.js @@ -0,0 +1,72 @@ +dojo.provide("dojo.uuid.LightweightGenerator"); + +/** + * The LightweightGenerator is intended to be small and fast, + * but not necessarily good. + * + * Small: The LightweightGenerator has a small footprint. + * Once comments are stripped, it's only about 25 lines of + * code, and it doesn't dojo.require() any other packages. + * + * Fast: The LightweightGenerator can generate lots of new + * UUIDs fairly quickly (at least, more quickly than the other + * dojo UUID generators). + * + * Not necessarily good: We use Math.random() as our source + * of randomness, which may or may not provide much randomness. + */ +dojo.uuid.LightweightGenerator = new function() { + + var HEX_RADIX = 16; + +// -------------------------------------------------- +// Private functions +// -------------------------------------------------- + function _generateRandomEightCharacterHexString() { + // Make random32bitNumber be a randomly generated floating point number + // between 0 and (4,294,967,296 - 1), inclusive. + var random32bitNumber = Math.floor( (Math.random() % 1) * Math.pow(2, 32) ); + var eightCharacterHexString = random32bitNumber.toString(HEX_RADIX); + while (eightCharacterHexString.length < 8) { + eightCharacterHexString = "0" + eightCharacterHexString; + } + return eightCharacterHexString; // for example: "3B12F1DF" + } + +// -------------------------------------------------- +// Public functions +// -------------------------------------------------- + +/** + * This function generates random UUIDs, meaning "version 4" UUIDs. + * For example, a typical generated value would be something like + * "3b12f1df-5232-4804-897e-917bf397618a". + * + * Examples: + *

+ *   var string = dojo.uuid.LightweightGenerator.generate();
+ *   var string = dojo.uuid.LightweightGenerator.generate(String);
+ *   var uuid   = dojo.uuid.LightweightGenerator.generate(dojo.uuid.Uuid);
+ * 
+ * + * @param returnType Optional. The type of instance to return. + * @return A newly generated version 4 UUID. + */ + this.generate = function(returnType) { + var hyphen = "-"; + var versionCodeForRandomlyGeneratedUuids = "4"; // 8 == binary2hex("0100") + var variantCodeForDCEUuids = "8"; // 8 == binary2hex("1000") + var a = _generateRandomEightCharacterHexString(); + var b = _generateRandomEightCharacterHexString(); + b = b.substring(0, 4) + hyphen + versionCodeForRandomlyGeneratedUuids + b.substring(5, 8); + var c = _generateRandomEightCharacterHexString(); + c = variantCodeForDCEUuids + c.substring(1, 4) + hyphen + c.substring(4, 8); + var d = _generateRandomEightCharacterHexString(); + var returnValue = a + hyphen + b + hyphen + c + d; + returnValue = returnValue.toLowerCase(); + if (returnType && (returnType != String)) { + returnValue = new returnType(returnValue); + } + return returnValue; + }; +}(); diff --git a/source/web/scripts/ajax/src/uuid/NameBasedGenerator.js b/source/web/scripts/ajax/src/uuid/NameBasedGenerator.js new file mode 100644 index 0000000000..60676d5989 --- /dev/null +++ b/source/web/scripts/ajax/src/uuid/NameBasedGenerator.js @@ -0,0 +1,33 @@ +dojo.provide("dojo.uuid.NameBasedGenerator"); + +dojo.uuid.NameBasedGenerator = new function() { + +/** + * This function generates name-based UUIDs, meaning "version 3" + * and "version 5" UUIDs. + * + * Examples: + *
+ *   var string = dojo.uuid.NameBasedGenerator.generate();
+ *   var string = dojo.uuid.NameBasedGenerator.generate(String);
+ *   var uuid   = dojo.uuid.NameBasedGenerator.generate(dojo.uuid.Uuid);
+ * 
+ * + * @param returnType Optional. The type of instance to return. + * @return A newly generated version 3 or version 5 UUID. + */ + this.generate = function(returnType) { + dojo.unimplemented('dojo.uuid.NameBasedGenerator.generate'); + + // FIXME: + // For an algorithm to generate name-based UUIDs, + // see sections 4.3 of RFC 4122: + // http://www.ietf.org/rfc/rfc4122.txt + + var returnValue = "00000000-0000-0000-0000-000000000000"; // FIXME + if (returnType && (returnType != String)) { + returnValue = new returnType(returnValue); + } + return returnValue; + }; +}(); \ No newline at end of file diff --git a/source/web/scripts/ajax/src/uuid/NilGenerator.js b/source/web/scripts/ajax/src/uuid/NilGenerator.js new file mode 100644 index 0000000000..f400f3230d --- /dev/null +++ b/source/web/scripts/ajax/src/uuid/NilGenerator.js @@ -0,0 +1,28 @@ +dojo.provide("dojo.uuid.NilGenerator"); + +dojo.uuid.NilGenerator = new function() { + +/** + * This function returns the Nil UUID: + * "00000000-0000-0000-0000-000000000000". + * The Nil UUID is described in section 4.1.7 of + * RFC 4122: http://www.ietf.org/rfc/rfc4122.txt + * + * Examples: + *
+ *   var string = dojo.uuid.NilGenerator.generate();
+ *   var string = dojo.uuid.NilGenerator.generate(String);
+ *   var uuid   = dojo.uuid.NilGenerator.generate(dojo.uuid.Uuid);
+ * 
+ * + * @param returnType Optional. The type of instance to return. + * @return The nil UUID. + */ + this.generate = function(returnType) { + var returnValue = "00000000-0000-0000-0000-000000000000"; + if (returnType && (returnType != String)) { + returnValue = new returnType(returnValue); + } + return returnValue; + }; +}(); \ No newline at end of file diff --git a/source/web/scripts/ajax/src/uuid/RandomGenerator.js b/source/web/scripts/ajax/src/uuid/RandomGenerator.js new file mode 100644 index 0000000000..62386550ee --- /dev/null +++ b/source/web/scripts/ajax/src/uuid/RandomGenerator.js @@ -0,0 +1,34 @@ +dojo.provide("dojo.uuid.RandomGenerator"); + +dojo.uuid.RandomGenerator = new function() { + +/** + * This function generates random UUIDs, meaning "version 4" UUIDs. + * For example, a typical generated value would be something like + * "3b12f1df-5232-4804-897e-917bf397618a". + * + * Examples: + *
+ *   var string = dojo.uuid.RandomGenerator.generate();
+ *   var string = dojo.uuid.RandomGenerator.generate(String);
+ *   var uuid   = dojo.uuid.RandomGenerator.generate(dojo.uuid.Uuid);
+ * 
+ * + * @param returnType Optional. The type of instance to return. + * @return A newly generated version 4 UUID. + */ + this.generate = function(returnType) { + dojo.unimplemented('dojo.uuid.RandomGenerator.generate'); + + // FIXME: + // For an algorithm to generate a random UUID, see + // sections 4.4 and 4.5 of RFC 4122: + // http://www.ietf.org/rfc/rfc4122.txt + + var returnValue = "00000000-0000-0000-0000-000000000000"; // FIXME + if (returnType && (returnType != String)) { + returnValue = new returnType(returnValue); + } + return returnValue; + }; +}(); diff --git a/source/web/scripts/ajax/src/uuid/TimeBasedGenerator.js b/source/web/scripts/ajax/src/uuid/TimeBasedGenerator.js new file mode 100644 index 0000000000..2ab52dd1e3 --- /dev/null +++ b/source/web/scripts/ajax/src/uuid/TimeBasedGenerator.js @@ -0,0 +1,381 @@ +dojo.provide("dojo.uuid.TimeBasedGenerator"); +dojo.require("dojo.lang.*"); + +dojo.uuid.TimeBasedGenerator = new function() { + +// -------------------------------------------------- +// Public constants +// -------------------------------------------------- + // Number of hours between October 15, 1582 and January 1, 1970: + this.GREGORIAN_CHANGE_OFFSET_IN_HOURS = 3394248; + + // Number of seconds between October 15, 1582 and January 1, 1970: + // this.GREGORIAN_CHANGE_OFFSET_IN_SECONDS = 12219292800; + +// -------------------------------------------------- +// Private variables +// -------------------------------------------------- + var _uuidPseudoNodeString = null; + var _uuidClockSeqString = null; + var _dateValueOfPreviousUuid = null; + var _nextIntraMillisecondIncrement = 0; + var _cachedMillisecondsBetween1582and1970 = null; + var _cachedHundredNanosecondIntervalsPerMillisecond = null; + var _uniformNode = null; + var HEX_RADIX = 16; + +// -------------------------------------------------- +// Private functions +// -------------------------------------------------- + +/** + * Given an array which holds a 64-bit number broken into 4 16-bit elements, + * this method carries any excess bits (greater than 16-bits) from each array + * element into the next. + * + * @param arrayA An array with 4 elements, each of which is a 16-bit number. + */ + function _carry(arrayA) { + arrayA[2] += arrayA[3] >>> 16; + arrayA[3] &= 0xFFFF; + arrayA[1] += arrayA[2] >>> 16; + arrayA[2] &= 0xFFFF; + arrayA[0] += arrayA[1] >>> 16; + arrayA[1] &= 0xFFFF; + dojo.lang.assert((arrayA[0] >>> 16) === 0); + } + +/** + * Given a floating point number, this method returns an array which holds a + * 64-bit number broken into 4 16-bit elements. + * + * @param x A floating point number. + * @return An array with 4 elements, each of which is a 16-bit number. + */ + function _get64bitArrayFromFloat(x) { + var result = new Array(0, 0, 0, 0); + result[3] = x % 0x10000; + x -= result[3]; + x /= 0x10000; + result[2] = x % 0x10000; + x -= result[2]; + x /= 0x10000; + result[1] = x % 0x10000; + x -= result[1]; + x /= 0x10000; + result[0] = x; + return result; + } + +/** + * Takes two arrays, each of which holds a 64-bit number broken into 4 + * 16-bit elements, and returns a new array that holds a 64-bit number + * that is the sum of the two original numbers. + * + * @param arrayA An array with 4 elements, each of which is a 16-bit number. + * @param arrayB An array with 4 elements, each of which is a 16-bit number. + * @return An array with 4 elements, each of which is a 16-bit number. + */ + function _addTwo64bitArrays(arrayA, arrayB) { + dojo.lang.assertType(arrayA, Array); + dojo.lang.assertType(arrayB, Array); + dojo.lang.assert(arrayA.length == 4); + dojo.lang.assert(arrayB.length == 4); + + var result = new Array(0, 0, 0, 0); + result[3] = arrayA[3] + arrayB[3]; + result[2] = arrayA[2] + arrayB[2]; + result[1] = arrayA[1] + arrayB[1]; + result[0] = arrayA[0] + arrayB[0]; + _carry(result); + return result; + } + +/** + * Takes two arrays, each of which holds a 64-bit number broken into 4 + * 16-bit elements, and returns a new array that holds a 64-bit number + * that is the product of the two original numbers. + * + * @param arrayA An array with 4 elements, each of which is a 16-bit number. + * @param arrayB An array with 4 elements, each of which is a 16-bit number. + * @return An array with 4 elements, each of which is a 16-bit number. + */ + function _multiplyTwo64bitArrays(arrayA, arrayB) { + dojo.lang.assertType(arrayA, Array); + dojo.lang.assertType(arrayB, Array); + dojo.lang.assert(arrayA.length == 4); + dojo.lang.assert(arrayB.length == 4); + + var overflow = false; + if (arrayA[0] * arrayB[0] !== 0) { overflow = true; } + if (arrayA[0] * arrayB[1] !== 0) { overflow = true; } + if (arrayA[0] * arrayB[2] !== 0) { overflow = true; } + if (arrayA[1] * arrayB[0] !== 0) { overflow = true; } + if (arrayA[1] * arrayB[1] !== 0) { overflow = true; } + if (arrayA[2] * arrayB[0] !== 0) { overflow = true; } + dojo.lang.assert(!overflow); + + var result = new Array(0, 0, 0, 0); + result[0] += arrayA[0] * arrayB[3]; + _carry(result); + result[0] += arrayA[1] * arrayB[2]; + _carry(result); + result[0] += arrayA[2] * arrayB[1]; + _carry(result); + result[0] += arrayA[3] * arrayB[0]; + _carry(result); + result[1] += arrayA[1] * arrayB[3]; + _carry(result); + result[1] += arrayA[2] * arrayB[2]; + _carry(result); + result[1] += arrayA[3] * arrayB[1]; + _carry(result); + result[2] += arrayA[2] * arrayB[3]; + _carry(result); + result[2] += arrayA[3] * arrayB[2]; + _carry(result); + result[3] += arrayA[3] * arrayB[3]; + _carry(result); + return result; + } + +/** + * Pads a string with leading zeros and returns the result. + * For example: + *
+ *   result = _padWithLeadingZeros("abc", 6);
+ *   dojo.lang.assert(result == "000abc");
+ * 
+ * + * @param string A string to add padding to. + * @param desiredLength The number of characters the return string should have. + * @return A string. + */ + function _padWithLeadingZeros(string, desiredLength) { + while (string.length < desiredLength) { + string = "0" + string; + } + return string; + } + +/** + * Returns a randomly generated 8-character string of hex digits. + * + * @return An 8-character hex string. + */ + function _generateRandomEightCharacterHexString() { + // FIXME: This probably isn't a very high quality random number. + + // Make random32bitNumber be a randomly generated floating point number + // between 0 and (4,294,967,296 - 1), inclusive. + var random32bitNumber = Math.floor( (Math.random() % 1) * Math.pow(2, 32) ); + + var eightCharacterString = random32bitNumber.toString(HEX_RADIX); + while (eightCharacterString.length < 8) { + eightCharacterString = "0" + eightCharacterString; + } + return eightCharacterString; + } + +/** + * Generates a time-based UUID, meaning a version 1 UUID. JavaScript + * code running in a browser doesn't have access to the IEEE 802.3 address + * of the computer, so if a node value isn't supplied, we generate a random + * pseudonode value instead. + * + * @param node Optional. A 12-character string to use as the node in the new UUID. + * @return Returns a 36 character string, which will look something like "b4308fb0-86cd-11da-a72b-0800200c9a66". + */ + function _generateUuidString(node) { + dojo.lang.assertType(node, String, {optional: true}); + if (node) { + dojo.lang.assert(node.length == 12); + } else { + if (_uniformNode) { + node = _uniformNode; + } else { + if (!_uuidPseudoNodeString) { + var pseudoNodeIndicatorBit = 0x8000; + var random15bitNumber = Math.floor( (Math.random() % 1) * Math.pow(2, 15) ); + var leftmost4HexCharacters = (pseudoNodeIndicatorBit | random15bitNumber).toString(HEX_RADIX); + _uuidPseudoNodeString = leftmost4HexCharacters + _generateRandomEightCharacterHexString(); + } + node = _uuidPseudoNodeString; + } + } + if (!_uuidClockSeqString) { + var variantCodeForDCEUuids = 0x8000; // 10--------------, i.e. uses only first two of 16 bits. + var random14bitNumber = Math.floor( (Math.random() % 1) * Math.pow(2, 14) ); + _uuidClockSeqString = (variantCodeForDCEUuids | random14bitNumber).toString(HEX_RADIX); + } + + // Maybe we should think about trying to make the code more readable to + // newcomers by creating a class called "WholeNumber" that encapsulates + // the methods and data structures for working with these arrays that + // hold 4 16-bit numbers? And then these variables below have names + // like "wholeSecondsPerHour" rather than "arraySecondsPerHour"? + var now = new Date(); + var millisecondsSince1970 = now.valueOf(); // milliseconds since midnight 01 January, 1970 UTC. + var nowArray = _get64bitArrayFromFloat(millisecondsSince1970); + if (!_cachedMillisecondsBetween1582and1970) { + var arraySecondsPerHour = _get64bitArrayFromFloat(60 * 60); + var arrayHoursBetween1582and1970 = _get64bitArrayFromFloat(dojo.uuid.TimeBasedGenerator.GREGORIAN_CHANGE_OFFSET_IN_HOURS); + var arraySecondsBetween1582and1970 = _multiplyTwo64bitArrays(arrayHoursBetween1582and1970, arraySecondsPerHour); + var arrayMillisecondsPerSecond = _get64bitArrayFromFloat(1000); + _cachedMillisecondsBetween1582and1970 = _multiplyTwo64bitArrays(arraySecondsBetween1582and1970, arrayMillisecondsPerSecond); + _cachedHundredNanosecondIntervalsPerMillisecond = _get64bitArrayFromFloat(10000); + } + var arrayMillisecondsSince1970 = nowArray; + var arrayMillisecondsSince1582 = _addTwo64bitArrays(_cachedMillisecondsBetween1582and1970, arrayMillisecondsSince1970); + var arrayHundredNanosecondIntervalsSince1582 = _multiplyTwo64bitArrays(arrayMillisecondsSince1582, _cachedHundredNanosecondIntervalsPerMillisecond); + + if (now.valueOf() == _dateValueOfPreviousUuid) { + arrayHundredNanosecondIntervalsSince1582[3] += _nextIntraMillisecondIncrement; + _carry(arrayHundredNanosecondIntervalsSince1582); + _nextIntraMillisecondIncrement += 1; + if (_nextIntraMillisecondIncrement == 10000) { + // If we've gotten to here, it means we've already generated 10,000 + // UUIDs in this single millisecond, which is the most that the UUID + // timestamp field allows for. So now we'll just sit here and wait + // for a fraction of a millisecond, so as to ensure that the next + // time this method is called there will be a different millisecond + // value in the timestamp field. + while (now.valueOf() == _dateValueOfPreviousUuid) { + now = new Date(); + } + } + } else { + _dateValueOfPreviousUuid = now.valueOf(); + _nextIntraMillisecondIncrement = 1; + } + + var hexTimeLowLeftHalf = arrayHundredNanosecondIntervalsSince1582[2].toString(HEX_RADIX); + var hexTimeLowRightHalf = arrayHundredNanosecondIntervalsSince1582[3].toString(HEX_RADIX); + var hexTimeLow = _padWithLeadingZeros(hexTimeLowLeftHalf, 4) + _padWithLeadingZeros(hexTimeLowRightHalf, 4); + var hexTimeMid = arrayHundredNanosecondIntervalsSince1582[1].toString(HEX_RADIX); + hexTimeMid = _padWithLeadingZeros(hexTimeMid, 4); + var hexTimeHigh = arrayHundredNanosecondIntervalsSince1582[0].toString(HEX_RADIX); + hexTimeHigh = _padWithLeadingZeros(hexTimeHigh, 3); + var hyphen = "-"; + var versionCodeForTimeBasedUuids = "1"; // binary2hex("0001") + var resultUuid = hexTimeLow + hyphen + hexTimeMid + hyphen + + versionCodeForTimeBasedUuids + hexTimeHigh + hyphen + + _uuidClockSeqString + hyphen + node; + resultUuid = resultUuid.toLowerCase(); + return resultUuid; + } + +// -------------------------------------------------- +// Public functions +// -------------------------------------------------- + +/** + * Sets the 'node' value that will be included in generated UUIDs. + * + * @param node A 12-character hex string representing a pseudoNode or hardwareNode. + */ + this.setNode = function(node) { + dojo.lang.assert((node === null) || (node.length == 12)); + _uniformNode = node; + }; + +/** + * Returns the 'node' value that will be included in generated UUIDs. + * + * @return A 12-character hex string representing a pseudoNode or hardwareNode. + */ + this.getNode = function() { + return _uniformNode; + }; + +/** + * This function generates time-based UUIDs, meaning "version 1" UUIDs. + * + * For more info, see + * http://www.webdav.org/specs/draft-leach-uuids-guids-01.txt + * http://www.infonuovo.com/dma/csdocs/sketch/instidid.htm + * http://kruithof.xs4all.nl/uuid/uuidgen + * http://www.opengroup.org/onlinepubs/009629399/apdxa.htm#tagcjh_20 + * http://jakarta.apache.org/commons/sandbox/id/apidocs/org/apache/commons/id/uuid/clock/Clock.html + * + * Examples: + *
+ *   var generate = dojo.uuid.TimeBasedGenerator.generate;
+ *   var uuid;   // an instance of dojo.uuid.Uuid
+ *   var string; // a simple string literal
+ *   string = generate();
+ *   string = generate(String);
+ *   uuid   = generate(dojo.uuid.Uuid);
+ *   string = generate("017bf397618a");
+ *   string = generate({node: "017bf397618a"});         // hardwareNode
+ *   string = generate({node: "f17bf397618a"});         // pseudoNode
+ *   string = generate({hardwareNode: "017bf397618a"});
+ *   string = generate({pseudoNode:   "f17bf397618a"});
+ *   string = generate({node: "017bf397618a", returnType: String});
+ *   uuid   = generate({node: "017bf397618a", returnType: dojo.uuid.Uuid});
+ *   dojo.uuid.TimeBasedGenerator.setNode("017bf397618a");
+ *   string = generate(); // the generated UUID has node == "017bf397618a"
+ *   uuid   = generate(dojo.uuid.Uuid); // the generated UUID has node == "017bf397618a"
+ * 
+ * + * @param class The type of instance to return. + * @param node A 12-character hex string representing a pseudoNode or hardwareNode. + * @namedParam node A 12-character hex string representing a pseudoNode or hardwareNode. + * @namedParam hardwareNode A 12-character hex string containing an IEEE 802.3 network node identificator. + * @namedParam pseudoNode A 12-character hex string representing a pseudoNode. + * @namedParam returnType The type of instance to return. + * @return A newly generated version 1 UUID. + */ + this.generate = function(input) { + var nodeString = null; + var returnType = null; + + if (input) { + if (dojo.lang.isObject(input) && !dojo.lang.isBuiltIn(input)) { + var namedParameters = input; + dojo.lang.assertValidKeywords(namedParameters, ["node", "hardwareNode", "pseudoNode", "returnType"]); + var node = namedParameters["node"]; + var hardwareNode = namedParameters["hardwareNode"]; + var pseudoNode = namedParameters["pseudoNode"]; + nodeString = (node || pseudoNode || hardwareNode); + if (nodeString) { + var firstCharacter = nodeString.charAt(0); + var firstDigit = parseInt(firstCharacter, HEX_RADIX); + if (hardwareNode) { + dojo.lang.assert((firstDigit >= 0x0) && (firstDigit <= 0x7)); + } + if (pseudoNode) { + dojo.lang.assert((firstDigit >= 0x8) && (firstDigit <= 0xF)); + } + } + returnType = namedParameters["returnType"]; + dojo.lang.assertType(returnType, Function, {optional: true}); + } else { + if (dojo.lang.isString(input)) { + nodeString = input; + returnType = null; + } else { + if (dojo.lang.isFunction(input)) { + nodeString = null; + returnType = input; + } + } + } + if (nodeString) { + dojo.lang.assert(nodeString.length == 12); + var integer = parseInt(nodeString, HEX_RADIX); + dojo.lang.assert(isFinite(integer)); + } + dojo.lang.assertType(returnType, Function, {optional: true}); + } + + var uuidString = _generateUuidString(nodeString); + var returnValue; + if (returnType && (returnType != String)) { + returnValue = new returnType(uuidString); + } else { + returnValue = uuidString; + } + return returnValue; + }; +}(); diff --git a/source/web/scripts/ajax/src/uuid/Uuid.js b/source/web/scripts/ajax/src/uuid/Uuid.js new file mode 100644 index 0000000000..3e6f3a857b --- /dev/null +++ b/source/web/scripts/ajax/src/uuid/Uuid.js @@ -0,0 +1,413 @@ +dojo.provide("dojo.uuid.Uuid"); +dojo.require("dojo.lang.*"); + +// ------------------------------------------------------------------- +// Constructor +// ------------------------------------------------------------------- +/** + * The Uuid class offers methods for inspecting existing UUIDs. + * + * Examples: + *
+ *   var uuid;
+ *   uuid = new dojo.uuid.Uuid("3b12f1df-5232-4804-897e-917bf397618a");
+ *   uuid = new dojo.uuid.Uuid(); // "00000000-0000-0000-0000-000000000000"
+ *   uuid = new dojo.uuid.Uuid(dojo.uuid.RandomGenerator);
+ *   uuid = new dojo.uuid.Uuid(dojo.uuid.TimeBasedGenerator);
+ *
+ *   dojo.uuid.Uuid.setGenerator(dojo.uuid.RandomGenerator);
+ *   uuid = new dojo.uuid.Uuid();
+ *   dojo.lang.assert(!uuid.isEqual(dojo.uuid.Uuid.NIL_UUID));
+ * 
+ * + * @scope public instance constructor + * @param uuidString A 36-character string that conforms to the UUID spec. + * @param generator A UUID generator, such as dojo.uuid.TimeBasedGenerator. + */ +dojo.uuid.Uuid = function(input) { + this._uuidString = dojo.uuid.Uuid.NIL_UUID; + if (input) { + if (dojo.lang.isString(input)) { + this._uuidString = input.toLowerCase(); + dojo.lang.assert(this.isValid()); + } else { + if (dojo.lang.isObject(input) && input.generate) { + var generator = input; + this._uuidString = generator.generate(); + dojo.lang.assert(this.isValid()); + } else { + // we got passed something other than a string + dojo.lang.assert(false, "The dojo.uuid.Uuid() constructor must be initializated with a UUID string."); + } + } + } else { + var ourGenerator = dojo.uuid.Uuid.getGenerator(); + if (ourGenerator) { + this._uuidString = ourGenerator.generate(); + dojo.lang.assert(this.isValid()); + } + } +}; + +// ------------------------------------------------------------------- +// Public constants +// ------------------------------------------------------------------- +dojo.uuid.Uuid.NIL_UUID = "00000000-0000-0000-0000-000000000000"; +dojo.uuid.Uuid.Version = { + UNKNOWN: 0, + TIME_BASED: 1, + DCE_SECURITY: 2, + NAME_BASED_MD5: 3, + RANDOM: 4, + NAME_BASED_SHA1: 5 }; +dojo.uuid.Uuid.Variant = { + NCS: "0", + DCE: "10", + MICROSOFT: "110", + UNKNOWN: "111" }; +dojo.uuid.Uuid.HEX_RADIX = 16; + +// ------------------------------------------------------------------- +// Public class methods +// ------------------------------------------------------------------- +/** + * Given two UUIDs to compare, this method returns 0, 1, or -1. + * This method is designed to be used by sorting routines, like + * the JavaScript built-in Array sort() method. + * This implementation is intended to match the sample + * implementation in IETF RFC 4122: + * http://www.ietf.org/rfc/rfc4122.txt + * + * Example: + *
+ *   var generator = dojo.uuid.TimeBasedGenerator;
+ *   var a = new dojo.uuid.Uuid(generator);
+ *   var b = new dojo.uuid.Uuid(generator);
+ *   var c = new dojo.uuid.Uuid(generator);
+ *   var array = new Array(a, b, c);
+ *   array.sort(dojo.uuid.Uuid.compare);
+ * 
+ * + * @param uuidOne A dojo.uuid.Uuid instance, or a string representing a UUID. + * @param uuidTwo A dojo.uuid.Uuid instance, or a string representing a UUID. + * @return Returns either 0, 1, or -1. + */ +dojo.uuid.Uuid.compare = function(uuidOne, uuidTwo) { + var uuidStringOne = uuidOne.toString(); + var uuidStringTwo = uuidTwo.toString(); + if (uuidStringOne > uuidStringTwo) return 1; + if (uuidStringOne < uuidStringTwo) return -1; + return 0; +}; + +/** + * Sets the default generator, which will be used by the + * "new dojo.uuid.Uuid()" constructor if no parameters + * are passed in. + * + * @param generator A UUID generator, such as dojo.uuid.TimeBasedGenerator. + * @return Returns true or false. True if this UUID is equal to the otherUuid. + */ +dojo.uuid.Uuid.setGenerator = function(generator) { + dojo.lang.assert(!generator || (dojo.lang.isObject(generator) && generator.generate)); + dojo.uuid.Uuid._ourGenerator = generator; +}; + +/** + * Returns the default generator. See setGenerator(). + * + * @return A UUID generator, such as dojo.uuid.TimeBasedGenerator. + */ +dojo.uuid.Uuid.getGenerator = function(generator) { + return dojo.uuid.Uuid._ourGenerator; +}; + +// ------------------------------------------------------------------- +// Public instance methods +// ------------------------------------------------------------------- +/** + * Returns a 36-character string representing the UUID, such + * as "3b12f1df-5232-4804-897e-917bf397618a". + * + * Examples: + *
+ *   var uuid = new dojo.uuid.Uuid(dojo.uuid.TimeBasedGenerator);
+ *   var s;
+ *   s = uuid.toString();       //  eb529fec-6498-11d7-b236-000629ba5445
+ *   s = uuid.toString('{}');   // {eb529fec-6498-11d7-b236-000629ba5445}
+ *   s = uuid.toString('()');   // (eb529fec-6498-11d7-b236-000629ba5445)
+ *   s = uuid.toString('""');   // "eb529fec-6498-11d7-b236-000629ba5445"
+ *   s = uuid.toString("''");   // 'eb529fec-6498-11d7-b236-000629ba5445'
+ *   s = uuid.toString('!-');   //  eb529fec649811d7b236000629ba5445
+ *   s = uuid.toString('urn');  //  urn:uuid:eb529fec-6498-11d7-b236-000629ba5445
+ * 
+ * + * @param uuidOne A dojo.uuid.Uuid instance, or a string representing a UUID. + * @return Returns a standard 36-character UUID string, or something similar. + */ +dojo.uuid.Uuid.prototype.toString = function(format) { + if (format) { + switch (format) { + case '{}': + return '{' + this._uuidString + '}'; + break; + case '()': + return '(' + this._uuidString + ')'; + break; + case '""': + return '"' + this._uuidString + '"'; + break; + case "''": + return "'" + this._uuidString + "'"; + break; + case 'urn': + return 'urn:uuid:' + this._uuidString; + break; + case '!-': + return this._uuidString.split('-').join(''); + break; + default: + // we got passed something other than what we expected + dojo.lang.assert(false, "The toString() method of dojo.uuid.Uuid was passed a bogus format."); + } + } else { + return this._uuidString; + } +}; + +/** + * Compares this UUID to another UUID, and returns 0, 1, or -1. + * This implementation is intended to match the sample + * implementation in IETF RFC 4122: + * http://www.ietf.org/rfc/rfc4122.txt + * + * @param otherUuid A dojo.uuid.Uuid instance, or a string representing a UUID. + * @return Returns either 0, 1, or -1. + */ +dojo.uuid.Uuid.prototype.compare = function(otherUuid) { + return dojo.uuid.Uuid.compare(this, otherUuid); +}; + +/** + * Returns true if this UUID is equal to the otherUuid, or + * false otherwise. + * + * @param otherUuid A dojo.uuid.Uuid instance, or a string representing a UUID. + * @return Returns true or false. True if this UUID is equal to the otherUuid. + */ +dojo.uuid.Uuid.prototype.isEqual = function(otherUuid) { + return (this.compare(otherUuid) == 0); +}; + +/** + * Returns true if the UUID was initialized with a valid value. + * + * @return True if the UUID is valid, or false if it is not. + */ +dojo.uuid.Uuid.prototype.isValid = function() { + try { + dojo.lang.assertType(this._uuidString, String); + dojo.lang.assert(this._uuidString.length == 36); + dojo.lang.assert(this._uuidString == this._uuidString.toLowerCase()); + var arrayOfParts = this._uuidString.split("-"); + dojo.lang.assert(arrayOfParts.length == 5); + dojo.lang.assert(arrayOfParts[0].length == 8); + dojo.lang.assert(arrayOfParts[1].length == 4); + dojo.lang.assert(arrayOfParts[2].length == 4); + dojo.lang.assert(arrayOfParts[3].length == 4); + dojo.lang.assert(arrayOfParts[4].length == 12); + for (var i in arrayOfParts) { + var part = arrayOfParts[i]; + var integer = parseInt(part, dojo.uuid.Uuid.HEX_RADIX); + dojo.lang.assert(isFinite(integer)); + } + return true; + } catch (e) { + return false; + } +}; + +/** + * Returns a variant code that indicates what type of UUID this is. + * For example: + *
+ *   var uuid = new dojo.uuid.Uuid("3b12f1df-5232-4804-897e-917bf397618a");
+ *   var variant = uuid.getVariant();
+ *   dojo.lang.assert(variant == dojo.uuid.Uuid.Variant.DCE);
+ * 
+ * + * @return Returns one of the enumarted dojo.uuid.Uuid.Variant values. + */ +dojo.uuid.Uuid.prototype.getVariant = function() { + // "3b12f1df-5232-4804-897e-917bf397618a" + // ^ + // | + // (variant "10__" == DCE) + var variantCharacter = this._uuidString.charAt(19); + var variantNumber = parseInt(variantCharacter, dojo.uuid.Uuid.HEX_RADIX); + dojo.lang.assert((variantNumber >= 0) && (variantNumber <= 16)); + + if (!dojo.uuid.Uuid._ourVariantLookupTable) { + var Variant = dojo.uuid.Uuid.Variant; + var lookupTable = []; + + lookupTable[0x0] = Variant.NCS; // 0000 + lookupTable[0x1] = Variant.NCS; // 0001 + lookupTable[0x2] = Variant.NCS; // 0010 + lookupTable[0x3] = Variant.NCS; // 0011 + + lookupTable[0x4] = Variant.NCS; // 0100 + lookupTable[0x5] = Variant.NCS; // 0101 + lookupTable[0x6] = Variant.NCS; // 0110 + lookupTable[0x7] = Variant.NCS; // 0111 + + lookupTable[0x8] = Variant.DCE; // 1000 + lookupTable[0x9] = Variant.DCE; // 1001 + lookupTable[0xA] = Variant.DCE; // 1010 + lookupTable[0xB] = Variant.DCE; // 1011 + + lookupTable[0xC] = Variant.MICROSOFT; // 1100 + lookupTable[0xD] = Variant.MICROSOFT; // 1101 + lookupTable[0xE] = Variant.UNKNOWN; // 1110 + lookupTable[0xF] = Variant.UNKNOWN; // 1111 + + dojo.uuid.Uuid._ourVariantLookupTable = lookupTable; + } + + return dojo.uuid.Uuid._ourVariantLookupTable[variantNumber]; +}; + +/** + * Returns a version number that indicates what type of UUID this is. + * For example: + *
+ *   var uuid = new dojo.uuid.Uuid("b4308fb0-86cd-11da-a72b-0800200c9a66");
+ *   var version = uuid.getVersion();
+ *   dojo.lang.assert(version == dojo.uuid.Uuid.Version.TIME_BASED);
+ * 
+ * + * @return Returns one of the enumerated dojo.uuid.Uuid.Version values. + * @throws Throws an Error if this is not a DCE Variant UUID. + */ +dojo.uuid.Uuid.prototype.getVersion = function() { + if (!this._versionNumber) { + var errorMessage = "Called getVersion() on a dojo.uuid.Uuid that was not a DCE Variant UUID."; + dojo.lang.assert(this.getVariant() == dojo.uuid.Uuid.Variant.DCE, errorMessage); + + // "b4308fb0-86cd-11da-a72b-0800200c9a66" + // ^ + // | + // (version 1 == TIME_BASED) + var versionCharacter = this._uuidString.charAt(14); + this._versionNumber = parseInt(versionCharacter, dojo.uuid.Uuid.HEX_RADIX); + } + return this._versionNumber; +}; + +/** + * If this is a version 1 UUID (a time-based UUID), this method returns a + * 12-character string with the "node" or "pseudonode" portion of the UUID, + * which is the rightmost 12 characters. + * Throws an Error if this is not a version 1 UUID. + * + * @return Returns a 12-character string, which will look something like "917bf397618a". + * @throws Throws an Error if this is not a version 1 UUID. + */ +dojo.uuid.Uuid.prototype.getNode = function() { + if (!this._nodeString) { + var errorMessage = "Called getNode() on a dojo.uuid.Uuid that was not a TIME_BASED UUID."; + dojo.lang.assert(this.getVersion() == dojo.uuid.Uuid.Version.TIME_BASED, errorMessage); + + var arrayOfStrings = this._uuidString.split('-'); + this._nodeString = arrayOfStrings[4]; + } + return this._nodeString; +}; + +/** + * If this is a version 1 UUID (a time-based UUID), this method returns + * the timestamp value encoded in the UUID. The caller can ask for the + * timestamp to be returned either as a JavaScript Date object or as a + * 15-character string of hex digits. + * Throws an Error if this is not a version 1 UUID. + * + * Examples: + *
+ *   var uuid = new dojo.uuid.Uuid("b4308fb0-86cd-11da-a72b-0800200c9a66");
+ *   var date, string, hexString;
+ *   date   = uuid.getTimestamp();         // returns a JavaScript Date
+ *   date   = uuid.getTimestamp(Date);     // 
+ *   string = uuid.getTimestamp(String);   // "Mon, 16 Jan 2006 20:21:41 GMT"
+ *   hexString = uuid.getTimestamp("hex"); // "1da86cdb4308fb0"
+ * 
+ * + * @return Returns the timestamp value as a JavaScript Date object or a 15-character string of hex digits. + * @throws Throws an Error if this is not a version 1 UUID. + */ +dojo.uuid.Uuid.prototype.getTimestamp = function(returnType) { + var errorMessage = "Called getTimestamp() on a dojo.uuid.Uuid that was not a TIME_BASED UUID."; + dojo.lang.assert(this.getVersion() == dojo.uuid.Uuid.Version.TIME_BASED, errorMessage); + + if (!returnType) {returnType = null}; + switch (returnType) { + case "string": + case String: + return this.getTimestamp(Date).toUTCString(); + break; + case "hex": + // Return a 15-character string of hex digits containing the + // timestamp for this UUID, with the high-order bits first. + if (!this._timestampAsHexString) { + var arrayOfStrings = this._uuidString.split('-'); + var hexTimeLow = arrayOfStrings[0]; + var hexTimeMid = arrayOfStrings[1]; + var hexTimeHigh = arrayOfStrings[2]; + + // Chop off the leading "1" character, which is the UUID + // version number for time-based UUIDs. + hexTimeHigh = hexTimeHigh.slice(1); + + this._timestampAsHexString = hexTimeHigh + hexTimeMid + hexTimeLow; + dojo.lang.assert(this._timestampAsHexString.length == 15); + } + return this._timestampAsHexString; + break; + case null: // no returnType was specified, so default to Date + case "date": + case Date: + // Return a JavaScript Date object. + if (!this._timestampAsDate) { + var GREGORIAN_CHANGE_OFFSET_IN_HOURS = 3394248; + + var arrayOfParts = this._uuidString.split('-'); + var timeLow = parseInt(arrayOfParts[0], dojo.uuid.Uuid.HEX_RADIX); + var timeMid = parseInt(arrayOfParts[1], dojo.uuid.Uuid.HEX_RADIX); + var timeHigh = parseInt(arrayOfParts[2], dojo.uuid.Uuid.HEX_RADIX); + var hundredNanosecondIntervalsSince1582 = timeHigh & 0x0FFF; + hundredNanosecondIntervalsSince1582 <<= 16; + hundredNanosecondIntervalsSince1582 += timeMid; + // What we really want to do next is shift left 32 bits, but the + // result will be too big to fit in an int, so we'll multiply by 2^32, + // and the result will be a floating point approximation. + hundredNanosecondIntervalsSince1582 *= 0x100000000; + hundredNanosecondIntervalsSince1582 += timeLow; + var millisecondsSince1582 = hundredNanosecondIntervalsSince1582 / 10000; + + // Again, this will be a floating point approximation. + // We can make things exact later if we need to. + var secondsPerHour = 60 * 60; + var hoursBetween1582and1970 = GREGORIAN_CHANGE_OFFSET_IN_HOURS; + var secondsBetween1582and1970 = hoursBetween1582and1970 * secondsPerHour; + var millisecondsBetween1582and1970 = secondsBetween1582and1970 * 1000; + var millisecondsSince1970 = millisecondsSince1582 - millisecondsBetween1582and1970; + + this._timestampAsDate = new Date(millisecondsSince1970); + } + return this._timestampAsDate; + break; + default: + // we got passed something other than a valid returnType + dojo.lang.assert(false, "The getTimestamp() method dojo.uuid.Uuid was passed a bogus returnType: " + returnType); + break; + } +}; diff --git a/source/web/scripts/ajax/src/uuid/__package__.js b/source/web/scripts/ajax/src/uuid/__package__.js new file mode 100644 index 0000000000..ca642ccf25 --- /dev/null +++ b/source/web/scripts/ajax/src/uuid/__package__.js @@ -0,0 +1,12 @@ +dojo.kwCompoundRequire({ + common: [ + "dojo.uuid.Uuid", + "dojo.uuid.LightweightGenerator", + "dojo.uuid.RandomGenerator", + "dojo.uuid.TimeBasedGenerator", + "dojo.uuid.NameBasedGenerator", + "dojo.uuid.NilGenerator" + ] +}); +dojo.provide("dojo.uuid.*"); + diff --git a/source/web/scripts/ajax/src/validate.js b/source/web/scripts/ajax/src/validate.js new file mode 100644 index 0000000000..dc912eb8d8 --- /dev/null +++ b/source/web/scripts/ajax/src/validate.js @@ -0,0 +1,2 @@ +dojo.provide("dojo.validate"); +dojo.require("dojo.validate.common"); diff --git a/source/web/scripts/ajax/src/validate/__package__.js b/source/web/scripts/ajax/src/validate/__package__.js new file mode 100644 index 0000000000..0e0ef6093c --- /dev/null +++ b/source/web/scripts/ajax/src/validate/__package__.js @@ -0,0 +1,11 @@ +dojo.require("dojo.validate"); +dojo.kwCompoundRequire({ + common: ["dojo.validate.check", + "dojo.validate.datetime", + "dojo.validate.de", + "dojo.validate.jp", + "dojo.validate.us", + "dojo.validate.web" + ] +}); +dojo.provide("dojo.validate.*"); diff --git a/source/web/scripts/ajax/src/validate/check.js b/source/web/scripts/ajax/src/validate/check.js new file mode 100644 index 0000000000..7af404d4be --- /dev/null +++ b/source/web/scripts/ajax/src/validate/check.js @@ -0,0 +1,247 @@ +dojo.provide("dojo.validate.check"); +dojo.require("dojo.validate.common"); +dojo.require("dojo.lang.common"); + +/** + Validates user input of an HTML form based on input profile. + + @param form The form object to be validated. + @param profile The input profile that specifies how the form fields are to be validated. + @return results An object that contains several methods summarizing the results of the validation. +*/ +dojo.validate.check = function(form, profile) { + // Essentially private properties of results object + var missing = []; + var invalid = []; + + // results object summarizes the validation + var results = { + isSuccessful: function() {return ( !this.hasInvalid() && !this.hasMissing() );}, + hasMissing: function() {return ( missing.length > 0 );}, + getMissing: function() {return missing;}, + isMissing: function(elemname) { + for (var i = 0; i < missing.length; i++) { + if ( elemname == missing[i] ) { return true; } + } + return false; + }, + hasInvalid: function() {return ( invalid.length > 0 );}, + getInvalid: function() {return invalid;}, + isInvalid: function(elemname) { + for (var i = 0; i < invalid.length; i++) { + if ( elemname == invalid[i] ) { return true; } + } + return false; + } + }; + + // Filters are applied before fields are validated. + // Trim removes white space at the front and end of the fields. + if ( profile.trim instanceof Array ) { + for (var i = 0; i < profile.trim.length; i++) { + var elem = form[profile.trim[i]]; + if ( elem.type != "text" && elem.type != "textarea" && elem.type != "password" ) { continue; } + elem.value = elem.value.replace(/(^\s*|\s*$)/g, ""); + } + } + // Convert to uppercase + if ( profile.uppercase instanceof Array ) { + for (var i = 0; i < profile.uppercase.length; i++) { + var elem = form[profile.uppercase[i]]; + if ( elem.type != "text" && elem.type != "textarea" && elem.type != "password" ) { continue; } + elem.value = elem.value.toUpperCase(); + } + } + // Convert to lowercase + if ( profile.lowercase instanceof Array ) { + for (var i = 0; i < profile.lowercase.length; i++) { + var elem = form[profile.lowercase[i]]; + if ( elem.type != "text" && elem.type != "textarea" && elem.type != "password" ) { continue; } + elem.value = elem.value.toLowerCase(); + } + } + // Uppercase first letter + if ( profile.ucfirst instanceof Array ) { + for (var i = 0; i < profile.ucfirst.length; i++) { + var elem = form[profile.ucfirst[i]]; + if ( elem.type != "text" && elem.type != "textarea" && elem.type != "password" ) { continue; } + elem.value = elem.value.replace(/\b\w+\b/g, function(word) { return word.substring(0,1).toUpperCase() + word.substring(1).toLowerCase(); }); + } + } + // Remove non digits characters from the input. + if ( profile.digit instanceof Array ) { + for (var i = 0; i < profile.digit.length; i++) { + var elem = form[profile.digit[i]]; + if ( elem.type != "text" && elem.type != "textarea" && elem.type != "password" ) { continue; } + elem.value = elem.value.replace(/\D/g, ""); + } + } + + // See if required input fields have values missing. + if ( profile.required instanceof Array ) { + for (var i = 0; i < profile.required.length; i++) { + if(!dojo.lang.isString(profile.required[i])){ continue; } + var elem = form[profile.required[i]]; + // Are textbox, textarea, or password fields blank. + if ( (elem.type == "text" || elem.type == "textarea" || elem.type == "password") && /^\s*$/.test(elem.value) ) { + missing[missing.length] = elem.name; + } + // Does drop-down box have option selected. + else if ( (elem.type == "select-one" || elem.type == "select-multiple") && elem.selectedIndex == -1 ) { + missing[missing.length] = elem.name; + } + // Does radio button group (or check box group) have option checked. + else if ( elem instanceof Array ) { + var checked = false; + for (var j = 0; j < elem.length; j++) { + if (elem[j].checked) { checked = true; } + } + if ( !checked ) { + missing[missing.length] = elem[0].name; + } + } + } + } + + // See if checkbox groups and select boxes have x number of required values. + if ( profile.required instanceof Array ) { + for (var i = 0; i < profile.required.length; i++) { + if(!dojo.lang.isObject(profile.required[i])){ continue; } + var elem, numRequired; + for (var name in profile.required[i]) { + elem = form[name]; + numRequired = profile.required[i][name]; + } + // case 1: elem is a check box group + if ( elem instanceof Array ) { + var checked = 0; + for (var j = 0; j < elem.length; j++) { + if (elem[j].checked) { checked++; } + } + if ( checked < numRequired ) { + missing[missing.length] = elem[0].name; + } + } + // case 2: elem is a select box + else if ( elem.type == "select-multiple" ) { + var selected = 0; + for (var j = 0; j < elem.options.length; j++) { + if (elem.options[j].selected) { selected++; } + } + if ( selected < numRequired ) { + missing[missing.length] = elem.name; + } + } + } + } + + // Dependant fields are required when the target field is present (not blank). + // Todo: Support dependant and target fields that are radio button groups, or select drop-down lists. + // Todo: Make the dependancy based on a specific value of the target field. + // Todo: allow dependant fields to have several required values, like {checkboxgroup: 3}. + if(dojo.lang.isObject(profile.dependancies)){ + // properties of dependancies object are the names of dependant fields to be checked + for (name in profile.dependancies) { + var elem = form[name]; // the dependant element + if ( elem.type != "text" && elem.type != "textarea" && elem.type != "password" ) { continue; } // limited support + if ( /\S+/.test(elem.value) ) { continue; } // has a value already + if ( results.isMissing(elem.name) ) { continue; } // already listed as missing + var target = form[profile.dependancies[name]]; + if ( target.type != "text" && target.type != "textarea" && target.type != "password" ) { continue; } // limited support + if ( /^\s*$/.test(target.value) ) { continue; } // skip if blank + missing[missing.length] = elem.name; // ok the dependant field is missing + } + } + + // Find invalid input fields. + if(dojo.lang.isObject(profile.constraints)){ + // constraint properties are the names of fields to be validated + for(name in profile.constraints){ + var elem = form[name]; + if( (elem.type != "text")&& + (elem.type != "textarea")&& + (elem.type != "password")){ + continue; + } + // skip if blank - its optional unless required, in which case it + // is already listed as missing. + if( /^\s*$/.test(elem.value)){ continue; } + + var isValid = true; + // case 1: constraint value is validation function + if(dojo.lang.isFunction(profile.constraints[name])){ + isValid = profile.constraints[name](elem.value); + } else if(dojo.lang.isArray(profile.constraints[name])){ + // handle nested arrays for multiple constraints + if (dojo.lang.isArray(profile.constraints[name][0])) { + for (var i=0; i + * constraints:{ + * fieldName: [functionToCall, param1, param2, etc.], + * fieldName: [[functionToCallFirst, param1],[functionToCallSecond,param2]] + * } + * + * + * This function evaluates a single array function in the format of: + *
+ *     [functionName, argument1, argument2, etc]
+ * 
+ * + * The function will be parsed out and evaluated against the incoming parameters. + * + * @param profile - The dojo.validate.check() profile that this evaluation is against. + * @param constraint - The single [] array of function and arguments for the function. + * @param fieldName - The form dom name of the field being validated. + * @param elem - The form element field. + * + * @return Boolean, True if constraint passed, false otherwise. + */ +dojo.validate.evaluateConstraint=function(profile, constraint, fieldName, elem){ + var isValidSomething = constraint[0]; + var params = constraint.slice(1); + params.unshift(elem.value); + if(typeof isValidSomething != "undefined"){ + return isValidSomething.apply(null, params); + } + return false; +} diff --git a/source/web/scripts/ajax/src/validate/common.js b/source/web/scripts/ajax/src/validate/common.js new file mode 100644 index 0000000000..404bb5db90 --- /dev/null +++ b/source/web/scripts/ajax/src/validate/common.js @@ -0,0 +1,221 @@ +dojo.provide("dojo.validate.common"); + +dojo.require("dojo.regexp"); + +// *** Validation Functions **** + +/** + Checks if a string has non whitespace characters. + Parameters allow you to constrain the length. + + @param value A string. + @param flags An object. + flags.length If set, checks if there are exactly flags.length number of characters. + flags.minlength If set, checks if there are at least flags.minlength number of characters. + flags.maxlength If set, checks if there are at most flags.maxlength number of characters. + @return true or false. +*/ +dojo.validate.isText = function(value, flags) { + flags = (typeof flags == "object") ? flags : {}; + + // test for text + if ( /^\s*$/.test(value) ) { return false; } + + // length tests + if ( typeof flags.length == "number" && flags.length != value.length ) { return false; } + if ( typeof flags.minlength == "number" && flags.minlength > value.length ) { return false; } + if ( typeof flags.maxlength == "number" && flags.maxlength < value.length ) { return false; } + + return true; +} + +/** + Validates whether a string is in an integer format. + + @param value A string. + @param flags An object. + flags.signed The leading plus-or-minus sign. Can be true, false, or [true, false]. + Default is [true, false], (i.e. sign is optional). + flags.separator The character used as the thousands separator. Default is no separator. + For more than one symbol use an array, e.g. [",", ""], makes ',' optional. + @return true or false. +*/ +dojo.validate.isInteger = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.integer(flags) + "$"); + return re.test(value); +} + +/** + Validates whether a string is a real valued number. + Format is the usual exponential notation. + + @param value A string. + @param flags An object. + flags.places The integer number of decimal places. + If not given, the decimal part is optional and the number of places is unlimited. + flags.decimal The character used for the decimal point. Default is ".". + flags.exponent Express in exponential notation. Can be true, false, or [true, false]. + Default is [true, false], (i.e. the exponential part is optional). + flags.eSigned The leading plus-or-minus sign on the exponent. Can be true, false, + or [true, false]. Default is [true, false], (i.e. sign is optional). + flags in regexp.integer can be applied. + @return true or false. +*/ +dojo.validate.isRealNumber = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.realNumber(flags) + "$"); + return re.test(value); +} + +/** + Validates whether a string denotes a monetary value. + + @param value A string. + @param flags An object. + flags.signed The leading plus-or-minus sign. Can be true, false, or [true, false]. + Default is [true, false], (i.e. sign is optional). + flags.symbol A currency symbol such as Yen "�", Pound "�", or the Euro sign "�". + Default is "$". For more than one symbol use an array, e.g. ["$", ""], makes $ optional. + flags.placement The symbol can come "before" the number or "after". Default is "before". + flags.separator The character used as the thousands separator. The default is ",". + flags.fractional The appropriate number of decimal places for fractional currency (e.g. cents) + Can be true, false, or [true, false]. Default is [true, false], (i.e. cents are optional). + flags.decimal The character used for the decimal point. Default is ".". + @return true or false. +*/ +dojo.validate.isCurrency = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.currency(flags) + "$"); + return re.test(value); +} + +/** + Validates whether a string denoting an integer, + real number, or monetary value is between a max and min. + + @param value A string. + @param flags An object. + flags.max A number, which the value must be less than or equal to for the validation to be true. + flags.min A number, which the value must be greater than or equal to for the validation to be true. + flags.decimal The character used for the decimal point. Default is ".". + @return true or false. +*/ +dojo.validate.isInRange = function(value, flags) { + // FIXME: we need something that will prevent things like value="a" from + // evaluating to true, but the following breaks things like +1,000 + /*if(isNaN(value)){ + return false; + }*/ + // assign default values to missing paramters + flags = (typeof flags == "object") ? flags : {}; + var max = (typeof flags.max == "number") ? flags.max : Infinity; + var min = (typeof flags.min == "number") ? flags.min : -Infinity; + var dec = (typeof flags.decimal == "string") ? flags.decimal : "."; + + // splice out anything not part of a number + var pattern = "[^" + dec + "\\deE+-]"; + value = value.replace(RegExp(pattern, "g"), ""); + + // trim ends of things like e, E, or the decimal character + value = value.replace(/^([+-]?)(\D*)/, "$1"); + value = value.replace(/(\D*)$/, ""); + + // replace decimal with ".". The minus sign '-' could be the decimal! + pattern = "(\\d)[" + dec + "](\\d)"; + value = value.replace(RegExp(pattern, "g"), "$1.$2"); + + value = Number(value); + if ( value < min || value > max ) { return false; } + + return true; +} + + +/** + Validates any sort of number based format. + Use it for phone numbers, social security numbers, zip-codes, etc. + The value can be validated against one format or one of multiple formats. + + Format + # Stands for a digit, 0-9. + ? Stands for an optional digit, 0-9 or nothing. + All other characters must appear literally in the expression. + + Example + "(###) ###-####" -> (510) 542-9742 + "(###) ###-#### x#???" -> (510) 542-9742 x153 + "###-##-####" -> 506-82-1089 i.e. social security number + "#####-####" -> 98225-1649 i.e. zip code + + @param value A string. + @param flags An object. + flags.format A string or an Array of strings for multiple formats. + @return true or false +*/ +dojo.validate.isNumberFormat = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.numberFormat(flags) + "$", "i"); + return re.test(value); +} + +/** + Procedural API Description + + The main aim is to make input validation expressible in a simple format. + You define profiles which declare the required and optional fields and any constraints they might have. + The results are provided as an object that makes it easy to handle missing and invalid input. + + Usage + + var results = dojo.validate.check(form, profile); + + Profile Object + + var profile = { + // filters change the field value and are applied before validation. + trim: ["tx1", "tx2"], + uppercase: ["tx9"], + lowercase: ["tx5", "tx6", "tx7"], + ucfirst: ["tx10"], + digit: ["tx11"], + + // required input fields that are blank will be reported missing. + // required radio button groups and drop-down lists with no selection will be reported missing. + // checkbox groups and selectboxes can be required to have more than one value selected. + // List required fields by name and use this notation to require more than one value: {checkboxgroup: 2}, {selectboxname: 3}. + required: ["tx7", "tx8", "pw1", "ta1", "rb1", "rb2", "cb3", "s1", {"doubledip":2}, {"tripledip":3}], + + // dependant/conditional fields are required if the target field is present and not blank. + // At present only textbox, password, and textarea fields are supported. + dependancies: { + cc_exp: "cc_no", + cc_type: "cc_no", + }, + + // Fields can be validated using any boolean valued function. + // Use arrays to specify parameters in addition to the field value. + constraints: { + field_name1: myValidationFunction, + field_name2: dojo.validate.isInteger, + field_name3: [myValidationFunction, additional parameters], + field_name4: [dojo.validate.isValidDate, "YYYY.MM.DD"], + field_name5: [dojo.validate.isEmailAddress, false, true], + }, + + // Confirm is a sort of conditional validation. + // It associates each field in its property list with another field whose value should be equal. + // If the values are not equal, the field in the property list is reported as Invalid. Unless the target field is blank. + confirm: { + email_confirm: "email", + pw2: "pw1", + } + }; + + Results Object + + isSuccessful(): Returns true if there were no invalid or missing fields, else it returns false. + hasMissing(): Returns true if the results contain any missing fields. + getMissing(): Returns a list of required fields that have values missing. + isMissing(field): Returns true if the field is required and the value is missing. + hasInvalid(): Returns true if the results contain fields with invalid data. + getInvalid(): Returns a list of fields that have invalid values. + isInvalid(field): Returns true if the field has an invalid value. + +*/ diff --git a/source/web/scripts/ajax/src/validate/datetime.js b/source/web/scripts/ajax/src/validate/datetime.js new file mode 100644 index 0000000000..3219b98c68 --- /dev/null +++ b/source/web/scripts/ajax/src/validate/datetime.js @@ -0,0 +1,158 @@ +dojo.provide("dojo.validate.datetime"); +dojo.require("dojo.validate.common"); + +/** + Validates a time value in any International format. + The value can be validated against one format or one of multiple formats. + + Format + h 12 hour, no zero padding. + hh 12 hour, has leading zero. + H 24 hour, no zero padding. + HH 24 hour, has leading zero. + m minutes, no zero padding. + mm minutes, has leading zero. + s seconds, no zero padding. + ss seconds, has leading zero. + All other characters must appear literally in the expression. + + Example + "h:m:s t" -> 2:5:33 PM + "HH:mm:ss" -> 14:05:33 + + @param value A string. + @param flags An object. + flags.format A string or an array of strings. Default is "h:mm:ss t". + flags.amSymbol The symbol used for AM. Default is "AM". + flags.pmSymbol The symbol used for PM. Default is "PM". + @return true or false +*/ +dojo.validate.isValidTime = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.time(flags) + "$", "i"); + return re.test(value); +} + +/** + Validates 12-hour time format. + Zero-padding is not allowed for hours, required for minutes and seconds. + Seconds are optional. + + @param value A string. + @return true or false +*/ +dojo.validate.is12HourTime = function(value) { + return dojo.validate.isValidTime(value, {format: ["h:mm:ss t", "h:mm t"]}); +} + +/** + Validates 24-hour military time format. + Zero-padding is required for hours, minutes, and seconds. + Seconds are optional. + + @param value A string. + @return true or false +*/ +dojo.validate.is24HourTime = function(value) { + return dojo.validate.isValidTime(value, {format: ["HH:mm:ss", "HH:mm"]} ); +} + +/** + Returns true if the date conforms to the format given and is a valid date. Otherwise returns false. + + @param dateValue A string for the date. + @param format A string, default is "MM/DD/YYYY". + @return true or false + + Accepts any type of format, including ISO8601. + All characters in the format string are treated literally except the following tokens: + + YYYY - matches a 4 digit year + M - matches a non zero-padded month + MM - matches a zero-padded month + D - matches a non zero-padded date + DD - matches a zero-padded date + DDD - matches an ordinal date, 001-365, and 366 on leapyear + ww - matches week of year, 01-53 + d - matches day of week, 1-7 + + Examples: These are all today's date. + + Date Format + 2005-W42-3 YYYY-Www-d + 2005-292 YYYY-DDD + 20051019 YYYYMMDD + 10/19/2005 M/D/YYYY + 19.10.2005 D.M.YYYY +*/ +dojo.validate.isValidDate = function(dateValue, format) { + // Default is the American format + if (typeof format == "object" && typeof format.format == "string"){ format = format.format; } + if (typeof format != "string") { format = "MM/DD/YYYY"; } + + // Create a literal regular expression based on format + var reLiteral = format.replace(/([$^.*+?=!:|\/\\\(\)\[\]\{\}])/g, "\\$1"); + + // Convert all the tokens to RE elements + reLiteral = reLiteral.replace( "YYYY", "([0-9]{4})" ); + reLiteral = reLiteral.replace( "MM", "(0[1-9]|10|11|12)" ); + reLiteral = reLiteral.replace( "M", "([1-9]|10|11|12)" ); + reLiteral = reLiteral.replace( "DDD", "(00[1-9]|0[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6])" ); + reLiteral = reLiteral.replace( "DD", "(0[1-9]|[12][0-9]|30|31)" ); + reLiteral = reLiteral.replace( "D", "([1-9]|[12][0-9]|30|31)" ); + reLiteral = reLiteral.replace( "ww", "(0[1-9]|[1-4][0-9]|5[0-3])" ); + reLiteral = reLiteral.replace( "d", "([1-7])" ); + + // Anchor pattern to begining and end of string + reLiteral = "^" + reLiteral + "$"; + + // Dynamic RE that parses the original format given + var re = new RegExp(reLiteral); + + // Test if date is in a valid format + if (!re.test(dateValue)) return false; + + // Parse date to get elements and check if date is valid + // Assume valid values for date elements not given. + var year = 0, month = 1, date = 1, dayofyear = 1, week = 1, day = 1; + + // Capture tokens + var tokens = format.match( /(YYYY|MM|M|DDD|DD|D|ww|d)/g ); + + // Capture date values + var values = re.exec(dateValue); + + // Match up tokens with date values + for (var i = 0; i < tokens.length; i++) { + switch (tokens[i]) { + case "YYYY": + year = Number(values[i+1]); break; + case "M": + case "MM": + month = Number(values[i+1]); break; + case "D": + case "DD": + date = Number(values[i+1]); break; + case "DDD": + dayofyear = Number(values[i+1]); break; + case "ww": + week = Number(values[i+1]); break; + case "d": + day = Number(values[i+1]); break; + } + } + + // Leap years are divisible by 4, but not by 100, unless by 400 + var leapyear = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + + // 31st of a month with 30 days + if (date == 31 && (month == 4 || month == 6 || month == 9 || month == 11)) return false; + + // February 30th or 31st + if (date >= 30 && month == 2) return false; + + // February 29th outside a leap year + if (date == 29 && month == 2 && !leapyear) return false; + if (dayofyear == 366 && !leapyear) return false; + + return true; +} diff --git a/source/web/scripts/ajax/src/validate/de.js b/source/web/scripts/ajax/src/validate/de.js new file mode 100644 index 0000000000..14b79e21ca --- /dev/null +++ b/source/web/scripts/ajax/src/validate/de.js @@ -0,0 +1,20 @@ +dojo.provide("dojo.validate.de"); +dojo.require("dojo.validate.common"); + +/** + Validates German currency. + + @param value A string. + @return true or false. +*/ +dojo.validate.isGermanCurrency = function(value) { + var flags = { + symbol: "�", + placement: "after", + decimal: ",", + separator: "." + }; + return dojo.validate.isCurrency(value, flags); +} + + diff --git a/source/web/scripts/ajax/src/validate/jp.js b/source/web/scripts/ajax/src/validate/jp.js new file mode 100644 index 0000000000..9ddf4b35fe --- /dev/null +++ b/source/web/scripts/ajax/src/validate/jp.js @@ -0,0 +1,18 @@ +dojo.provide("dojo.validate.jp"); +dojo.require("dojo.validate.common"); + +/** + Validates Japanese currency. + + @param value A string. + @return true or false. +*/ +dojo.validate.isJapaneseCurrency = function(value) { + var flags = { + symbol: "�", + cents: false + }; + return dojo.validate.isCurrency(value, flags); +} + + diff --git a/source/web/scripts/ajax/src/validate/us.js b/source/web/scripts/ajax/src/validate/us.js new file mode 100644 index 0000000000..891969807d --- /dev/null +++ b/source/web/scripts/ajax/src/validate/us.js @@ -0,0 +1,84 @@ +dojo.provide("dojo.validate.us"); +dojo.require("dojo.validate.common"); + +/** + Validates U.S. currency. + + @param value A string. + @param flags An object. + flags in validate.isCurrency can be applied. + @return true or false. +*/ +dojo.validate.us.isCurrency = function(value, flags) { + return dojo.validate.isCurrency(value, flags); +} + + +/** + Validates US state and territory abbreviations. + + @param value A two character string. + @param flags An object. + flags.allowTerritories Allow Guam, Puerto Rico, etc. Default is true. + flags.allowMilitary Allow military 'states', e.g. Armed Forces Europe (AE). Default is true. + @return true or false +*/ +dojo.validate.us.isState = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.us.state(flags) + "$", "i"); + return re.test(value); +} + +/** + Validates 10 US digit phone number for several common formats: + + @param value The telephone number string + @return true or false +*/ +dojo.validate.us.isPhoneNumber = function(value) { + var flags = { + format: [ + "###-###-####", + "(###) ###-####", + "(###) ### ####", + "###.###.####", + "###/###-####", + "### ### ####", + "###-###-#### x#???", + "(###) ###-#### x#???", + "(###) ### #### x#???", + "###.###.#### x#???", + "###/###-#### x#???", + "### ### #### x#???", + "##########" + ] + }; + + return dojo.validate.isNumberFormat(value, flags); +} + +// Validates social security number +dojo.validate.us.isSocialSecurityNumber = function(value) { + var flags = { + format: [ + "###-##-####", + "### ## ####", + "#########" + ] + }; + + return dojo.validate.isNumberFormat(value, flags); +} + +// Validates U.S. zip-code +dojo.validate.us.isZipCode = function(value) { + var flags = { + format: [ + "#####-####", + "##### ####", + "#########", + "#####" + ] + }; + + return dojo.validate.isNumberFormat(value, flags); +} diff --git a/source/web/scripts/ajax/src/validate/web.js b/source/web/scripts/ajax/src/validate/web.js new file mode 100644 index 0000000000..6b8eecfba0 --- /dev/null +++ b/source/web/scripts/ajax/src/validate/web.js @@ -0,0 +1,95 @@ +dojo.provide("dojo.validate.web"); +dojo.require("dojo.validate.common"); + +/** + Validates an IP address. + Supports 5 formats for IPv4: dotted decimal, dotted hex, dotted octal, decimal and hexadecimal. + Supports 2 formats for Ipv6. + + @param value A string. + @param flags An object. All flags are boolean with default = true. + flags.allowDottedDecimal Example, 207.142.131.235. No zero padding. + flags.allowDottedHex Example, 0x18.0x11.0x9b.0x28. Case insensitive. Zero padding allowed. + flags.allowDottedOctal Example, 0030.0021.0233.0050. Zero padding allowed. + flags.allowDecimal Example, 3482223595. A decimal number between 0-4294967295. + flags.allowHex Example, 0xCF8E83EB. Hexadecimal number between 0x0-0xFFFFFFFF. + Case insensitive. Zero padding allowed. + flags.allowIPv6 IPv6 address written as eight groups of four hexadecimal digits. + flags.allowHybrid IPv6 address written as six groups of four hexadecimal digits + followed by the usual 4 dotted decimal digit notation of IPv4. x:x:x:x:x:x:d.d.d.d + @return true or false +*/ +dojo.validate.isIpAddress = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.ipAddress(flags) + "$", "i"); + return re.test(value); +} + +/** + Checks if a string could be a valid URL. + + @param value A string. + @param flags An object. + flags.scheme Can be true, false, or [true, false]. + This means: required, not allowed, or either. + flags in regexp.host can be applied. + flags in regexp.ipAddress can be applied. + flags in regexp.tld can be applied. + @return true or false +*/ +dojo.validate.isUrl = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.url(flags) + "$", "i"); + return re.test(value); +} + +/** + Checks if a string could be a valid email address. + + @param value A string. + @param flags An object. + flags.allowCruft Allow address like . Default is false. + flags in regexp.host can be applied. + flags in regexp.ipAddress can be applied. + flags in regexp.tld can be applied. + @return true or false. +*/ +dojo.validate.isEmailAddress = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.emailAddress(flags) + "$", "i"); + return re.test(value); +} + +/** + Checks if a string could be a valid email address list. + + @param value A string. + @param flags An object. + flags.listSeparator The character used to separate email addresses. Default is ";", ",", "\n" or " ". + flags in regexp.emailAddress can be applied. + flags in regexp.host can be applied. + flags in regexp.ipAddress can be applied. + flags in regexp.tld can be applied. + @return true or false. +*/ +dojo.validate.isEmailAddressList = function(value, flags) { + var re = new RegExp("^" + dojo.regexp.emailAddressList(flags) + "$", "i"); + return re.test(value); +} + +/** + Check if value is an email address list. If an empty list + is returned, the value didn't pass the test or it was empty. + + @param value A string + @param flags An object (same as isEmailAddressList) + @return array of emails +*/ +dojo.validate.getEmailAddressList = function(value, flags) { + if(!flags) { flags = {}; } + if(!flags.listSeparator) { flags.listSeparator = "\\s;,"; } + + if ( dojo.validate.isEmailAddressList(value, flags) ) { + return value.split(new RegExp("\\s*[" + flags.listSeparator + "]\\s*")); + } + return []; +} + + diff --git a/source/web/scripts/ajax/src/widget/AccordionContainer.js b/source/web/scripts/ajax/src/widget/AccordionContainer.js new file mode 100644 index 0000000000..04908d4ea5 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/AccordionContainer.js @@ -0,0 +1,60 @@ +dojo.provide("dojo.widget.AccordionContainer"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.html.*"); +dojo.require("dojo.widget.AccordionPane"); + +dojo.widget.defineWidget( + "dojo.widget.AccordionContainer", + dojo.widget.HtmlWidget, + { + isContainer: true, + labelNodeClass: "", + containerNodeClass: "", + allowCollapse: false, + + addChild: function(widget, overrideContainerNode, pos, ref, insertIndex){ + if (widget.widgetType != "AccordionPane") { + var wrapper=dojo.widget.createWidget("AccordionPane",{label: widget.label, open: widget.open, labelNodeClass: this.labelNodeClass, containerNodeClass: this.containerNodeClass, allowCollapse: this.allowCollapse }); + wrapper.addChild(widget); + this.addWidgetAsDirectChild(wrapper); + this.registerChild(wrapper); + wrapper.setSizes(); + return wrapper; + } else { + dojo.html.addClass(widget.containerNode, this.containerNodeClass); + dojo.html.addClass(widget.labelNode, this.labelNodeClass); + this.addWidgetAsDirectChild(widget); + this.registerChild(widget); + widget.setSizes(); + return widget; + } + }, + + postCreate: function() { + var tmpChildren = this.children; + this.children=[]; + dojo.html.removeChildren(this.domNode); + dojo.lang.forEach(tmpChildren, dojo.lang.hitch(this,"addChild")); + }, + + removeChild: function(widget) { + dojo.widget.AccordionContainer.superclass.removeChild.call(this, widget); + if(this.children[0]){ + this.children[0].setSizes(); + } + }, + + onResized: function(){ + this.children[0].setSizes(); + } + } +); + +// These arguments can be specified for the children of a Accordion +// Since any widget can be specified as a child, mix them +// into the base widget class. (This is a hack, but it's effective.) +dojo.lang.extend(dojo.widget.Widget, { + label: "", + open: false +}); diff --git a/source/web/scripts/ajax/src/widget/AccordionPane.js b/source/web/scripts/ajax/src/widget/AccordionPane.js new file mode 100644 index 0000000000..517db601e8 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/AccordionPane.js @@ -0,0 +1,84 @@ +dojo.provide("dojo.widget.AccordionPane"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.TitlePane"); +dojo.require("dojo.html.selection"); + +dojo.widget.defineWidget( + "dojo.widget.AccordionPane", + dojo.widget.TitlePane, +{ + // parameters + open: false, + allowCollapse: true, + label: "", + labelNodeClass: "", + containerNodeClass: "", + + // methods + postCreate: function() { + dojo.widget.AccordionPane.superclass.postCreate.call(this); + this.domNode.widgetType=this.widgetType; + this.setSizes(); + dojo.html.addClass(this.labelNode, this.labelNodeClass); + dojo.html.disableSelection(this.labelNode); + dojo.html.addClass(this.containerNode, this.containerNodeClass); + }, + + collapse: function() { + //dojo.fx.html.wipeOut(this.containerNode,250); + //var anim = dojo.fx.html.wipe(this.containerNode, 1000, this.containerNode.offsetHeight, 0, null, true); + this.containerNode.style.display="none"; + this.open=false; + }, + + expand: function() { + //dojo.fx.html.wipeIn(this.containerNode,250); + this.containerNode.style.display="block"; + //var anim = dojo.fx.html.wipe(this.containerNode, 1000, 0, this.containerNode.scrollHeight, null, true); + this.open=true; + }, + + getCollapsedHeight: function() { + return dojo.html.getMarginBox(this.labelNode).height+1; + }, + + setSizes: function() { + var siblings = this.domNode.parentNode.childNodes; + var height=dojo.html.getBorderBox(this.domNode.parentNode).height-this.getCollapsedHeight(); + + this.siblingWidgets = []; + + for (var x=0; x', + + postCreate: function(){ + this.cellWidth = this.width; + this.cellHeight = this.height; + + var img = new Image(); + var self = this; + + img.onload = function(){ self.initAni(img.width, img.height); }; + img.src = this.aniSrc; + }, + + initAni: function(w, h){ + + this.domNode.src = this.blankSrc; + this.domNode.width = this.cellWidth; + this.domNode.height = this.cellHeight; + this.domNode.style.backgroundImage = 'url('+this.aniSrc+')'; + this.domNode.style.backgroundRepeat = 'no-repeat'; + + this.aniCols = Math.floor(w/this.cellWidth); + this.aniRows = Math.floor(h/this.cellHeight); + this.aniCells = this.aniCols * this.aniRows; + this.aniFrame = 0; + + window.setInterval(dojo.lang.hitch(this, 'tick'), this.interval); + }, + + tick: function(){ + + this.aniFrame++; + if (this.aniFrame == this.aniCells) this.aniFrame = 0; + + var col = this.aniFrame % this.aniCols; + var row = Math.floor(this.aniFrame / this.aniCols); + + var bx = -1 * col * this.cellWidth; + var by = -1 * row * this.cellHeight; + + this.domNode.style.backgroundPosition = bx+'px '+by+'px'; + } + } +); diff --git a/source/web/scripts/ajax/src/widget/Button.js b/source/web/scripts/ajax/src/widget/Button.js new file mode 100644 index 0000000000..0061e01e9a --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Button.js @@ -0,0 +1,303 @@ +dojo.provide("dojo.widget.Button"); + +dojo.require("dojo.lang.extras"); +dojo.require("dojo.html.*"); +dojo.require("dojo.html.selection"); +dojo.require("dojo.widget.*"); + +dojo.widget.defineWidget( + "dojo.widget.Button", + dojo.widget.HtmlWidget, + { + isContainer: true, + + // Constructor arguments + caption: "", + disabled: false, + + templatePath: dojo.uri.dojoUri("src/widget/templates/ButtonTemplate.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/ButtonTemplate.css"), + + // button images + inactiveImg: "src/widget/templates/images/soriaButton-", + activeImg: "src/widget/templates/images/soriaActive-", + pressedImg: "src/widget/templates/images/soriaPressed-", + disabledImg: "src/widget/templates/images/soriaDisabled-", + width2height: 1.0/3.0, + + // attach points + containerNode: null, + leftImage: null, + centerImage: null, + rightImage: null, + + fillInTemplate: function(args, frag){ + if(this.caption != ""){ + this.containerNode.appendChild(document.createTextNode(this.caption)); + } + dojo.html.disableSelection(this.containerNode); + }, + + postCreate: function(args, frag){ + this.sizeMyself(); + }, + + sizeMyself: function(){ + // we cannot size correctly if any of our ancestors are hidden (display:none), + // so temporarily attach to document.body + if(this.domNode.parentNode){ + var placeHolder = document.createElement("span"); + dojo.html.insertBefore(placeHolder, this.domNode); + } + dojo.body().appendChild(this.domNode); + + this.sizeMyselfHelper(); + + // Put this.domNode back where it was originally + if(placeHolder){ + dojo.html.insertBefore(this.domNode, placeHolder); + dojo.html.removeNode(placeHolder); + } + }, + + sizeMyselfHelper: function(){ + var mb = dojo.html.getMarginBox(this.containerNode); + this.height = mb.height; + this.containerWidth = mb.width; + var endWidth= this.height * this.width2height; + + this.containerNode.style.left=endWidth+"px"; + + this.leftImage.height = this.rightImage.height = this.centerImage.height = this.height; + this.leftImage.width = this.rightImage.width = endWidth+1; + this.centerImage.width = this.containerWidth; + this.centerImage.style.left=endWidth+"px"; + this._setImage(this.disabled ? this.disabledImg : this.inactiveImg); + + if ( this.disabled ) { + dojo.html.prependClass(this.domNode, "dojoButtonDisabled"); + } else { + dojo.html.removeClass(this.domNode, "dojoButtonDisabled"); + } + + this.domNode.style.height=this.height + "px"; + this.domNode.style.width= (this.containerWidth+2*endWidth) + "px"; + }, + + onMouseOver: function(e){ + if( this.disabled ){ return; } + dojo.html.prependClass(this.domNode, "dojoButtonHover"); + this._setImage(this.activeImg); + }, + + onMouseDown: function(e){ + if( this.disabled ){ return; } + dojo.html.prependClass(this.domNode, "dojoButtonDepressed"); + dojo.html.removeClass(this.domNode, "dojoButtonHover"); + this._setImage(this.pressedImg); + }, + onMouseUp: function(e){ + if( this.disabled ){ return; } + dojo.html.prependClass(this.domNode, "dojoButtonHover"); + dojo.html.removeClass(this.domNode, "dojoButtonDepressed"); + this._setImage(this.activeImg); + }, + + onMouseOut: function(e){ + if( this.disabled ){ return; } + if( e.toElement && dojo.html.isDescendantOf(e.toElement, this.domNode) ){ + return; // Ignore IE mouseOut events that dont actually leave button - Prevents hover image flicker in IE + } + dojo.html.removeClass(this.domNode, "dojoButtonHover"); + this._setImage(this.inactiveImg); + }, + + buttonClick: function(e){ + if( !this.disabled ) { this.onClick(e); } + }, + + onClick: function(e) { }, + + _setImage: function(prefix){ + this.leftImage.src=dojo.uri.dojoUri(prefix + "l.gif"); + this.centerImage.src=dojo.uri.dojoUri(prefix + "c.gif"); + this.rightImage.src=dojo.uri.dojoUri(prefix + "r.gif"); + }, + + _toggleMenu: function(menuId){ + var menu = dojo.widget.getWidgetById(menuId); + if ( !menu ) { return; } + + if ( menu.open && !menu.isShowingNow) { + var pos = dojo.html.getAbsolutePosition(this.domNode, false); + menu.open(pos.x, pos.y+this.height, this); + } else if ( menu.close && menu.isShowingNow ){ + menu.close(); + } else { + menu.toggle(); + } + }, + + setCaption: function(content){ + this.caption=content; + this.containerNode.innerHTML=content; + this.sizeMyself(); + }, + + setDisabled: function(disabled){ + this.disabled=disabled; + this.sizeMyself(); + } + }); + +/**** DropDownButton - push the button and a menu shows up *****/ +dojo.widget.defineWidget( + "dojo.widget.DropDownButton", + dojo.widget.Button, + { + menuId: "", + + arrow: null, + + downArrow: "src/widget/templates/images/whiteDownArrow.gif", + disabledDownArrow: "src/widget/templates/images/whiteDownArrow.gif", + + fillInTemplate: function(args, frag){ + dojo.widget.DropDownButton.superclass.fillInTemplate.call(this, args, frag); + + this.arrow = document.createElement("img"); + dojo.html.setClass(this.arrow, "downArrow"); + }, + + sizeMyselfHelper: function(){ + // draw the arrow (todo: why is the arror in containerNode rather than outside it?) + this.arrow.src = dojo.uri.dojoUri(this.disabled ? this.disabledDownArrow : this.downArrow); + this.containerNode.appendChild(this.arrow); + + dojo.widget.DropDownButton.superclass.sizeMyselfHelper.call(this); + }, + + onClick: function (e){ + this._toggleMenu(this.menuId); + } + }); + +/**** ComboButton - left side is normal button, right side shows menu *****/ +dojo.widget.defineWidget( + "dojo.widget.ComboButton", + dojo.widget.Button, + { + menuId: "", + + templatePath: dojo.uri.dojoUri("src/widget/templates/ComboButtonTemplate.html"), + + // attach points + leftPart: null, + rightPart: null, + arrowBackgroundImage: null, + + // constants + splitWidth: 2, // pixels between left&right part of button + arrowWidth: 5, // width of segment holding down arrow + + sizeMyselfHelper: function(e){ + var mb = dojo.html.getMarginBox(this.containerNode); + this.height = mb.height; + this.containerWidth = mb.width; + var endWidth= this.height/3; + + // left part + this.leftImage.height = this.rightImage.height = this.centerImage.height = + this.arrowBackgroundImage.height = this.height; + this.leftImage.width = endWidth+1; + this.centerImage.width = this.containerWidth; + this.leftPart.style.height = this.height + "px"; + this.leftPart.style.width = endWidth + this.containerWidth + "px"; + this._setImageL(this.disabled ? this.disabledImg : this.inactiveImg); + + // right part + this.arrowBackgroundImage.width=this.arrowWidth; + this.rightImage.width = endWidth+1; + this.rightPart.style.height = this.height + "px"; + this.rightPart.style.width = this.arrowWidth + endWidth + "px"; + this._setImageR(this.disabled ? this.disabledImg : this.inactiveImg); + + // outer container + this.domNode.style.height=this.height + "px"; + var totalWidth = this.containerWidth+this.splitWidth+this.arrowWidth+2*endWidth; + this.domNode.style.width= totalWidth + "px"; + }, + + /** functions on left part of button**/ + leftOver: function(e){ + if( this.disabled ){ return; } + dojo.html.prependClass(this.leftPart, "dojoButtonHover"); + this._setImageL(this.activeImg); + }, + + leftDown: function(e){ + if( this.disabled ){ return; } + dojo.html.prependClass(this.leftPart, "dojoButtonDepressed"); + dojo.html.removeClass(this.leftPart, "dojoButtonHover"); + this._setImageL(this.pressedImg); + }, + leftUp: function(e){ + if( this.disabled ){ return; } + dojo.html.prependClass(this.leftPart, "dojoButtonHover"); + dojo.html.removeClass(this.leftPart, "dojoButtonDepressed"); + this._setImageL(this.activeImg); + }, + + leftOut: function(e){ + if( this.disabled ){ return; } + dojo.html.removeClass(this.leftPart, "dojoButtonHover"); + this._setImageL(this.inactiveImg); + }, + + leftClick: function(e){ + if ( !this.disabled ) { + this.onClick(e); + } + }, + + _setImageL: function(prefix){ + this.leftImage.src=dojo.uri.dojoUri(prefix + "l.gif"); + this.centerImage.src=dojo.uri.dojoUri(prefix + "c.gif"); + }, + + /*** functions on right part of button ***/ + rightOver: function(e){ + if( this.disabled ){ return; } + dojo.html.prependClass(this.rightPart, "dojoButtonHover"); + this._setImageR(this.activeImg); + }, + + rightDown: function(e){ + if( this.disabled ){ return; } + dojo.html.prependClass(this.rightPart, "dojoButtonDepressed"); + dojo.html.removeClass(this.rightPart, "dojoButtonHover"); + this._setImageR(this.pressedImg); + }, + rightUp: function(e){ + if( this.disabled ){ return; } + dojo.html.prependClass(this.rightPart, "dojoButtonHover"); + dojo.html.removeClass(this.rightPart, "dojoButtonDepressed"); + this._setImageR(this.activeImg); + }, + + rightOut: function(e){ + if( this.disabled ){ return; } + dojo.html.removeClass(this.rightPart, "dojoButtonHover"); + this._setImageR(this.inactiveImg); + }, + + rightClick: function(e){ + if( this.disabled ){ return; } + this._toggleMenu(this.menuId); + }, + + _setImageR: function(prefix){ + this.arrowBackgroundImage.src=dojo.uri.dojoUri(prefix + "c.gif"); + this.rightImage.src=dojo.uri.dojoUri(prefix + "r.gif"); + } + }); diff --git a/source/web/scripts/ajax/src/widget/Button2.js b/source/web/scripts/ajax/src/widget/Button2.js new file mode 100644 index 0000000000..4bffcf1833 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Button2.js @@ -0,0 +1,33 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.provide("dojo.widget.Button2"); +dojo.require("dojo.widget.Button"); +dojo.require("dojo.widget.*"); + +dojo.widget.tags.addParseTreeHandler("dojo:button2"); +dojo.widget.tags.addParseTreeHandler("dojo:dropdownbutton2"); +dojo.widget.tags.addParseTreeHandler("dojo:combobutton2"); + +dojo.deprecated("dojo.widget.Button2", "Use dojo.widget.Button instead", "0.4"); + +dojo.requireAfterIf("html", "dojo.widget.html.Button2"); + +dojo.widget.Button2 = function(){} +dojo.inherits(dojo.widget.Button2, dojo.widget.Button); +dojo.lang.extend(dojo.widget.Button2, { widgetType: "Button2" }); + +dojo.widget.DropDownButton2 = function(){} +dojo.inherits(dojo.widget.DropDownButton2, dojo.widget.DropDownButton); +dojo.lang.extend(dojo.widget.DropDownButton2, { widgetType: "DropDownButton2" }); + +dojo.widget.ComboButton2 = function(){} +dojo.inherits(dojo.widget.ComboButton2, dojo.widget.ComboButton); +dojo.lang.extend(dojo.widget.ComboButton2, { widgetType: "ComboButton2" }); diff --git a/source/web/scripts/ajax/src/widget/Chart.js b/source/web/scripts/ajax/src/widget/Chart.js new file mode 100644 index 0000000000..30971c2647 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Chart.js @@ -0,0 +1,195 @@ +dojo.provide("dojo.widget.Chart"); +dojo.provide("dojo.widget.Chart.PlotTypes"); +dojo.provide("dojo.widget.Chart.DataSeries"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.graphics.color"); +dojo.require("dojo.graphics.color.hsl"); +dojo.widget.tags.addParseTreeHandler("dojo:chart"); + +// Base class for svg and vml implementations of Chart +dojo.declare( + "dojo.widget.Chart", + null, + function(){ + this.series = []; + }, +{ + isContainer: false, + + // FIXME: why is this a mixin method? + assignColors: function(){ + var hue=30; + var sat=120; + var lum=120; + var steps = Math.round(330/this.series.length); + + for(var i=0; i=range.start; i--){ + var n = parseFloat(this.values[i].value); + if(!isNaN(n)){ t += n; c++; } + } + t /= Math.max(c,1); + return t; + }, + + getMovingAverage: function(len){ + var range = this.createRange(len); + if(range.index<0){ return 0; } + var t = 0; + var c = 0; + for(var i=range.index; i>=range.start; i--){ + var n = parseFloat(this.values[i].value); + if(!isNaN(n)){ t += n; c++; } + } + t /= Math.max(c,1); + return t; + }, + + getVariance: function(len){ + var range = this.createRange(len); + if(range.index < 0){ return 0; } + var t = 0; // FIXME: for tom: wtf are t, c, and s? + var s = 0; + var c = 0; + for(var i=range.index; i>=range.start; i--){ + var n = parseFloat(this.values[i].value); + if(!isNaN(n)){ + t += n; + s += Math.pow(n,2); + c++; + } + } + return (s/c)-Math.pow(t/c,2); + }, + + getStandardDeviation: function(len){ + return Math.sqrt(this.getVariance(len)); + }, + + getMax: function(len){ + var range = this.createRange(len); + if(range.index < 0){ return 0; } + var t = 0; + for (var i=range.index; i>=range.start; i--){ + var n=parseFloat(this.values[i].value); + if (!isNaN(n)){ + t=Math.max(n,t); + } + } + return t; + }, + + getMin: function(len){ + var range=this.createRange(len); + if(range.index < 0){ return 0; } + var t = 0; + for(var i=range.index; i>=range.start; i--){ + var n = parseFloat(this.values[i].value); + if(!isNaN(n)){ + t=Math.min(n,t); + } + } + return t; + }, + + getMedian: function(len){ + var range = this.createRange(len); + + if(range.index<0){ return 0; } + + var a = []; + for (var i=range.index; i>=range.start; i--){ + var n=parseFloat(this.values[i].value); + if (!isNaN(n)){ + var b=false; + for(var j=0; j0){ return a[Math.ceil(a.length/2)]; } + return 0; + }, + + getMode: function(len){ + var range=this.createRange(len); + if(range.index<0){ return 0; } + var o = {}; + var ret = 0 + var m = 0; + for(var i=range.index; i>=range.start; i--){ + var n=parseFloat(this.values[i].value); + if(!isNaN(n)){ + if (!o[this.values[i].value]) o[this.values[i].value] = 1; + else o[this.values[i].value]++; + } + } + for(var p in o){ + if(mlabel text rather than + // + this.inputNode.id = this.widgetId; + var inputId = this.inputNode.id; + if(inputId != null){ + var labels = document.getElementsByTagName("label"); + if (labels != null && labels.length > 0){ + for(var i=0; i"); + with(this.bgIframe.style){ + position = "absolute"; + left = top = "0px"; + display = "none"; + } + dojo.body().appendChild(this.bgIframe); + dojo.html.setOpacity(this.bgIframe, 0); + } + }, + + click: function (e) { + this.onColorSelect(e.currentTarget.color); + e.currentTarget.style.borderColor = "gray"; + }, + + onColorSelect: function (color) { }, + + hide: function (){ + this.domNode.parentNode.removeChild(this.domNode); + if(this.bgIframe){ + this.bgIframe.style.display = "none"; + } + }, + + showAt: function (x, y) { + with(this.domNode.style){ + top = y + "px"; + left = x + "px"; + zIndex = 999; + } + dojo.body().appendChild(this.domNode); + if(this.bgIframe){ + with(this.bgIframe.style){ + display = "block"; + top = y + "px"; + left = x + "px"; + zIndex = 998; + var s = dojo.html.getMarginBox(this.domNode); + width = s.width + "px"; + height = s.height + "px"; + } + + } + } +}); diff --git a/source/web/scripts/ajax/src/widget/ComboBox.js b/source/web/scripts/ajax/src/widget/ComboBox.js new file mode 100644 index 0000000000..66ad2a06b9 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/ComboBox.js @@ -0,0 +1,837 @@ +dojo.provide("dojo.widget.ComboBox"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.event.*"); +dojo.require("dojo.io.*"); +dojo.require("dojo.lfx.*"); +dojo.require("dojo.html.*"); +dojo.require("dojo.html.display"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.html.iframe"); +dojo.require("dojo.string"); +dojo.require("dojo.widget.html.stabile"); +dojo.require("dojo.widget.Menu2"); +dojo.require("dojo.html.csshack"); + +dojo.widget.incrementalComboBoxDataProvider = function(url, limit, timeout){ + this.searchUrl = url; + this.inFlight = false; + this.activeRequest = null; + this.allowCache = false; + + this.cache = {}; + + this.init = function(cbox){ + this.searchUrl = cbox.dataUrl; + }; + + this.addToCache = function(keyword, data){ + if(this.allowCache){ + this.cache[keyword] = data; + } + }; + + this.startSearch = function(searchStr, type, ignoreLimit){ + if(this.inFlight){ + // FIXME: implement backoff! + } + var tss = encodeURIComponent(searchStr); + var realUrl = dojo.string.substituteParams(this.searchUrl, {"searchString": tss}); + var _this = this; + var request = dojo.io.bind({ + url: realUrl, + method: "get", + mimetype: "text/json", + load: function(type, data, evt){ + _this.inFlight = false; + if(!dojo.lang.isArray(data)){ + var arrData = []; + for(var key in data){ + arrData.push([data[key], key]); + } + data = arrData; + } + _this.addToCache(searchStr, data); + _this.provideSearchResults(data); + } + }); + this.inFlight = true; + }; +}; + +dojo.widget.ComboBoxDataProvider = function(dataPairs, limit, timeout){ + // NOTE: this data provider is designed as a naive reference + // implementation, and as such it is written more for readability than + // speed. A deployable data provider would implement lookups, search + // caching (and invalidation), and a significantly less naive data + // structure for storage of items. + + this.data = []; + this.searchTimeout = timeout || 500; + this.searchLimit = limit || 30; + this.searchType = "STARTSTRING"; // may also be "STARTWORD" or "SUBSTRING" + this.caseSensitive = false; + // for caching optimizations + this._lastSearch = ""; + this._lastSearchResults = null; + + this.init = function(cbox, node){ + if(!dojo.string.isBlank(cbox.dataUrl)){ + this.getData(cbox.dataUrl); + }else{ + // check to see if we can populate the list from here yet + var opts = node.getElementsByTagName("option"); + var ol = opts.length; + var data = []; + for(var x=0; x= this.searchLimit)){ + break; + } + // FIXME: we should avoid copies if possible! + var dataLabel = new String((!this.caseSensitive) ? this.data[x][0].toLowerCase() : this.data[x][0]); + if(dataLabel.length < searchStr.length){ + // this won't ever be a good search, will it? What if we start + // to support regex search? + continue; + } + + if(st == "STARTSTRING"){ + // jum.debug(dataLabel.substr(0, searchStr.length)) + // jum.debug(searchStr); + if(searchStr == dataLabel.substr(0, searchStr.length)){ + ret.push(this.data[x]); + } + }else if(st == "SUBSTRING"){ + // this one is a gimmie + if(dataLabel.indexOf(searchStr) >= 0){ + ret.push(this.data[x]); + } + }else if(st == "STARTWORD"){ + // do a substring search and then attempt to determine if the + // preceeding char was the beginning of the string or a + // whitespace char. + var idx = dataLabel.indexOf(searchStr); + if(idx == 0){ + // implicit match + ret.push(this.data[x]); + } + if(idx <= 0){ + // if we didn't match or implicily matched, march onward + continue; + } + // otherwise, we have to go figure out if the match was at the + // start of a word... + // this code is taken almost directy from nWidgets + var matches = false; + while(idx!=-1){ + // make sure the match either starts whole string, or + // follows a space, or follows some punctuation + if(" ,/(".indexOf(dataLabel.charAt(idx-1)) != -1){ + // FIXME: what about tab chars? + matches = true; break; + } + idx = dataLabel.indexOf(searchStr, idx+1); + } + if(!matches){ + continue; + }else{ + ret.push(this.data[x]); + } + } + } + this.provideSearchResults(ret); + }; + + this.provideSearchResults = function(resultsDataPairs){ + }; + + this.addData = function(pairs){ + // FIXME: incredibly naive and slow! + this.data = this.data.concat(pairs); + }; + + this.setData = function(pdata){ + // populate this.data and initialize lookup structures + this.data = pdata; + }; + + if(dataPairs){ + this.setData(dataPairs); + } +}; + +dojo.widget.defineWidget( + "dojo.widget.ComboBox", + dojo.widget.HtmlWidget, + { + // Applies to any renderer + isContainer: false, + + forceValidOption: false, + searchType: "stringstart", + dataProvider: null, + + startSearch: function(searchString){}, + selectNextResult: function(){}, + selectPrevResult: function(){}, + setSelectedResult: function(){}, + + // HTML specific stuff + autoComplete: true, + formInputName: "", + name: "", // clone in the name from the DOM node + textInputNode: null, + comboBoxValue: null, + comboBoxSelectionValue: null, + optionsListWrapper: null, + optionsListNode: null, + downArrowNode: null, + cbTableNode: null, + searchTimer: null, + searchDelay: 100, + dataUrl: "", + fadeTime: 200, + // maxListLength limits list to X visible rows, scroll on rest + maxListLength: 8, + // mode can also be "remote" for JSON-returning live search or "html" for + // dumber live search + mode: "local", + selectedResult: null, + _highlighted_option: null, + _prev_key_backspace: false, + _prev_key_esc: false, + _gotFocus: false, + _mouseover_list: false, + dataProviderClass: "dojo.widget.ComboBoxDataProvider", + buttonSrc: dojo.uri.dojoUri("src/widget/templates/images/combo_box_arrow.png"), + + //the old implementation has builtin fade toggle, so we mimic it here + dropdownToggle: "fade", + + templatePath: dojo.uri.dojoUri("src/widget/templates/ComboBox.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/ComboBox.css"), + + + setValue: function(value) { + this.comboBoxValue.value = value; + if (this.textInputNode.value != value) { // prevent mucking up of selection + this.textInputNode.value = value; + } + dojo.widget.html.stabile.setState(this.widgetId, this.getState(), true); + this.onValueChanged(value); + }, + + // for user to override + onValueChanged: function(){ }, + + getValue: function() { + return this.comboBoxValue.value; + }, + + getState: function() { + return {value: this.getValue()}; + }, + + setState: function(state) { + this.setValue(state.value); + }, + + getCaretPos: function(element){ + // khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22 + if(dojo.lang.isNumber(element.selectionStart)){ + // FIXME: this is totally borked on Moz < 1.3. Any recourse? + return element.selectionStart; + }else if(dojo.render.html.ie){ + // in the case of a mouse click in a popup being handled, + // then the document.selection is not the textarea, but the popup + // var r = document.selection.createRange(); + // hack to get IE 6 to play nice. What a POS browser. + var tr = document.selection.createRange().duplicate(); + var ntr = element.createTextRange(); + tr.move("character",0); + ntr.move("character",0); + try { + // If control doesnt have focus, you get an exception. + // Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes). + // There appears to be no workaround for this - googled for quite a while. + ntr.setEndPoint("EndToEnd", tr); + return String(ntr.text).replace(/\r/g,"").length; + } catch (e) { + return 0; // If focus has shifted, 0 is fine for caret pos. + } + + } + }, + + setCaretPos: function(element, location){ + location = parseInt(location); + this.setSelectedRange(element, location, location); + }, + + setSelectedRange: function(element, start, end){ + if(!end){ end = element.value.length; } // NOTE: Strange - should be able to put caret at start of text? + // Mozilla + // parts borrowed from http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130 + if(element.setSelectionRange){ + element.focus(); + element.setSelectionRange(start, end); + }else if(element.createTextRange){ // IE + var range = element.createTextRange(); + with(range){ + collapse(true); + moveEnd('character', end); + moveStart('character', start); + select(); + } + }else{ //otherwise try the event-creation hack (our own invention) + // do we need these? + element.value = element.value; + element.blur(); + element.focus(); + // figure out how far back to go + var dist = parseInt(element.value.length)-end; + var tchar = String.fromCharCode(37); + var tcc = tchar.charCodeAt(0); + for(var x = 0; x < dist; x++){ + var te = document.createEvent("KeyEvents"); + te.initKeyEvent("keypress", true, true, null, false, false, false, false, tcc, tcc); + element.dispatchEvent(te); + } + } + }, + + // does the keyboard related stuff + _handleKeyEvents: function(evt){ + if(evt.ctrlKey || evt.altKey){ return; } + + // reset these + this._prev_key_backspace = false; + this._prev_key_esc = false; + + var k = dojo.event.browser.keys; + var doSearch = true; + + // mozilla quirk + // space has no keyCode in mozilla + var keyCode = evt.keyCode; + if(keyCode==0 && evt.charCode==k.KEY_SPACE){ + keyCode = k.KEY_SPACE; + } + // Safari quirk. keyCodes for arrow keys are different + if (dojo.render.html.safari) { + switch(keyCode) { + case 63232: keyCode = k.KEY_UP_ARROW; break; + case 63233: keyCode = k.KEY_DOWN_ARROW; break; + } + } + switch(keyCode){ + case k.KEY_DOWN_ARROW: + if(!this.popupWidget.isShowingNow){ + this.startSearchFromInput(); + } + this.highlightNextOption(); + dojo.event.browser.stopEvent(evt); + return; + case k.KEY_UP_ARROW: + this.highlightPrevOption(); + dojo.event.browser.stopEvent(evt); + return; + case k.KEY_ENTER: + // prevent submitting form if we press enter with list open + if(this.popupWidget.isShowingNow){ + dojo.event.browser.stopEvent(evt); + } + // fallthrough + case k.KEY_TAB: + // using linux alike tab for autocomplete + if(!this.autoComplete && this.popupWidget.isShowingNow && this._highlighted_option){ + dojo.event.browser.stopEvent(evt); + this.selectOption({ 'target': this._highlighted_option, 'noHide': false}); + + // put caret last + this.setSelectedRange(this.textInputNode, this.textInputNode.value.length, null); + }else{ + this.selectOption(); + return; + } + break; + case k.KEY_SPACE: + if(this.popupWidget.isShowingNow && this._highlighted_option){ + dojo.event.browser.stopEvent(evt); + this.selectOption(); + this.hideResultList(); + return; + } + break; + case k.KEY_ESCAPE: + this.hideResultList(); + this._prev_key_esc = true; + return; + case k.KEY_BACKSPACE: + this._prev_key_backspace = true; + if(!this.textInputNode.value.length){ + this.setAllValues("", ""); + this.hideResultList(); + doSearch = false; + } + break; + case k.KEY_RIGHT_ARROW: // fall through + case k.KEY_LEFT_ARROW: // fall through + case k.KEY_SHIFT: + doSearch = false; + break; + default:// non char keys (F1-F12 etc..) shouldn't open list + if(evt.charCode==0){ + doSearch = false; + } + } + + if(this.searchTimer){ + clearTimeout(this.searchTimer); + } + if(doSearch){ + // if we have gotten this far we dont want to keep our highlight + this.blurOptionNode(); + + // need to wait a tad before start search so that the event bubbles through DOM and we have value visible + this.searchTimer = setTimeout(dojo.lang.hitch(this, this.startSearchFromInput), this.searchDelay); + } + }, + + onKeyDown: function(evt){ + // IE needs to stop keyDown others need to stop keyPress + if(!document.createEvent){ // only IE + this._handleKeyEvents(evt); + } + // FIXME: What about ESC ?? + }, + + onKeyPress: function(evt){ + if(document.createEvent){ // never IE + this._handleKeyEvents(evt); + } + }, + + // When inputting characters using an input method, such as Asian + // languages, it will generate this event instead of onKeyDown event + compositionEnd: function(evt){ + this._handleKeyEvents(evt); + }, + + onKeyUp: function(evt){ + this.setValue(this.textInputNode.value); + }, + + setSelectedValue: function(value){ + // FIXME, not sure what to do here! + this.comboBoxSelectionValue.value = value; + }, + + setAllValues: function(value1, value2){ + this.setValue(value1); + this.setSelectedValue(value2); + }, + + // does the actual highlight + focusOptionNode: function(node){ + if(this._highlighted_option != node){ + this.blurOptionNode(); + this._highlighted_option = node; + dojo.html.addClass(this._highlighted_option, "dojoComboBoxItemHighlight"); + } + }, + + // removes highlight on highlighted + blurOptionNode: function(){ + if(this._highlighted_option){ + dojo.html.removeClass(this._highlighted_option, "dojoComboBoxItemHighlight"); + this._highlighted_option = null; + } + }, + + highlightNextOption: function(){ + if((!this._highlighted_option) || !this._highlighted_option.parentNode){ + this.focusOptionNode(this.optionsListNode.firstChild); + }else if(this._highlighted_option.nextSibling){ + this.focusOptionNode(this._highlighted_option.nextSibling); + } + dojo.html.scrollIntoView(this._highlighted_option); + }, + + highlightPrevOption: function(){ + if(this._highlighted_option && this._highlighted_option.previousSibling){ + this.focusOptionNode(this._highlighted_option.previousSibling); + }else{ + this._highlighted_option = null; + this.hideResultList(); + return; + } + dojo.html.scrollIntoView(this._highlighted_option); + }, + + itemMouseOver: function(evt){ + if (evt.target === this.optionsListNode) { return; } + this.focusOptionNode(evt.target); + dojo.html.addClass(this._highlighted_option, "dojoComboBoxItemHighlight"); + }, + + itemMouseOut: function(evt){ + if (evt.target === this.optionsListNode) { return; } + this.blurOptionNode(); + }, + + // reset button size; this function is called when the input area has changed size + onResize: function(){ + var inputSize = dojo.html.getBorderBox(this.textInputNode); + var buttonSize = { width: inputSize.height, height: inputSize.height}; + dojo.html.setMarginBox(this.downArrowNode, buttonSize); + }, + + postMixInProperties: function(args, frag){ + this.inherited("postMixInProperties"); + + // set image size before instantiating template; + // changing it afterwards doesn't work on FF + var inputNode = this.getFragNodeRef(frag); + var inputSize = dojo.html.getBorderBox(inputNode); + this.initialButtonSize = inputSize.height + "px"; + }, + + fillInTemplate: function(args, frag){ + // FIXME: need to get/assign DOM node names for form participation here. + this.comboBoxValue.name = this.name; + this.comboBoxSelectionValue.name = this.name+"_selected"; + + var source = this.getFragNodeRef(frag); + dojo.html.copyStyle(this.textInputNode, source); + + var dpClass; + if(this.mode == "remote"){ + dpClass = dojo.widget.incrementalComboBoxDataProvider; + }else if(typeof this.dataProviderClass == "string"){ + dpClass = dojo.evalObjPath(this.dataProviderClass) + }else{ + dpClass = this.dataProviderClass; + } + this.dataProvider = new dpClass(); + this.dataProvider.init(this, this.getFragNodeRef(frag)); + + this.popupWidget = new dojo.widget.createWidget("PopupContainer", + {toggle: this.dropdownToggle, toggleDuration: this.toggleDuration}); + dojo.event.connect(this, 'destroy', this.popupWidget, 'destroy'); + this.optionsListNode = this.popupWidget.domNode; + this.domNode.appendChild(this.optionsListNode); + dojo.html.addClass(this.optionsListNode, 'dojoComboBoxOptions'); + dojo.event.connect(this.optionsListNode, 'onclick', this, 'selectOption'); + dojo.event.connect(this.optionsListNode, 'onmouseover', this, '_onMouseOver'); + dojo.event.connect(this.optionsListNode, 'onmouseout', this, '_onMouseOut'); + + dojo.event.connect(this.optionsListNode, "onmouseover", this, "itemMouseOver"); + dojo.event.connect(this.optionsListNode, "onmouseout", this, "itemMouseOut"); + }, + + focus: function(){ + // summary + // set focus to input node from code + this.tryFocus(); + }, + + openResultList: function(results){ + this.clearResultList(); + if(!results.length){ + this.hideResultList(); + } + + if( (this.autoComplete)&& + (results.length)&& + (!this._prev_key_backspace)&& + (this.textInputNode.value.length > 0)){ + var cpos = this.getCaretPos(this.textInputNode); + // only try to extend if we added the last character at the end of the input + if((cpos+1) > this.textInputNode.value.length){ + // only add to input node as we would overwrite Capitalisation of chars + this.textInputNode.value += results[0][0].substr(cpos); + // build a new range that has the distance from the earlier + // caret position to the end of the first string selected + this.setSelectedRange(this.textInputNode, cpos, this.textInputNode.value.length); + } + } + + var even = true; + while(results.length){ + var tr = results.shift(); + if(tr){ + var td = document.createElement("div"); + td.appendChild(document.createTextNode(tr[0])); + td.setAttribute("resultName", tr[0]); + td.setAttribute("resultValue", tr[1]); + td.className = "dojoComboBoxItem "+((even) ? "dojoComboBoxItemEven" : "dojoComboBoxItemOdd"); + even = (!even); + this.optionsListNode.appendChild(td); + } + } + + // show our list (only if we have content, else nothing) + this.showResultList(); + }, + + onFocusInput: function(){ + this._hasFocus = true; + }, + + onBlurInput: function(){ + this._hasFocus = false; + this._handleBlurTimer(true, 500); + }, + + // collect all blur timers issues here + _handleBlurTimer: function(/*Boolean*/clear, /*Number*/ millisec){ + if(this.blurTimer && (clear || millisec)){ + clearTimeout(this.blurTimer); + } + if(millisec){ // we ignore that zero is false and never sets as that never happens in this widget + this.blurTimer = dojo.lang.setTimeout(this, "checkBlurred", millisec); + } + }, + + // these 2 are needed in IE and Safari as inputTextNode loses focus when scrolling optionslist + _onMouseOver: function(evt){ + if(!this._mouseover_list){ + this._handleBlurTimer(true, 0); + this._mouseover_list = true; + } + }, + + _onMouseOut:function(evt){ + var relTarget = evt.relatedTarget; + if(!relTarget || relTarget.parentNode!=this.optionsListNode){ + this._mouseover_list = false; + this._handleBlurTimer(true, 100); + this.tryFocus(); + } + }, + + _isInputEqualToResult: function(result){ + var input = this.textInputNode.value; + if(!this.dataProvider.caseSensitive){ + input = input.toLowerCase(); + result = result.toLowerCase(); + } + return (input == result); + }, + + _isValidOption: function(){ + var tgt = dojo.html.firstElement(this.optionsListNode); + var isValidOption = false; + var tgt = dojo.html.firstElement(this.optionsListNode); + var isValidOption = false; + while(!isValidOption && tgt){ + if(this._isInputEqualToResult(tgt.getAttribute("resultName"))){ + isValidOption = true; + }else{ + tgt = dojo.html.nextElement(tgt); + } + } + return isValidOption; + }, + + checkBlurred: function(){ + if(!this._hasFocus && !this._mouseover_list){ + this.hideResultList(); + // clear the list if the user empties field and moves away. + if(!this.textInputNode.value.length){ + this.setAllValues("", ""); + return; + } + + var isValidOption = this._isValidOption(); + // enforce selection from option list + if(this.forceValidOption && !isValidOption){ + this.setAllValues("", ""); + return; + } + if(!isValidOption){// clear + this.setSelectedValue(""); + } + } + }, + + sizeBackgroundIframe: function(){ + var mb = dojo.html.getMarginBox(this.optionsListNode); + if( mb.width==0 || mb.height==0 ){ + // need more time to calculate size + dojo.lang.setTimeout(this, "sizeBackgroundIframe", 100); + return; + } + }, + + selectOption: function(evt){ + var tgt = null; + if(!evt){ + evt = { target: this._highlighted_option }; + } + + if(!dojo.html.isDescendantOf(evt.target, this.optionsListNode)){ + // handle autocompletion where the the user has hit ENTER or TAB + + // if the input is empty do nothing + if(!this.textInputNode.value.length){ + return; + } + tgt = dojo.html.firstElement(this.optionsListNode); + + // user has input value not in option list + if(!tgt || !this._isInputEqualToResult(tgt.getAttribute("resultName"))){ + return; + } + // otherwise the user has accepted the autocompleted value + }else{ + tgt = evt.target; + } + + while((tgt.nodeType!=1)||(!tgt.getAttribute("resultName"))){ + tgt = tgt.parentNode; + if(tgt === dojo.body()){ + return false; + } + } + + this.textInputNode.value = tgt.getAttribute("resultName"); + this.selectedResult = [tgt.getAttribute("resultName"), tgt.getAttribute("resultValue")]; + this.setAllValues(tgt.getAttribute("resultName"), tgt.getAttribute("resultValue")); + if(!evt.noHide){ + this.hideResultList(); + this.setSelectedRange(this.textInputNode, 0, null); + } + this.tryFocus(); + }, + + clearResultList: function(){ + if(this.optionsListNode.innerHTML){ + this.optionsListNode.innerHTML = ""; // browser natively knows how to collect this memory + } + }, + + hideResultList: function(){ + this.popupWidget.close(); + }, + + showResultList: function(){ + // Our dear friend IE doesnt take max-height so we need to calculate that on our own every time + var childs = this.optionsListNode.childNodes; + if(childs.length){ + var visibleCount = this.maxListLength; + if(childs.length < visibleCount){ + visibleCount = childs.length; + } + + with(this.optionsListNode.style) + { + display = ""; + if(visibleCount == childs.length){ + //no scrollbar is required, so unset height to let browser calcuate it, + //as in css, overflow is already set to auto + height = ""; + }else{ + //show it first to get the correct dojo.style.getOuterHeight(childs[0]) + //FIXME: shall we cache the height of the item? + height = visibleCount * dojo.html.getMarginBox(childs[0]).height +"px"; + } + width = (dojo.html.getMarginBox(this.domNode).width-2)+"px"; + + } + this.popupWidget.open(this.cbTableNode, this, this.downArrowNode); + }else{ + this.hideResultList(); + } + }, + + handleArrowClick: function(){ + this._handleBlurTimer(true, 0); + this.tryFocus(); + if(this.popupWidget.isShowingNow){ + this.hideResultList(); + }else{ + // forces full population of results, if they click + // on the arrow it means they want to see more options + this.startSearch(""); + } + }, + + tryFocus: function(){ + try { + this.textInputNode.focus(); + } catch (e) { + // element isn't focusable if disabled, or not visible etc - not easy to test for. + }; + }, + + startSearchFromInput: function(){ + this.startSearch(this.textInputNode.value); + }, + + postCreate: function(){ + dojo.event.connect(this, "startSearch", this.dataProvider, "startSearch"); + dojo.event.connect(this.dataProvider, "provideSearchResults", this, "openResultList"); + dojo.event.connect(this.textInputNode, "onblur", this, "onBlurInput"); + dojo.event.connect(this.textInputNode, "onfocus", this, "onFocusInput"); + + var s = dojo.widget.html.stabile.getState(this.widgetId); + if (s) { + this.setState(s); + } + } + } +); + + diff --git a/source/web/scripts/ajax/src/widget/ContentPane.js b/source/web/scripts/ajax/src/widget/ContentPane.js new file mode 100644 index 0000000000..97faa3ced5 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/ContentPane.js @@ -0,0 +1,552 @@ +// This widget doesn't do anything; is basically the same as
. +// It's useful as a child of LayoutContainer, SplitContainer, or TabContainer. +// But note that those classes can contain any widget as a child. + +dojo.provide("dojo.widget.ContentPane"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.string"); +dojo.require("dojo.string.extras"); +dojo.require("dojo.html.style"); + +dojo.widget.defineWidget( + "dojo.widget.ContentPane", + dojo.widget.HtmlWidget, + function(){ + // per widgetImpl variables + this._styleNodes = []; + this._onLoadStack = []; + this._onUnLoadStack = []; + this._callOnUnLoad = false; + this.scriptScope; // undefined for now + this._ioBindObj; + + // loading option + this.bindArgs = {}; // example bindArgs="preventCache:false;" overrides cacheContent + }, + { + isContainer: true, + + // loading options + adjustPaths: true, // fix relative paths in content to fit in this page + href: "", // only usable on construction, use setUrl or setContent after that + extractContent: true, // extract visible content from inside of .... + parseContent: true, // construct all widgets that is in content + cacheContent: true, + preload: false, // force load of data even if pane is hidden + refreshOnShow: false, // use with cacheContent: false + handler: "", // generate pane content from a java function + executeScripts: false, // if true scripts in content will be evaled after content is innerHTML'ed + + postCreate: function(args, frag, parentComp){ + if (this.handler!==""){ + this.setHandler(this.handler); + } + if(this.isShowing() || this.preload){ + this.loadContents(); + } + }, + + show: function(){ + // if refreshOnShow is true, reload the contents every time; otherwise, load only the first time + if(this.refreshOnShow){ + this.refresh(); + }else{ + this.loadContents(); + } + dojo.widget.ContentPane.superclass.show.call(this); + }, + + refresh: function(){ + this.isLoaded=false; + this.loadContents(); + }, + + loadContents: function() { + if ( this.isLoaded ){ + return; + } + if ( dojo.lang.isFunction(this.handler)) { + this._runHandler(); + } else if ( this.href != "" ) { + this._downloadExternalContent(this.href, this.cacheContent && !this.refreshOnShow); + }else{ + this.isLoaded=true; + } + }, + + + setUrl: function(/*String or dojo.uri.Uri*/ url) { + // summary: + // Reset the (external defined) content of this pane and replace with new url + this.href = url; + this.isLoaded = false; + if ( this.preload || this.isShowing() ){ + this.loadContents(); + } + }, + + abort: function(){ + // summary + // abort download of content + var bind = this._ioBindObj; + if(!bind || !bind.abort){ return; } + bind.abort(); + delete this._ioBindObj; + }, + + _downloadExternalContent: function(url, useCache) { + this.abort(); + this._handleDefaults("Loading...", "onDownloadStart"); + var self = this; + this._ioBindObj = dojo.io.bind( + this._cacheSetting({ + url: url, + mimetype: "text/html", + load: function(type, data, xhr){ + self.onDownloadEnd.call(self, url, data); + }, + error: function(type, err, xhr){ + // XHR insnt a normal JS object, IE doesnt have prototype on XHR so we cant extend it or shallowCopy it + var e = { + responseText: xhr.responseText, + status: xhr.status, + statusText: xhr.statusText, + responseHeaders: xhr.getAllResponseHeaders(), + _text: "Error loading '" + url + "' (" + xhr.status + " "+ xhr.statusText + ")" + }; + self._handleDefaults.call(self, e, "onDownloadError"); + self.onLoad(); + } + }, useCache) + ); + }, + + _cacheSetting: function(bindObj, useCache){ + for(var x in this.bindArgs){ + if(dojo.lang.isUndefined(bindObj[x])){ + bindObj[x] = this.bindArgs[x]; + } + } + + if(dojo.lang.isUndefined(bindObj.useCache)){ bindObj.useCache = useCache; } + if(dojo.lang.isUndefined(bindObj.preventCache)){ bindObj.preventCache = !useCache; } + if(dojo.lang.isUndefined(bindObj.mimetype)){ bindObj.mimetype = "text/html"; } + return bindObj; + }, + + // called when setContent is finished + onLoad: function(e){ + this._runStack("_onLoadStack"); + this.isLoaded=true; + }, + + // called before old content is cleared + onUnLoad: function(e){ + this._runStack("_onUnLoadStack"); + delete this.scriptScope; + }, + + _runStack: function(stName){ + var st = this[stName]; var err = ""; + var scope = this.scriptScope || window; + for(var i = 0;i < st.length; i++){ + try{ + st[i].call(scope); + }catch(e){ + err += "\n"+st[i]+" failed: "+e.description; + } + } + this[stName] = []; + + if(err.length){ + var name = (stName== "_onLoadStack") ? "addOnLoad" : "addOnUnLoad"; + this._handleDefaults(name+" failure\n "+err, "onExecError", true); + } + }, + + addOnLoad: function(/*Function or Object ?*/ obj, /*Function*/ func){ + // summary + // same as to dojo.addOnLoad but does not take "function_name" as a string + this._pushOnStack(this._onLoadStack, obj, func); + }, + + addOnUnLoad: function(/*Function or Object ?*/ obj, /*Function*/ func){ + // summary + // same as to dojo.addUnOnLoad but does not take "function_name" as a string + this._pushOnStack(this._onUnLoadStack, obj, func); + }, + + _pushOnStack: function(stack, obj, func){ + if(typeof func == 'undefined') { + stack.push(obj); + }else{ + stack.push(function(){ obj[func](); }); + } + }, + + destroy: function(){ + // make sure we call onUnLoad + this.onUnLoad(); + dojo.widget.ContentPane.superclass.destroy.call(this); + }, + + // called when content script eval error or Java error occurs, preventDefault-able + onExecError: function(e){ /*stub*/ }, + + // called on DOM faults, require fault etc in content, preventDefault-able + onContentError: function(e){ /*stub*/ }, + + // called when download error occurs, preventDefault-able + onDownloadError: function(e){ /*stub*/ }, + + // called before download starts, preventDefault-able + onDownloadStart: function(e){ /*stub*/ }, + + // called when download is finished + onDownloadEnd: function(/*String*/ url, /*content*/ data){ + data = this.splitAndFixPaths(data, url); + this.setContent(data); + }, + + // usefull if user wants to prevent default behaviour ie: _setContent("Error...") + _handleDefaults: function(e, handler, useAlert){ + if(!handler){ handler = "onContentError"; } + + if(dojo.lang.isString(e)){ e = {_text: e}; } + + if(!e._text){ e._text = e.toString(); } + + e.toString = function(){ return this._text; }; + + if(typeof e.returnValue != "boolean"){ + e.returnValue = true; + } + if(typeof e.preventDefault != "function"){ + e.preventDefault = function(){ this.returnValue = false; }; + } + // call our handler + this[handler](e); + if(e.returnValue){ + if(useAlert){ + alert(e.toString()); + }else{ + // makes sure scripts can clean up after themselves, before we setContent + if(this._callOnUnLoad){ this.onUnLoad(); } + this._callOnUnLoad = false; // makes sure we dont try to call onUnLoad again on this event, + // ie onUnLoad before 'Loading...' but not before clearing 'Loading...' + this._setContent(e.toString()); + } + } + }, + + // pathfixes, require calls, css stuff and neccesary content clean + splitAndFixPaths: function(/*String*/s, /*dojo.uri.Uri?*/url){ + // summary: + // fixes all relative paths in (hopefully) all cases for example images, remote scripts, links etc. + // splits up content in different pieces, scripts, title, style, link and whats left becomes .xml + + // init vars + var titles = [], scripts = [],tmp = []; + var match = [], requires = [], attr = [], styles = []; + var str = '', path = '', fix = '', tagFix = '', tag = '', origPath = ''; + + if(!url) { url = "./"; } // point to this page if not set + + if(s){ // make sure we dont run regexes on empty content + + /************** ***********/ + // khtml is picky about dom faults, you can't attach a <style> or <title> node as child of body + // must go into head, so we need to cut out those tags + var regex = /<title[^>]*>([\s\S]*?)<\/title>/i; + while(match = regex.exec(s)){ + titles.push(match[1]); + s = s.substring(0, match.index) + s.substr(match.index + match[0].length); + }; + + /************** adjust paths *****************/ + if(this.adjustPaths){ + // attributepaths one tag can have multiple paths example: + // <input src="..." style="url(..)"/> or <a style="url(..)" href=".."> + // strip out the tag and run fix on that. + // this guarantees that we won't run replace on another tag's attribute + it was easier do + var regexFindTag = /<[a-z][a-z0-9]*[^>]*\s(?:(?:src|href|style)=[^>])+[^>]*>/i; + var regexFindAttr = /\s(src|href|style)=(['"]?)([\w()\[\]\/.,\\'"-:;#=&?\s@]+?)\2/i; + // these are the supported protocols, all other is considered relative + var regexProtocols = /^(?:[#]|(?:(?:https?|ftps?|file|javascript|mailto|news):))/; + + while(tag = regexFindTag.exec(s)){ + str += s.substring(0, tag.index); + s = s.substring((tag.index + tag[0].length), s.length); + tag = tag[0]; + + // loop through attributes + tagFix = ''; + while(attr = regexFindAttr.exec(tag)){ + path = ""; origPath = attr[3]; + switch(attr[1].toLowerCase()){ + case "src":// falltrough + case "href": + if(regexProtocols.exec(origPath)){ + path = origPath; + } else { + path = (new dojo.uri.Uri(url, origPath).toString()); + } + break; + case "style":// style + path = dojo.html.fixPathsInCssText(origPath, url); + break; + default: + path = origPath; + } + + fix = " " + attr[1] + "=" + attr[2] + path + attr[2]; + + // slices up tag before next attribute check + tagFix += tag.substring(0, attr.index) + fix; + tag = tag.substring((attr.index + attr[0].length), tag.length); + } + str += tagFix + tag; //dojo.debug(tagFix + tag); + } + s = str+s; + } + + /**************** cut out all <style> and <link rel="stylesheet" href=".."> **************/ + regex = /(?:<(style)[^>]*>([\s\S]*?)<\/style>|<link ([^>]*rel=['"]?stylesheet['"]?[^>]*)>)/i; + while(match = regex.exec(s)){ + if(match[1] && match[1].toLowerCase() == "style"){ + styles.push(dojo.html.fixPathsInCssText(match[2],url)); + }else if(attr = match[3].match(/href=(['"]?)([^'">]*)\1/i)){ + styles.push({path: attr[2]}); + } + s = s.substring(0, match.index) + s.substr(match.index + match[0].length); + }; + + /***************** cut out all <script> tags, push them into scripts array ***************/ + var regex = /<script([^>]*)>([\s\S]*?)<\/script>/i; + var regexSrc = /src=(['"]?)([^"']*)\1/i; + var regexDojoJs = /.*(\bdojo\b\.js(?:\.uncompressed\.js)?)$/; + var regexInvalid = /(?:var )?\bdjConfig\b(?:[\s]*=[\s]*\{[^}]+\}|\.[\w]*[\s]*=[\s]*[^;\n]*)?;?|dojo\.hostenv\.writeIncludes\(\s*\);?/g; + var regexRequires = /dojo\.(?:(?:require(?:After)?(?:If)?)|(?:widget\.(?:manager\.)?registerWidgetPackage)|(?:(?:hostenv\.)?setModulePrefix)|defineNamespace)\((['"]).*?\1\)\s*;?/; + + while(match = regex.exec(s)){ + if(this.executeScripts && match[1]){ + if(attr = regexSrc.exec(match[1])){ + // remove a dojo.js or dojo.js.uncompressed.js from remoteScripts + // we declare all files named dojo.js as bad, regardless of path + if(regexDojoJs.exec(attr[2])){ + dojo.debug("Security note! inhibit:"+attr[2]+" from beeing loaded again."); + }else{ + scripts.push({path: attr[2]}); + } + } + } + if(match[2]){ + // remove all invalid variables etc like djConfig and dojo.hostenv.writeIncludes() + var sc = match[2].replace(regexInvalid, ""); + if(!sc){ continue; } + + // cut out all dojo.require (...) calls, if we have execute + // scripts false widgets dont get there require calls + // takes out possible widgetpackage registration as well + while(tmp = regexRequires.exec(sc)){ + requires.push(tmp[0]); + sc = sc.substring(0, tmp.index) + sc.substr(tmp.index + tmp[0].length); + } + if(this.executeScripts){ + scripts.push(sc); + } + } + s = s.substr(0, match.index) + s.substr(match.index + match[0].length); + } + + /********* extract content *********/ + if(this.extractContent){ + match = s.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im); + if(match) { s = match[1]; } + } + + /******** scan for scriptScope in html eventHandlers and replace with link to this pane *********/ + if(this.executeScripts){ + var regex = /(<[a-zA-Z][a-zA-Z0-9]*\s[^>]*\S=(['"])[^>]*[^\.\]])scriptScope([^>]*>)/; + str = ""; + while(tag = regex.exec(s)){ + tmp = ((tag[2]=="'") ? '"': "'"); + str += s.substring(0, tag.index); + s = s.substr(tag.index).replace(regex, "$1dojo.widget.byId("+ tmp + this.widgetId + tmp + ").scriptScope$3"); + } + s = str + s; + } + } + + return {"xml": s, // Object + "styles": styles, + "titles": titles, + "requires": requires, + "scripts": scripts, + "url": url}; + }, + + + _setContent: function(cont){ + this.destroyChildren(); + + // remove old stylenodes from HEAD + for(var i = 0; i < this._styleNodes.length; i++){ + if(this._styleNodes[i] && this._styleNodes[i].parentNode){ + this._styleNodes[i].parentNode.removeChild(this._styleNodes[i]); + } + } + this._styleNodes = []; + + var node = this.containerNode || this.domNode; + while(node.firstChild){ + try{ + dojo.event.browser.clean(node.firstChild); + }catch(e){} + node.removeChild(node.firstChild); + } + try{ + if(typeof cont != "string"){ + node.innerHTML = ""; + node.appendChild(cont); + }else{ + node.innerHTML = cont; + } + }catch(e){ + e._text = "Could'nt load content:"+e.description; + this._handleDefaults(e, "onContentError"); + } + }, + + setContent: function(/*String or DOMNode*/ data){ + // summary: + // Destroys old content and sets new content, and possibly initialize any widgets within 'data' + this.abort(); + if(this._callOnUnLoad){ this.onUnLoad(); }// this tells a remote script clean up after itself + this._callOnUnLoad = true; + + if(!data || dojo.html.isNode(data)){ + // if we do a clean using setContent(""); or setContent(#node) bypass all parsing, extractContent etc + this._setContent(data); + this.onResized(); + this.onLoad(); + }else{ + // need to run splitAndFixPaths? ie. manually setting content + // adjustPaths is taken care of inside splitAndFixPaths + if(!data.xml){ + this.href = ""; // so we can refresh safely + data = this.splitAndFixPaths(data); + } + + this._setContent(data.xml); + + // insert styles from content (in same order they came in) + for(var i = 0; i < data.styles.length; i++){ + if(data.styles[i].path){ + this._styleNodes.push(dojo.html.insertCssFile(data.styles[i].path)); + }else{ + this._styleNodes.push(dojo.html.insertCssText(data.styles[i])); + } + } + + if(this.parseContent){ + for(var i = 0; i < data.requires.length; i++){ + try{ + eval(data.requires[i]); + } catch(e){ + e._text = "Error in packageloading calls, "+e.description; + this._handleDefaults(e, "onContentError", true); + } + } + } + // need to allow async load, Xdomain uses it + // is inline function because we cant send args to dojo.addOnLoad + var _self = this; + function asyncParse(){ + if(_self.executeScripts){ + _self._executeScripts(data.scripts); + } + + if(_self.parseContent){ + var node = _self.containerNode || _self.domNode; + var parser = new dojo.xml.Parse(); + var frag = parser.parseElement(node, null, true); + // createSubComponents not createComponents because frag has already been created + dojo.widget.getParser().createSubComponents(frag, _self); + } + + _self.onResized(); + _self.onLoad(); + } + // try as long as possible to make setContent sync call + if(dojo.hostenv.isXDomain && data.requires.length){ + dojo.addOnLoad(asyncParse); + }else{ + asyncParse(); + } + } + }, + + // Generate pane content from given java function + setHandler: function(handler) { + var fcn = dojo.lang.isFunction(handler) ? handler : window[handler]; + if(!dojo.lang.isFunction(fcn)) { + // FIXME: needs testing! somebody with java knowledge needs to try this + this._handleDefaults("Unable to set handler, '" + handler + "' not a function.", "onExecError", true); + return; + } + this.handler = function() { + return fcn.apply(this, arguments); + } + }, + + _runHandler: function() { + var ret = true; + if(dojo.lang.isFunction(this.handler)) { + this.handler(this, this.domNode); + ret = false; + } + this.onLoad(); + return ret; + }, + + _executeScripts: function(scripts) { + // loop through the scripts in the order they came in + var self = this; + var tmp = "", code = ""; + for(var i = 0; i < scripts.length; i++){ + if(scripts[i].path){ // remotescript + dojo.io.bind(this._cacheSetting({ + "url": scripts[i].path, + "load": function(type, scriptStr){ + dojo.lang.hitch(self, tmp = scriptStr); + }, + "error": function(type, error){ + error._text = type + " downloading remote script"; + self._handleDefaults.call(self, error, "onExecError", true); + }, + "mimetype": "text/plain", + "sync": true + }, this.cacheContent)); + code += tmp; + }else{ + code += scripts[i]; + } + } + + try{ + // initialize a new anonymous container for our script, dont make it part of this widgets scope chain + // instead send in a variable that points to this widget, usefull to connect events to onLoad, onUnLoad etc.. + delete this.scriptScope; + this.scriptScope = new (new Function('_container_', code+'; return this;'))(self); + }catch(e){ + e._text = "Error running scripts from content:\n"+e.description; + this._handleDefaults(e, "onExecError", true); + } + } + } +); + diff --git a/source/web/scripts/ajax/src/widget/ContextMenu.js b/source/web/scripts/ajax/src/widget/ContextMenu.js new file mode 100644 index 0000000000..b47f4a7541 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/ContextMenu.js @@ -0,0 +1,33 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.provide("dojo.widget.ContextMenu"); + +dojo.deprecated("dojo.widget.ContextMenu", "use dojo.widget.Menu2", "0.4"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.DomWidget"); + +dojo.widget.ContextMenu = function(){ + dojo.widget.Widget.call(this); + this.widgetType = "ContextMenu"; + this.isContainer = true; + this.isOpened = false; + + // copy children widgets output directly to parent (this node), to avoid + // errors trying to insert an <li> under a <div> + this.snarfChildDomOutput = true; + +} + +dojo.inherits(dojo.widget.ContextMenu, dojo.widget.Widget); +dojo.widget.tags.addParseTreeHandler("dojo:contextmenu"); + +dojo.requireAfterIf("html", "dojo.widget.html.ContextMenu"); diff --git a/source/web/scripts/ajax/src/widget/DatePicker.js b/source/web/scripts/ajax/src/widget/DatePicker.js new file mode 100644 index 0000000000..f604581d8b --- /dev/null +++ b/source/web/scripts/ajax/src/widget/DatePicker.js @@ -0,0 +1,379 @@ +dojo.provide("dojo.widget.DatePicker"); +dojo.provide("dojo.widget.DatePicker.util"); +dojo.require("dojo.date"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.event.*"); +dojo.require("dojo.dom"); +dojo.require("dojo.html.style"); +dojo.require("dojo.i18n.calendar.GregorianNames"); + +/* + Some assumptions: + - I'm planning on always showing 42 days at a time, and we can scroll by week, + not just by month or year + - To get a sense of what month to highlight, I basically initialize on the + first Saturday of each month, since that will be either the first of two or + the second of three months being partially displayed, and then I work forwards + and backwards from that point. + Currently, I assume that dates are stored in the RFC 3339 format, + because I find it to be most human readable and easy to parse + http://www.faqs.org/rfcs/rfc3339.html: 2005-06-30T08:05:00-07:00 +*/ + +dojo.widget.defineWidget( + "dojo.widget.DatePicker", + dojo.widget.HtmlWidget, + function() { + // today's date, JS Date object + this.today = ""; + // selected date, JS Date object + this.date = ""; + // rfc 3339 date + this.storedDate = ""; + // date currently selected in the UI, stored in year, month, date in the format that will be actually displayed + this.currentDate = {}; + // stored in year, month, date in the format that will be actually displayed + this.firstSaturday = {}; + }, + { + lang: "", // would be better if this could be undefined if the attribute is undefined + dayWidth: 'narrow', + + classNames: { + previous: "previousMonth", + current: "currentMonth", + next: "nextMonth", + currentDate: "currentDate", + selectedDate: "selectedItem" + }, + templatePath: dojo.uri.dojoUri("src/widget/templates/DatePicker.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/DatePicker.css"), + + fillInTemplate: function(){ + dojo.widget.DatePicker.call(this); + this.initData(); + this.initUI(); + }, + initData: function() { + this.today = new Date(); + if(this.storedDate && (this.storedDate.split("-").length > 2)) { + this.date = dojo.widget.DatePicker.util.fromRfcDate(this.storedDate); + } else { + this.date = this.today; + } + // calendar math is simplified if time is set to 0 + this.today.setHours(0); + this.date.setHours(0); + var month = this.date.getMonth(); + var tempSaturday = dojo.widget.DatePicker.util.initFirstSaturday(this.date.getMonth().toString(), this.date.getFullYear()); + this.firstSaturday.year = tempSaturday.year; + this.firstSaturday.month = tempSaturday.month; + this.firstSaturday.date = tempSaturday.date; + }, + + setDate: function(rfcDate) { + this.storedDate = rfcDate; + }, + + initUI: function() { + var dayLabels = dojo.i18n.calendar.GregorianNames.getNames('days', this.dayWidth, 'standAlone', this.lang); + var dayLabelNodes = this.dayLabelsRow.getElementsByTagName("td"); + for(var i=0; i<7; i++) { + dayLabelNodes.item(i).innerHTML = dayLabels[i]; + } + + this.selectedIsUsed = false; + this.currentIsUsed = false; + var currentClassName = ""; + var previousDate = new Date(); + var calendarNodes = this.calendarDatesContainerNode.getElementsByTagName("td"); + var currentCalendarNode; + // set hours of date such that there is no chance of rounding error due to + // time change in local time zones + previousDate.setHours(8); + var nextDate = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date, 8); + + if(this.firstSaturday.date < 7) { + // this means there are days to show from the previous month + var dayInWeek = 6; + for (var i=this.firstSaturday.date; i>0; i--) { + currentCalendarNode = calendarNodes.item(dayInWeek); + currentCalendarNode.innerHTML = nextDate.getDate(); + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current")); + dayInWeek--; + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, false); + } + for(var i=dayInWeek; i>-1; i--) { + currentCalendarNode = calendarNodes.item(i); + currentCalendarNode.innerHTML = nextDate.getDate(); + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "previous")); + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, false); + } + } else { + nextDate.setDate(this.firstSaturday.date-6); + for(var i=0; i<7; i++) { + currentCalendarNode = calendarNodes.item(i); + currentCalendarNode.innerHTML = nextDate.getDate(); + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current")); + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, true); + } + } + previousDate.setDate(this.firstSaturday.date); + previousDate.setMonth(this.firstSaturday.month); + previousDate.setFullYear(this.firstSaturday.year); + nextDate = this.incrementDate(previousDate, true); + var count = 7; + currentCalendarNode = calendarNodes.item(count); + while((nextDate.getMonth() == previousDate.getMonth()) && (count<42)) { + currentCalendarNode.innerHTML = nextDate.getDate(); + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current")); + currentCalendarNode = calendarNodes.item(++count); + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, true); + } + + while(count < 42) { + currentCalendarNode.innerHTML = nextDate.getDate(); + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "next")); + currentCalendarNode = calendarNodes.item(++count); + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, true); + } + this.setMonthLabel(this.firstSaturday.month); + this.setYearLabels(this.firstSaturday.year); + }, + + incrementDate: function(date, bool) { + // bool: true to increase, false to decrease + var time = date.getTime(); + var increment = 1000 * 60 * 60 * 24; + time = (bool) ? (time + increment) : (time - increment); + var returnDate = new Date(); + returnDate.setTime(time); + return returnDate; + }, + + incrementWeek: function(evt) { + var date = this.firstSaturday.date; + var month = this.firstSaturday.month; + var year = this.firstSaturday.year; + switch(evt.target) { + case this.increaseWeekNode.getElementsByTagName("img").item(0): + case this.increaseWeekNode: + date = date + 7; + if (date>this._daysIn(month,year)) { + date = date - this._daysIn(month,year); + if (month < 11) { + month++; + } else { + month=0; + year++; + } + } + break; + case this.decreaseWeekNode.getElementsByTagName("img").item(0): + case this.decreaseWeekNode: + if (date > 7) { + date = date - 7; + } else { + var diff = 7 - date; + if (month > 0) { + month--; + date = this._daysIn(month,year) - diff; + }else { + year--; + month=11; + date = 31 - diff; + } + } + break; + + } + + this.firstSaturday.date=date; + this.firstSaturday.month=month; + this.firstSaturday.year=year; + this.initUI(); + }, + + incrementMonth: function(evt) { + var month = this.firstSaturday.month; + var year = this.firstSaturday.year; + switch(evt.currentTarget) { + case this.increaseMonthNode: + if(month < 11) { + month++; + } else { + month = 0; + year++; + + this.setYearLabels(year); + } + break; + case this.decreaseMonthNode: + if(month > 0) { + month--; + } else { + month = 11; + year--; + this.setYearLabels(year); + } + break; + case this.increaseMonthNode.getElementsByTagName("img").item(0): + if(month < 11) { + month++; + } else { + month = 0; + year++; + this.setYearLabels(year); + } + break; + case this.decreaseMonthNode.getElementsByTagName("img").item(0): + if(month > 0) { + month--; + } else { + month = 11; + year--; + this.setYearLabels(year); + } + break; + } + var tempSaturday = dojo.widget.DatePicker.util.initFirstSaturday(month.toString(), year); + this.firstSaturday.year = tempSaturday.year; + this.firstSaturday.month = tempSaturday.month; + this.firstSaturday.date = tempSaturday.date; + this.initUI(); + }, + + incrementYear: function(evt) { + var year = this.firstSaturday.year; + switch(evt.target) { + case this.nextYearLabelNode: + year++; + break; + case this.previousYearLabelNode: + year--; + break; + } + var tempSaturday = dojo.widget.DatePicker.util.initFirstSaturday(this.firstSaturday.month.toString(), year); + this.firstSaturday.year = tempSaturday.year; + this.firstSaturday.month = tempSaturday.month; + this.firstSaturday.date = tempSaturday.date; + this.initUI(); + }, + + _daysIn: function(month,year) { + var daysIn = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + if (month==1) { + return (year%400 == 0) ? 29: (year%100 == 0) ? 28: (year%4 == 0) ? 29: 28; + } else { + return daysIn[month]; + } + }, + + onIncrementDate: function(evt) { + dojo.unimplemented('dojo.widget.DatePicker.onIncrementDate'); + }, + + onIncrementWeek: function(evt) { + evt.stopPropagation(); + this.incrementWeek(evt); + }, + + onIncrementMonth: function(evt) { + evt.stopPropagation(); + this.incrementMonth(evt); + }, + + onIncrementYear: function(evt) { + evt.stopPropagation(); + this.incrementYear(evt); + }, + + setMonthLabel: function(monthIndex) { + this.monthLabelNode.innerHTML = dojo.i18n.calendar.GregorianNames.getNames('months', 'wide', 'standAlone', this.lang)[monthIndex]; + }, + + setYearLabels: function(year) { + this.previousYearLabelNode.innerHTML = year - 1; + this.currentYearLabelNode.innerHTML = year; + this.nextYearLabelNode.innerHTML = year + 1; + }, + + getDateClassName: function(date, monthState) { + var currentClassName = this.classNames[monthState]; + if ((!this.selectedIsUsed) && (date.getDate() == this.date.getDate()) && (date.getMonth() == this.date.getMonth()) && (date.getFullYear() == this.date.getFullYear())) { + currentClassName = this.classNames.selectedDate + " " + currentClassName; + this.selectedIsUsed = 1; + } + if((!this.currentIsUsed) && (date.getDate() == this.today.getDate()) && (date.getMonth() == this.today.getMonth()) && (date.getFullYear() == this.today.getFullYear())) { + currentClassName = currentClassName + " " + this.classNames.currentDate; + this.currentIsUsed = 1; + } + return currentClassName; + }, + + onClick: function(evt) { + dojo.event.browser.stopEvent(evt); + }, + + onSetDate: function(evt) { + var eventTarget = (evt.target.nodeType == dojo.dom.ELEMENT_NODE) ? + evt.target : evt.target.parentNode; + dojo.event.browser.stopEvent(evt); + this.selectedIsUsed = 0; + this.todayIsUsed = 0; + var month = this.firstSaturday.month; + var year = this.firstSaturday.year; + if (dojo.html.hasClass(eventTarget, this.classNames["next"])) { + month = ++month % 12; + // if month is now == 0, add a year + year = (month==0) ? ++year : year; + } else if (dojo.html.hasClass(eventTarget, this.classNames["previous"])) { + month = --month % 12; + // if month is now == 0, add a year + year = (month==11) ? --year : year; + } + this.date = new Date(year, month, eventTarget.innerHTML); + this.setDate(dojo.widget.DatePicker.util.toRfcDate(this.date)); + this.initUI(); + } + } +); + +dojo.widget.DatePicker.util = new function() { + this.toRfcDate = function(jsDate) { + if(!jsDate) { + jsDate = new Date(); + } + // because this is a date picker and not a time picker, we don't return a time + return dojo.date.format(jsDate, "%Y-%m-%d"); + } + + this.fromRfcDate = function(rfcDate) { + // backwards compatible support for use of "any" instead of just not + // including the time + if(rfcDate.indexOf("Tany")!=-1) { + rfcDate = rfcDate.replace("Tany",""); + } + var jsDate = new Date(); + dojo.date.setIso8601(jsDate, rfcDate); + return jsDate; + } + + this.initFirstSaturday = function(month, year) { + if(!month) { + month = this.date.getMonth(); + } + if(!year) { + year = this.date.getFullYear(); + } + var firstOfMonth = new Date(year, month, 1); + return {year: year, month: month, date: 7 - firstOfMonth.getDay()}; + } +} diff --git a/source/web/scripts/ajax/src/widget/DebugConsole.js b/source/web/scripts/ajax/src/widget/DebugConsole.js new file mode 100644 index 0000000000..0a26236d1c --- /dev/null +++ b/source/web/scripts/ajax/src/widget/DebugConsole.js @@ -0,0 +1,16 @@ +dojo.provide("dojo.widget.DebugConsole"); +dojo.require("dojo.widget.Widget"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.FloatingPane"); + +dojo.widget.defineWidget( + "dojo.widget.DebugConsole", + dojo.widget.FloatingPane, +{ + fillInTemplate: function() { + dojo.widget.DebugConsole.superclass.fillInTemplate.apply(this, arguments); + this.containerNode.id = "debugConsoleClientPane"; + djConfig.isDebug = true; + djConfig.debugContainerId = this.containerNode.id; + } +}); diff --git a/source/web/scripts/ajax/src/widget/Dialog.js b/source/web/scripts/ajax/src/widget/Dialog.js new file mode 100644 index 0000000000..09940eed64 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Dialog.js @@ -0,0 +1,282 @@ +dojo.provide("dojo.widget.Dialog"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.ContentPane"); +dojo.require("dojo.event.*"); +dojo.require("dojo.graphics.color"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.html.display"); +dojo.require("dojo.html.iframe"); + +dojo.declare( + "dojo.widget.ModalDialogBase", + null, + { + isContainer: true, + _scrollConnected: false, + + // provide a focusable element or element id if you need to + // work around FF's tendency to send focus into outer space on hide + focusElement: "", + + shared: {bg: null, bgIframe: null}, + bgColor: "black", + bgOpacity: 0.4, + followScroll: true, + _fromTrap: false, + + trapTabs: function(e){ + if(e.target == this.tabStart) { + if(this._fromTrap) { + this._fromTrap = false; + } else { + this._fromTrap = true; + this.tabEnd.focus(); + } + } else if(e.target == this.tabEnd) { + if(this._fromTrap) { + this._fromTrap = false; + } else { + this._fromTrap = true; + this.tabStart.focus(); + } + } + }, + + clearTrap: function(e) { + var _this = this; + setTimeout(function() { + _this._fromTrap = false; + }, 100); + }, + + //if the target mixin class already defined postCreate, + //dojo.widget.ModalDialogBase.prototype.postCreate.call(this) + //should be called in its postCreate() + postCreate: function() { + with(this.domNode.style) { + position = "absolute"; + zIndex = 999; + display = "none"; + overflow = "visible"; + } + var b = dojo.body(); + b.appendChild(this.domNode); + + if(!this.shared.bg){ + this.shared.bg = document.createElement("div"); + this.shared.bg.className = "dialogUnderlay"; + with(this.shared.bg.style) { + position = "absolute"; + left = top = "0px"; + zIndex = 998; + display = "none"; + } + this.setBackgroundColor(this.bgColor); + b.appendChild(this.shared.bg); + + this.shared.bgIframe = new dojo.html.BackgroundIframe(this.shared.bg); + } + }, + + setBackgroundColor: function(color) { + if(arguments.length >= 3) { + color = new dojo.graphics.color.Color(arguments[0], arguments[1], arguments[2]); + } else { + color = new dojo.graphics.color.Color(color); + } + this.shared.bg.style.backgroundColor = color.toString(); + return this.bgColor = color; + }, + + setBackgroundOpacity: function(op) { + if(arguments.length == 0) { op = this.bgOpacity; } + dojo.html.setOpacity(this.shared.bg, op); + try { + this.bgOpacity = dojo.html.getOpacity(this.shared.bg); + } catch (e) { + this.bgOpacity = op; + } + return this.bgOpacity; + }, + + sizeBackground: function() { + if(this.bgOpacity > 0) { + var viewport = dojo.html.getViewport(); + var h = Math.max( + dojo.doc().documentElement.scrollHeight || dojo.body().scrollHeight, + viewport.height); + var w = viewport.width; + this.shared.bg.style.width = w + "px"; + this.shared.bg.style.height = h + "px"; + } + this.shared.bgIframe.onResized(); + }, + + showBackground: function() { + this.sizeBackground(); + if(this.bgOpacity > 0) { + this.shared.bg.style.display = "block"; + } + }, + + placeModalDialog: function() { + var scroll_offset = dojo.html.getScroll().offset; + var viewport_size = dojo.html.getViewport(); + + // find the size of the dialog + var mb = dojo.html.getMarginBox(this.containerNode); + + var x = scroll_offset.x + (viewport_size.width - mb.width)/2; + var y = scroll_offset.y + (viewport_size.height - mb.height)/2; + + with(this.domNode.style) { + left = x + "px"; + top = y + "px"; + } + }, + + //if the target mixin class already defined show, + //dojo.widget.ModalDialogBase.prototype.show.call(this) + //should be called in its show() + show: function() { + this.setBackgroundOpacity(); + this.showBackground(); + + this.inherited("show"); + }, + + //if the target mixin class already defined hide, + //dojo.widget.ModalDialogBase.prototype.hide.call(this) + //should be called in its hide() + hide: function(){ + // workaround for FF focus going into outer space + if (this.focusElement) { + dojo.byId(this.focusElement).focus(); + dojo.byId(this.focusElement).blur(); + } + + this.shared.bg.style.display = "none"; + this.shared.bg.style.width = this.shared.bg.style.height = "1px"; + + this.inherited("hide"); + } + }); + +dojo.widget.defineWidget( + "dojo.widget.Dialog", + [dojo.widget.ContentPane, dojo.widget.ModalDialogBase], + { + templatePath: dojo.uri.dojoUri("src/widget/templates/Dialog.html"), + + anim: null, + blockDuration: 0, + lifetime: 0, + + show: function() { + dojo.widget.ModalDialogBase.prototype.show.call(this); + + if (this.followScroll && !this._scrollConnected){ + this._scrollConnected = true; + dojo.event.connect(window, "onscroll", this, "onScroll"); + } + + if(this.lifetime){ + this.timeRemaining = this.lifetime; + if(!this.blockDuration){ + dojo.event.connect(this.shared.bg, "onclick", this, "hide"); + }else{ + dojo.event.disconnect(this.shared.bg, "onclick", this, "hide"); + } + if(this.timerNode){ + this.timerNode.innerHTML = Math.ceil(this.timeRemaining/1000); + } + if(this.blockDuration && this.closeNode){ + if(this.lifetime > this.blockDuration){ + this.closeNode.style.visibility = "hidden"; + }else{ + this.closeNode.style.display = "none"; + } + } + this.timer = setInterval(dojo.lang.hitch(this, "onTick"), 100); + } + + this.checkSize(); + }, + + onLoad: function(){ + // when href is specified we need to reposition + // the dialog after the data is loaded + this.placeModalDialog(); + }, + + fillInTemplate: function(){ + // dojo.event.connect(this.domNode, "onclick", this, "killEvent"); + }, + + hide: function(){ + dojo.widget.ModalDialogBase.prototype.hide.call(this) + + if(this.timer){ + clearInterval(this.timer); + } + + if (this._scrollConnected){ + this._scrollConnected = false; + dojo.event.disconnect(window, "onscroll", this, "onScroll"); + } + }, + + setTimerNode: function(node){ + this.timerNode = node; + }, + + setCloseControl: function(node) { + this.closeNode = node; + dojo.event.connect(node, "onclick", this, "hide"); + }, + + setShowControl: function(node) { + dojo.event.connect(node, "onclick", this, "show"); + }, + + onTick: function(){ + if(this.timer){ + this.timeRemaining -= 100; + if(this.lifetime - this.timeRemaining >= this.blockDuration){ + dojo.event.connect(this.shared.bg, "onclick", this, "hide"); + if(this.closeNode){ + this.closeNode.style.visibility = "visible"; + } + } + if(!this.timeRemaining){ + clearInterval(this.timer); + this.hide(); + }else if(this.timerNode){ + this.timerNode.innerHTML = Math.ceil(this.timeRemaining/1000); + } + } + }, + + onScroll: function(){ + this.placeModalDialog(); + this.domNode.style.display = "block"; + }, + + // Called when the browser window's size is changed + checkSize: function() { + if(this.isShowing()){ + this.sizeBackground(); + this.placeModalDialog(); + this.domNode.style.display="block"; + this.onResized(); + } + }, + + killEvent: function(evt){ + evt.preventDefault(); + evt.stopPropagation(); + } + + } +); diff --git a/source/web/scripts/ajax/src/widget/DocPane.js b/source/web/scripts/ajax/src/widget/DocPane.js new file mode 100644 index 0000000000..245c0ae093 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/DocPane.js @@ -0,0 +1,372 @@ +dojo.provide("dojo.widget.DocPane"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.io.*"); +dojo.require("dojo.event.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.Editor2"); +dojo.require("dojo.widget.Dialog"); + +dojo.require("dojo.html.common"); +dojo.require("dojo.html.display"); + +dojo.widget.defineWidget( + "dojo.widget.DocPane", + dojo.widget.HtmlWidget, + function(){ + dojo.event.topic.subscribe("/docs/function/results", this, "onDocResults"); + dojo.event.topic.subscribe("/docs/package/results", this, "onPkgResults"); + dojo.event.topic.subscribe("/docs/function/detail", this, "onDocSelectFunction"); + }, + { + // Template parameters + dialog: null, + dialogBg: null, + dialogFg: null, + logIn: null, + edit: null, + save: null, + cancel: null, + detail: null, + result: null, + packag: null, + fn: null, + fnLink: null, + count: null, + row: null, + summary: null, + description: null, + variables: null, + vRow: null, + vLink: null, + vDesc: null, + methods: null, + mRow: null, + mLink: null, + mDesc: null, + requires: null, + rRow: null, + rRow2: null, + rH3: null, + rLink: null, + parameters: null, + pRow: null, + pLink: null, + pDesc: null, + pOpt: null, + pType: null, + sType: null, + sName: null, + sParams: null, + sPType: null, + sPTypeSave: null, + sPName: null, + sPNameSave: null, + pkgDescription: null, + + // Fields and methods + _appends: [], + templatePath: dojo.uri.dojoUri("src/widget/templates/DocPane.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/DocPane.css"), + isContainer: true, + + fillInTemplate: function(){ + this.requires = dojo.html.removeNode(this.requires); + this.rRow.style.display = "none"; + this.rRow2.style.display = "none"; + + this.methods = dojo.html.removeNode(this.methods); + this.mRow.style.display = "none"; + + this.dialog = dojo.widget.createWidget("dialog", {}, this.dialog); + this.dialog.setCloseControl(this.cancel); + dojo.html.setOpacity(this.dialogBg, 0.8); + dojo.html.setOpacity(this.dialogFg, 1); + + dojo.event.connect(this.edit, "onclick", dojo.lang.hitch(this, function(){ + if(!this._isLoggedIn){ + this.dialog.show(); + } + })); + dojo.event.connect(this.logIn, "onclick", this, "_logIn"); + dojo.event.connect(this.save, "onclick", this, "_save"); + dojo.event.connect(dojo.docs, "logInSuccess", this, "_loggedIn"); + + /* + this.pkgDescription = dojo.widget.createWidget("editor2", { + toolbarAlwaysVisible: true + }, this.pkgDescription); + */ + + this.homeSave = this.containerNode.cloneNode(true); + this.detailSave = dojo.html.removeNode(this.detail); + this.resultSave = dojo.html.removeNode(this.result); + this.packageSave = dojo.html.removeNode(this.packag); + this.results = dojo.html.removeNode(this.results); + this.rowParent = this.row.parentNode; + this.rowSave = dojo.html.removeNode(this.row); + this.vParent = this.vRow.parentNode; + this.vSave = dojo.html.removeNode(this.vRow); + this.pParent = this.pRow.parentNode; + this.pSave = dojo.html.removeNode(this.pRow); + this.sPTypeSave = dojo.html.removeNode(this.sPType); + this.sPNameSave = dojo.html.removeNode(this.sPName); + this.navSave = dojo.html.removeNode(this.nav); + }, + + _logIn: function(){ + dojo.docs.setUserName(this.userName.value); + dojo.docs.setPassword(this.password.value); + }, + + _loggedIn: function(){ + this._isLoggedIn = true; + this.dialog.hide(); + this.pkgEditor = dojo.widget.createWidget("editor2", { + toolbarAlwaysVisible: true + }, this.pkgDescription); + }, + + _save: function(){ + if(this.pkgEditor){ + dojo.docs.savePackage(this._pkgPath, { + description: this.pkgEditor.getEditorContent() + }); + } + }, + + onDocSelectFunction: function(message){ + dojo.debug("onDocSelectFunction()"); + for(var key in message){ + dojo.debug(key + ": " + dojo.json.serialize(message[key])); + } + var meta = message.meta; + if(meta){ + var variables = meta.variables; + var this_variables = meta.this_variables; + var child_variables = meta.child_variables; + var parameters = meta.parameters; + } + var doc = message.doc; + dojo.debug(dojo.json.serialize(doc)); + + var appends = this._appends; + dojo.html.removeChildren(this.domNode); + this.fn.innerHTML = message.name; + + this.variables.style.display = "block"; + var all = []; + if(variables){ + all = variables; + } + if(this_variables){ + all = all.concat(this_variables); + } + if(child_variables){ + all = all.concat(child_variables); + } + if(!all.length){ + this.variables.style.display = "none"; + }else{ + for(var i = 0, one; one = all[i]; i++){ + this.vLink.innerHTML = one; + this.vDesc.parentNode.style.display = "none"; + appends.push(this.vParent.appendChild(this.vSave.cloneNode(true))); + } + } + + this.sParams.innerHTML = ""; + var first = true; + for(var param in parameters){ + var paramType = parameters[param].type; + var paramSummary = parameters[param].summary; + var paramName = param; + this.parameters.style.display = "block"; + this.pLink.innerHTML = paramName; + this.pOpt.style.display = "none"; + if(parameters[param].opt){ + this.pOpt.style.display = "inline"; + } + this.pType.parentNode.style.display = "none"; + if(parameters[param][0]){ + this.pType.parentNode.style.display = "inline"; + this.pType.innerHTML = paramType; + } + this.pDesc.parentNode.style.display = "none"; + if(paramSummary){ + this.pDesc.parentNode.style.display = "inline"; + this.pDesc.innerHTML = paramSummary; + } + appends.push(this.pParent.appendChild(this.pSave.cloneNode(true))); + + if(!first) { + this.sParams.appendChild(document.createTextNode(", ")); + } + first = false; + if(paramType){ + dojo.debug(this.sPTypeSave); + this.sPTypeSave.innerHTML = paramType; + this.sParams.appendChild(this.sPTypeSave.cloneNode(true)); + this.sParams.appendChild(document.createTextNode(" ")); + } + dojo.debug(this.sPNameSave); + this.sPNameSave.innerHTML = paramName; + this.sParams.appendChild(this.sPNameSave.cloneNode(true)) + } + + if(message.returns){ + this.sType.innerHTML = message.returns; + }else{ + this.sType.innerHTML = "void"; + } + + this.sName.innerHTML = message.name; + + this.domNode.appendChild(this.navSave); + this.domNode.appendChild(this.detailSave.cloneNode(true)); + + for(var i = 0, append; append = appends[i]; i++){ + dojo.html.removeNode(append); + } + }, + + onPkgResults: function(/*Object*/ results){ + if(this.pkgEditor){ + this.pkgEditor.close(true); + dojo.debug(this.pkgDescription); + } + var methods = results.methods; + var requires = results.requires; + var description = results.description; + this._pkgPath = results.path; + var requireLinks = []; + var appends = this._appends; + while(appends.length){ + dojo.html.removeNode(appends.shift()); + } + + dojo.html.removeChildren(this.domNode); + + this.pkg.innerHTML = results.pkg; + + var hasRequires = false; + for(var env in requires){ + hasRequires = true; + + this.rH3.style.display = "none"; + if(env != "common"){ + this.rH3.style.display = ""; + this.rH3.innerHTML = env; + } + + for(var i = 0, require; require = requires[env][i]; i++){ + requireLinks.push({ + name: require + }); + this.rLink.innerHTML = require; + this.rLink.href = "#" + require; + var rRow2 = this.rRow2.parentNode.insertBefore(this.rRow2.cloneNode(true), this.rRow2); + rRow2.style.display = ""; + appends.push(rRow2); + } + var rRow = this.rRow.parentNode.insertBefore(this.rRow.cloneNode(true), this.rRow); + rRow.style.display = ""; + appends.push(rRow); + } + + if(hasRequires){ + appends.push(this.packageSave.appendChild(this.requires.cloneNode(true))); + } + + if(results.size){ + for(var i = 0, method; method = methods[i]; i++){ + this.mLink.innerHTML = method.name; + this.mLink.href = "#" + method.name; + this.mDesc.parentNode.style.display = "none"; + if(method.summary){ + this.mDesc.parentNode.style.display = "inline"; + this.mDesc.innerHTML = method.summary; + } + var mRow = this.mRow.parentNode.insertBefore(this.mRow.cloneNode(true), this.mRow); + mRow.style.display = ""; + appends.push(mRow); + } + appends.push(this.packageSave.appendChild(this.methods.cloneNode(true))); + } + + this.domNode.appendChild(this.packageSave); + + /* + dojo.debug(description); + function fillContent(){ + this.pkgDescription.replaceEditorContent(description); + this.pkgDescription._updateHeight(); + } + if(this.pkgDescription.isLoaded){ + fillContent(); + }else{ + dojo.event.connect(this.pkgDescription, "onLoad", dojo.lang.hitch(this, fillContent)); + } + */ + this.pkgDescription.innerHTML = description; + + function makeSelect(fOrP, x){ + return function(e) { + dojo.event.topic.publish("/docs/" + fOrP + "/select", x); + } + } + + var as = this.domNode.getElementsByTagName("a"); + for(var i = 0, a; a = as[i]; i++){ + if(a.className == "docMLink"){ + dojo.event.connect(a, "onclick", makeSelect("function", methods[i])); + }else if(a.className == "docRLink"){ + dojo.event.connect(a, "onclick", makeSelect("package", requireLinks[i])); + } + } + }, + + onDocResults: function(message){ + var results = message.docResults; + + if(results.length == 1){ + dojo.event.topic.publish("/docs/function/select", results[0]); + return; + } + + dojo.html.removeChildren(this.domNode); + + this.count.innerHTML = results.length; + var appends = []; + for(var i = 0, row; row = results[i]; i++){ + this.fnLink.innerHTML = row.name; + this.fnLink.href = "#" + row.name; + if(row.id){ + this.fnLink.href = this.fnLink.href + "," + row.id; + } + this.summary.parentNode.style.display = "none"; + if(row.summary){ + this.summary.parentNode.style.display = "inline"; + this.summary.innerHTML = row.summary; + } + appends.push(this.rowParent.appendChild(this.rowSave.cloneNode(true))); + } + + function makeSelect(x){ + return function(e) { + dojo.event.topic.publish("/docs/function/select", x); + } + } + + this.domNode.appendChild(this.resultSave.cloneNode(true)); + var as = this.domNode.getElementsByTagName("a"); + for(var i = 0, a; a = as[i]; i++){ + dojo.event.connect(a, "onclick", makeSelect(results[i])); + } + + for(var i = 0, append; append = appends[i]; i++){ + this.rowParent.removeChild(append); + } + } + } +); diff --git a/source/web/scripts/ajax/src/widget/DomWidget.js b/source/web/scripts/ajax/src/widget/DomWidget.js new file mode 100644 index 0000000000..5727593d68 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/DomWidget.js @@ -0,0 +1,617 @@ +dojo.provide("dojo.widget.DomWidget"); + +dojo.require("dojo.event.*"); +dojo.require("dojo.widget.Widget"); +dojo.require("dojo.dom"); +dojo.require("dojo.html.style"); +dojo.require("dojo.xml.Parse"); +dojo.require("dojo.uri.*"); +dojo.require("dojo.lang.func"); +dojo.require("dojo.lang.extras"); + +dojo.widget._cssFiles = {}; +dojo.widget._cssStrings = {}; +dojo.widget._templateCache = {}; + +dojo.widget.defaultStrings = { + dojoRoot: dojo.hostenv.getBaseScriptUri(), + baseScriptUri: dojo.hostenv.getBaseScriptUri() +}; + +dojo.widget.buildFromTemplate = function() { + dojo.lang.forward("fillFromTemplateCache"); +} + +// static method to build from a template w/ or w/o a real widget in place +dojo.widget.fillFromTemplateCache = function(obj, templatePath, templateString, avoidCache){ + // dojo.debug("avoidCache:", avoidCache); + var tpath = templatePath || obj.templatePath; + + // DEPRECATED: use Uri objects, not strings + if (tpath && !(tpath instanceof dojo.uri.Uri)) { + tpath = dojo.uri.dojoUri(tpath); + dojo.deprecated("templatePath should be of type dojo.uri.Uri", null, "0.4"); + } + + var tmplts = dojo.widget._templateCache; + if(!obj["widgetType"]) { // don't have a real template here + do { + var dummyName = "__dummyTemplate__" + dojo.widget._templateCache.dummyCount++; + } while(tmplts[dummyName]); + obj.widgetType = dummyName; + } + var wt = obj.widgetType; + + var ts = tmplts[wt]; + if(!ts){ + tmplts[wt] = { "string": null, "node": null }; + if(avoidCache){ + ts = {}; + }else{ + ts = tmplts[wt]; + } + } + if((!obj.templateString)&&(!avoidCache)){ + obj.templateString = templateString || ts["string"]; + } + if((!obj.templateNode)&&(!avoidCache)){ + obj.templateNode = ts["node"]; + } + if((!obj.templateNode)&&(!obj.templateString)&&(tpath)){ + // fetch a text fragment and assign it to templateString + // NOTE: we rely on blocking IO here! + var tstring = dojo.hostenv.getText(tpath); + if(tstring){ + // strip <?xml ...?> declarations so that external SVG and XML + // documents can be added to a document without worry + tstring = tstring.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, ""); + var matches = tstring.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im); + if(matches){ + tstring = matches[1]; + } + }else{ + tstring = ""; + } + obj.templateString = tstring; + if(!avoidCache){ + tmplts[wt]["string"] = tstring; + } + } + if((!ts["string"])&&(!avoidCache)){ + ts.string = obj.templateString; + } +} +dojo.widget._templateCache.dummyCount = 0; + +dojo.widget.attachProperties = ["dojoAttachPoint", "id"]; +dojo.widget.eventAttachProperty = "dojoAttachEvent"; +dojo.widget.onBuildProperty = "dojoOnBuild"; +dojo.widget.waiNames = ["waiRole", "waiState"]; +dojo.widget.wai = { + waiRole: { name: "waiRole", + namespace: "http://www.w3.org/TR/xhtml2", + alias: "x2", + prefix: "wairole:" + }, + waiState: { name: "waiState", + namespace: "http://www.w3.org/2005/07/aaa" , + alias: "aaa", + prefix: "" + }, + setAttr: function(node, ns, attr, value){ + if(dojo.render.html.ie){ + node.setAttribute(this[ns].alias+":"+ attr, this[ns].prefix+value); + }else{ + node.setAttributeNS(this[ns].namespace, attr, this[ns].prefix+value); + } + }, + + getAttr: function(node, ns, attr){ + if(dojo.render.html.ie){ + return node.getAttribute(this[ns].alias+":"+attr); + }else{ + return node.getAttributeNS(this[ns].namespace, attr); + } + } +}; + +dojo.widget.attachTemplateNodes = function(rootNode, targetObj, events){ + // FIXME: this method is still taking WAAAY too long. We need ways of optimizing: + // a.) what we are looking for on each node + // b.) the nodes that are subject to interrogation (use xpath instead?) + // c.) how expensive event assignment is (less eval(), more connect()) + // var start = new Date(); + var elementNodeType = dojo.dom.ELEMENT_NODE; + + function trim(str){ + return str.replace(/^\s+|\s+$/g, ""); + } + + if(!rootNode){ + rootNode = targetObj.domNode; + } + + if(rootNode.nodeType != elementNodeType){ + return; + } + // alert(events.length); + + var nodes = rootNode.all || rootNode.getElementsByTagName("*"); + var _this = targetObj; + for(var x=-1; x<nodes.length; x++){ + var baseNode = (x == -1) ? rootNode : nodes[x]; + // FIXME: is this going to have capitalization problems? Could use getAttribute(name, 0); to get attributes case-insensitve + var attachPoint = []; + for(var y=0; y<this.attachProperties.length; y++){ + var tmpAttachPoint = baseNode.getAttribute(this.attachProperties[y]); + if(tmpAttachPoint){ + attachPoint = tmpAttachPoint.split(";"); + for(var z=0; z<attachPoint.length; z++){ + if(dojo.lang.isArray(targetObj[attachPoint[z]])){ + targetObj[attachPoint[z]].push(baseNode); + }else{ + targetObj[attachPoint[z]]=baseNode; + } + } + break; + } + } + // continue; + + // FIXME: we need to put this into some kind of lookup structure + // instead of direct assignment + var tmpltPoint = baseNode.getAttribute(this.templateProperty); + if(tmpltPoint){ + targetObj[tmpltPoint]=baseNode; + } + + dojo.lang.forEach(dojo.widget.waiNames, function(name){ + var wai = dojo.widget.wai[name]; + var val = baseNode.getAttribute(wai.name); + if(val){ + if(val.indexOf('-') == -1){ + dojo.widget.wai.setAttr(baseNode, wai.name, "role", val); + }else{ + // this is a state-value pair + var statePair = val.split('-'); + dojo.widget.wai.setAttr(baseNode, wai.name, statePair[0], statePair[1]); + } + } + }, this); + + var attachEvent = baseNode.getAttribute(this.eventAttachProperty); + if(attachEvent){ + // NOTE: we want to support attributes that have the form + // "domEvent: nativeEvent; ..." + var evts = attachEvent.split(";"); + for(var y=0; y<evts.length; y++){ + if((!evts[y])||(!evts[y].length)){ continue; } + var thisFunc = null; + var tevt = trim(evts[y]); + if(evts[y].indexOf(":") >= 0){ + // oh, if only JS had tuple assignment + var funcNameArr = tevt.split(":"); + tevt = trim(funcNameArr[0]); + thisFunc = trim(funcNameArr[1]); + } + if(!thisFunc){ + thisFunc = tevt; + } + + var tf = function(){ + var ntf = new String(thisFunc); + return function(evt){ + if(_this[ntf]){ + _this[ntf](dojo.event.browser.fixEvent(evt, this)); + } + }; + }(); + dojo.event.browser.addListener(baseNode, tevt, tf, false, true); + // dojo.event.browser.addListener(baseNode, tevt, dojo.lang.hitch(_this, thisFunc)); + } + } + + for(var y=0; y<events.length; y++){ + //alert(events[x]); + var evtVal = baseNode.getAttribute(events[y]); + if((evtVal)&&(evtVal.length)){ + var thisFunc = null; + var domEvt = events[y].substr(4); // clober the "dojo" prefix + thisFunc = trim(evtVal); + var funcs = [thisFunc]; + if(thisFunc.indexOf(";")>=0){ + funcs = dojo.lang.map(thisFunc.split(";"), trim); + } + for(var z=0; z<funcs.length; z++){ + if(!funcs[z].length){ continue; } + var tf = function(){ + var ntf = new String(funcs[z]); + return function(evt){ + if(_this[ntf]){ + _this[ntf](dojo.event.browser.fixEvent(evt, this)); + } + } + }(); + dojo.event.browser.addListener(baseNode, domEvt, tf, false, true); + // dojo.event.browser.addListener(baseNode, domEvt, dojo.lang.hitch(_this, funcs[z])); + } + } + } + + var onBuild = baseNode.getAttribute(this.onBuildProperty); + if(onBuild){ + eval("var node = baseNode; var widget = targetObj; "+onBuild); + } + } + +} + +dojo.widget.getDojoEventsFromStr = function(str){ + // var lstr = str.toLowerCase(); + var re = /(dojoOn([a-z]+)(\s?))=/gi; + var evts = str ? str.match(re)||[] : []; + var ret = []; + var lem = {}; + for(var x=0; x<evts.length; x++){ + if(evts[x].legth < 1){ continue; } + var cm = evts[x].replace(/\s/, ""); + cm = (cm.slice(0, cm.length-1)); + if(!lem[cm]){ + lem[cm] = true; + ret.push(cm); + } + } + return ret; +} + +/* +dojo.widget.buildAndAttachTemplate = function(obj, templatePath, templateCssPath, templateString, targetObj) { + this.buildFromTemplate(obj, templatePath, templateCssPath, templateString); + var node = dojo.dom.createNodesFromText(obj.templateString, true)[0]; + this.attachTemplateNodes(node, targetObj||obj, dojo.widget.getDojoEventsFromStr(templateString)); + return node; +} +*/ + +dojo.declare("dojo.widget.DomWidget", dojo.widget.Widget, + function() { + if((arguments.length>0)&&(typeof arguments[0] == "object")){ + this.create(arguments[0]); + } + }, +{ + templateNode: null, + templateString: null, + templateCssString: null, + preventClobber: false, + domNode: null, // this is our visible representation of the widget! + containerNode: null, // holds child elements + + // Process the given child widget, inserting it's dom node as a child of our dom node + // FIXME: should we support addition at an index in the children arr and + // order the display accordingly? Right now we always append. + addChild: function(widget, overrideContainerNode, pos, ref, insertIndex){ + if(!this.isContainer){ // we aren't allowed to contain other widgets, it seems + dojo.debug("dojo.widget.DomWidget.addChild() attempted on non-container widget"); + return null; + }else{ + if(insertIndex == undefined){ + insertIndex = this.children.length; + } + this.addWidgetAsDirectChild(widget, overrideContainerNode, pos, ref, insertIndex); + this.registerChild(widget, insertIndex); + } + return widget; + }, + + addWidgetAsDirectChild: function(widget, overrideContainerNode, pos, ref, insertIndex){ + if((!this.containerNode)&&(!overrideContainerNode)){ + this.containerNode = this.domNode; + } + var cn = (overrideContainerNode) ? overrideContainerNode : this.containerNode; + if(!pos){ pos = "after"; } + if(!ref){ + if(!cn){ cn = dojo.body(); } + ref = cn.lastChild; + } + if(!insertIndex) { insertIndex = 0; } + widget.domNode.setAttribute("dojoinsertionindex", insertIndex); + + // insert the child widget domNode directly underneath my domNode, in the + // specified position (by default, append to end) + if(!ref){ + cn.appendChild(widget.domNode); + }else{ + // FIXME: was this meant to be the (ugly hack) way to support insert @ index? + //dojo.dom[pos](widget.domNode, ref, insertIndex); + + // CAL: this appears to be the intended way to insert a node at a given position... + if (pos == 'insertAtIndex'){ + // dojo.debug("idx:", insertIndex, "isLast:", ref === cn.lastChild); + dojo.dom.insertAtIndex(widget.domNode, ref.parentNode, insertIndex); + }else{ + // dojo.debug("pos:", pos, "isLast:", ref === cn.lastChild); + if((pos == "after")&&(ref === cn.lastChild)){ + cn.appendChild(widget.domNode); + }else{ + dojo.dom.insertAtPosition(widget.domNode, cn, pos); + } + } + } + }, + + // Record that given widget descends from me + registerChild: function(widget, insertionIndex){ + + // we need to insert the child at the right point in the parent's + // 'children' array, based on the insertionIndex + + widget.dojoInsertionIndex = insertionIndex; + + var idx = -1; + for(var i=0; i<this.children.length; i++){ + if (this.children[i].dojoInsertionIndex < insertionIndex){ + idx = i; + } + } + + this.children.splice(idx+1, 0, widget); + + widget.parent = this; + widget.addedTo(this, idx+1); + + // If this widget was created programatically, then it was erroneously added + // to dojo.widget.manager.topWidgets. Fix that here. + delete dojo.widget.manager.topWidgets[widget.widgetId]; + }, + + removeChild: function(widget){ + // detach child domNode from parent domNode + dojo.dom.removeNode(widget.domNode); + + // remove child widget from parent widget + return dojo.widget.DomWidget.superclass.removeChild.call(this, widget); + }, + + getFragNodeRef: function(frag){ + if(!frag || !frag[this.namespace+":"+this.widgetType.toLowerCase()]){ + dojo.raise("Error: no frag for widget type " + this.widgetType + + " with namespace "+this.namespace + ", id " + this.widgetId + + " (maybe a widget has set it's type incorrectly)"); + } + return frag ? frag[this.namespace+":"+this.widgetType.toLowerCase()]["nodeRef"] : null; + }, + + // Replace source domNode with generated dom structure, and register + // widget with parent. + postInitialize: function(args, frag, parentComp){ + + //dojo.profile.start(this.widgetType + " postInitialize"); + + var sourceNodeRef = this.getFragNodeRef(frag); + // Stick my generated dom into the output tree + //alert(this.widgetId + ": replacing " + sourceNodeRef + " with " + this.domNode.innerHTML); + if (parentComp && (parentComp.snarfChildDomOutput || !sourceNodeRef)){ + // Add my generated dom as a direct child of my parent widget + // This is important for generated widgets, and also cases where I am generating an + // <li> node that can't be inserted back into the original DOM tree + parentComp.addWidgetAsDirectChild(this, "", "insertAtIndex", "", args["dojoinsertionindex"], sourceNodeRef); + } else if (sourceNodeRef){ + // Do in-place replacement of the my source node with my generated dom + if(this.domNode && (this.domNode !== sourceNodeRef)){ + var oldNode = sourceNodeRef.parentNode.replaceChild(this.domNode, sourceNodeRef); + } + } + + // Register myself with my parent, or with the widget manager if + // I have no parent + // TODO: the code below erroneously adds all programatically generated widgets + // to topWidgets (since we don't know who the parent is until after creation finishes) + if ( parentComp ) { + parentComp.registerChild(this, args.dojoinsertionindex); + } else { + dojo.widget.manager.topWidgets[this.widgetId]=this; + } + + + //dojo.profile.end(this.widgetType + " postInitialize"); + + // Expand my children widgets + /* dojoDontFollow is important for a very special case + * basically if you have a widget that you instantiate from script + * and that widget is a container, and it contains a reference to a parent + * instance, the parser will start recursively parsing until the browser + * complains. So the solution is to set an initialization property of + * dojoDontFollow: true and then it won't recurse where it shouldn't + */ + if(this.isContainer && !frag["dojoDontFollow"]){ + //alert("recurse from " + this.widgetId); + // build any sub-components with us as the parent + var fragParser = dojo.widget.getParser(); + fragParser.createSubComponents(frag, this); + } + }, + + // method over-ride + buildRendering: function(args, frag){ + // DOM widgets construct themselves from a template + var ts = dojo.widget._templateCache[this.widgetType]; + + // Handle style for this widget here, as even if templatePath + // is not set, style specified by templateCssString or templateCssPath + // should be applied. templateCssString has higher priority + // than templateCssPath + if(args["templatecsspath"]){ + args["templateCssPath"] = args["templatecsspath"]; + } + var cpath = args["templateCssPath"] || this.templateCssPath; + // DEPRECATED: use Uri objects, not strings + if (cpath && !(cpath instanceof dojo.uri.Uri)) { + cpath = dojo.uri.dojoUri(cpath); + dojo.deprecated("templateCssPath should be of type dojo.uri.Uri", null, "0.4"); + } + if(cpath && !dojo.widget._cssFiles[cpath.toString()]){ + if((!this.templateCssString)&&(cpath)){ + this.templateCssString = dojo.hostenv.getText(cpath); + this.templateCssPath = null; + } + dojo.widget._cssFiles[cpath.toString()] = true; + } + + if((this["templateCssString"])&&(!this.templateCssString["loaded"])){ + dojo.html.insertCssText(this.templateCssString, null, cpath); + if(!this.templateCssString){ this.templateCssString = ""; } + this.templateCssString.loaded = true; + } + if( + (!this.preventClobber)&&( + (this.templatePath)|| + (this.templateNode)|| + ( + (this["templateString"])&&(this.templateString.length) + )|| + ( + (typeof ts != "undefined")&&( (ts["string"])||(ts["node"]) ) + ) + ) + ){ + // if it looks like we can build the thing from a template, do it! + this.buildFromTemplate(args, frag); + }else{ + // otherwise, assign the DOM node that was the source of the widget + // parsing to be the root node + this.domNode = this.getFragNodeRef(frag); + } + this.fillInTemplate(args, frag); // this is where individual widgets + // will handle population of data + // from properties, remote data + // sets, etc. + }, + + buildFromTemplate: function(args, frag){ + // var start = new Date(); + // copy template properties if they're already set in the templates object + // dojo.debug("buildFromTemplate:", this); + var avoidCache = false; + if(args["templatepath"]){ + avoidCache = true; + args["templatePath"] = args["templatepath"]; + } + dojo.widget.fillFromTemplateCache( this, + args["templatePath"], + null, + avoidCache); + var ts = dojo.widget._templateCache[this.widgetType]; + if((ts)&&(!avoidCache)){ + if(!this.templateString.length){ + this.templateString = ts["string"]; + } + if(!this.templateNode){ + this.templateNode = ts["node"]; + } + } + var matches = false; + var node = null; + // var tstr = new String(this.templateString); + var tstr = this.templateString; + // attempt to clone a template node, if there is one + if((!this.templateNode)&&(this.templateString)){ + matches = this.templateString.match(/\$\{([^\}]+)\}/g); + if(matches) { + // if we do property replacement, don't create a templateNode + // to clone from. + var hash = this.strings || {}; + // FIXME: should this hash of default replacements be cached in + // templateString? + for(var key in dojo.widget.defaultStrings) { + if(dojo.lang.isUndefined(hash[key])) { + hash[key] = dojo.widget.defaultStrings[key]; + } + } + // FIXME: this is a lot of string munging. Can we make it faster? + for(var i = 0; i < matches.length; i++) { + var key = matches[i]; + key = key.substring(2, key.length-1); + var kval = (key.substring(0, 5) == "this.") ? dojo.lang.getObjPathValue(key.substring(5), this) : hash[key]; + var value; + if((kval)||(dojo.lang.isString(kval))){ + value = (dojo.lang.isFunction(kval)) ? kval.call(this, key, this.templateString) : kval; + tstr = tstr.replace(matches[i], value); + } + } + }else{ + // otherwise, we are required to instantiate a copy of the template + // string if one is provided. + + // FIXME: need to be able to distinguish here what should be done + // or provide a generic interface across all DOM implementations + // FIMXE: this breaks if the template has whitespace as its first + // characters + // node = this.createNodesFromText(this.templateString, true); + // this.templateNode = node[0].cloneNode(true); // we're optimistic here + this.templateNode = this.createNodesFromText(this.templateString, true)[0]; + if(!avoidCache){ + ts.node = this.templateNode; + } + } + } + if((!this.templateNode)&&(!matches)){ + dojo.debug("DomWidget.buildFromTemplate: could not create template"); + return false; + }else if(!matches){ + node = this.templateNode.cloneNode(true); + if(!node){ return false; } + }else{ + node = this.createNodesFromText(tstr, true)[0]; + } + + // recurse through the node, looking for, and attaching to, our + // attachment points which should be defined on the template node. + + this.domNode = node; + // dojo.profile.start("attachTemplateNodes"); + this.attachTemplateNodes(this.domNode, this); + // dojo.profile.end("attachTemplateNodes"); + + // relocate source contents to templated container node + // this.containerNode must be able to receive children, or exceptions will be thrown + if (this.isContainer && this.containerNode){ + var src = this.getFragNodeRef(frag); + if (src){ + dojo.dom.moveChildren(src, this.containerNode); + } + } + }, + + attachTemplateNodes: function(baseNode, targetObj){ + if(!targetObj){ targetObj = this; } + return dojo.widget.attachTemplateNodes(baseNode, targetObj, + dojo.widget.getDojoEventsFromStr(this.templateString)); + }, + + fillInTemplate: function(){ + // dojo.unimplemented("dojo.widget.DomWidget.fillInTemplate"); + }, + + // method over-ride + destroyRendering: function(){ + try{ + delete this.domNode; + }catch(e){ /* squelch! */ } + }, + + // FIXME: method over-ride + cleanUp: function(){}, + + getContainerHeight: function(){ + dojo.unimplemented("dojo.widget.DomWidget.getContainerHeight"); + }, + + getContainerWidth: function(){ + dojo.unimplemented("dojo.widget.DomWidget.getContainerWidth"); + }, + + createNodesFromText: function(){ + dojo.unimplemented("dojo.widget.DomWidget.createNodesFromText"); + } +}); diff --git a/source/web/scripts/ajax/src/widget/DropdownButton.js b/source/web/scripts/ajax/src/widget/DropdownButton.js new file mode 100644 index 0000000000..e81fe99762 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/DropdownButton.js @@ -0,0 +1,29 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.provide("dojo.widget.DropdownButton"); + +dojo.deprecated("dojo.widget.DropdownButton", "use dojo.widget.ComboButton", "0.4"); + +// Draws a button with a down arrow; +// when you press the down arrow something appears (usually a menu) + +dojo.require("dojo.widget.*"); + +dojo.widget.tags.addParseTreeHandler("dojo:dropdownbutton"); + +dojo.widget.DropdownButton = function(){ + dojo.widget.Widget.call(this); + + this.widgetType = "DropdownButton"; +} +dojo.inherits(dojo.widget.DropdownButton, dojo.widget.Widget); + +dojo.requireAfterIf("html", "dojo.widget.html.DropdownButton"); diff --git a/source/web/scripts/ajax/src/widget/DropdownContainer.js b/source/web/scripts/ajax/src/widget/DropdownContainer.js new file mode 100644 index 0000000000..396c0e67a7 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/DropdownContainer.js @@ -0,0 +1,62 @@ +dojo.provide("dojo.widget.DropdownContainer"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.Menu2"); // for PopupContainer +dojo.require("dojo.event.*"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.html.display"); +dojo.require("dojo.html.iframe"); +dojo.require("dojo.html.util"); + +dojo.widget.defineWidget( + "dojo.widget.DropdownContainer", + dojo.widget.HtmlWidget, + { + inputWidth: "7em", + inputId: "", + inputName: "", + iconURL: dojo.uri.dojoUri("src/widget/templates/images/combo_box_arrow.png"), + iconAlt: "", + + inputNode: null, + buttonNode: null, + containerNode: null, + + containerToggle: "plain", + containerToggleDuration: 150, + containerAnimInProgress: false, + + templateString: '<span style="white-space:nowrap"><input type="text" value="" style="vertical-align:middle;" dojoAttachPoint="inputNode" autocomplete="off" /> <img src="${this.iconURL}" alt="${this.iconAlt}" dojoAttachEvent="onclick: onIconClick;" dojoAttachPoint="buttonNode" style="vertical-align:middle; cursor:pointer; cursor:hand;" /></span>', + templateCssPath: "", + + fillInTemplate: function(args, frag){ + var source = this.getFragNodeRef(frag); + + this.popup = dojo.widget.createWidget("PopupContainer", {toggle: this.containerToggle}); + + this.containerNode = this.popup.domNode; + + this.domNode.appendChild(this.popup.domNode); + + if(this.inputId){ this.inputNode.id = this.inputId; } + if(this.inputName){ this.inputNode.name = this.inputName; } + this.inputNode.style.width = this.inputWidth; + + dojo.event.connect(this.inputNode, "onchange", this, "onInputChange"); + }, + + onIconClick: function(evt){ + if(!this.popup.isShowingNow){ + this.popup.open(this.inputNode, this, this.buttonNode); + }else{ + this.popup.close(); + } + }, + + hideContainer: function(){ + this.popup.close(); + }, + + onInputChange: function(){} + } +); diff --git a/source/web/scripts/ajax/src/widget/DropdownDatePicker.js b/source/web/scripts/ajax/src/widget/DropdownDatePicker.js new file mode 100644 index 0000000000..db3e5c3bbb --- /dev/null +++ b/source/web/scripts/ajax/src/widget/DropdownDatePicker.js @@ -0,0 +1,55 @@ +dojo.provide("dojo.widget.DropdownDatePicker"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.DropdownContainer"); +dojo.require("dojo.widget.DatePicker"); +dojo.require("dojo.event.*"); +dojo.require("dojo.html.*"); + +dojo.widget.defineWidget( + "dojo.widget.DropdownDatePicker", + dojo.widget.DropdownContainer, + { + iconURL: dojo.uri.dojoUri("src/widget/templates/images/dateIcon.gif"), + iconAlt: "Select a Date", + zIndex: "10", + datePicker: null, + + dateFormat: "%m/%d/%Y", + date: null, + + fillInTemplate: function(args, frag){ + dojo.widget.DropdownDatePicker.superclass.fillInTemplate.call(this, args, frag); + var source = this.getFragNodeRef(frag); + + if(args.date){ this.date = new Date(args.date); } + + var dpNode = document.createElement("div"); + this.containerNode.appendChild(dpNode); + + var dateProps = { widgetContainerId: this.widgetId }; + if(this.date){ + dateProps["date"] = this.date; + dateProps["storedDate"] = dojo.widget.DatePicker.util.toRfcDate(this.date); + this.inputNode.value = dojo.date.format(this.date, this.dateFormat); + } + this.datePicker = dojo.widget.createWidget("DatePicker", dateProps, dpNode); + dojo.event.connect(this.datePicker, "onSetDate", this, "onSetDate"); + this.containerNode.style.zIndex = this.zIndex; + this.containerNode.style.backgroundColor = "transparent"; + }, + + onSetDate: function(){ + this.inputNode.value = dojo.date.format(this.datePicker.date, this.dateFormat); + this.hideContainer(); + }, + + onInputChange: function(){ + var tmp = new Date(this.inputNode.value); + this.datePicker.date = tmp; + this.datePicker.setDate(dojo.widget.DatePicker.util.toRfcDate(tmp)); + this.datePicker.initData(); + this.datePicker.initUI(); + } + } +); diff --git a/source/web/scripts/ajax/src/widget/Editor.js b/source/web/scripts/ajax/src/widget/Editor.js new file mode 100644 index 0000000000..0a140949ae --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Editor.js @@ -0,0 +1,523 @@ +/* TODO: + * - font selector + * - test, bug fix, more features :) +*/ +dojo.provide("dojo.widget.Editor"); +dojo.provide("dojo.widget.Editor"); +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.Toolbar"); +dojo.require("dojo.widget.RichText"); +dojo.require("dojo.widget.ColorPalette"); +dojo.require("dojo.string.extras"); + +dojo.widget.tags.addParseTreeHandler("dojo:Editor"); + +dojo.widget.Editor = function() { + dojo.widget.HtmlWidget.call(this); + this.contentFilters = []; + this._toolbars = []; +} +dojo.inherits(dojo.widget.Editor, dojo.widget.HtmlWidget); + +dojo.widget.Editor.itemGroups = { + textGroup: ["bold", "italic", "underline", "strikethrough"], + blockGroup: ["formatBlock", "fontName", "fontSize"], + justifyGroup: ["justifyleft", "justifycenter", "justifyright"], + commandGroup: ["save", "cancel"], + colorGroup: ["forecolor", "hilitecolor"], + listGroup: ["insertorderedlist", "insertunorderedlist"], + indentGroup: ["outdent", "indent"], + linkGroup: ["createlink", "insertimage", "inserthorizontalrule"] +}; + +dojo.widget.Editor.formatBlockValues = { + "Normal": "p", + "Main heading": "h2", + "Sub heading": "h3", + "Sub sub heading": "h4", + "Preformatted": "pre" +}; + +dojo.widget.Editor.fontNameValues = { + "Arial": "Arial, Helvetica, sans-serif", + "Verdana": "Verdana, sans-serif", + "Times New Roman": "Times New Roman, serif", + "Courier": "Courier New, monospace" +}; + +dojo.widget.Editor.fontSizeValues = { + "1 (8 pt)" : "1", + "2 (10 pt)": "2", + "3 (12 pt)": "3", + "4 (14 pt)": "4", + "5 (18 pt)": "5", + "6 (24 pt)": "6", + "7 (36 pt)": "7" +}; + +dojo.widget.Editor.defaultItems = [ + "commandGroup", "|", "blockGroup", "|", "textGroup", "|", "colorGroup", "|", "justifyGroup", "|", "listGroup", "indentGroup", "|", "linkGroup" +]; + +// ones we support by default without asking the RichText component +// NOTE: you shouldn't put buttons like bold, italic, etc in here +dojo.widget.Editor.supportedCommands = ["save", "cancel", "|", "-", "/", " "]; + +dojo.lang.extend(dojo.widget.Editor, { + widgetType: "Editor", + + saveUrl: "", + saveMethod: "post", + saveArgName: "editorContent", + closeOnSave: false, + items: dojo.widget.Editor.defaultItems, + formatBlockItems: dojo.lang.shallowCopy(dojo.widget.Editor.formatBlockValues), + fontNameItems: dojo.lang.shallowCopy(dojo.widget.Editor.fontNameValues), + fontSizeItems: dojo.lang.shallowCopy(dojo.widget.Editor.fontSizeValues), + + // used to get the properties of an item if it is given as a string + getItemProperties: function(name) { + var props = {}; + switch(name.toLowerCase()) { + case "bold": + case "italic": + case "underline": + case "strikethrough": + props.toggleItem = true; + break; + + case "justifygroup": + props.defaultButton = "justifyleft"; + props.preventDeselect = true; + props.buttonGroup = true; + break; + + case "listgroup": + props.buttonGroup = true; + break; + + case "save": + case "cancel": + props.label = dojo.string.capitalize(name); + break; + + case "forecolor": + case "hilitecolor": + props.name = name; + props.toggleItem = true; // FIXME: they aren't exactly toggle items + props.icon = this.getCommandImage(name); + break; + + case "formatblock": + props.name = "formatBlock"; + props.values = this.formatBlockItems; + break; + + case "fontname": + props.name = "fontName"; + props.values = this.fontNameItems; + + case "fontsize": + props.name = "fontSize"; + props.values = this.fontSizeItems; + } + return props; + }, + + validateItems: true, // set to false to add items, regardless of support + focusOnLoad: true, + minHeight: "1em", + + _richText: null, // RichText widget + _richTextType: "RichText", + + _toolbarContainer: null, // ToolbarContainer widget + _toolbarContainerType: "ToolbarContainer", + + _toolbars: [], + _toolbarType: "Toolbar", + + _toolbarItemType: "ToolbarItem", + + buildRendering: function(args, frag) { + // get the node from args/frag + var node = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"]; + var trt = dojo.widget.createWidget(this._richTextType, { + focusOnLoad: this.focusOnLoad, + minHeight: this.minHeight + }, node) + var _this = this; + // this appears to fix a weird timing bug on Safari + setTimeout(function(){ + _this.setRichText(trt); + + _this.initToolbar(); + + _this.fillInTemplate(args, frag); + }, 0); + }, + + setRichText: function(richText) { + if(this._richText && this._richText == richText) { + dojo.debug("Already set the richText to this richText!"); + return; + } + + if(this._richText && !this._richText.isClosed) { + dojo.debug("You are switching richTexts yet you haven't closed the current one. Losing reference!"); + } + this._richText = richText; + dojo.event.connect(this._richText, "close", this, "onClose"); + dojo.event.connect(this._richText, "onLoad", this, "onLoad"); + dojo.event.connect(this._richText, "onDisplayChanged", this, "updateToolbar"); + if(this._toolbarContainer) { + this._toolbarContainer.enable(); + this.updateToolbar(true); + } + }, + + initToolbar: function() { + // var tic = new Date(); + if(this._toolbarContainer) { return; } // only create it once + this._toolbarContainer = dojo.widget.createWidget(this._toolbarContainerType); + var tb = this.addToolbar(); + var last = true; + for(var i = 0; i < this.items.length; i++) { + if(this.items[i] == "\n") { // new row + tb = this.addToolbar(); + } else { + if((this.items[i] == "|")&&(!last)){ + last = true; + }else{ + last = this.addItem(this.items[i], tb); + } + } + } + this.insertToolbar(this._toolbarContainer.domNode, this._richText.domNode); + // alert(new Date - tic); + }, + + // allow people to override this so they can make their own placement logic + insertToolbar: function(tbNode, richTextNode) { + dojo.html.insertBefore(tbNode, richTextNode); + //dojo.html.insertBefore(this._toolbarContainer.domNode, this._richText.domNode); + }, + + addToolbar: function(toolbar) { + this.initToolbar(); + if(!(toolbar instanceof dojo.widget.Toolbar)) { + toolbar = dojo.widget.createWidget(this._toolbarType); + } + this._toolbarContainer.addChild(toolbar); + this._toolbars.push(toolbar); + return toolbar; + }, + + addItem: function(item, tb, dontValidate) { + if(!tb) { tb = this._toolbars[0]; } + var cmd = ((item)&&(!dojo.lang.isUndefined(item["getValue"]))) ? cmd = item["getValue"](): item; + + var groups = dojo.widget.Editor.itemGroups; + if(item instanceof dojo.widget.ToolbarItem) { + tb.addChild(item); + } else if(groups[cmd]) { + var group = groups[cmd]; + var worked = true; + if(cmd == "justifyGroup" || cmd == "listGroup") { + var btnGroup = [cmd]; + for(var i = 0 ; i < group.length; i++) { + if(dontValidate || this.isSupportedCommand(group[i])) { + btnGroup.push(this.getCommandImage(group[i])); + }else{ + worked = false; + } + } + if(btnGroup.length){ + /* + // the addChild interface is assinine. Work around it. + var tprops = this.getItemProperties(cmd); + var tmpGroup = dojo.widget.createWidget("ToolbarButtonGroup", tprops); + dojo.debug(btnGroup); + dojo.event.connect(tmpGroup, "onClick", this, "_action"); + dojo.event.connect(tmpGroup, "onChangeSelect", this, "_action"); + */ + var btn = tb.addChild(btnGroup, null, this.getItemProperties(cmd)); + dojo.event.connect(btn, "onClick", this, "_action"); + dojo.event.connect(btn, "onChangeSelect", this, "_action"); + } + return worked; + } else { + for(var i = 0; i < group.length; i++) { + if(!this.addItem(group[i], tb)){ + worked = false; + } + } + return worked; + } + } else { + if((!dontValidate)&&(!this.isSupportedCommand(cmd))){ + return false; + } + if(dontValidate || this.isSupportedCommand(cmd)) { + cmd = cmd.toLowerCase(); + if(cmd == "formatblock") { + var select = dojo.widget.createWidget("ToolbarSelect", { + name: "formatBlock", + values: this.formatBlockItems + }); + tb.addChild(select); + var _this = this; + dojo.event.connect(select, "onSetValue", function(item, value) { + _this.onAction("formatBlock", value); + }); + } else if(cmd == "fontname") { + var select = dojo.widget.createWidget("ToolbarSelect", { + name: "fontName", + values: this.fontNameItems + }); + tb.addChild(select); + dojo.event.connect(select, "onSetValue", dojo.lang.hitch(this, function(item, value) { + this.onAction("fontName", value); + })); + } else if(cmd == "fontsize") { + var select = dojo.widget.createWidget("ToolbarSelect", { + name: "fontSize", + values: this.fontSizeItems + }); + tb.addChild(select); + dojo.event.connect(select, "onSetValue", dojo.lang.hitch(this, function(item, value) { + this.onAction("fontSize", value); + })); + } else if(dojo.lang.inArray(cmd, ["forecolor", "hilitecolor"])) { + var btn = tb.addChild(dojo.widget.createWidget("ToolbarColorDialog", this.getItemProperties(cmd))); + dojo.event.connect(btn, "onSetValue", this, "_setValue"); + } else { + var btn = tb.addChild(this.getCommandImage(cmd), null, this.getItemProperties(cmd)); + if(cmd == "save"){ + dojo.event.connect(btn, "onClick", this, "_save"); + }else if(cmd == "cancel"){ + dojo.event.connect(btn, "onClick", this, "_close"); + } else { + dojo.event.connect(btn, "onClick", this, "_action"); + dojo.event.connect(btn, "onChangeSelect", this, "_action"); + } + } + } + } + return true; + }, + + enableToolbar: function() { + if(this._toolbarContainer) { + this._toolbarContainer.domNode.style.display = ""; + this._toolbarContainer.enable(); + } + }, + + disableToolbar: function(hide){ + if(hide){ + if(this._toolbarContainer){ + this._toolbarContainer.domNode.style.display = "none"; + } + }else{ + if(this._toolbarContainer){ + this._toolbarContainer.disable(); + } + } + }, + + _updateToolbarLastRan: null, + _updateToolbarTimer: null, + _updateToolbarFrequency: 500, + + updateToolbar: function(force) { + if(!this._toolbarContainer) { return; } + + // keeps the toolbar from updating too frequently + // TODO: generalize this functionality? + var diff = new Date() - this._updateToolbarLastRan; + if(!force && this._updateToolbarLastRan && (diff < this._updateToolbarFrequency)) { + clearTimeout(this._updateToolbarTimer); + var _this = this; + this._updateToolbarTimer = setTimeout(function() { + _this.updateToolbar(); + }, this._updateToolbarFrequency/2); + return; + } else { + this._updateToolbarLastRan = new Date(); + } + // end frequency checker + + var items = this._toolbarContainer.getItems(); + for(var i = 0; i < items.length; i++) { + var item = items[i]; + if(item instanceof dojo.widget.ToolbarSeparator) { continue; } + var cmd = item._name; + if (cmd == "save" || cmd == "cancel") { continue; } + else if(cmd == "justifyGroup") { + try { + if(!this._richText.queryCommandEnabled("justifyleft")) { + item.disable(false, true); + } else { + item.enable(false, true); + var jitems = item.getItems(); + for(var j = 0; j < jitems.length; j++) { + var name = jitems[j]._name; + var value = this._richText.queryCommandValue(name); + if(typeof value == "boolean" && value) { + value = name; + break; + } else if(typeof value == "string") { + value = "justify"+value; + } else { + value = null; + } + } + if(!value) { value = "justifyleft"; } // TODO: query actual style + item.setValue(value, false, true); + } + } catch(err) {} + } else if(cmd == "listGroup") { + var litems = item.getItems(); + for(var j = 0; j < litems.length; j++) { + this.updateItem(litems[j]); + } + } else { + this.updateItem(item); + } + } + }, + + updateItem: function(item) { + try { + var cmd = item._name; + var enabled = this._richText.queryCommandEnabled(cmd); + item.setEnabled(enabled, false, true); + + var active = this._richText.queryCommandState(cmd); + if(active && cmd == "underline") { + // don't activate underlining if we are on a link + active = !this._richText.queryCommandEnabled("unlink"); + } + item.setSelected(active, false, true); + return true; + } catch(err) { + return false; + } + }, + + supportedCommands: dojo.widget.Editor.supportedCommands.concat(), + + isSupportedCommand: function(cmd) { + // FIXME: how do we check for ActiveX? + var yes = dojo.lang.inArray(cmd, this.supportedCommands); + if(!yes) { + try { + var richText = this._richText || dojo.widget.HtmlRichText.prototype; + yes = richText.queryCommandAvailable(cmd); + } catch(E) {} + } + return yes; + }, + + getCommandImage: function(cmd) { + if(cmd == "|") { + return cmd; + } else { + return dojo.uri.dojoUri("src/widget/templates/buttons/" + cmd + ".gif"); + } + }, + + _action: function(e) { + this._fire("onAction", e.getValue()); + }, + + _setValue: function(a, b) { + this._fire("onAction", a.getValue(), b); + }, + + _save: function(e){ + // FIXME: how should this behave when there's a larger form in play? + if(!this._richText.isClosed){ + if(this.saveUrl.length){ + var content = {}; + content[this.saveArgName] = this.getHtml(); + dojo.io.bind({ + method: this.saveMethod, + url: this.saveUrl, + content: content + }); + }else{ + dojo.debug("please set a saveUrl for the editor"); + } + if(this.closeOnSave){ + this._richText.close(e.getName().toLowerCase() == "save"); + } + } + }, + + _close: function(e) { + if(!this._richText.isClosed) { + this._richText.close(e.getName().toLowerCase() == "save"); + } + }, + + onAction: function(cmd, value) { + switch(cmd) { + case "createlink": + if(!(value = prompt("Please enter the URL of the link:", "http://"))) { + return; + } + break; + case "insertimage": + if(!(value = prompt("Please enter the URL of the image:", "http://"))) { + return; + } + break; + } + this._richText.execCommand(cmd, value); + }, + + fillInTemplate: function(args, frag) { + // dojo.event.connect(this, "onResized", this._richText, "onResized"); + }, + + _fire: function(eventName) { + if(dojo.lang.isFunction(this[eventName])) { + var args = []; + if(arguments.length == 1) { + args.push(this); + } else { + for(var i = 1; i < arguments.length; i++) { + args.push(arguments[i]); + } + } + this[eventName].apply(this, args); + } + }, + + getHtml: function(){ + this._richText.contentFilters = this._richText.contentFilters.concat(this.contentFilters); + return this._richText.getEditorContent(); + }, + + getEditorContent: function(){ + return this.getHtml(); + }, + + onClose: function(save, hide){ + this.disableToolbar(hide); + if(save) { + this._fire("onSave"); + } else { + this._fire("onCancel"); + } + }, + + // events baby! + onLoad: function(){}, + onSave: function(){}, + onCancel: function(){} +}); + diff --git a/source/web/scripts/ajax/src/widget/Editor2.js b/source/web/scripts/ajax/src/widget/Editor2.js new file mode 100644 index 0000000000..0c2474904b --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Editor2.js @@ -0,0 +1,395 @@ +/* TODO: + * - font selector + * - test, bug fix, more features :) +*/ +dojo.provide("dojo.widget.Editor2"); +dojo.require("dojo.io.*"); +dojo.require("dojo.html.*"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.html.iframe"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.RichText"); +dojo.require("dojo.widget.Editor2Toolbar"); +// dojo.require("dojo.widget.ColorPalette"); +// dojo.require("dojo.string.extras"); + +//The current focused Editor2 Instance +dojo.widget.Editor2._CurrentInstance = null; + +dojo.widget.defineWidget( + "dojo.widget.Editor2", + dojo.widget.RichText, + { + saveUrl: "", + saveMethod: "post", + saveArgName: "editorContent", + closeOnSave: false, + shareToolbar: false, + toolbarAlwaysVisible: false, + htmlEditing: false, + _inHtmlMode: false, + _htmlEditNode: null, + + commandList: dojo.widget.Editor2Toolbar.prototype.commandList, + toolbarWidget: null, + scrollInterval: null, + toolbarTemplatePath: null, + + + editorOnLoad: function(){ + var toolbars = dojo.widget.byType("Editor2Toolbar"); + if((!toolbars.length)||(!this.shareToolbar)){ + var tbOpts = {}; + this.toolbarTemplatePath = this.toolbarTemplatePath || "src/widget/templates/EditorToolbarOneline.html"; + tbOpts.templatePath = dojo.uri.dojoUri(this.toolbarTemplatePath); + if(this.toolbarWidget){ + this.toolbarWidget.show(); + }else{ + this.toolbarWidget = dojo.widget.createWidget("Editor2Toolbar", + tbOpts, this.domNode, "before"); + dojo.event.connect(this, "close", this.toolbarWidget, "hide"); + dojo.event.connect(this, "destroy", this.toolbarWidget, "destroy"); + this.toolbarWidget.hideUnusableButtons(this); + } + + if(this.object){ + this.tbBgIframe = new dojo.html.BackgroundIframe(this.toolbarWidget.domNode); + this.tbBgIframe.iframe.style.height = "30px"; + } + + // need to set position fixed to wherever this thing has landed + if(this.toolbarAlwaysVisible){ + var src = document.documentElement||window; + this.scrollInterval = setInterval(dojo.lang.hitch(this, "globalOnScrollHandler"), 100); + // dojo.event.connect(src, "onscroll", this, "globalOnScrollHandler"); + dojo.event.connect("before", this, "destroyRendering", this, "unhookScroller"); + } + }else{ + // FIXME: should we try harder to explicitly manage focus in + // order to prevent too many editors from all querying + // for button status concurrently? + // FIXME: selecting in one shared toolbar doesn't clobber + // selection in the others. This is problematic. + this.toolbarWidget = toolbars[0]; + } + dojo.event.topic.registerPublisher("Editor2.clobberFocus", this, "clobberFocus"); + dojo.event.topic.subscribe("Editor2.clobberFocus", this, "setBlur"); + dojo.event.connect(this.toolbarWidget.linkButton, "onclick", + dojo.lang.hitch(this, function(){ + var range; + if(this.document.selection){ + range = this.document.selection.createRange().text; + }else if(dojo.render.html.mozilla){ + range = this.window.getSelection().toString(); + } + if(range.length){ + this.toolbarWidget.exec("createlink", + prompt("Please enter the URL of the link:", "http://")); + }else{ + alert("Please select text to link"); + } + }) + ); + + dojo.event.connect(this.toolbarWidget, "formatSelectClick", this, "focus"); + dojo.event.connect(this.toolbarWidget, "saveClick", this, "save"); + dojo.event.connect(this.toolbarWidget, "insertimageClick", this, "insertImage"); + dojo.event.connect(this, "execCommand", this, "focus"); + + if(this.htmlEditing){ + var tb = this.toolbarWidget.htmltoggleButton; + if(tb){ + tb.style.display = ""; + dojo.event.connect(this.toolbarWidget, "htmltoggleClick", + this, "toggleHtmlEditing"); + } + } + }, + + clobberFocus: function(){}, + save: function(){ dojo.debug("Editor2.save"); }, + insertImage: function(){ dojo.debug("Editor2.insertImage"); }, + toggleHtmlEditing: function(){ + if(this===dojo.widget.Editor2._CurrentInstance){ + if(!this._inHtmlMode){ + this._inHtmlMode = true; + this.toolbarWidget.highlightButton("htmltoggle"); + if(!this._htmlEditNode){ + this._htmlEditNode = document.createElement("textarea"); + dojo.html.insertBefore(this._htmlEditNode, this.domNode); + } + this._htmlEditNode.style.display = ""; + this._htmlEditNode.style.width = "100%"; + this._htmlEditNode.style.height = dojo.html.getBorderBox(this.editNode).height+"px"; + this._htmlEditNode.value = this.editNode.innerHTML; + this.domNode.style.display = "none"; + }else{ + this._inHtmlMode = false; + this.domNode.style.display = ""; + this.toolbarWidget.unhighlightButton("htmltoggle"); + dojo.lang.setTimeout(this, "replaceEditorContent", 1, this._htmlEditNode.value); + this._htmlEditNode.style.display = "none"; + this.editNode.focus(); + } + } + }, + + setFocus: function(){ + if(dojo.widget.Editor2._CurrentInstance == this){ return; } + + if(this.toolbarWidget){ + this.clobberFocus(); + // dojo.debug("setFocus:", this); + dojo.widget.Editor2._CurrentInstance = this; + dojo.event.connect(this.toolbarWidget, "exec", this, "execCommand"); + } + }, + + setBlur: function(){ + // dojo.debug("setBlur:", this); + dojo.event.disconnect(this.toolbarWidget, "exec", this, "execCommand"); + }, + + _scrollSetUp: false, + _fixEnabled: false, + _scrollThreshold: false, + _handleScroll: true, + globalOnScrollHandler: function(){ + var isIE = dojo.render.html.ie; + if(!this._handleScroll){ return; } + var dh = dojo.html; + var tdn = this.toolbarWidget.domNode; + var db = dojo.body(); + var totalHeight = dh.getMarginBox(tdn).height; + if(!this._scrollSetUp){ + this._scrollSetUp = true; + var editorWidth = dh.getMarginBox(this.domNode).width; + this._scrollThreshold = dh.abs(tdn, false).y; + // dojo.debug("threshold:", this._scrollThreshold); + if((isIE)&&(db)&&(dh.getStyle(db, "background-image")=="none")){ + with(db.style){ + backgroundImage = "url(" + dojo.uri.dojoUri("src/widget/templates/images/blank.gif") + ")"; + backgroundAttachment = "fixed"; + } + } + } + + var scrollPos = (window["pageYOffset"]) ? window["pageYOffset"] : (document["documentElement"]||document["body"]).scrollTop; + + // FIXME: need to have top and bottom thresholds so toolbar doesn't keep scrolling past the bottom + if(scrollPos > this._scrollThreshold){ + // dojo.debug(scrollPos); + if(!this._fixEnabled){ + this.domNode.style.marginTop = totalHeight+"px"; + if(isIE){ + // FIXME: should we just use setBehvior() here instead? + var cl = dojo.html.abs(tdn).x; + document.body.appendChild(tdn); + tdn.style.left = cl+dojo.html.getPixelValue(document.body, "margin-left")+"px"; + dojo.html.addClass(tdn, "IEFixedToolbar"); + if(this.object){ + dojo.html.addClass(this.tbBgIframe, "IEFixedToolbar"); + } + + }else{ + with(tdn.style){ + position = "fixed"; + top = "0px"; + } + } + tdn.style.zIndex = 1000; + this._fixEnabled = true; + } + // if we're showing the floating toolbar, make sure that if + // we've scrolled past the bottom of the editor that we hide + // the toolbar for this instance of the editor. + + // TODO: when we get multiple editor toolbar support working + // correctly, ensure that we check this against the scroll + // position of the bottom-most editor instance. + if(!dojo.render.html.safari){ + // safari reports a bunch of things incorrectly here + var eHeight = (this.height) ? parseInt(this.height) : ((this.object) ? dojo.html.getBorderBox(this.editNode).height : this._lastHeight); + if(scrollPos > (this._scrollThreshold+eHeight)){ + tdn.style.display = "none"; + }else{ + tdn.style.display = ""; + } + } + + }else if(this._fixEnabled){ + this.domNode.style.marginTop = null; + with(tdn.style){ + position = ""; + top = ""; + zIndex = ""; + if(isIE){ + marginTop = ""; + } + } + if(isIE){ + dojo.html.removeClass(tdn, "IEFixedToolbar"); + dojo.html.insertBefore(tdn, this._htmlEditNode||this.domNode); + } + this._fixEnabled = false; + } + }, + + unhookScroller: function(){ + this._handleScroll = false; + clearInterval(this.scrollInterval); + // var src = document["documentElement"]||window; + // dojo.event.disconnect(src, "onscroll", this, "globalOnScrollHandler"); + if(dojo.render.html.ie){ + dojo.html.removeClass(this.toolbarWidget.domNode, "IEFixedToolbar"); + } + }, + + _updateToolbarLastRan: null, + _updateToolbarTimer: null, + _updateToolbarFrequency: 500, + + updateToolbar: function(force){ + if((!this.isLoaded)||(!this.toolbarWidget)){ return; } + + // keeps the toolbar from updating too frequently + // TODO: generalize this functionality? + var diff = new Date() - this._updateToolbarLastRan; + if( (!force)&&(this._updateToolbarLastRan)&& + ((diff < this._updateToolbarFrequency)) ){ + + clearTimeout(this._updateToolbarTimer); + var _this = this; + this._updateToolbarTimer = setTimeout(function() { + _this.updateToolbar(); + }, this._updateToolbarFrequency/2); + return; + + }else{ + this._updateToolbarLastRan = new Date(); + } + // end frequency checker + + // FIXME: SEVERE: This forEach block breaks undo on IE + dojo.lang.forEach(this.commandList, + function(cmd){ + if((cmd == "inserthtml") || (cmd == "save")){ return; } + try{ + if(this.queryCommandEnabled(cmd)){ + if(this.queryCommandState(cmd)){ + this.toolbarWidget.highlightButton(cmd); + }else{ + this.toolbarWidget.unhighlightButton(cmd); + } + } + }catch(e){ + // alert(cmd+":"+e); + } + }, + this + ); + + var h = dojo.render.html; + + // safari f's us for selection primitives + if(h.safari){ return; } + + var selectedNode = (h.ie) ? this.document.selection.createRange().parentElement() : this.window.getSelection().anchorNode; + // make sure we actuall have an element + while((selectedNode)&&(selectedNode.nodeType != 1)){ + selectedNode = selectedNode.parentNode; + } + if(!selectedNode){ return; } + + var formats = ["p", "pre", "h1", "h2", "h3", "h4"]; + // gotta run some specialized updates for the various + // formatting options + var type = formats[dojo.lang.find(formats, selectedNode.nodeName.toLowerCase())]; + while((selectedNode)&&(selectedNode!=this.editNode)&&(!type)){ + selectedNode = selectedNode.parentNode; + type = formats[dojo.lang.find(formats, selectedNode.nodeName.toLowerCase())]; + } + if(!type){ + type = ""; + }else{ + if(type.charAt(0)=="h"){ + this.toolbarWidget.unhighlightButton("bold"); + } + } + this.toolbarWidget.selectFormat(type); + }, + + updateItem: function(item) { + try { + var cmd = item._name; + var enabled = this._richText.queryCommandEnabled(cmd); + item.setEnabled(enabled, false, true); + + var active = this._richText.queryCommandState(cmd); + if(active && cmd == "underline") { + // don't activate underlining if we are on a link + active = !this._richText.queryCommandEnabled("unlink"); + } + item.setSelected(active, false, true); + return true; + } catch(err) { + return false; + } + }, + + _save: function(e){ + // FIXME: how should this behave when there's a larger form in play? + if(!this.isClosed){ + dojo.debug("save attempt"); + if(this.saveUrl.length){ + var content = {}; + content[this.saveArgName] = this.getHtml(); + dojo.io.bind({ + method: this.saveMethod, + url: this.saveUrl, + content: content + }); + }else{ + dojo.debug("please set a saveUrl for the editor"); + } + if(this.closeOnSave){ + this.close(e.getName().toLowerCase() == "save"); + } + } + } + }, + "html", + function(){ + var cp = dojo.widget.Editor2.prototype; + if(!cp._wrappersSet){ + cp._wrappersSet = true; + + cp.onDisplayChanged = (function(odc){ + return function(){ + try{ + odc.call(this); + this.updateToolbar(); + }catch(e){} + }; + })(cp.onDisplayChanged); + + cp.onLoad = (function(ol){ + return function(){ + try{ + ol.call(this); + }catch(e){ // FIXME: debug why this is throwing errors in IE! + dojo.debug(e); + } + this.editorOnLoad(); + }; + })(cp.onLoad); + + cp.onFocus = (function(of){ + return function(){ + of.call(this); + this.setFocus(); + }; + })(cp.onFocus); + } + } +); diff --git a/source/web/scripts/ajax/src/widget/Editor2Toolbar.js b/source/web/scripts/ajax/src/widget/Editor2Toolbar.js new file mode 100644 index 0000000000..bfd4ba00ac --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Editor2Toolbar.js @@ -0,0 +1,317 @@ +dojo.provide("dojo.widget.Editor2Toolbar"); + +dojo.require("dojo.lang.*"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.event.*"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.html.display"); +dojo.require("dojo.widget.RichText"); +dojo.require("dojo.widget.ColorPalette"); + +dojo.widget.defineWidget( + "dojo.widget.Editor2Toolbar", + dojo.widget.HtmlWidget, + { + commandList: [ "bold", "italic", "underline", "subscript", "superscript", + "fontname", "fontsize", "forecolor", "hilitecolor", "justifycenter", + "justifyfull", "justifyleft", "justifyright", "cut", "copy", "paste", + "delete", "undo", "redo", "createlink", "unlink", "removeformat", + "inserthorizontalrule", "insertimage", "insertorderedlist", + "insertunorderedlist", "indent", "outdent", "formatblock", "strikethrough", + "inserthtml", "blockdirltr", "blockdirrtl", "dirltr", "dirrtl", + "inlinedirltr", "inlinedirrtl", "inserttable", "insertcell", + "insertcol", "insertrow", "deletecells", "deletecols", "deleterows", + "mergecells", "splitcell" + ], + + templatePath: dojo.uri.dojoUri("src/widget/templates/EditorToolbarOneline.html"), + // templatePath: dojo.uri.dojoUri("src/widget/templates/EditorToolbar.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/EditorToolbar.css"), + + forecolorPalette: null, + hilitecolorPalette: null, + + // DOM Nodes + wikiwordButton: null, + htmltoggleButton: null, + insertimageButton: null, + styleDropdownButton: null, + styleDropdownContainer: null, + copyButton: null, + boldButton: null, + italicButton: null, + underlineButton: null, + justifycenterButton: null, + justifyleftButton: null, + justifyfullButton: null, + justifyrightButton: null, + pasteButton: null, + undoButton: null, + redoButton: null, + linkButton: null, + insertunorderedlistButton: null, + insertorderedlistButton: null, + forecolorButton: null, + forecolorDropDown: null, + hilitecolorButton: null, + hilitecolorDropDown: null, + formatSelectBox: null, + inserthorizontalruleButton: null, + strikethroughButton: null, + clickInterceptDiv: null, + oneLineTr: null, + saveButton: null, + + buttonClick: function(e){ e.preventDefault(); /* dojo.debug("buttonClick"); */ }, + + buttonMouseOver: function(e){ }, + buttonMouseOut: function(e){ }, + + + // event signals + preventSelect: function(e){ if(dojo.render.html.safari){ e.preventDefault(); } }, + wikiwordClick: function(){ }, + insertimageClick: function(){ }, + htmltoggleClick: function(){ }, + saveClick: function(){ }, + + styleDropdownClick: function(){ + dojo.debug("styleDropdownClick:", this.styleDropdownContainer); + dojo.html.toggleShowing(this.styleDropdownContainer); + }, + + copyClick: function(){ this.exec("copy"); }, + boldClick: function(){ this.exec("bold"); }, + italicClick: function(){ this.exec("italic"); }, + underlineClick: function(){ this.exec("underline"); }, + justifyleftClick: function(){ this.exec("justifyleft"); }, + justifycenterClick: function(){ this.exec("justifycenter"); }, + justifyfullClick: function(){ this.exec("justifyfull"); }, + justifyrightClick: function(){ this.exec("justifyright"); }, + pasteClick: function(){ this.exec("paste"); }, + undoClick: function(){ this.exec("undo"); }, + redoClick: function(){ this.exec("redo"); }, + linkClick: function(){ + // FIXME: we need to alert the user if they haven't selected any text + // this.exec( "createlink", + // prompt("Please enter the URL of the link:", "http://")); + }, + insertunorderedlistClick: function(){ this.exec("insertunorderedlist"); }, + insertorderedlistClick: function(){ this.exec("insertorderedlist"); }, + inserthorizontalruleClick: function(){ this.exec("inserthorizontalrule"); }, + strikethroughClick: function(){ this.exec("strikethrough"); }, + + formatSelectClick: function(){ + var sv = this.formatSelectBox.value.toLowerCase(); + this.exec("formatblock", sv); + }, + + normalTextClick: function(){ this.exec("formatblock", "p"); }, + h1TextClick: function(){ this.exec("formatblock", "h1"); }, + h2TextClick: function(){ this.exec("formatblock", "h2"); }, + h3TextClick: function(){ this.exec("formatblock", "h3"); }, + h4TextClick: function(){ this.exec("formatblock", "h4"); }, + indentClick: function(){ this.exec("indent"); }, + outdentClick: function(){ this.exec("outdent"); }, + + + hideAllDropDowns: function(){ + this.domNode.style.height = ""; + dojo.lang.forEach(dojo.widget.byType("Editor2Toolbar"), function(tb){ + try{ + dojo.html.hide(tb.forecolorDropDown); + dojo.html.hide(tb.hilitecolorDropDown); + dojo.html.hide(tb.styleDropdownContainer); + if(tb.clickInterceptDiv){ + dojo.html.hide(tb.clickInterceptDiv); + } + }catch(e){} + if(dojo.render.html.ie){ + try{ + dojo.html.hide(tb.forecolorPalette.bgIframe); + }catch(e){} + try{ + dojo.html.hide(tb.hilitecolorPalette.bgIframe); + }catch(e){} + } + }); + }, + + selectFormat: function(format){ + if(this.formatSelectBox) { + dojo.lang.forEach(this.formatSelectBox.options, function(item){ + if(item.value.toLowerCase() == format.toLowerCase()){ + // FIXME: SEVERE: setting selected on this item breaks the undo stack on IE + item.selected = true; + } + }); + } + }, + + forecolorClick: function(e){ + this.colorClick(e, "forecolor"); + }, + + hilitecolorClick: function(e){ + this.colorClick(e, "hilitecolor"); + }, + + // FIXME: these methods aren't currently dealing with clicking in the + // general document to hide the menu + colorClick: function(e, type){ + var h = dojo.render.html; + this.hideAllDropDowns(); + // FIXME: if we've been "popped out", we need to set the height of the toolbar. + e.stopPropagation(); + var dd = this[type+"DropDown"]; + var pal = this[type+"Palette"]; + dojo.html.toggleShowing(dd); + if(!pal){ + pal = this[type+"Palette"] = dojo.widget.createWidget("ColorPalette", {}, dd, "first"); + var fcp = pal.domNode; + var mb = dojo.html.getMarginBox(fcp); + with(dd.style){ + width = mb.width + "px"; + height = mb.height + "px"; + zIndex = 1002; + position = "absolute"; + } + + dojo.event.connect( "after", + pal, "onColorSelect", + this, "exec", + function(mi){ mi.args.unshift(type); return mi.proceed(); } + ); + + dojo.event.connect( "after", + pal, "onColorSelect", + dojo.html, "toggleShowing", + this, function(mi){ mi.args.unshift(dd); return mi.proceed(); } + ); + + var cid = this.clickInterceptDiv; + if(!cid){ + cid = this.clickInterceptDiv = document.createElement("div"); + document.body.appendChild(cid); + with(cid.style){ + backgroundColor = "transparent"; + top = left = "0px"; + height = width = "100%"; + position = "absolute"; + border = "none"; + display = "none"; + zIndex = 1001; + } + dojo.event.connect(cid, "onclick", function(){ cid.style.display = "none"; }); + } + dojo.event.connect(pal, "onColorSelect", function(){ cid.style.display = "none"; }); + + dojo.event.kwConnect({ + srcObj: document.body, + srcFunc: "onclick", + targetObj: this, + targetFunc: "hideAllDropDowns", + once: true + }); + document.body.appendChild(dd); + } + dojo.html.toggleShowing(this.clickInterceptDiv); + var pos = dojo.html.abs(this[type+"Button"]); + dojo.html.placeOnScreenPoint(dd, pos.x, pos.y, 0, false); + if(pal.bgIframe){ + var mb = dojo.html.getMarginBox(dd); + with(pal.bgIframe.style){ + display = "block"; + left = dd.style.left; + top = dd.style.top; + width = mb.width+"px"; + height = mb.height+"px"; + } + } + }, + + uninitialize: function(){ + if(!dojo.render.html.ie){ + // apparently this causes leakage on IE! + dojo.event.kwDisconnect({ + srcObj: document.body, + srcFunc: "onclick", + targetObj: this, + targetFunc: "hideAllDropDowns", + once: true + }); + } + }, + + // stub for observers + exec: function(what, arg){ /* dojo.debug(what, new Date()); */ }, + + hideUnusableButtons: function(obj){ + var op = obj||dojo.widget.RichText.prototype; + dojo.lang.forEach(this.commandList, + function(cmd){ + if(this[cmd+"Button"]){ + var cb = this[cmd+"Button"]; + if(!op.queryCommandAvailable(cmd)){ + cb.style.display = "none"; + cb.parentNode.style.display = "none"; + } + } + }, + this); + if(this.oneLineTr){ + var lastVisibleIsSpacer = false; + var lastVisible = false; + var tds = this.oneLineTr.getElementsByTagName("td"); + dojo.lang.forEach(tds, function(td){ + if(td.getAttribute("isSpacer")){ + if(td.style.display != "none"){ + if(lastVisibleIsSpacer){ + td.style.display = "none"; + } + lastVisibleIsSpacer = true; + }else{ + lastVisible = td; + lastVisibleIsSpacer = true; + } + }else{ + if(td.style.display != "none"){ + lastVisible = td; + lastVisibleIsSpacer = false; + } + } + }); + } + }, + + highlightButton: function(name){ + var bn = name+"Button"; + if(this[bn]){ + with(this[bn].style){ + backgroundColor = "White"; + border = "1px solid #aeaeab"; + } + } + }, + + unhighlightButton: function(name){ + var bn = name+"Button"; + if(this[bn]){ + // dojo.debug("unhighlighting:", name); + with(this[bn].style){ + backgroundColor = ""; + border = ""; + } + } + } + }, + "html", + function(){ + // dojo.event.connect(this, "fillInTemplate", this, "hideUnusableButtons"); + dojo.event.connect(this, "fillInTemplate", dojo.lang.hitch(this, function(){ + if(dojo.render.html.ie){ + this.domNode.style.zoom = 1.0; + } + })); + } +); diff --git a/source/web/scripts/ajax/src/widget/FilteringTable.js b/source/web/scripts/ajax/src/widget/FilteringTable.js new file mode 100644 index 0000000000..9b78cfe3c4 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/FilteringTable.js @@ -0,0 +1,871 @@ +dojo.provide("dojo.widget.FilteringTable"); + +dojo.require("dojo.date"); +dojo.require("dojo.json"); +dojo.require("dojo.data.SimpleStore"); +dojo.require("dojo.html.*"); +dojo.require("dojo.html.util"); +dojo.require("dojo.html.style"); +dojo.require("dojo.html.selection"); +dojo.require("dojo.event.*"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); + +dojo.widget.defineWidget( + "dojo.widget.FilteringTable", + dojo.widget.HtmlWidget, + function(){ + this.store=new dojo.data.SimpleStore(); + + //declare per instance changeable widget properties + this.valueField="Id"; + this.multiple=false; + this.maxSelect=0; + this.maxSortable=1; // how many columns can be sorted at once. + this.minRows=0; + this.defaultDateFormat = "%D"; + this.isInitialized=false; + + this.columns=[]; + this.sortInformation=[{ + index:0, + direction:0 + }]; + + // CSS definitions + this.headClass=""; + this.tbodyClass=""; + this.headerClass=""; + this.headerUpClass="selectedUp"; + this.headerDownClass="selectedDown"; + this.rowClass=""; + this.rowAlternateClass="alt"; + this.rowSelectedClass="selected"; + this.columnSelected="sorted-column"; + }, +{ + // dojo widget properties + isContainer: false, + templatePath: null, + templateCssPath: null, + + // methods. + getTypeFromString: function(s){ + var parts = s.split("."), i = 0, obj = dj_global; + do{ + obj = obj[parts[i++]]; + } while (i < parts.length && obj); + return (obj != dj_global) ? obj : null; + }, + + // custom data access. + getByRow: function(/*HTMLTableRow*/row){ + return this.store.getByKey(dojo.html.getAttribute(row, "value")); + }, + getDataByRow: function(/*HTMLTableRow*/row){ + return this.store.getDataByKey(dojo.html.getAttribute(row, "value")); + }, + + getSelectedData: function(){ + // summary + // returns all objects that are selected. + var data=this.store.get(); + var a=[]; + for(var i=0; i<data.length; i++){ + if(data[i].isSelected){ + a.push(data[i].src); + } + } + if(this.multiple){ + return a; // array + } else { + return a[0]; // object + } + }, + + isSelected: function(/* object */obj){ + var data = this.store.get(); + for(var i=0; i<data.length; i++){ + if(data[i].src == obj){ + return true; // boolean + } + } + return false; // boolean + }, + isValueSelected: function(/* string */val){ + var v = this.store.getByKey(val); + if(v){ + return v.isSelected; + } + return false; + }, + isIndexSelected: function(/* number */idx){ + var v = this.store.getByIndex(idx); + if(v){ + return v.isSelected; + } + return false; + }, + isRowSelected: function(/* HTMLTableRow */row){ + var v = this.getByRow(row); + if(v){ + return v.isSelected; + } + return false; + }, + + reset: function(){ + this.store.clearData(); + this.columns = []; + this.sortInformation = [ {index:0, direction:0} ]; + this.resetSelections(); + this.isInitialized = false; + this.onReset(); + }, + resetSelections: function(){ + this.store.forEach(function(element){ + element.isSelected = false; + }); + }, + onReset:function(){ }, + + // selection and toggle functions + select: function(/*object*/ obj){ + var data = this.store.get(); + for(var i=0; i<data.length; i++){ + if(data[i].src == obj){ + data[i].isSelected = true; + break; + } + } + this.onDataSelect(obj); + }, + selectByValue: function(/*string*/ val){ + this.select(this.getDataByKey(val)); + }, + selectByIndex: function(/*number*/ idx){ + this.select(this.getDataByIndex(idx)); + }, + selectByRow: function(/*HTMLTableRow*/ row){ + this.select(this.getDataByRow(row)); + }, + onDataSelect: function(/* object */obj){ }, + + toggleSelection: function(/*object*/obj){ + var data = this.store.get(); + for(var i=0; i<data.length; i++){ + if(data[i].src == obj){ + data[i].isSelected = !data[i].isSelected; + break; + } + } + this.onDataToggle(obj); + }, + toggleSelectionByValue: function(/*string*/val){ + this.toggleSelection(this.getDataByKey(val)); + }, + toggleSelectionByIndex: function(/*number*/idx){ + this.toggleSelection(this.getDataByIndex(idx)); + }, + toggleSelectionByRow: function(/*HTMLTableRow*/row){ + this.toggleSelection(this.getDataByRow(row)); + }, + onDataToggle: function(/* object */obj){ }, + + // parsing functions, from HTML to metadata/SimpleStore + _meta:{ + field:null, + format:null, + filterer:null, + noSort:false, + sortType:"String", + dataType:String, + sortFunction:null, + filterFunction:null, + label:null, + align:"left", + valign:"middle", + getField:function(){ + return this.field || this.label; + }, + getType:function(){ + return this.dataType; + } + }, + createMetaData: function(/* object */obj){ + // summary + // Take a JSON-type structure and make it into a ducktyped metadata object. + for(var p in this._meta){ + // rudimentary mixin + if(!obj[p]){ + obj[p] = this._meta[p]; + } + } + if(!obj.label){ + obj.label=obj.field; + } + if(!obj.filterFunction){ + obj.filterFunction=this._defaultFilter; + } + return obj; // object + }, + parseMetadata: function(/* HTMLTableHead */head){ + this.columns=[]; + this.sortInformation=[]; + var row = head.getElementsByTagName("tr")[0]; + var cells = row.getElementsByTagName("td"); + if (cells.length == 0){ + cells = row.getElementsByTagName("th"); + } + for(var i=0; i<cells.length; i++){ + var o = this.createMetaData({ }); + + // presentation attributes + if(dojo.html.hasAttribute(cells[i], "align")){ + o.align = dojo.html.getAttribute(cells[i],"align"); + } + if(dojo.html.hasAttribute(cells[i], "valign")){ + o.valign = dojo.html.getAttribute(cells[i],"valign"); + } + if(dojo.html.hasAttribute(cells[i], "nosort")){ + o.noSort = (dojo.html.getAttribute(cells[i],"nosort")=="true"); + } + if(dojo.html.hasAttribute(cells[i], "sortusing")){ + var trans = dojo.html.getAttribute(cells[i],"sortusing"); + var f = this.getTypeFromString(trans); + if (f != null && f != window && typeof(f)=="function"){ + o.sortFunction=f; + } + } + if(dojo.html.hasAttribute(cells[i], "field")){ + o.field=dojo.html.getAttribute(cells[i],"field"); + } else { + o.field="field"+i; + } + if(dojo.html.hasAttribute(cells[i], "format")){ + o.format=dojo.html.getAttribute(cells[i],"format"); + } + if(dojo.html.hasAttribute(cells[i], "dataType")){ + var sortType = dojo.html.getAttribute(cells[i],"dataType"); + // FIXME: switch this to let sorting happen based on dojo.html.renderedTextContent + if(sortType.toLowerCase()=="html" || sortType.toLowerCase()=="markup"){ + o.sortType = "__markup__"; // always convert to "__markup__" + o.noSort = true; + }else{ + var type = this.getTypeFromString(sortType); + if(type){ + o.sortType = sortType; + o.dataType = type; + } + } + } + o.label = dojo.html.renderedTextContent(cells[i]); + + // TODO: set up filtering mechanisms here. + if(dojo.html.hasAttribute(cells[i], "filterusing")){ + var trans = dojo.html.getAttribute(cells[i],"filterusing"); + var f = this.getTypeFromString(trans); + if (f != null && f != window && typeof(f)=="function"){ + o.filterFunction=f; + } + } + + this.columns.push(o); + + // check to see if there's a default sort, and set the properties necessary + if(dojo.html.hasAttribute(cells[i], "sort")){ + var info = { + index:i, + direction:0 + }; + var dir = dojo.html.getAttribute(cells[i], "sort"); + if(!isNaN(parseInt(dir))){ + dir = parseInt(dir); + info.direction = (dir != 0) ? 1 : 0; + }else{ + info.direction = (dir.toLowerCase() == "desc") ? 1 : 0; + } + this.sortInformation.push(info); + } + } + if(this.sortInformation.length == 0){ + this.sortInformation.push({ + index:0, + direction:0 + }); + } else if (this.sortInformation.length > this.maxSortable){ + this.sortInformation.length = this.maxSortable; + } + }, + parseData: function(/* HTMLTableBody */body){ + // summary + // Parse HTML data into native JSON structure for the store. + var rows = body.rows; + if(rows.length == 0 && this.columns.length == 0) return; // there's no data, ignore me. + + // create a data constructor based on what we've got for the fields. + var self=this; + var ctor=function(row){ + var obj = {}; + for(var i=0; i<self.columns.length; i++){ + var o = obj; + var data = row.cells[i].innerHTML; + var p = self.columns[i].getField(); + if(p.indexOf(".") > -1){ + p = p.split("."); + while(p.length>1){ + var pr = p.shift(); + o[pr] = {}; + o = o[pr]; + } + p = p[0]; + } + + if(self.columns[i].sortType=="__markup__") o[p] = String(data); + else{ + var type = self.columns[i].getType(); + if(data){ + o[p] = new type(data); + } else { + o[p] = new type(); + } + } + } + return obj; + }; + + // we have initialization data, let's parse it. + var arr=[]; + var selected=[]; + for(var i=0; i<rows.length; i++){ + var row = rows[i]; + var o = ctor(row); // yay. magic. love. sigh. + o[this.valueField] = dojo.html.getAttribute(row,"value"); + if(dojo.html.getAttribute(row,"selected")=="true"){ + selected.push(o); + } + arr.push(o); + } + this.store.setData(arr); + + for(var i=0; i<selected.length; i++){ + this.select(selected[i]); + } + this.renderSelections(); + + // say that we are already initialized so that we don't kill anything + this.isInitialized=true; + }, + + // standard events + onSelect: function(/* HTMLEvent */e){ + // summary + // Handles the onclick event of any element. + var row = dojo.html.getParentByType(e.target,"tr"); + var body = dojo.html.getParentByType(row,"tbody"); + if(this.multiple){ + if(e.shiftKey){ + var startRow; + var rows=body.rows; + for(var i=0;i<rows.length;i++){ + if(rows[i]==row){ + break; + } + if(this.isRowSelected(rows[i])){ + startRow=rows[i]; + } + } + if(!startRow){ + startRow = row; + for(; i<rows.length; i++){ + if(this.isRowSelected(rows[i])){ + row = rows[i]; + break; + } + } + } + this.resetSelections(); + if(startRow == row){ + this.toggleSelectionByRow(row); + } else { + var doSelect = false; + for(var i=0; i<rows.length; i++){ + if(rows[i] == startRow){ + doSelect=true; + } + if(doSelect){ + this.selectByRow(rows[i]); + } + if(rows[i] == row){ + doSelect = false; + } + } + } + } else { + this.toggleSelectionByRow(row); + } + } else { + this.resetSelections(); + this.toggleSelectionByRow(row); + } + this.renderSelections(); + }, + onSort: function(/* HTMLEvent */e){ + // summary + // Sort the table based on the column selected. + var oldIndex=this.sortIndex; + var oldDirection=this.sortDirection; + + var source=e.target; + var row=dojo.html.getParentByType(source,"tr"); + var cellTag="td"; + if(row.getElementsByTagName(cellTag).length==0){ + cellTag="th"; + } + + var headers=row.getElementsByTagName(cellTag); + var header=dojo.html.getParentByType(source,cellTag); + + for(var i=0; i<headers.length; i++){ + dojo.html.setClass(headers[i], this.headerClass); + if(headers[i]==header){ + if(this.sortInformation[0].index != i){ + this.sortInformation.unshift({ + index:i, + direction:0 + }); + } else { + this.sortInformation[0] = { + index:i, + direction:(~this.sortInformation[0].direction)&1 + }; + } + } + } + + this.sortInformation.length = Math.min(this.sortInformation.length, this.maxSortable); + for(var i=0; i<this.sortInformation.length; i++){ + var idx=this.sortInformation[i].index; + var dir=(~this.sortInformation[i].direction)&1; + dojo.html.setClass(headers[idx], dir==0?this.headerDownClass:this.headerUpClass); + } + this.render(); + }, + onFilter: function(){ + // summary + // show or hide rows based on the parameters of the passed filter. + }, + + // Filtering methods + _defaultFilter: function(/* HTMLTableRow */row){ + // summary + // Always return true as the result of the default filter. + return true; + }, + setFilter: function(/* string */field, /* function */fn){ + // summary + // set a filtering function on the passed field. + for(var i=0; i<this.columns.length; i++){ + if(this.columns[i].getField() == field){ + this.columns[i].filterFunction=fn; + break; + } + } + this.applyFilters(); + }, + setFilterByIndex: function(/* number */idx, /* function */fn){ + // summary + // set a filtering function on the passed column index. + this.columns[idx].filterFunction=fn; + this.applyFilters(); + }, + clearFilter: function(/* string */field){ + // summary + // clear a filtering function on the passed field. + for(var i=0; i<this.columns.length; i++){ + if(this.columns[i].getField() == field){ + this.columns[i].filterFunction=this._defaultFilter; + break; + } + } + this.applyFilters(); + }, + clearFilterByIndex: function(/* string */field){ + // summary + // clear a filtering function on the passed column index. + this.columns[idx].filterFunction=this._defaultFilter; + this.applyFilters(); + }, + clearFilters: function(){ + // summary + // clears all filters. + for(var i=0; i<this.columns.length; i++){ + this.columns[i].filterFunction=this._defaultFilter; + } + // we'll do the clear manually, it will be faster. + var rows=this.domNode.tBodies[0].rows; + for(var i=0; i<rows.length; i++){ + rows[i].style.display=""; + } + this.onFilter(); + }, + applyFilters: function(){ + // summary + // apply all filters to the table. + var rows=this.domNode.tBodies[0].rows; + for(var i=0; i<rows.length; i++){ + var b=true; + var row=rows[i]; + for(var j=0; j<this.columns.length; j++){ + if(!this.columns[j].filterFunction(this.store.getField(this.getDataByRow(row), this.columns[j].getField()))){ + b=false; + break; + } + } + row.style.display=(b?"":"none"); + } + this.onFilter(); + }, + + // sorting functionality + createSorter: function(/* array */info){ + // summary + // creates a custom function to be used for sorting. + var self=this; + var sortFunctions=[]; // our function stack. + + function createSortFunction(field, dir){ + return function(rowA, rowB){ + var a = self.store.getField(self.getDataByRow(rowA), field); + var b = self.store.getField(self.getDataByRow(rowB), field); + var ret = 0; + if(a > b) ret = 1; + if(a < b) ret = -1; + return dir * ret; + } + } + + var current=0; + var max = Math.min(info.length, this.maxSortable, this.columns.length); + while(current < max){ + var field = this.columns[info[current].index].getField(); + var direction = (info[current].direction == 0) ? 1 : -1; + sortFunctions.push(createSortFunction(field, direction)); + current++; + } + + return function(rowA, rowB){ + var idx=0; + while(idx < sortFunctions.length){ + var ret = sortFunctions[idx++](rowA, rowB); + if(ret != 0) return ret; + } + // if we got here then we must be equal. + return 0; + }; // function + }, + + // rendering + createRow: function(/* object */obj){ + var row=document.createElement("tr"); + dojo.html.disableSelection(row); + if(obj.key != null){ + row.setAttribute("value", obj.key); + } + for(var j=0; j<this.columns.length; j++){ + var cell=document.createElement("td"); + cell.setAttribute("align", this.columns[j].align); + cell.setAttribute("valign", this.columns[j].valign); + dojo.html.disableSelection(cell); + var val = this.store.getField(obj.src, this.columns[j].getField()); + if(typeof(val)=="undefined"){ + val=""; + } + if (this.columns[j].sortType=="__markup__"){ + cell.innerHTML=val; + } else { + if(this.columns[j].getType()==Date) { + val=new Date(val); + if(!isNaN(val)){ + var format = this.defaultDateFormat; + if(this.columns[j].format){ + format = this.columns[j].format; + } + cell.appendChild(document.createTextNode(dojo.date.format(val, format))); + } else { + cell.appendChild(document.createTextNode(val)); + } + } else if ("Number number int Integer float Float".indexOf(this.columns[j].getType())>-1){ + // TODO: number formatting + if(val.length == 0){ + val="0"; + } + var n = parseFloat(val, 10) + ""; + // TODO: numeric formatting + rounding :) + if(n.indexOf(".")>-1){ + n = dojo.math.round(parseFloat(val,10),2); + } + cell.appendChild(document.createTextNode(n)); + }else{ + cell.appendChild(document.createTextNode(val)); + } + } + row.appendChild(cell); + } + return row; + }, + prefill: function(){ + // summary + // if there's no data in the table, then prefill it with this.minRows. + this.isInitialized = false; + var body = this.domNode.tBodies[0]; + while (body.childNodes.length > 0){ + body.removeChild(body.childNodes[0]); + } + + if(this.minRows>0){ + for(var i=0; i < this.minRows; i++){ + var row = document.createElement("tr"); + if(this.alternateRows){ + dojo.html[((i % 2 == 1)?"addClass":"removeClass")](row, this.rowAlternateClass); + } + row.setAttribute("emptyRow","true"); + for(var j=0; j<this.columns.length; j++){ + var cell = document.createElement("td"); + cell.innerHTML = " "; + row.appendChild(cell); + } + body.appendChild(row); + } + } + }, + init: function(){ + // summary + // initializes the table of data + this.isInitialized=false; + + // if there is no thead, create it now. + var head=this.domNode.getElementsByTagName("thead")[0]; + if(head.getElementsByTagName("tr").length == 0){ + // render the column code. + var row=document.createElement("tr"); + for(var i=0; i<this.columns.length; i++){ + var cell=document.createElement("td"); + cell.setAttribute("align", this.columns[i].align); + cell.setAttribute("valign", this.columns[i].valign); + dojo.html.disableSelection(cell); + cell.innerHTML=this.columns[i].label; + row.appendChild(cell); + + // attach the events. + if(!this.columns[i].noSort){ + dojo.event.connect(cell, "onclick", this, "onSort"); + } + } + dojo.html.prependChild(row, head); + } + + if(this.store.get().length == 0){ + return false; + } + + var idx=this.domNode.tBodies[0].rows.length; + if(!idx || idx==0 || this.domNode.tBodies[0].rows[0].getAttribute("emptyrow")=="true"){ + idx = 0; + var body = this.domNode.tBodies[0]; + while(body.childNodes.length>0){ + body.removeChild(body.childNodes[0]); + } + + var data = this.store.get(); + for(var i=0; i<data.length; i++){ + var row = this.createRow(data[i]); + body.appendChild(row); + dojo.event.connect(row, "onclick", this, "onSelect"); + idx++; + } + } + + // add empty rows + if(this.minRows > 0 && idx < this.minRows){ + idx = this.minRows - idx; + for(var i=0; i<idx; i++){ + row=document.createElement("tr"); + row.setAttribute("emptyRow","true"); + for(var j=0; j<this.columns.length; j++){ + cell=document.createElement("td"); + cell.innerHTML=" "; + row.appendChild(cell); + } + body.appendChild(row); + } + } + + // last but not least, show any columns that have sorting already on them. + var row=this.domNode.getElementsByTagName("thead")[0].rows[0]; + var cellTag="td"; + if(row.getElementsByTagName(cellTag).length==0) cellTag="th"; + var headers=row.getElementsByTagName(cellTag); + for(var i=0; i<headers.length; i++){ + dojo.html.setClass(headers[i], this.headerClass); + } + for(var i=0; i<this.sortInformation.length; i++){ + var idx=this.sortInformation[i].index; + var dir=(~this.sortInformation[i].direction)&1; + dojo.html.setClass(headers[idx], dir==0?this.headerDownClass:this.headerUpClass); + } + + this.isInitialized=true; + return this.isInitialized; + }, + render: function(){ + /* The method that should be called once underlying changes + * are made, including sorting, filtering, data changes. + * Rendering the selections themselves are a different method, + * which render() will call as the last step. + ****************************************************************/ + if(!this.isInitialized){ + var b = this.init(); + if(!b){ + this.prefill(); + return; + } + } + + // do the sort + var rows=[]; + var body=this.domNode.tBodies[0]; + var emptyRowIdx=-1; + for(var i=0; i<body.rows.length; i++){ + if(body.rows[i].getAttribute("emptyRow")){ + emptyRowIdx=i; + break; + } + rows.push(body.rows[i]); + } + + // build the sorting function, and do the sorting. + var sortFunction = this.createSorter(this.sortInformation); + if(sortFunction){ + rows.sort(sortFunction); + } + if(emptyRowIdx>-1){ + for(var i=emptyRowIdx; i<body.rows.length; i++){ + rows.push(body.rows[i]); + } + } + + // append the rows without killing them, this should help with the HTML problems. + for(var i=0; i<rows.length; i++){ + if(this.alternateRows){ + dojo.html[((i%2==1)?"addClass":"removeClass")](rows[i], this.rowAlternateClass); + } + body.appendChild(rows[i]); + } + + // now re-render any selections. + this.renderSelections(); + }, + renderSelections: function(){ + var body=this.domNode.tBodies[0]; + for(var i=0; i<body.rows.length; i++){ + dojo.html[(this.isRowSelected(body.rows[i])?"addClass":"removeClass")](body.rows[i], this.rowSelectedClass); + } + }, + renderFilter: function(){ + }, + + // widget lifetime handlers + initialize: function(){ + var self=this; + // connect up binding listeners here. + dojo.event.connect(this.store, "onSetData", function(){ + self.store.forEach(function(element){ + element.isSelected = false; + }); + self.isInitialized=false; + var body = self.domNode.tBodies[0]; + if(body){ + while(body.childNodes.length>0){ + body.removeChild(body.childNodes[0]); + } + } + self.render(); + }); + dojo.event.connect(this.store, "onClearData", function(){ + self.render(); + }); + dojo.event.connect(this.store, "onAddData", function(addedObject){ + var row=self.createRow(addedObject); + self.domNode.tBodies[0].appendChild(row); + self.render(); + }); + dojo.event.connect(this.store, "onAddDataRange", function(arr){ + for(var i=0; i<arr.length; i++){ + arr[i].isSelected=false; + var row=self.createRow(arr[i]); + self.domNode.tBodies[0].appendChild(row); + }; + self.render(); + }); + dojo.event.connect(this.store, "onRemoveData", function(removedObject){ + var rows = self.domNode.tBodies[0].rows; + for(var i=0; i<rows.length; i++){ + if(self.getDataByRow(rows[i]) == removedObject.src){ + rows[i].parentNode.removeChild(rows[i]); + break; + } + } + self.render(); + }); + }, +// fillInTemplate: function(args, frag){ }, + postCreate: function(){ + // summary + // finish widget initialization. + + this.store.keyField = this.valueField; + + if(this.domNode){ + // start by making sure domNode is a table element; + if(this.domNode.nodeName.toLowerCase() != "table"){ + } + + // see if there is columns set up already + if(this.domNode.getElementsByTagName("thead")[0]){ + var head=this.domNode.getElementsByTagName("thead")[0]; + if(this.headClass.length > 0){ + head.className = this.headClass; + } + dojo.html.disableSelection(this.domNode); + this.parseMetadata(head); + + var header="td"; + if(head.getElementsByTagName(header).length==0){ + header="th"; + } + var headers = head.getElementsByTagName(header); + for(var i=0; i<headers.length; i++){ + if(!this.columns[i].noSort){ + dojo.event.connect(headers[i], "onclick", this, "onSort"); + } + } + } else { + this.domNode.appendChild(document.createElement("thead")); + } + + // if the table doesn't have a tbody already, add one and grab a reference to it + if (this.domNode.tBodies.length < 1) { + var body = document.createElement("tbody"); + this.domNode.appendChild(body); + } else { + var body = this.domNode.tBodies[0]; + } + + if (this.tbodyClass.length > 0){ + body.className = this.tbodyClass; + } + this.parseData(body); + } + } +}); diff --git a/source/web/scripts/ajax/src/widget/FisheyeList.js b/source/web/scripts/ajax/src/widget/FisheyeList.js new file mode 100644 index 0000000000..a3ad61837e --- /dev/null +++ b/source/web/scripts/ajax/src/widget/FisheyeList.js @@ -0,0 +1,720 @@ +dojo.provide("dojo.widget.FisheyeList"); +dojo.provide("dojo.widget.FisheyeListItem"); + +// +// TODO +// fix SVG support, and turn it on only if the browser supports it +// fix really long labels in vertical mode +// + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.html.style"); +dojo.require("dojo.html.selection"); +dojo.require("dojo.html.util"); +dojo.require("dojo.event"); + +dojo.widget.defineWidget( + "dojo.widget.FisheyeList", + dojo.widget.HtmlWidget, +{ + templateString: '<div class="dojoHtmlFisheyeListBar"></div>', + templateCssPath: dojo.uri.dojoUri("src/widget/templates/FisheyeList.css"), + + EDGE: { + CENTER: 0, + LEFT: 1, + RIGHT: 2, + TOP: 3, + BOTTOM: 4 + }, + + isContainer: true, + snarfChildDomOutput: true, + + pos: {x: -1, y: -1}, // current cursor position, relative to the grid + + // for conservative trigger mode, when triggered, timerScale is gradually increased from 0 to 1 + timerScale: 1.0, + + ///////////////////////////////////////////////////////////////// + // + // i spy OPTIONS!!!! + // + + itemWidth: 40, + itemHeight: 40, + + itemMaxWidth: 150, + itemMaxHeight: 150, + + orientation: 'horizontal', + + conservativeTrigger: false, // don't active menu until mouse is over an image (macintosh style) + + effectUnits: 2, + itemPadding: 10, + + attachEdge: 'center', + labelEdge: 'bottom', + + enableCrappySvgSupport: false, + + + // + // + // + ///////////////////////////////////////////////////////////////// + + fillInTemplate: function(args, frag) { + //dojo.debug(this.orientation); + + dojo.html.disableSelection(this.domNode); + + this.isHorizontal = (this.orientation == 'horizontal') ? 1 : 0; + this.selectedNode = -1; + + this.isOver = false; + this.hitX1 = -1; + this.hitY1 = -1; + this.hitX2 = -1; + this.hitY2 = -1; + + // + // only some edges make sense... + // + + this.anchorEdge = this.toEdge(this.attachEdge, this.EDGE.CENTER); + this.labelEdge = this.toEdge(this.labelEdge, this.EDGE.TOP); + + if ( this.isHorizontal && (this.anchorEdge == this.EDGE.LEFT )){ this.anchorEdge = this.EDGE.CENTER; } + if ( this.isHorizontal && (this.anchorEdge == this.EDGE.RIGHT )){ this.anchorEdge = this.EDGE.CENTER; } + if (!this.isHorizontal && (this.anchorEdge == this.EDGE.TOP )){ this.anchorEdge = this.EDGE.CENTER; } + if (!this.isHorizontal && (this.anchorEdge == this.EDGE.BOTTOM)){ this.anchorEdge = this.EDGE.CENTER; } + + if (this.labelEdge == this.EDGE.CENTER){ this.labelEdge = this.EDGE.TOP; } + if ( this.isHorizontal && (this.labelEdge == this.EDGE.LEFT )){ this.labelEdge = this.EDGE.TOP; } + if ( this.isHorizontal && (this.labelEdge == this.EDGE.RIGHT )){ this.labelEdge = this.EDGE.TOP; } + if (!this.isHorizontal && (this.labelEdge == this.EDGE.TOP )){ this.labelEdge = this.EDGE.LEFT; } + if (!this.isHorizontal && (this.labelEdge == this.EDGE.BOTTOM)){ this.labelEdge = this.EDGE.LEFT; } + + + // + // figure out the proximity size + // + + this.proximityLeft = this.itemWidth * (this.effectUnits - 0.5); + this.proximityRight = this.itemWidth * (this.effectUnits - 0.5); + this.proximityTop = this.itemHeight * (this.effectUnits - 0.5); + this.proximityBottom = this.itemHeight * (this.effectUnits - 0.5); + + if (this.anchorEdge == this.EDGE.LEFT){ + this.proximityLeft = 0; + } + if (this.anchorEdge == this.EDGE.RIGHT){ + this.proximityRight = 0; + } + if (this.anchorEdge == this.EDGE.TOP){ + this.proximityTop = 0; + } + if (this.anchorEdge == this.EDGE.BOTTOM){ + this.proximityBottom = 0; + } + if (this.anchorEdge == this.EDGE.CENTER){ + this.proximityLeft /= 2; + this.proximityRight /= 2; + this.proximityTop /= 2; + this.proximityBottom /= 2; + } + }, + + postCreate: function(args, frag) { + this.initializePositioning(); + + // + // in liberal trigger mode, activate menu whenever mouse is close + // + if( !this.conservativeTrigger ){ + dojo.event.connect(document.documentElement, "onmousemove", this, "mouseHandler"); + } + + // Deactivate the menu if mouse is moved off screen (doesn't work for FF?) + dojo.event.connect(document.documentElement, "onmouseout", this, "onBodyOut"); + dojo.event.connect(this, "addChild", this, "initializePositioning"); + }, + + initializePositioning: function(){ + this.itemCount = this.children.length; + + this.barWidth = (this.isHorizontal ? this.itemCount : 1) * this.itemWidth; + this.barHeight = (this.isHorizontal ? 1 : this.itemCount) * this.itemHeight; + + this.totalWidth = this.proximityLeft + this.proximityRight + this.barWidth; + this.totalHeight = this.proximityTop + this.proximityBottom + this.barHeight; + + // + // calculate effect ranges for each item + // + + for (var i=0; i<this.children.length; i++){ + + this.children[i].posX = this.itemWidth * (this.isHorizontal ? i : 0); + this.children[i].posY = this.itemHeight * (this.isHorizontal ? 0 : i); + + this.children[i].cenX = this.children[i].posX + (this.itemWidth / 2); + this.children[i].cenY = this.children[i].posY + (this.itemHeight / 2); + + var isz = this.isHorizontal ? this.itemWidth : this.itemHeight; + var r = this.effectUnits * isz; + var c = this.isHorizontal ? this.children[i].cenX : this.children[i].cenY; + var lhs = this.isHorizontal ? this.proximityLeft : this.proximityTop; + var rhs = this.isHorizontal ? this.proximityRight : this.proximityBottom; + var siz = this.isHorizontal ? this.barWidth : this.barHeight; + + var range_lhs = r; + var range_rhs = r; + + if (range_lhs > c+lhs){ range_lhs = c+lhs; } + if (range_rhs > (siz-c+rhs)){ range_rhs = siz-c+rhs; } + + this.children[i].effectRangeLeft = range_lhs / isz; + this.children[i].effectRangeRght = range_rhs / isz; + + //dojo.debug('effect range for '+i+' is '+range_lhs+'/'+range_rhs); + } + + + // + // create the bar + // + + this.domNode.style.width = this.barWidth + 'px'; + this.domNode.style.height = this.barHeight + 'px'; + + + // + // position the items + // + for (var i=0; i<this.children.length; i++){ + var itm = this.children[i]; + var elm = itm.domNode; + elm.style.left = itm.posX + 'px'; + elm.style.top = itm.posY + 'px'; + elm.style.width = this.itemWidth + 'px'; + elm.style.height = this.itemHeight + 'px'; + + if ( itm.svgNode ) { + itm.svgNode.style.position = 'absolute'; + itm.svgNode.style.left = this.itemPadding+'%'; + itm.svgNode.style.top = this.itemPadding+'%'; + itm.svgNode.style.width = (100 - 2 * this.itemPadding) + '%'; + itm.svgNode.style.height = (100 - 2 * this.itemPadding) + '%'; + itm.svgNode.style.zIndex = 1; + + itm.svgNode.setSize(this.itemWidth, this.itemHeight); + } else { + itm.imgNode.style.left = this.itemPadding+'%'; + itm.imgNode.style.top = this.itemPadding+'%'; + itm.imgNode.style.width = (100 - 2 * this.itemPadding) + '%'; + itm.imgNode.style.height = (100 - 2 * this.itemPadding) + '%'; + } + } + + // + // calc the grid + // + + this.calcHitGrid(); + }, + + onBodyOut: function(e){ + // clicking over an object inside of body causes this event to fire; ignore that case + if( dojo.html.overElement(dojo.body(), e) ){ + return; + } + this.setDormant(e); + }, + + // when mouse moves out of menu's range + setDormant: function(e){ + if( !this.isOver ){ return; } // already dormant? + this.isOver = false; + + if ( this.conservativeTrigger ) { + // user can't re-trigger the menu expansion + // until he mouses over a icon again + dojo.event.disconnect(document.documentElement, "onmousemove", this, "mouseHandler"); + } + this.onGridMouseMove(-1, -1); + }, + + // when mouse is moved into menu's range + setActive: function(e){ + if( this.isOver ){ return; } // already activated? + this.isOver = true; + + if ( this.conservativeTrigger ) { + // switch event handlers so that we handle mouse events from anywhere near + // the menu + dojo.event.connect(document.documentElement, "onmousemove", this, "mouseHandler"); + + this.timerScale=0.0; + + // call mouse handler to do some initial necessary calculations/positioning + this.mouseHandler(e); + + // slowly expand the icon size so it isn't jumpy + this.expandSlowly(); + } + }, + + // when mouse is moved + mouseHandler: function(e) { + if ((e.pageX >= this.hitX1) && (e.pageX <= this.hitX2) && + (e.pageY >= this.hitY1) && (e.pageY <= this.hitY2)){ + if( !this.isOver ){ + this.setActive(e); + } + this.onGridMouseMove(e.pageX-this.hitX1, e.pageY-this.hitY1); + }else{ + if (this.isOver){ + this.setDormant(e); + } + } + }, + + onResized: function() { + this.calcHitGrid(); + }, + + onGridMouseMove: function(x, y){ + this.pos = {x:x, y:y}; + this.paint(); + }, + + paint: function(){ + var x=this.pos.x; + var y=this.pos.y; + + if( this.itemCount <= 0 ){ return; } + + // + // figure out our main index + // + + var pos = this.isHorizontal ? x : y; + var prx = this.isHorizontal ? this.proximityLeft : this.proximityTop; + var siz = this.isHorizontal ? this.itemWidth : this.itemHeight; + var sim = this.isHorizontal ? + (1.0-this.timerScale)*this.itemWidth + this.timerScale*this.itemMaxWidth : + (1.0-this.timerScale)*this.itemHeight + this.timerScale*this.itemMaxHeight ; + + var cen = ((pos - prx) / siz) - 0.5; + var max_off_cen = (sim / siz) - 0.5; + + if (max_off_cen > this.effectUnits){ max_off_cen = this.effectUnits; } + + + // + // figure out our off-axis weighting + // + + var off_weight = 0; + + if (this.anchorEdge == this.EDGE.BOTTOM){ + var cen2 = (y - this.proximityTop) / this.itemHeight; + off_weight = (cen2 > 0.5) ? 1 : y / (this.proximityTop + (this.itemHeight / 2)); + } + if (this.anchorEdge == this.EDGE.TOP){ + var cen2 = (y - this.proximityTop) / this.itemHeight; + off_weight = (cen2 < 0.5) ? 1 : (this.totalHeight - y) / (this.proximityBottom + (this.itemHeight / 2)); + } + if (this.anchorEdge == this.EDGE.RIGHT){ + var cen2 = (x - this.proximityLeft) / this.itemWidth; + off_weight = (cen2 > 0.5) ? 1 : x / (this.proximityLeft + (this.itemWidth / 2)); + } + if (this.anchorEdge == this.EDGE.LEFT){ + var cen2 = (x - this.proximityLeft) / this.itemWidth; + off_weight = (cen2 < 0.5) ? 1 : (this.totalWidth - x) / (this.proximityRight + (this.itemWidth / 2)); + } + if (this.anchorEdge == this.EDGE.CENTER){ + + if (this.isHorizontal){ + off_weight = y / (this.totalHeight); + }else{ + off_weight = x / (this.totalWidth); + } + + if (off_weight > 0.5){ + off_weight = 1 - off_weight; + } + + off_weight *= 2; + } + + + // + // set the sizes + // + + for(var i=0; i<this.itemCount; i++){ + + var weight = this.weightAt(cen, i); + + if (weight < 0){weight = 0;} + + this.setitemsize(i, weight * off_weight); + } + + // + // set the positions + // + + var main_p = Math.round(cen); + var offset = 0; + + if (cen < 0){ + main_p = 0; + + }else if (cen > this.itemCount - 1){ + + main_p = this.itemCount -1; + + }else{ + + offset = (cen - main_p) * ((this.isHorizontal ? this.itemWidth : this.itemHeight) - this.children[main_p].sizeMain); + } + + this.positionElementsFrom(main_p, offset); + }, + + weightAt: function(cen, i){ + + var dist = Math.abs(cen - i); + + var limit = ((cen - i) > 0) ? this.children[i].effectRangeRght : this.children[i].effectRangeLeft; + + return (dist > limit) ? 0 : (1 - dist / limit); + }, + + positionFromNode: function(p, w){ + + // + // we need to grow all the nodes growing out from node 'i' + // + + this.setitemsize(p, w); + + var wx = w; + for(var i=p; i<this.itemCount; i++){ + wx = 0.8 * wx; + this.setitemsize(i, wx); + } + + var wx = w; + for(var i=p; i>=0; i--){ + wx = 0.8 * wx; + this.setitemsize(i, wx); + } + }, + + setitemsize: function(p, scale){ + scale *= this.timerScale; + var w = Math.round(this.itemWidth + ((this.itemMaxWidth - this.itemWidth ) * scale)); + var h = Math.round(this.itemHeight + ((this.itemMaxHeight - this.itemHeight) * scale)); + + if (this.isHorizontal){ + + this.children[p].sizeW = w; + this.children[p].sizeH = h; + + this.children[p].sizeMain = w; + this.children[p].sizeOff = h; + + var y = 0; + + if (this.anchorEdge == this.EDGE.TOP){ + + y = (this.children[p].cenY - (this.itemHeight / 2)); + + }else if (this.anchorEdge == this.EDGE.BOTTOM){ + + y = (this.children[p].cenY - (h - (this.itemHeight / 2))); + + }else{ + + y = (this.children[p].cenY - (h / 2)); + } + + this.children[p].usualX = Math.round(this.children[p].cenX - (w / 2)); + + this.children[p].domNode.style.top = y + 'px'; + + this.children[p].domNode.style.left = this.children[p].usualX + 'px'; + + }else{ + + this.children[p].sizeW = w; + this.children[p].sizeH = h; + + this.children[p].sizeOff = w; + this.children[p].sizeMain = h; + + var x = 0; + + if (this.anchorEdge == this.EDGE.LEFT){ + + x = this.children[p].cenX - (this.itemWidth / 2); + + }else if (this.anchorEdge == this.EDGE.RIGHT){ + + x = this.children[p].cenX - (w - (this.itemWidth / 2)); + }else{ + + x = this.children[p].cenX - (w / 2); + } + + this.children[p].domNode.style.left = x + 'px'; + this.children[p].usualY = Math.round(this.children[p].cenY - (h / 2)); + + this.children[p].domNode.style.top = this.children[p].usualY + 'px'; + } + + this.children[p].domNode.style.width = w + 'px'; + this.children[p].domNode.style.height = h + 'px'; + + if (this.children[p].svgNode){ + this.children[p].svgNode.setSize(w, h); + } + }, + + positionElementsFrom: function(p, offset){ + + var pos = 0; + + if (this.isHorizontal){ + pos = Math.round(this.children[p].usualX + offset); + this.children[p].domNode.style.left = pos + 'px'; + }else{ + pos = Math.round(this.children[p].usualY + offset); + this.children[p].domNode.style.top = pos + 'px'; + } + this.positionLabel(this.children[p]); + + + // + // position before + // + + var bpos = pos; + + for(var i=p-1; i>=0; i--){ + + bpos -= this.children[i].sizeMain; + + if (this.isHorizontal){ + this.children[i].domNode.style.left = bpos + 'px'; + }else{ + this.children[i].domNode.style.top = bpos + 'px'; + } + this.positionLabel(this.children[i]); + } + + // + // position after + // + + var apos = pos; + + for(var i=p+1; i<this.itemCount; i++){ + + apos += this.children[i-1].sizeMain; + + if (this.isHorizontal){ + this.children[i].domNode.style.left = apos + 'px'; + }else{ + this.children[i].domNode.style.top = apos + 'px'; + } + this.positionLabel(this.children[i]); + } + + }, + + positionLabel: function(itm){ + + var x = 0; + var y = 0; + + var mb = dojo.html.getMarginBox(itm.lblNode); + + if (this.labelEdge == this.EDGE.TOP){ + x = Math.round((itm.sizeW / 2) - (mb.width / 2)); + y = -mb.height; + } + + if (this.labelEdge == this.EDGE.BOTTOM){ + x = Math.round((itm.sizeW / 2) - (mb.width / 2)); + y = itm.sizeH; + } + + if (this.labelEdge == this.EDGE.LEFT){ + x = -mb.width; + y = Math.round((itm.sizeH / 2) - (mb.height / 2)); + } + + if (this.labelEdge == this.EDGE.RIGHT){ + x = itm.sizeW; + y = Math.round((itm.sizeH / 2) - (mb.height / 2)); + } + + itm.lblNode.style.left = x + 'px'; + itm.lblNode.style.top = y + 'px'; + }, + + calcHitGrid: function(){ + + var pos = dojo.html.getAbsolutePosition(this.domNode, true); + + this.hitX1 = pos.x - this.proximityLeft; + this.hitY1 = pos.y - this.proximityTop; + this.hitX2 = this.hitX1 + this.totalWidth; + this.hitY2 = this.hitY1 + this.totalHeight; + + //dojo.debug(this.hitX1+','+this.hitY1+' // '+this.hitX2+','+this.hitY2); + }, + + toEdge: function(inp, def){ + return this.EDGE[inp.toUpperCase()] || def; + }, + + // slowly expand the image to user specified max size + expandSlowly: function(){ + if( !this.isOver ){ return; } + this.timerScale += 0.2; + this.paint(); + if ( this.timerScale<1.0 ) { + dojo.lang.setTimeout(this, "expandSlowly", 10); + } + }, + + destroy: function(){ + // need to disconnect when we destroy + dojo.event.disconnect(document.documentElement, "onmouseout", this, "onBodyOut"); + dojo.event.disconnect(document.documentElement, "onmousemove", this, "mouseHandler"); + dojo.widget.FisheyeList.superclass.destroy.call(this); + } +}); + +dojo.widget.defineWidget( + "dojo.widget.FisheyeListItem", + dojo.widget.HtmlWidget, +{ + // Constructor arguments + iconSrc: "", + svgSrc: "", + caption: "", + + blankImgPath: dojo.uri.dojoUri("src/widget/templates/images/blank.gif"), + + templateString: + '<div class="dojoHtmlFisheyeListItem">' + + ' <img class="dojoHtmlFisheyeListItemImage" dojoAttachPoint="imgNode" dojoAttachEvent="onMouseOver;onMouseOut;onClick">' + + ' <div class="dojoHtmlFisheyeListItemLabel" dojoAttachPoint="lblNode"></div>' + + '</div>', + + imgNode: null, + + fillInTemplate: function() { + // + // set image + // TODO: turn on/off SVG support based on browser version. + // this.parent.enableCrappySvgSupport is not available to this function + // + if (this.svgSrc != ""){ + this.svgNode = this.createSvgNode(this.svgSrc); + this.domNode.appendChild(this.svgNode); + this.imgNode.style.display = 'none'; + } else if((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4)==".png")&&(dojo.render.html.ie)&&(!dojo.render.html.ie70)){ + this.imgNode.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.iconSrc+"', sizingMethod='scale')"; + this.imgNode.src = this.blankImgPath.toString(); + } else { + this.imgNode.src = this.iconSrc; + } + + // + // Label + // + if ( this.lblNode ) { + this.lblNode.appendChild(document.createTextNode(this.caption)); + } + dojo.html.disableSelection(this.domNode); + }, + + createSvgNode: function(src){ + + var elm = document.createElement('embed'); + elm.src = src; + elm.type = 'image/svg+xml'; + //elm.style.border = '1px solid black'; + elm.style.width = '1px'; + elm.style.height = '1px'; + elm.loaded = 0; + elm.setSizeOnLoad = false; + + elm.onload = function(){ + this.svgRoot = this.getSVGDocument().rootElement; + this.svgDoc = this.getSVGDocument().documentElement; + this.zeroWidth = this.svgRoot.width.baseVal.value; + this.zeroHeight = this.svgRoot.height.baseVal.value; + this.loaded = true; + + if (this.setSizeOnLoad){ + this.setSize(this.setWidth, this.setHeight); + } + } + + elm.setSize = function(w, h){ + if (!this.loaded){ + this.setWidth = w; + this.setHeight = h; + this.setSizeOnLoad = true; + return; + } + + this.style.width = w+'px'; + this.style.height = h+'px'; + this.svgRoot.width.baseVal.value = w; + this.svgRoot.height.baseVal.value = h; + + var scale_x = w / this.zeroWidth; + var scale_y = h / this.zeroHeight; + + for(var i=0; i<this.svgDoc.childNodes.length; i++){ + if (this.svgDoc.childNodes[i].setAttribute){ + this.svgDoc.childNodes[i].setAttribute( "transform", "scale("+scale_x+","+scale_y+")" ); + } + } + } + + return elm; + }, + + onMouseOver: function(e) { + // in conservative mode, don't activate the menu until user mouses over an icon + if( !this.parent.isOver ){ + this.parent.setActive(e); + } + if ( this.caption != "" ) { + dojo.html.addClass(this.lblNode, "selected"); + this.parent.positionLabel(this); + } + }, + + onMouseOut: function() { + dojo.html.removeClass(this.lblNode, "selected"); + }, + + onClick: function() { + } +}); + diff --git a/source/web/scripts/ajax/src/widget/FloatingPane.js b/source/web/scripts/ajax/src/widget/FloatingPane.js new file mode 100644 index 0000000000..8582c4084c --- /dev/null +++ b/source/web/scripts/ajax/src/widget/FloatingPane.js @@ -0,0 +1,354 @@ +dojo.provide("dojo.widget.FloatingPane"); + +// +// this widget provides a window-like floating pane +// + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.Manager"); +dojo.require("dojo.html.*"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.html.shadow"); +dojo.require("dojo.html.iframe"); +dojo.require("dojo.html.selection"); +dojo.require("dojo.widget.html.layout"); +dojo.require("dojo.widget.ContentPane"); +dojo.require("dojo.dnd.HtmlDragMove"); +dojo.require("dojo.dnd.HtmlDragMoveSource"); +dojo.require("dojo.dnd.HtmlDragMoveObject"); +dojo.require("dojo.widget.ResizeHandle"); + +dojo.widget.defineWidget( + "dojo.widget.FloatingPane", + dojo.widget.ContentPane, + { + // Constructor arguments + title: '', + iconSrc: '', + hasShadow: false, + constrainToContainer: false, + taskBarId: "", + resizable: true, + titleBarDisplay: "fancy", + + windowState: "normal", + displayCloseAction: false, + displayMinimizeAction: false, + displayMaximizeAction: false, + + maxTaskBarConnectAttempts: 5, + taskBarConnectAttempts: 0, + + templatePath: dojo.uri.dojoUri("src/widget/templates/FloatingPane.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/FloatingPane.css"), + + drag: null, + + fillInTemplate: function(args, frag){ + // Copy style info from input node to output node + var source = this.getFragNodeRef(frag); + dojo.html.copyStyle(this.domNode, source); + + // necessary for safari, khtml (for computing width/height) + dojo.body().appendChild(this.domNode); + + // if display:none then state=minimized, otherwise state=normal + if(!this.isShowing()){ + this.windowState="minimized"; + } + + // <img src=""> can hang IE! better get rid of it + if(this.iconSrc==""){ + dojo.html.removeNode(this.titleBarIcon); + }else{ + this.titleBarIcon.src = this.iconSrc.toString();// dojo.uri.Uri obj req. toString() + } + + if(this.titleBarDisplay!="none"){ + this.titleBar.style.display=""; + dojo.html.disableSelection(this.titleBar); + + this.titleBarIcon.style.display = (this.iconSrc=="" ? "none" : ""); + + this.minimizeAction.style.display = (this.displayMinimizeAction ? "" : "none"); + this.maximizeAction.style.display= + (this.displayMaximizeAction && this.windowState!="maximized" ? "" : "none"); + this.restoreAction.style.display= + (this.displayMaximizeAction && this.windowState=="maximized" ? "" : "none"); + this.closeAction.style.display= (this.displayCloseAction ? "" : "none"); + + this.drag = new dojo.dnd.HtmlDragMoveSource(this.domNode); + if (this.constrainToContainer) { + this.drag.constrainTo(); + } + this.drag.setDragHandle(this.titleBar); + + var self = this; + + dojo.event.topic.subscribe("dragMove", + function (info){ + if (info.source.domNode == self.domNode){ + dojo.event.topic.publish('floatingPaneMove', { source: self } ); + } + } + ); + } + + if(this.resizable){ + this.resizeBar.style.display=""; + this.resizeHandle = dojo.widget.createWidget("ResizeHandle", {targetElmId: this.widgetId, id:this.widgetId+"_resize"}); + this.resizeBar.appendChild(this.resizeHandle.domNode); + } + + // add a drop shadow + if(this.hasShadow){ + this.shadow=new dojo.html.shadow(this.domNode); + } + + // Prevent IE bleed-through problem + this.bgIframe = new dojo.html.BackgroundIframe(this.domNode); + + if( this.taskBarId ){ + this.taskBarSetup(); + } + + // counteract body.appendChild above + dojo.body().removeChild(this.domNode); + + dojo.widget.FloatingPane.superclass.fillInTemplate.call(this, args, frag); + }, + + postCreate: function(){ + if (dojo.hostenv.post_load_) { + this.setInitialWindowState(); + } else { + dojo.addOnLoad(this, "setInitialWindowState"); + } + }, + + maximizeWindow: function(evt) { + var mb = dojo.html.getMarginBox(this.domNode); + this.previous={ + width: mb.width || this.width, + height: mb.height || this.height, + left: this.domNode.style.left, + top: this.domNode.style.top, + bottom: this.domNode.style.bottom, + right: this.domNode.style.right + }; + if(this.domNode.parentNode.style.overflow.toLowerCase() != 'hidden'){ + this.parentPrevious={ + overflow: this.domNode.parentNode.style.overflow + }; + dojo.debug(this.domNode.parentNode.style.overflow); + this.domNode.parentNode.style.overflow = 'hidden'; + } + + this.domNode.style.left = + dojo.html.getPixelValue(this.domNode.parentNode, "padding-left", true) + "px"; + this.domNode.style.top = + dojo.html.getPixelValue(this.domNode.parentNode, "padding-top", true) + "px"; + + if ((this.domNode.parentNode.nodeName.toLowerCase() == 'body')) { + var viewport = dojo.html.getViewport(); + var padding = dojo.html.getPadding(dojo.body()); + this.resizeTo(viewport.width-padding.width, viewport.height-padding.height); + } else { + var content = dojo.html.getContentBox(this.domNode.parentNode); + this.resizeTo(content.width, content.height); + } + this.maximizeAction.style.display="none"; + this.restoreAction.style.display=""; + + //disable resize and drag + if(this.resizeHandle){ + this.resizeHandle.domNode.style.display="none"; + } + this.drag.setDragHandle(null); + + this.windowState="maximized"; + }, + + minimizeWindow: function(evt) { + this.hide(); + for(var attr in this.parentPrevious){ + this.domNode.parentNode.style[attr] = this.parentPrevious[attr]; + } + this.lastWindowState = this.windowState; + this.windowState = "minimized"; + }, + + restoreWindow: function(evt) { + if (this.windowState=="minimized") { + this.show(); + if(this.lastWindowState == "maximized"){ + this.domNode.parentNode.style.overflow = 'hidden'; + this.windowState="maximized"; + }else{ //normal + this.windowState="normal"; + } + } else if (this.windowState=="maximized"){ + for(var attr in this.previous){ + this.domNode.style[attr] = this.previous[attr]; + } + for(var attr in this.parentPrevious){ + this.domNode.parentNode.style[attr] = this.parentPrevious[attr]; + } + this.resizeTo(this.previous.width, this.previous.height); + this.previous=null; + this.parentPrevious=null; + + this.restoreAction.style.display="none"; + this.maximizeAction.style.display=this.displayMaximizeAction ? "" : "none"; + + if(this.resizeHandle){ + this.resizeHandle.domNode.style.display=""; + } + this.drag.setDragHandle(this.titleBar); + this.windowState="normal"; + } else { //normal + // do nothing + } + }, + + toggleDisplay: function(){ + if(this.windowState=="minimized"){ + this.restoreWindow(); + }else{ + this.minimizeWindow(); + } + }, + + closeWindow: function(evt) { + dojo.html.removeNode(this.domNode); + this.destroy(); + }, + + onMouseDown: function(evt) { + this.bringToTop(); + }, + + bringToTop: function() { + var floatingPanes= dojo.widget.manager.getWidgetsByType(this.widgetType); + var windows = []; + for (var x=0; x<floatingPanes.length; x++) { + if (this.widgetId != floatingPanes[x].widgetId) { + windows.push(floatingPanes[x]); + } + } + + windows.sort(function(a,b) { + return a.domNode.style.zIndex - b.domNode.style.zIndex; + }); + + windows.push(this); + + var floatingPaneStartingZ = 100; + for (x=0; x<windows.length;x++) { + windows[x].domNode.style.zIndex = floatingPaneStartingZ + x*2; + } + }, + + setInitialWindowState: function() { + if(this.isShowing()){ + this.width=-1; // force resize + var mb = dojo.html.getMarginBox(this.domNode); + this.resizeTo(mb.width, mb.height); + } + if (this.windowState == "maximized") { + this.maximizeWindow(); + this.show(); + return; + } + + if (this.windowState=="normal") { + this.show(); + return; + } + + if (this.windowState=="minimized") { + this.hide(); + return; + } + + this.windowState="minimized"; + }, + + // add icon to task bar, connected to me + taskBarSetup: function() { + var taskbar = dojo.widget.getWidgetById(this.taskBarId); + if (!taskbar){ + if (this.taskBarConnectAttempts < this.maxTaskBarConnectAttempts) { + dojo.lang.setTimeout(this, this.taskBarSetup, 50); + this.taskBarConnectAttempts++; + } else { + dojo.debug("Unable to connect to the taskBar"); + } + return; + } + taskbar.addChild(this); + }, + + show: function(){ + dojo.widget.FloatingPane.superclass.show.apply(this, arguments); + this.bringToTop(); + }, + + onShow: function(){ + dojo.widget.FloatingPane.superclass.onShow.call(this); + var mb = dojo.html.getMarginBox(this.domNode); + this.resizeTo(mb.width, mb.height); + }, + + // This is called when the user adjusts the size of the floating pane + resizeTo: function(w, h){ + dojo.html.setMarginBox(this.domNode, { width: w, height: h }); + + dojo.widget.html.layout(this.domNode, + [ + {domNode: this.titleBar, layoutAlign: "top"}, + {domNode: this.resizeBar, layoutAlign: "bottom"}, + {domNode: this.containerNode, layoutAlign: "client"} + ] ); + + // If any of the children have layoutAlign specified, obey it + dojo.widget.html.layout(this.containerNode, this.children, "top-bottom"); + + this.bgIframe.onResized(); + if(this.shadow){ this.shadow.size(w, h); } + this.onResized(); + }, + + checkSize: function() { + // checkSize() is called when the user has resized the browser window, + // but that doesn't affect this widget (or this widget's children) + // so it can be safely ignored... + // TODO: unless we are maximized. then we should resize ourself. + } + } +); + +dojo.require("dojo.widget.Dialog"); +dojo.widget.defineWidget( + "dojo.widget.ModalFloatingPane", + [dojo.widget.FloatingPane, dojo.widget.ModalDialogBase], + { + windowState: "minimized", + displayCloseAction: true, + postCreate: function(){ + dojo.widget.ModalDialogBase.prototype.postCreate.call(this); + dojo.widget.ModalFloatingPane.superclass.postCreate.call(this); + }, + show: function(){ + dojo.widget.ModalFloatingPane.superclass.show.apply(this, arguments); + dojo.widget.ModalDialogBase.prototype.show.call(this); + this.placeModalDialog(); + //place the background div under this modal pane + this.shared.bg.style.zIndex = this.domNode.style.zIndex-1; + }, + closeWindow: function(){ + this.hide(); + dojo.widget.ModalFloatingPane.superclass.closeWindow.apply(this, arguments); + } + } +); \ No newline at end of file diff --git a/source/web/scripts/ajax/src/widget/GoogleMap.js b/source/web/scripts/ajax/src/widget/GoogleMap.js new file mode 100644 index 0000000000..2b6589101c --- /dev/null +++ b/source/web/scripts/ajax/src/widget/GoogleMap.js @@ -0,0 +1,198 @@ +dojo.provide("dojo.widget.GoogleMap"); +dojo.require("dojo.event.*"); +dojo.require("dojo.math"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.uri.Uri"); +dojo.require("dojo.widget.HtmlWidget"); + +(function(){ + var gkey = djConfig["gMapKey"]||djConfig["googleMapKey"]; + + // the Google API key mechanism sucks. We're hardcoding here for love and affection but I don't like it. + var uri=new dojo.uri.Uri(window.location.href); + if(uri.host=="www.dojotoolkit.org"){ + gkey="ABQIAAAACUNdgv_7FGOmUslbm9l6_hRqjp7ri2mNiOEYqetD3xnFHpt5rBSjszDd1sdufPyQKUTyCf_YxoIxvw"; + } + else if(uri.host=="blog.dojotoolkit.org"){ + gkey="ABQIAAAACUNdgv_7FGOmUslbm9l6_hSkep6Av1xaMhVn3yCLkorJeXeLARQ6fammI_P3qSGleTJhoI5_1JmP_Q"; + } + else if(uri.host=="archive.dojotoolkit.org"){ + gkey="ABQIAAAACUNdgv_7FGOmUslbm9l6_hTaQpDt0dyGLIHbXMPTzg1kWeAfwRTwZNyrUfbfxYE9yIvRivEjcXoDTg"; + } + else if(uri.host=="dojotoolkit.org"){ + gkey="ABQIAAAACUNdgv_7FGOmUslbm9l6_hSaOaO_TgJ5c3mtQFnk5JO2zD5dZBRZk-ieqVs7BORREYNzAERmcJoEjQ"; + } + + if(!dojo.hostenv.post_load_){ + if(!gkey || gkey==""){ + dojo.raise("dojo.widget.GoogleMap: The Google Map widget requires a proper API key in order to be used."); + } + var tag = "<scr"+"ipt src='http://maps.google.com/maps?file=api&v=2&key="+gkey+"'></scri"+"pt>"; + if(!dj_global["GMap2"]){ + document.write(tag); + } + }else{ + dojo.debug("Cannot initialize Google Map system after the page has been loaded! Please either manually include the script block provided by Google in your page or require() the GoogleMap widget before onload has fired."); + } +})(); + +dojo.widget.defineWidget( + "dojo.widget.GoogleMap", + dojo.widget.HtmlWidget, + function(){ + this.map=null; + this.geocoder=null; + this.data=[]; + this.datasrc=""; + this.controls=["largemap","scale","maptype"]; + }, +{ + templatePath:null, + templateCssPath:null, + isContainer: false, + + _defaultPoint:{lat:39.10662, lng: -94.578209}, + + setControls:function(){ + var methodmap={ + largemap:GLargeMapControl, + smallmap:GSmallMapControl, + smallzoom:GSmallZoomControl, + scale:GScaleControl, + maptype:GMapTypeControl, + overview:GOverviewMapControl + }; + for(var i=0; i<this.controls.length; i++){ + this.map.addControl(new (methodmap[this.controls[i].toLowerCase()])()); + } + }, + + findCenter:function(bounds){ + if(this.data.length==1){ + return (new GLatLng(this.data[0].lat, this.data[0].lng)); + } + var clat=(bounds.getNorthEast().lat()+bounds.getSouthWest().lat())/2; + var clng=(bounds.getNorthEast().lng()+bounds.getSouthWest().lng())/2; + return (new GLatLng(clat,clng)); + }, + + createPinpoint:function(pt,overlay){ + var m=new GMarker(pt); + if(overlay){ + GEvent.addListener(m,"click",function(){ + m.openInfoWindowHtml("<div>"+overlay+"</div>"); + }); + } + return m; + }, + plot:function(obj){ + var p=new GLatLng(obj.lat,obj.lng); + var d=obj.description||null; + var m=this.createPinpoint(p,d); + this.map.addOverlay(m); + }, + plotAddress:function(address){ + var self=this; + this.geocoder.getLocations(address, function(response){ + if(!response || response.Status.code != 200){ + alert("The address \"" + address + "\" was not found."); + return; + } + var obj={ + lat:response.Placemark[0].Point.coordinates[1], + lng:response.Placemark[0].Point.coordinates[0], + description:response.Placemark[0].address + }; + self.data.push(obj); + self.render(); + }); + }, + + parse:function(table){ + this.data=[]; + + // get the column indices + var h=table.getElementsByTagName("thead")[0]; + if(!h){ + return; + } + + var a=[]; + var cols=h.getElementsByTagName("td"); + if(cols.length==0){ + cols=h.getElementsByTagName("th"); + } + for(var i=0; i<cols.length; i++){ + var c=cols[i].innerHTML.toLowerCase(); + if(c=="long") c="lng"; + a.push(c); + } + + // parse the data + var b=table.getElementsByTagName("tbody")[0]; + if(!b){ + return; + } + for(var i=0; i<b.childNodes.length; i++){ + if(!(b.childNodes[i].nodeName&&b.childNodes[i].nodeName.toLowerCase()=="tr")){ + continue; + } + var cells=b.childNodes[i].getElementsByTagName("td"); + var o={}; + for(var j=0; j<a.length; j++){ + var col=a[j]; + if(col=="lat"||col=="lng"){ + o[col]=parseFloat(cells[j].innerHTML); + }else{ + o[col]=cells[j].innerHTML; + } + } + this.data.push(o); + } + }, + render:function(){ + if(this.data.length==0){ + this.map.setCenter(new GLatLng(this._defaultPoint.lat, this._defaultPoint.lng), 4); + return; + } + + // remove all overlays + this.map.clearOverlays(); + + var bounds=new GLatLngBounds(); + var d=this.data; + for(var i=0; i<d.length; i++){ + bounds.extend(new GLatLng(d[i].lat,d[i].lng)); + } + var zoom=Math.min((this.map.getBoundsZoomLevel(bounds)-1),14); + this.map.setCenter(this.findCenter(bounds), zoom); + + for(var i=0; i<this.data.length; i++){ + this.plot(this.data[i]); + } + }, + + initialize:function(args, frag){ + if(this.datasrc){ + this.parse(dojo.byId(this.datasrc)); + } + else if(this.domNode.getElementsByTagName("table")[0]){ + this.parse(this.domNode.getElementsByTagName("table")[0]); + } + }, + postCreate:function(){ + // clean the domNode before creating the map. + while(this.domNode.childNodes.length>0){ + this.domNode.removeChild(this.domNode.childNodes[0]); + } + if(this.domNode.style.position!="absolute"){ + this.domNode.style.position="relative"; + } + this.map=new GMap2(this.domNode); + try{ + this.geocoder=new GClientGeocoder(); + }catch(ex){} + this.render(); + this.setControls(); + } +}); diff --git a/source/web/scripts/ajax/src/widget/HslColorPicker.js b/source/web/scripts/ajax/src/widget/HslColorPicker.js new file mode 100644 index 0000000000..c93af0a618 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/HslColorPicker.js @@ -0,0 +1,129 @@ +dojo.provide("dojo.widget.svg.HslColorPicker"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.HslColorPicker"); +dojo.require("dojo.math"); +dojo.require("dojo.svg"); +dojo.require("dojo.graphics.color"); +dojo.require("dojo.graphics.color.hsl"); + +dojo.widget.defineWidget( + "dojo.widget.svg.HslColorPicker", + dojo.widget.HtmlWidget, + function(){ + dojo.debug("warning: the HslColorPicker is not a finished widget, and is not yet ready for general use"); + this.filterObject = {}; + }, +{ + hue: "0", + saturation: "0", + light: "0", + storedColor: "#0054aa", + + // widget props + templatePath: dojo.uri.dojoUri("src/widget/templates/HslColorPicker.svg"), + fillInTemplate: function() { + this.height = "131px"; + this.svgDoc = this.hueNode.ownerDocument; + this.leftGradientColorNode = this.hueNode.ownerDocument.getElementById("leftGradientColor"); + this.rightGradientColorNode = this.hueNode.ownerDocument.getElementById("rightGradientColor"); + this.hueNode.setAttributeNS(dojo.dom.xmlns.xlink, "href", dojo.uri.dojoUri("src/widget/templates/images/hue.png")); + var hsl = dojo.graphics.color.hex2hsl(this.storedColor); + this.hue = hsl[0]; + this.saturation = hsl[1]; + this.light = hsl[2]; + this.setSaturationStopColors(); + //this.setHueSlider(); + //this.setSaturationLightSlider(); + }, + setSaturationStopColors: function() { + //this.leftGradientStopColor = "rgb(" + dojo.graphics.color.hsl2rgb(this.hue, 20, 50).join(", ") + ")"; + //this.rightGradientStopColor = "rgb(" + dojo.graphics.color.hsl2rgb(this.hue, 100, 50).join(", ") + ")"; + //this.leftGradientStopColor = dojo.graphics.color.hsl2hex(this.hue, 20, 50); + //this.rightGradientStopColor = dojo.graphics.color.hsl2hex(this.hue, 100, 50); + this.leftGradientStopColor = dojo.graphics.color.rgb2hex(this.hsl2rgb(this.hue, 0, 50)); + this.rightGradientStopColor = dojo.graphics.color.rgb2hex(this.hsl2rgb(this.hue, 100, 50)); + this.leftGradientColorNode.setAttributeNS(null,'stop-color',this.leftGradientStopColor); + this.rightGradientColorNode.setAttributeNS(null,'stop-color',this.rightGradientStopColor); + }, + setHue: function(hue) { + this.hue = hue; + }, + setHueSlider: function() { + // FIXME: need to add some padding around the picker so you can see the slider at the top and bottom of the picker) + this.hueSliderNode.setAttribute("y", parseInt((this.hue/360) * parseInt(this.height) - 2) + "px" ); + }, + setSaturationLight: function(saturation, light) { + this.saturation = saturation; + this.light = light; + }, + setSaturationLightSlider: function() { + // TODO + }, + onHueClick: function(evt) { + // get the position that was clicked on the element + // FIXME: handle document scrolling, offset + var yPosition = parseInt(evt.clientY) - parseInt(evt.target.getAttribute("y")); + this.setHue( 360 - parseInt(yPosition*(360/parseInt(this.height))) ); + this.setSaturationStopColors(); + this.setStoredColor(dojo.graphics.color.hsl2hex(this.hue, this.saturation, this.light)); + }, + onHueDrag: function(evt) { + // TODO + }, + onSaturationLightClick: function(evt) { + // get the position that was clicked on the element + // FIXME: handle document scrolling, offset + var xPosition = parseInt(evt.clientX) - parseInt(evt.target.getAttribute("y")); + var yPosition = parseInt(evt.clientY) - parseInt(evt.target.getAttribute("y")); + var saturation = parseInt(parseInt(xPosition)*(101/106)); + var light = parseInt(parseInt(yPosition)*(101/106)); + this.setSaturationLight(saturation, light); + this.setStoredColor(dojo.graphics.color.hsl2hex(this.hue, this.saturation, this.light)); + }, + onSaturationLightDrag: function(evt) { + // TODO + }, + getStoredColor: function() { + return this.storedColor; + }, + setStoredColor: function(rgbHexColor) { + this.storedColor = rgbHexColor; + dojo.event.topic.publish("/" + this.widgetId + "/setStoredColor", this.filterObject); + }, + hsl2rgb: function(hue, saturation, light) + { + // hsl2rgb in dojo.graphics.color did not behave hte way I expected, so + // I'm using some old code I wrote until I figure out what the issue is + // first, check to see if saturation = 0 + function rgb(q1,q2,hue) { + if (hue>360) hue=hue-360; + if (hue<0) hue=hue+360; + if (hue<60) return (q1+(q2-q1)*hue/60); + else if (hue<180) return(q2); + else if (hue<240) return(q1+(q2-q1)*(240-hue)/60); + else return(q1); + } + this.rgb = rgb + + if (saturation==0) { + return [Math.round(light*255/100), Math.round(light*255/100), Math.round(light*255/100)]; + } else { + light = light/100; + saturation = saturation/100; + // check to see if light > 0.5 + if ((light)<0.5) { + var temp2 = (light)*(1.0+saturation) + } else { + var temp2 = (light+saturation-(light*saturation)) + } + var temp1 = 2.0*light - temp2; + var rgbcolor = []; + rgbcolor[0] = Math.round(rgb(temp1,temp2,parseInt(hue)+120)*255); + rgbcolor[1] = Math.round(rgb(temp1,temp2,hue)*255); + rgbcolor[2] = Math.round(rgb(temp1,temp2,parseInt(hue)-120)*255); + return rgbcolor; + } + } +}); diff --git a/source/web/scripts/ajax/src/widget/HtmlWidget.js b/source/web/scripts/ajax/src/widget/HtmlWidget.js new file mode 100644 index 0000000000..a7fd91ffba --- /dev/null +++ b/source/web/scripts/ajax/src/widget/HtmlWidget.js @@ -0,0 +1,154 @@ +dojo.provide("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.DomWidget"); +dojo.require("dojo.html.util"); +dojo.require("dojo.html.display"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.lang.extras"); +dojo.require("dojo.lang.func"); +dojo.require("dojo.lfx.toggle"); + +dojo.declare("dojo.widget.HtmlWidget", dojo.widget.DomWidget, { + widgetType: "HtmlWidget", + + templateCssPath: null, + templatePath: null, + + // for displaying/hiding widget + toggle: "plain", + toggleDuration: 150, + + animationInProgress: false, + + initialize: function(args, frag){ + }, + + postMixInProperties: function(args, frag){ + // now that we know the setting for toggle, get toggle object + // (default to plain toggler if user specified toggler not present) + this.toggleObj = + dojo.lfx.toggle[this.toggle.toLowerCase()] || dojo.lfx.toggle.plain; + }, + + getContainerHeight: function(){ + // NOTE: container height must be returned as the INNER height + dojo.unimplemented("dojo.widget.HtmlWidget.getContainerHeight"); + }, + + getContainerWidth: function(){ + return this.parent.domNode.offsetWidth; + }, + + setNativeHeight: function(height){ + var ch = this.getContainerHeight(); + }, + + createNodesFromText: function(txt, wrap){ + return dojo.html.createNodesFromText(txt, wrap); + }, + + destroyRendering: function(finalize){ + try{ + if(!finalize && this.domNode){ + dojo.event.browser.clean(this.domNode); + } + this.domNode.parentNode.removeChild(this.domNode); + delete this.domNode; + }catch(e){ /* squelch! */ } + }, + + ///////////////////////////////////////////////////////// + // Displaying/hiding the widget + ///////////////////////////////////////////////////////// + isShowing: function(){ + return dojo.html.isShowing(this.domNode); + }, + + toggleShowing: function(){ + // dojo.html.toggleShowing(this.domNode); + if(this.isHidden){ + this.show(); + }else{ + this.hide(); + } + }, + + show: function(){ + this.animationInProgress=true; + this.isHidden = false; + this.toggleObj.show(this.domNode, this.toggleDuration, null, + dojo.lang.hitch(this, this.onShow), this.explodeSrc); + }, + + // called after the show() animation has completed + onShow: function(){ + this.animationInProgress=false; + this.checkSize(); + }, + + hide: function(){ + this.animationInProgress = true; + this.isHidden = true; + this.toggleObj.hide(this.domNode, this.toggleDuration, null, + dojo.lang.hitch(this, this.onHide), this.explodeSrc); + }, + + // called after the hide() animation has completed + onHide: function(){ + this.animationInProgress=false; + }, + + ////////////////////////////////////////////////////////////////////////////// + // Sizing related methods + // If the parent changes size then for each child it should call either + // - resizeTo(): size the child explicitly + // - or checkSize(): notify the child the the parent has changed size + ////////////////////////////////////////////////////////////////////////////// + + // Test if my size has changed. + // If width & height are specified then that's my new size; otherwise, + // query outerWidth/outerHeight of my domNode + _isResized: function(w, h){ + // If I'm not being displayed then disregard (show() must + // check if the size has changed) + if(!this.isShowing()){ return false; } + + // If my parent has been resized and I have style="height: 100%" + // or something similar then my size has changed too. + var wh = dojo.html.getMarginBox(this.domNode); + var width=w||wh.width; + var height=h||wh.height; + if(this.width == width && this.height == height){ return false; } + + this.width=width; + this.height=height; + return true; + }, + + // Called when my parent has changed size, but my parent won't call resizeTo(). + // This is useful if my size is height:100% or something similar. + // Also called whenever I am shown, because the first time I am shown I may need + // to do size calculations. + checkSize: function(){ + if(!this._isResized()){ return; } + this.onResized(); + }, + + // Explicitly set this widget's size (in pixels). + resizeTo: function(w, h){ + if(!this._isResized(w,h)){ return; } + dojo.html.setMarginBox(this.domNode, { width: w, height: h }); + this.onResized(); + }, + + resizeSoon: function(){ + if(this.isShowing()){ + dojo.lang.setTimeout(this, this.onResized, 0); + } + }, + + // Called when my size has changed. + // Must notify children if their size has (possibly) changed + onResized: function(){ + dojo.lang.forEach(this.children, function(child){ if (child["checkSize"]) child.checkSize(); }); + } +}); diff --git a/source/web/scripts/ajax/src/widget/InlineEditBox.js b/source/web/scripts/ajax/src/widget/InlineEditBox.js new file mode 100644 index 0000000000..1fe73360af --- /dev/null +++ b/source/web/scripts/ajax/src/widget/InlineEditBox.js @@ -0,0 +1,161 @@ +dojo.provide("dojo.widget.InlineEditBox"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.lfx.*"); +dojo.require("dojo.graphics.color"); +dojo.require("dojo.string"); +dojo.require("dojo.html.*"); +dojo.require("dojo.html.layout"); + +dojo.widget.defineWidget( + "dojo.widget.InlineEditBox", + dojo.widget.HtmlWidget, + function(){ + // mutable objects need to be in constructor to give each instance its own copy + this.history = []; + }, +{ + templatePath: dojo.uri.dojoUri("src/widget/templates/InlineEditBox.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/InlineEditBox.css"), + + form: null, + editBox: null, + edit: null, + text: null, + textarea: null, + submitButton: null, + cancelButton: null, + mode: "text", + + minWidth: 100, //px. minimum width of edit box + minHeight: 200, //px. minimum width of edit box, if it's a TA + + editing: false, + textValue: "", + defaultText: "", + doFade: false, + + onSave: function(newValue, oldValue){}, + onUndo: function(value){}, + + postCreate: function(args, frag){ + // put original node back in the document, and attach handlers + // which hide it and display the editor + this.editable = this.getFragNodeRef(frag); + dojo.html.insertAfter(this.editable, this.form); + dojo.event.connect(this.editable, "onmouseover", this, "mouseover"); + dojo.event.connect(this.editable, "onmouseout", this, "mouseout"); + dojo.event.connect(this.editable, "onclick", this, "beginEdit"); + + this.textValue = dojo.string.trim(this.editable.innerHTML); + if(dojo.string.trim(this.textValue).length == 0){ + this.editable.innerHTML = this.defaultText; + } + }, + + mouseover: function(e){ + if(!this.editing){ + dojo.html.addClass(this.editable, "editableRegion"); + if(this.mode == "textarea"){ + dojo.html.addClass(this.editable, "editableTextareaRegion"); + } + } + }, + + mouseout: function(e){ + if(!this.editing){ + dojo.html.removeClass(this.editable, "editableRegion"); + dojo.html.removeClass(this.editable, "editableTextareaRegion"); + } + }, + + // When user clicks the text, then start editing. + // Hide the text and display the form instead. + beginEdit: function(e){ + if(this.editing){ return; } + this.mouseout(); + this.editing = true; + + // setup the form's <input> or <textarea> field, as specified by mode + var ee = this[this.mode.toLowerCase()]; + ee.value = dojo.string.trim(this.textValue); + ee.style.fontSize = dojo.html.getStyle(this.editable, "font-size"); + ee.style.fontWeight = dojo.html.getStyle(this.editable, "font-weight"); + ee.style.fontStyle = dojo.html.getStyle(this.editable, "font-style"); + var bb = dojo.html.getBorderBox(this.editable); + ee.style.width = Math.max(bb.width, this.minWidth) + "px"; + if(this.mode.toLowerCase()=="textarea"){ + ee.style.display = "block"; + ee.style.height = Math.max(bb.height, this.minHeight) + "px"; + } else { + ee.style.display = ""; + } + + // show the edit form and hide the read only version of the text + this.form.style.display = ""; + this.editable.style.display = "none"; + + ee.focus(); + ee.select(); + this.submitButton.disabled = true; + }, + + saveEdit: function(e){ + e.preventDefault(); + e.stopPropagation(); + var ee = this[this.mode.toLowerCase()]; + if((this.textValue != ee.value)&& + (dojo.string.trim(ee.value) != "")){ + this.doFade = true; + this.history.push(this.textValue); + this.onSave(ee.value, this.textValue); + this.textValue = ee.value; + this.editable.innerHTML = ""; + var textNode = document.createTextNode( this.textValue ); + this.editable.appendChild( textNode ); + }else{ + this.doFade = false; + } + this.finishEdit(e); + }, + + cancelEdit: function(e){ + if(!this.editing){ return false; } + this.editing = false; + this.form.style.display="none"; + this.editable.style.display = ""; + return true; + }, + + finishEdit: function(e){ + if(!this.cancelEdit(e)){ return; } + if(this.doFade) { + dojo.lfx.highlight(this.editable, dojo.graphics.color.hex2rgb("#ffc"), 700).play(300); + } + this.doFade = false; + }, + + setText: function(txt){ + // sets the text without informing the server + var tt = dojo.string.trim(txt); + this.textValue = tt + this.editable.innerHTML = tt; + }, + + undo: function(){ + if(this.history.length > 0){ + var value = this.history.pop(); + this.editable.innerHTML = value; + this.textValue = value; + this.onUndo(value); + } + }, + + checkForValueChange: function(){ + var ee = this[this.mode.toLowerCase()]; + if((this.textValue != ee.value)&& + (dojo.string.trim(ee.value) != "")){ + this.submitButton.disabled = false; + } + } +}); diff --git a/source/web/scripts/ajax/src/widget/LayoutContainer.js b/source/web/scripts/ajax/src/widget/LayoutContainer.js new file mode 100644 index 0000000000..cbd4d0135d --- /dev/null +++ b/source/web/scripts/ajax/src/widget/LayoutContainer.js @@ -0,0 +1,53 @@ +// +// this widget provides Delphi-style panel layout semantics +// + +dojo.provide("dojo.widget.LayoutContainer"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.html.layout"); + +dojo.widget.defineWidget( + "dojo.widget.LayoutContainer", + dojo.widget.HtmlWidget, +{ + isContainer: true, + + layoutChildPriority: 'top-bottom', + + postCreate: function(){ + dojo.widget.html.layout(this.domNode, this.children, this.layoutChildPriority); + }, + + addChild: function(child, overrideContainerNode, pos, ref, insertIndex){ + dojo.widget.LayoutContainer.superclass.addChild.call(this, child, overrideContainerNode, pos, ref, insertIndex); + dojo.widget.html.layout(this.domNode, this.children, this.layoutChildPriority); + }, + + removeChild: function(pane){ + dojo.widget.LayoutContainer.superclass.removeChild.call(this,pane); + dojo.widget.html.layout(this.domNode, this.children, this.layoutChildPriority); + }, + + onResized: function(){ + dojo.widget.html.layout(this.domNode, this.children, this.layoutChildPriority); + }, + + show: function(){ + // If this node was created while display=="none" then it + // hasn't been laid out yet. Do that now. + this.domNode.style.display=""; + this.checkSize(); + this.domNode.style.display="none"; + this.domNode.style.visibility=""; + + dojo.widget.LayoutContainer.superclass.show.call(this); + } +}); + +// This argument can be specified for the children of a LayoutContainer. +// Since any widget can be specified as a LayoutContainer child, mix it +// into the base widget class. (This is a hack, but it's effective.) +dojo.lang.extend(dojo.widget.Widget, { + layoutAlign: 'none' +}); diff --git a/source/web/scripts/ajax/src/widget/LinkPane.js b/source/web/scripts/ajax/src/widget/LinkPane.js new file mode 100644 index 0000000000..ad0e4a6c9e --- /dev/null +++ b/source/web/scripts/ajax/src/widget/LinkPane.js @@ -0,0 +1,31 @@ +// +// a div that loads from a URL. (Similar to an iframe, but +// it's in the same environment as the main window) +// + +dojo.provide("dojo.widget.LinkPane"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.ContentPane"); +dojo.require("dojo.html.style"); + +dojo.widget.defineWidget( + "dojo.widget.LinkPane", + dojo.widget.ContentPane, +{ + // I'm using a template because the user may specify the input as + // <a href="foo.html">label</a>, in which case we need to get rid of the + // <a> because we don't want a link. + templateString: '<div class="dojoLinkPane"></div>', + + fillInTemplate: function(args, frag){ + var source = this.getFragNodeRef(frag); + + // If user has specified node contents, they become the label + // (the link must be plain text) + this.label += source.innerHTML; + + var source = this.getFragNodeRef(frag); + dojo.html.copyStyle(this.domNode, source); + } +}); diff --git a/source/web/scripts/ajax/src/widget/Manager.js b/source/web/scripts/ajax/src/widget/Manager.js new file mode 100644 index 0000000000..37a4554f86 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Manager.js @@ -0,0 +1,330 @@ +dojo.provide("dojo.widget.Manager"); +dojo.require("dojo.lang.array"); +dojo.require("dojo.lang.func"); +dojo.require("dojo.event.*"); + +// Manager class +dojo.widget.manager = new function(){ + this.widgets = []; + this.widgetIds = []; + + // map of widgetId-->widget for widgets without parents (top level widgets) + this.topWidgets = {}; + + var widgetTypeCtr = {}; + var renderPrefixCache = []; + + this.getUniqueId = function (widgetType) { + return widgetType + "_" + (widgetTypeCtr[widgetType] != undefined ? + ++widgetTypeCtr[widgetType] : widgetTypeCtr[widgetType] = 0); + } + + this.add = function(widget){ + //dojo.profile.start("dojo.widget.manager.add"); + this.widgets.push(widget); + // Opera9 uses ID (caps) + if(!widget.extraArgs["id"]){ + widget.extraArgs["id"] = widget.extraArgs["ID"]; + } + // FIXME: the rest of this method is very slow! + if(widget.widgetId == ""){ + if(widget["id"]){ + widget.widgetId = widget["id"]; + }else if(widget.extraArgs["id"]){ + widget.widgetId = widget.extraArgs["id"]; + }else{ + widget.widgetId = this.getUniqueId(widget.widgetType); + } + } + if(this.widgetIds[widget.widgetId]){ + dojo.debug("widget ID collision on ID: "+widget.widgetId); + } + this.widgetIds[widget.widgetId] = widget; + // Widget.destroy already calls removeById(), so we don't need to + // connect() it here + //dojo.profile.end("dojo.widget.manager.add"); + } + + this.destroyAll = function(){ + for(var x=this.widgets.length-1; x>=0; x--){ + try{ + // this.widgets[x].destroyChildren(); + this.widgets[x].destroy(true); + delete this.widgets[x]; + }catch(e){ } + } + } + + // FIXME: we should never allow removal of the root widget until all others + // are removed! + this.remove = function(widgetIndex){ + if(dojo.lang.isNumber(widgetIndex)){ + var tw = this.widgets[widgetIndex].widgetId; + delete this.widgetIds[tw]; + this.widgets.splice(widgetIndex, 1); + }else{ + this.removeById(widgetIndex); + } + } + + // FIXME: suboptimal performance + this.removeById = function(id) { + if(!dojo.lang.isString(id)){ + id = id["widgetId"]; + if(!id){ dojo.debug("invalid widget or id passed to removeById"); return; } + } + for (var i=0; i<this.widgets.length; i++){ + if(this.widgets[i].widgetId == id){ + this.remove(i); + break; + } + } + } + + this.getWidgetById = function(id){ + if(dojo.lang.isString(id)){ + return this.widgetIds[id]; + } + return id; + } + + this.getWidgetsByType = function(type){ + var lt = type.toLowerCase(); + var ret = []; + dojo.lang.forEach(this.widgets, function(x){ + if(x.widgetType.toLowerCase() == lt){ + ret.push(x); + } + }); + return ret; + } + + this.getWidgetsByFilter = function(unaryFunc, onlyOne){ + var ret = []; + dojo.lang.every(this.widgets, function(x){ + if(unaryFunc(x)){ + ret.push(x); + if(onlyOne){return false;} + } + return true; + }); + return (onlyOne ? ret[0] : ret); + } + + this.getAllWidgets = function() { + return this.widgets.concat(); + } + + // added, trt 2006-01-20 + this.getWidgetByNode = function(/* DOMNode */ node){ + var w=this.getAllWidgets(); + node = dojo.byId(node); + for(var i=0; i<w.length; i++){ + if(w[i].domNode==node){ + return w[i]; + } + } + return null; + } + + // shortcuts, baby + this.byId = this.getWidgetById; + this.byType = this.getWidgetsByType; + this.byFilter = this.getWidgetsByFilter; + this.byNode = this.getWidgetByNode; + + // map of previousally discovered implementation names to constructors + var knownWidgetImplementations = {}; + + // support manually registered widget packages + var widgetPackages = ["dojo.widget"]; + for (var i=0; i<widgetPackages.length; i++) { + // convenience for checking if a package exists (reverse lookup) + widgetPackages[widgetPackages[i]] = true; + } + + this.registerWidgetPackage = function(pname) { + if(!widgetPackages[pname]){ + widgetPackages[pname] = true; + widgetPackages.push(pname); + } + } + + this.getWidgetPackageList = function() { + return dojo.lang.map(widgetPackages, function(elt) { return(elt!==true ? elt : undefined); }); + } + + this.getImplementation = function(widgetName, ctorObject, mixins, namespace){ + // try and find a name for the widget + var impl = this.getImplementationName(widgetName, namespace); + if(impl){ + // var tic = new Date(); + var ret; + if(ctorObject){ret = new impl(ctor);} + else{ret = new impl();} + // dojo.debug(new Date() - tic); + return ret; + } + } + + this.getImplementationName = function(widgetName, namespace){ + /* + * This is the overly-simplistic implemention of getImplementation (har + * har). In the future, we are going to want something that allows more + * freedom of expression WRT to specifying different specializations of + * a widget. + * + * Additionally, this implementation treats widget names as case + * insensitive, which does not necessarialy mesh with the markup which + * can construct a widget. + */ + if(!namespace){namespace="dojo";} + var lowerCaseWidgetName = widgetName.toLowerCase(); + + if(!knownWidgetImplementations[namespace]){knownWidgetImplementations[namespace]={};} + + var impl = knownWidgetImplementations[namespace][lowerCaseWidgetName]; + if(impl){ + return impl; + } + var ns = dojo.getNamespace(namespace); + if(ns){ns.load(widgetName);} + + // first store a list of the render prefixes we are capable of rendering + if(!renderPrefixCache.length){ + for(var renderer in dojo.render){ + if(dojo.render[renderer]["capable"] === true){ + var prefixes = dojo.render[renderer].prefixes; + for(var i = 0; i < prefixes.length; i++){ + renderPrefixCache.push(prefixes[i].toLowerCase()); + } + } + } + // make sure we don't HAVE to prefix widget implementation names + // with anything to get them to render + renderPrefixCache.push(""); + } + + var nsSearchArr = null; + + var deprWarningLogged=false; + for(var counter = 0; counter < 2; counter++){ + // look for a rendering-context specific version of our widget name + for(var i = 0; i < widgetPackages.length; i++){ + var widgetPackage = dojo.evalObjPath(widgetPackages[i]); + if(!widgetPackage) { continue; } + var pos = widgetPackages[i].indexOf("."); + if(pos > -1){ + var n = widgetPackages[i].substring(0,pos); + if(n != namespace){ + if(counter==0){continue;} + if(!deprWarningLogged){ + deprWarningLogged = true; + dojo.deprecated('dojo.widget.Manager.getImplementationName', 'Wrong namespace ('+namespace+ + ') specified. Developers should specify correct namespaces for all non-Dojo widgets', "0.5"); + } + } + } + + for (var j = 0; j < renderPrefixCache.length; j++) { + if (!widgetPackage[renderPrefixCache[j]]) { continue; } + for (var widgetClass in widgetPackage[renderPrefixCache[j]]) { + if (widgetClass.toLowerCase() != lowerCaseWidgetName) { continue; } + knownWidgetImplementations[namespace][lowerCaseWidgetName] = + widgetPackage[renderPrefixCache[j]][widgetClass]; + return knownWidgetImplementations[namespace][lowerCaseWidgetName]; + } + } + + for (var j = 0; j < renderPrefixCache.length; j++) { + for (var widgetClass in widgetPackage) { + if (widgetClass.toLowerCase() != (renderPrefixCache[j] + lowerCaseWidgetName) && + widgetClass.toLowerCase() != lowerCaseWidgetName) { continue; } + + knownWidgetImplementations[namespace][lowerCaseWidgetName] = + widgetPackage[widgetClass]; + return knownWidgetImplementations[namespace][lowerCaseWidgetName]; + } + } + } + var newNs = dojo.findNamespaceForWidget(lowerCaseWidgetName); + if(newNs){ + namespace = newNs.nsPrefix; + } + } + + throw new Error('Could not locate "' + widgetName + '" class'); + } + + // FIXME: does it even belong in this name space? + // NOTE: this method is implemented by DomWidget.js since not all + // hostenv's would have an implementation. + /*this.getWidgetFromPrimitive = function(baseRenderType){ + dojo.unimplemented("dojo.widget.manager.getWidgetFromPrimitive"); + } + + this.getWidgetFromEvent = function(nativeEvt){ + dojo.unimplemented("dojo.widget.manager.getWidgetFromEvent"); + }*/ + + // Catch window resize events and notify top level widgets + this.resizing=false; + this.onWindowResized = function(){ + if(this.resizing){ + return; // duplicate event + } + try{ + this.resizing=true; + for(var id in this.topWidgets){ + var child = this.topWidgets[id]; + if(child.checkSize ){ + child.checkSize(); + } + } + }catch(e){ + }finally{ + this.resizing=false; + } + } + if(typeof window != "undefined") { + dojo.addOnLoad(this, 'onWindowResized'); // initial sizing + dojo.event.connect(window, 'onresize', this, 'onWindowResized'); // window resize + } + + // FIXME: what else? +}; + +(function(){ + var dw = dojo.widget; + var dwm = dw.manager; + var h = dojo.lang.curry(dojo.lang, "hitch", dwm); + var g = function(oldName, newName){ + dw[(newName||oldName)] = h(oldName); + } + // copy the methods from the default manager (this) to the widget namespace + g("add", "addWidget"); + g("destroyAll", "destroyAllWidgets"); + g("remove", "removeWidget"); + g("removeById", "removeWidgetById"); + g("getWidgetById"); + g("getWidgetById", "byId"); + g("getWidgetsByType"); + g("getWidgetsByFilter"); + g("getWidgetsByType", "byType"); + g("getWidgetsByFilter", "byFilter"); + g("getWidgetByNode", "byNode"); + dw.all = function(n){ + var widgets = dwm.getAllWidgets.apply(dwm, arguments); + if(arguments.length > 0) { + return widgets[n]; + } + return widgets; + } + g("registerWidgetPackage"); + g("getImplementation", "getWidgetImplementation"); + g("getImplementationName", "getWidgetImplementationName"); + + dw.widgets = dwm.widgets; + dw.widgetIds = dwm.widgetIds; + dw.root = dwm.root; +})(); diff --git a/source/web/scripts/ajax/src/widget/Menu.js b/source/web/scripts/ajax/src/widget/Menu.js new file mode 100644 index 0000000000..bb15a7cda6 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Menu.js @@ -0,0 +1,59 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.provide("dojo.widget.Menu"); +dojo.provide("dojo.widget.DomMenu"); + +dojo.deprecated("dojo.widget.Menu, dojo.widget.DomMenu", "use dojo.widget.Menu2", "0.4"); + +dojo.require("dojo.widget.*"); + +dojo.widget.tags.addParseTreeHandler("dojo:menu"); + +/* Menu + *******/ + +dojo.widget.Menu = function () { + dojo.widget.Menu.superclass.constructor.call(this); +} +dojo.inherits(dojo.widget.Menu, dojo.widget.Widget); + +dojo.lang.extend(dojo.widget.Menu, { + widgetType: "Menu", + isContainer: true, + + items: [], + push: function(item){ + dojo.connect.event(item, "onSelect", this, "onSelect"); + this.items.push(item); + }, + onSelect: function(){} +}); + + +/* DomMenu + **********/ + +dojo.widget.DomMenu = function(){ + dojo.widget.DomMenu.superclass.constructor.call(this); +} +dojo.inherits(dojo.widget.DomMenu, dojo.widget.DomWidget); + +dojo.lang.extend(dojo.widget.DomMenu, { + widgetType: "Menu", + isContainer: true, + + push: function (item) { + dojo.widget.Menu.call(this, item); + this.domNode.appendChild(item.domNode); + } +}); + +dojo.requireAfterIf("html", "dojo.widget.html.Menu"); diff --git a/source/web/scripts/ajax/src/widget/Menu2.js b/source/web/scripts/ajax/src/widget/Menu2.js new file mode 100644 index 0000000000..5bb610dfa4 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Menu2.js @@ -0,0 +1,1047 @@ +dojo.provide("dojo.widget.PopupContainer"); +dojo.provide("dojo.widget.Menu2"); +dojo.provide("dojo.widget.PopupMenu2"); +dojo.provide("dojo.widget.MenuItem2"); +dojo.provide("dojo.widget.MenuBar2"); + +dojo.require("dojo.html.style"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.html.selection"); +dojo.require("dojo.html.iframe"); +dojo.require("dojo.event.*"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); + +//PopupContainerBase is the mixin class which provide popup behaviors: +//it can open in a given position x,y or around a given node. +//In addition, it handles animation and IE bleed through workaround. +//This class can not be used standalone: it should be mixed-in to a +//dojo.widget.HtmlWidget. Use PopupContainer instead if you want a +//a standalone popup widget +dojo.declare( + "dojo.widget.PopupContainerBase", + null, + function(){ + this.queueOnAnimationFinish = []; + }, +{ + isContainer: true, + templateString: '<div dojoAttachPoint="containerNode" style="display:none;position:absolute;" class="dojoPopupContainer" tabindex="-1"></div>', + snarfChildDomOutput: true, + + isShowingNow: false, + + currentSubpopup: null, + + beginZIndex: 1000, + + parentPopup: null, + popupIndex: 0, + + aroundBox: dojo.html.boxSizing.BORDER_BOX, //by default, popup around the BORDER box of the aroundNode in open() + + processKey: function(evt){ + return false; + }, + + //this function should be called in sub class where a custom + //templateString/templateStringPath is used (see Tooltip widget) + applyPopupBasicStyle: function(){ + with(this.domNode.style){ + display = 'none'; + position = 'absolute'; + } + }, + /** + * Open the popup at position (x,y), relative to dojo.body() + * Or open(node, parent, explodeSrc, aroundOrient) to open + * around node + */ + open: function(x, y, parent, explodeSrc, orient, padding){ + if (this.isShowingNow){ return; } + + // if I click right button and menu is opened, then it gets 2 commands: close -> open + // so close enables animation and next "open" is put to queue to occur at new location + if(this.animationInProgress){ + this.queueOnAnimationFinish.push(this.open, arguments); + return; + } + + var around = false, node, aroundOrient; + if(typeof x == 'object'){ + node = x; + aroundOrient = explodeSrc; + explodeSrc = parent; + parent = y; + around = true; + } + + // for unknown reasons even if the domNode is attached to the body in postCreate(), + // it's not attached here, so have to attach it here. + dojo.body().appendChild(this.domNode); + + // if explodeSrc isn't specified then explode from my parent widget + explodeSrc = explodeSrc || parent["domNode"] || []; + + //keep track of parent popup to decided whether this is a top level popup + var parentPopup = null; + this.isTopLevel = true; + while(parent){ + if(parent !== this && (parent instanceof dojo.widget.PopupContainer || parent.applyPopupBasicStyle != undefined)){ + parentPopup = parent; + this.isTopLevel = false; + parentPopup.setOpenedSubpopup(this); + break; + } + parent = parent.parent; + } + + this.parentPopup = parentPopup; + this.popupIndex = parentPopup ? parentPopup.popupIndex + 1 : 1; + + if(this.isTopLevel){ + var button = explodeSrc instanceof Array ? null : explodeSrc; + dojo.widget.PopupManager.opened(this, button); + } + + //convert explodeSrc from format [x, y] to + //{left: x, top: y, width: 0, height: 0} which is the new + //format required by dojo.html.toCoordinateObject + if(explodeSrc instanceof Array){ + explodeSrc = {left: explodeSrc[0], top: explodeSrc[1], width: 0, height: 0}; + } + + // display temporarily, and move into position, then hide again + with(this.domNode.style){ + display=""; + zIndex = this.beginZIndex + this.popupIndex; + } + + if(around){ + this.move(node, padding, aroundOrient); + }else{ + this.move(x, y, padding, orient); + } + this.domNode.style.display="none"; + + this.explodeSrc = explodeSrc; + + // then use the user defined method to display it + this.show(); + + this.isShowingNow = true; + }, + + /* Summery: calculate where to place the popup + move(node, padding, aroundOrient) */ + move: function(x, y, padding, orient){ + var around = (typeof x == "object"); + if(around){ + var aroundOrient=padding; + var node=x; + padding=y; + if(!aroundOrient){ //By default, attempt to open above the aroundNode, or below + aroundOrient = {'BL': 'TL', 'TL': 'BL'}; + } + dojo.html.placeOnScreenAroundElement(this.domNode, node, padding, this.aroundBox, aroundOrient); + }else{ + if(!orient){ orient = 'TL,TR,BL,BR';} + dojo.html.placeOnScreen(this.domNode, x, y, padding, true, orient); + } + }, + + close: function(){ + // If we are in the process of opening the menu and we are asked to close it + if(this.animationInProgress){ + this.queueOnAnimationFinish.push(this.close, []); + return; + } + + this.closeSubpopup(); + this.hide(); + if(this.bgIframe){ + this.bgIframe.hide(); + this.bgIframe.size({left: 0, top: 0, width: 0, height: 0}); + } + if(this.isTopLevel){ + dojo.widget.PopupManager.closed(this); + } + this.isShowingNow = false; + }, + + closeAll: function(){ + if (this.parentPopup){ + this.parentPopup.closeAll(); + }else{ + this.close(); + } + }, + + //call this when a embedded popup is shown + setOpenedSubpopup: function(popup) { + this.currentSubpopup = popup; + }, + + closeSubpopup: function() { + if(this.currentSubpopup == null){ return; } + + this.currentSubpopup.close(); + this.currentSubpopup = null; + }, + + onShow: function() { + this.inherited('onShow'); + // With some animation (wipe), after close, the size of the domnode is 0 + // and next time when shown, the open() function can not determine + // the correct place to popup, so we store the opened size here and + // set it after close (in function onHide()) + this.openedSize={w: this.domNode.style.width, h: this.domNode.style.height}; + // prevent IE bleed through + if(dojo.render.html.ie){ + if(!this.bgIframe){ + this.bgIframe = new dojo.html.BackgroundIframe(); + this.bgIframe.setZIndex(this.domNode); + } + + this.bgIframe.size(this.domNode); + this.bgIframe.show(); + } + this.processQueue(); + }, + + // do events from queue + processQueue: function() { + if (!this.queueOnAnimationFinish.length) return; + + var func = this.queueOnAnimationFinish.shift(); + var args = this.queueOnAnimationFinish.shift(); + + func.apply(this, args); + }, + + onHide: function() { + dojo.widget.HtmlWidget.prototype.onHide.call(this); + + //restore size of the domnode, see comment in + //function onShow() + if(this.openedSize){ + with(this.domNode.style){ + width=this.openedSize.w; + height=this.openedSize.h; + } + } + + this.processQueue(); + } +}); + +dojo.widget.defineWidget( + "dojo.widget.PopupContainer", + [dojo.widget.HtmlWidget, dojo.widget.PopupContainerBase], {}); + +dojo.widget.defineWidget( + "dojo.widget.PopupMenu2", + dojo.widget.PopupContainer, + function(){ + this.targetNodeIds = []; // fill this with nodeIds upon widget creation and it becomes context menu for those nodes + + this.eventNames = { + open: "" + }; + }, +{ + templateCssString: "", + currentSubmenuTrigger: null, + + eventNaming: "default", + + templateString: '<table class="dojoPopupMenu2" border=0 cellspacing=0 cellpadding=0 style="display: none;"><tbody dojoAttachPoint="containerNode"></tbody></table>', + templateCssPath: dojo.uri.dojoUri("src/widget/templates/Menu2.css"), + + submenuDelay: 500, + submenuOverlap: 5, + contextMenuForWindow: false, + openEvent: null, + + _highlighted_option: null, + + initialize: function(args, frag) { + if (this.eventNaming == "default") { + for (var eventName in this.eventNames) { + this.eventNames[eventName] = this.widgetId+"/"+eventName; + } + } + }, + + postCreate: function(){ + if (this.contextMenuForWindow){ + var doc = dojo.body(); + this.bindDomNode(doc); + } else if ( this.targetNodeIds.length > 0 ){ + dojo.lang.forEach(this.targetNodeIds, this.bindDomNode, this); + } + + this.subscribeSubitemsOnOpen(); + }, + + subscribeSubitemsOnOpen: function() { + var subItems = this.getChildrenOfType(dojo.widget.MenuItem2); + + for(var i=0; i<subItems.length; i++) { + dojo.event.topic.subscribe(this.eventNames.open, subItems[i], "menuOpen") + } + }, + + // get open event for current menu + getTopOpenEvent: function() { + var menu = this; + while (menu.parentPopup){ menu = menu.parentPopup; } + return menu.openEvent; + }, + + // attach menu to given node + bindDomNode: function(node){ + node = dojo.byId(node); + + var win = dojo.html.getElementWindow(node); + if(dojo.html.isTag(node,'iframe') == 'iframe'){ + win = dojo.html.iframeContentWindow(node); + node = dojo.withGlobal(win, dojo.body); + } + // fixes node so that it supports oncontextmenu if not natively supported, Konqueror, Opera more? + dojo.widget.Menu2.OperaAndKonqFixer.fixNode(node); + + dojo.event.kwConnect({ + srcObj: node, + srcFunc: "oncontextmenu", + targetObj: this, + targetFunc: "onOpen", + once: true + }); + + dojo.widget.PopupManager.registerWin(win); + }, + + // detach menu from given node + unBindDomNode: function(nodeName){ + var node = dojo.byId(nodeName); + dojo.event.kwDisconnect({ + srcObj: node, + srcFunc: "oncontextmenu", + targetObj: this, + targetFunc: "onOpen", + once: true + }); + + // cleans a fixed node, konqueror and opera + dojo.widget.Menu2.OperaAndKonqFixer.cleanNode(node); + }, + + moveToNext: function(evt){ + this.highlightOption(1); + return true; //do not pass to parent menu + }, + + moveToPrevious: function(evt){ + this.highlightOption(-1); + return true; //do not pass to parent menu + }, + + moveToParentMenu: function(evt){ + if(this._highlighted_option && this.parentPopup){ + //only process event in the focused menu + //and its immediate parentPopup to support + //MenuBar2 + if(evt._menu2UpKeyProcessed){ + return true; //do not pass to parent menu + }else{ + this._highlighted_option.onUnhover(); + this.closeSubpopup(); + evt._menu2UpKeyProcessed = true; + } + } + return false; + }, + + moveToChildMenu: function(evt){ + if(this._highlighted_option && this._highlighted_option.submenuId){ + this._highlighted_option._onClick(true); + return true; //do not pass to parent menu + } + return false; + }, + + selectCurrentItem: function(evt){ + if(this._highlighted_option){ + this._highlighted_option._onClick(); + return true; + } + return false; + }, + + //return true to stop the event being processed by the + //parent popupmenu + processKey: function(evt){ + if(evt.ctrlKey || evt.altKey){ return false; } + + var keyCode = evt.keyCode; + var rval = false; + var k = dojo.event.browser.keys; + + // mozilla quirk + // space has no keyCode in mozilla + var keyCode = evt.keyCode; + if(keyCode==0 && evt.charCode==k.KEY_SPACE){ + keyCode = k.KEY_SPACE; + } + + switch(keyCode){ + case k.KEY_DOWN_ARROW: + rval = this.moveToNext(evt); + break; + case k.KEY_UP_ARROW: + rval = this.moveToPrevious(evt); + break; + case k.KEY_RIGHT_ARROW: + rval = this.moveToChildMenu(evt); + break; + case k.KEY_LEFT_ARROW: + rval = this.moveToParentMenu(evt); + break; + case k.KEY_SPACE: //fall through + case k.KEY_ENTER: + if(rval = this.selectCurrentItem(evt)){ + break; + } + //fall through + case k.KEY_ESCAPE: + dojo.widget.PopupManager.currentMenu.close(); + rval = true; + break; + } + + return rval; + }, + + findValidItem: function(dir, curItem){ + if(curItem){ + curItem = dir>0 ? curItem.getNextSibling() : curItem.getPreviousSibling(); + } + + for(var i=0; i < this.children.length; ++i){ + if(!curItem){ + curItem = dir>0 ? this.children[0] : this.children[this.children.length-1]; + } + if(curItem.onHover){ + return curItem; + } + curItem = dir>0 ? curItem.getNextSibling() : curItem.getPreviousSibling(); + } + }, + + highlightOption: function(dir){ + var item; + // || !this._highlighted_option.parentNode + if((!this._highlighted_option)){ + item = this.findValidItem(dir); + }else{ + item = this.findValidItem(dir, this._highlighted_option); + } + if(item){ + if(this._highlighted_option) { + this._highlighted_option.onUnhover(); + } + item.onHover(); + dojo.html.scrollIntoView(item.domNode); + } + }, + + // User defined function to handle clicks on an item + onItemClick: function(item) {}, + + close: function(){ + if(this.animationInProgress){ + dojo.widget.PopupMenu2.superclass.close.call(this); + return; + } + + if(this._highlighted_option){ + this._highlighted_option.onUnhover(); + } + + dojo.widget.PopupMenu2.superclass.close.call(this); + }, + + //overwrite the default one + closeSubpopup: function(){ + if (this.currentSubpopup == null){ return; } + + this.currentSubpopup.close(); + this.currentSubpopup = null; + + this.currentSubmenuTrigger.is_open = false; + this.currentSubmenuTrigger.closedSubmenu(); + this.currentSubmenuTrigger = null; + }, + + // open the menu to the right of the current menu item + openSubmenu: function(submenu, from_item){ + var fromPos = dojo.html.getAbsolutePosition(from_item.domNode, true); + var our_w = dojo.html.getMarginBox(this.domNode).width; + var x = fromPos.x + our_w - this.submenuOverlap; + var y = fromPos.y; + + //the following is set in open, so we do not need it + //this.currentSubpopup = submenu; + submenu.open(x, y, this, from_item.domNode); + + this.currentSubmenuTrigger = from_item; + this.currentSubmenuTrigger.is_open = true; + }, + + onOpen: function(e){ + this.openEvent = e; + var x = e.pageX, y = e.pageY; + + var win = dojo.html.getElementWindow(e.target); + var iframe = win.frameElement; + if(iframe){ + var cood = dojo.html.getAbsolutePosition(iframe, true); + x += cood.x - dojo.withGlobal(win, dojo.html.getScroll).left; + y += cood.y - dojo.withGlobal(win, dojo.html.getScroll).top; + } + this.open(x, y, null, [x, y]); + + e.preventDefault(); + e.stopPropagation(); + } +}); + +dojo.widget.defineWidget( + "dojo.widget.MenuItem2", + dojo.widget.HtmlWidget, + function(){ + this.eventNames = { + engage: "" + }; + }, +{ + // Make 4 columns + // icon, label, accelerator-key, and right-arrow indicating sub-menu + templateString: + '<tr class="dojoMenuItem2" dojoAttachEvent="onMouseOver: onHover; onMouseOut: onUnhover; onClick: _onClick;">' + +'<td><div class="dojoMenuItem2Icon" style="${this.iconStyle}"></div></td>' + +'<td class="dojoMenuItem2Label"><span><span>${this.caption}</span>${this.caption}</span></td>' + +'<td class="dojoMenuItem2Accel"><span><span>${this.accelKey}</span>${this.accelKey}</span></td>' + +'<td><div class="dojoMenuItem2Submenu" style="display:${this.arrowDisplay};"></div></td>' + +'</tr>', + + // + // internal settings + // + + is_hovering: false, + hover_timer: null, + is_open: false, + topPosition: 0, + + // + // options + // + + caption: 'Untitled', + accelKey: '', + iconSrc: '', + submenuId: '', + disabled: false, + eventNaming: "default", + highlightClass: 'dojoMenuItem2Hover', + + postMixInProperties: function(){ + this.iconStyle=""; + if (this.iconSrc){ + if ((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4) == ".png") && (dojo.render.html.ie) && (!dojo.render.html.ie70)){ + this.iconStyle="filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.iconSrc+"', sizingMethod='image')"; + }else{ + this.iconStyle="background-image: url("+this.iconSrc+")"; + } + } + this.arrowDisplay = this.submenuId ? 'block' : 'none'; + }, + + fillInTemplate: function(){ + dojo.html.disableSelection(this.domNode); + + if (this.disabled){ + this.setDisabled(true); + } + + if (this.eventNaming == "default") { + for (var eventName in this.eventNames) { + this.eventNames[eventName] = this.widgetId+"/"+eventName; + } + } + }, + + onHover: function(){ + //this is to prevent some annoying behavior when both mouse and keyboard are used + this.onUnhover(); + + if (this.is_hovering){ return; } + if (this.is_open){ return; } + + if(this.parent._highlighted_option){ + this.parent._highlighted_option.onUnhover(); + } + this.parent.closeSubpopup(); + this.parent._highlighted_option = this; + dojo.widget.PopupManager.setFocusedMenu(this.parent); + + this.highlightItem(); + + if (this.is_hovering){ this.stopSubmenuTimer(); } + this.is_hovering = true; + this.startSubmenuTimer(); + }, + + onUnhover: function(){ + if(!this.is_open){ this.unhighlightItem(); } + + this.is_hovering = false; + + this.parent._highlighted_option = null; + + if(this.parent.parentPopup){ + dojo.widget.PopupManager.setFocusedMenu(this.parent.parentPopup); + } + + this.stopSubmenuTimer(); + }, + + // Internal function for clicks + _onClick: function(focus){ + var displayingSubMenu = false; + if (this.disabled){ return false; } + + if (this.submenuId){ + if (!this.is_open){ + this.stopSubmenuTimer(); + this.openSubmenu(); + } + displayingSubMenu = true; + }else{ + this.parent.closeAll(); + } + + // for some browsers the onMouseOut doesn't get called (?), so call it manually + if(!displayingSubMenu){ //only onUnhover when no submenu is available + this.onUnhover(); + } + + // user defined handler for click + this.onClick(); + + dojo.event.topic.publish(this.eventNames.engage, this); + + if(displayingSubMenu && focus){ + dojo.widget.getWidgetById(this.submenuId).highlightOption(1); + } + return; + }, + + // User defined function to handle clicks + // this default function call the parent + // menu's onItemClick + onClick: function() { + this.parent.onItemClick(this); + }, + + highlightItem: function(){ + dojo.html.addClass(this.domNode, this.highlightClass); + }, + + unhighlightItem: function(){ + dojo.html.removeClass(this.domNode, this.highlightClass); + }, + + startSubmenuTimer: function(){ + this.stopSubmenuTimer(); + + if (this.disabled){ return; } + + var self = this; + var closure = function(){ return function(){ self.openSubmenu(); } }(); + + this.hover_timer = dojo.lang.setTimeout(closure, this.parent.submenuDelay); + }, + + stopSubmenuTimer: function(){ + if (this.hover_timer){ + dojo.lang.clearTimeout(this.hover_timer); + this.hover_timer = null; + } + }, + + openSubmenu: function(){ + // first close any other open submenu + this.parent.closeSubpopup(); + + var submenu = dojo.widget.getWidgetById(this.submenuId); + if (submenu){ + this.parent.openSubmenu(submenu, this); + } + }, + + closedSubmenu: function(){ + this.onUnhover(); + }, + + setDisabled: function(value){ + this.disabled = value; + + if (this.disabled){ + dojo.html.addClass(this.domNode, 'dojoMenuItem2Disabled'); + }else{ + dojo.html.removeClass(this.domNode, 'dojoMenuItem2Disabled'); + } + }, + + enable: function(){ + this.setDisabled(false); + }, + + disable: function(){ + this.setDisabled(true); + }, + + menuOpen: function(message) { + } + +}); + +dojo.widget.defineWidget( + "dojo.widget.MenuSeparator2", + dojo.widget.HtmlWidget, +{ + templateString: '<tr class="dojoMenuSeparator2"><td colspan=4>' + +'<div class="dojoMenuSeparator2Top"></div>' + +'<div class="dojoMenuSeparator2Bottom"></div>' + +'</td></tr>', + + postCreate: function(){ + dojo.html.disableSelection(this.domNode); + } +}); + +// +// the popup manager makes sure we don't have several popups +// open at once. the root popup in an opening sequence calls +// opened(). when a root menu closes it calls closed(). then +// everything works. lovely. +// + +dojo.widget.PopupManager = new function(){ + + this.currentMenu = null; + this.currentButton = null; // button that opened current menu (if any) + this.currentFocusMenu = null; // the (sub)menu which receives key events + this.focusNode = null; + this.registeredWindows = []; + + //In Opera, only onkeypress works, while in IE, only onkeydown works + //In Moz, both work. so use onkeydown in IE, otherwise use onkeypress + //for keyevents (FIXME: safari/konqueror?) + this._keyEventName = dojo.doc().createEvent ? "onkeypress" : "onkeydown"; + + this.registerWin = function(win){ + if(!win.__PopupManagerRegistered) + { + dojo.event.connect(win.document, 'onmousedown', this, 'onClick'); + dojo.event.connect(win, "onscroll", this, "onClick"); + dojo.event.connect(win.document, this._keyEventName, this, 'onKeyPress'); + win.__PopupManagerRegistered = true; + this.registeredWindows.push(win); + } + }; + + /* + This function register all the iframes and the top window, + so that whereever the user clicks in the page, the popup + menu will be closed + In case you add an iframe after onload event, please call + dojo.widget.PopupManager.registerWin manually + */ + this.registerAllWindows = function(targetWindow){ + //starting from window.top, clicking everywhere in this page + //should close popup menus + if(!targetWindow) targetWindow = dojo.html.getDocumentWindow(window.top.document); //see comment below + + this.registerWin(targetWindow); + + for (var i = 0; i < targetWindow.frames.length; i++){ + //do not remove dojo.html.getDocumentWindow, see comment in it + var win = dojo.html.getDocumentWindow(targetWindow.frames[i].document); + if(win){ + this.registerAllWindows(win); + } + } + }; + + dojo.addOnLoad(this, "registerAllWindows"); + + this.closed = function(menu){ + if (this.currentMenu == menu){ + this.currentMenu = null; + this.currentButton = null; + this.currentFocusMenu = null; + } + }; + + this.opened = function(menu, button){ + if (menu == this.currentMenu){ return; } + + if (this.currentMenu){ + this.currentMenu.close(); + } + + this.currentMenu = menu; + this.currentFocusMenu = menu; + this.currentButton = button; + }; + + this.setFocusedMenu = function(menu){ + this.currentFocusMenu = menu; + }; + + this.onKeyPress = function(e){ + if(!this.currentMenu || !this.currentMenu.isShowingNow){ return; } + + var m = this.currentFocusMenu; + while (m){ + if(m.processKey(e)){ + e.preventDefault(); + e.stopPropagation(); + break; + } + m = m.parentPopup; + } + }, + + this.onClick = function(e){ + if (!this.currentMenu){ return; } + + var scrolloffset = dojo.html.getScroll().offset; + + // starting from the base menu, perform a hit test + // and exit when one succeeds + + var m = this.currentMenu; + + while (m){ + if(dojo.html.overElement(m.domNode, e) || dojo.html.isDescendantOf(e.target, m.domNode)){ + return; + } + m = m.currentSubpopup; + } + + // Also, if user clicked the button that opened this menu, then + // that button will send the menu a close() command, so this code + // shouldn't try to close the menu. Closing twice messes up animation. + if (this.currentButton && dojo.html.overElement(this.currentButton, e)){ + return; + } + + // the click didn't fall within the open menu tree + // so close it + + this.currentMenu.close(); + }; +} + +// ************************** make contextmenu work in konqueror and opera ********************* +dojo.widget.Menu2.OperaAndKonqFixer = new function(){ + var implement = true; + var delfunc = false; + + /** dom event check + * + * make a event and dispatch it and se if it calls function below, + * if it indeed is supported and we dont need to implement our own + */ + + // gets called if we have support for oncontextmenu + if (!dojo.lang.isFunction(dojo.doc().oncontextmenu)){ + dojo.doc().oncontextmenu = function(){ + implement = false; + delfunc = true; + } + } + + if (dojo.doc().createEvent){ // moz, safari has contextmenu event, need to do livecheck on this env. + try { + var e = dojo.doc().createEvent("MouseEvents"); + e.initMouseEvent("contextmenu", 1, 1, dojo.global(), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, null); + dojo.doc().dispatchEvent(e); + } catch (e) {/* assume not supported */} + } else { + // IE no need to implement custom contextmenu + implement = false; + } + + // clear this one if it wasn't there before + if (delfunc){ + delete dojo.doc().oncontextmenu; + } + /***** end dom event check *****/ + + + /** + * this fixes a dom node by attaching a custom oncontextmenu function that gets called when apropriate + * @param node a dom node + * + * no returns + */ + this.fixNode = function(node){ + if (implement){ + // attach stub oncontextmenu function + if (!dojo.lang.isFunction(node.oncontextmenu)){ + node.oncontextmenu = function(e){/*stub*/} + } + + // attach control function for oncontextmenu + if (dojo.render.html.opera){ + // opera + // listen to ctrl-click events + node._menufixer_opera = function(e){ + if (e.ctrlKey){ + this.oncontextmenu(e); + } + }; + + dojo.event.connect(node, "onclick", node, "_menufixer_opera"); + + } else { + // konqueror + // rightclick, listen to mousedown events + node._menufixer_konq = function(e){ + if (e.button==2 ){ + e.preventDefault(); // need to prevent browsers menu + this.oncontextmenu(e); + } + }; + + dojo.event.connect(node, "onmousedown", node, "_menufixer_konq"); + } + } + } + + /** + * this cleans up a fixed node, prevent memoryleak? + * @param node node to clean + * + * no returns + */ + this.cleanNode = function(node){ + if (implement){ + // checks needed if we gets a non fixed node + if (node._menufixer_opera){ + dojo.event.disconnect(node, "onclick", node, "_menufixer_opera"); + delete node._menufixer_opera; + } else if(node._menufixer_konq){ + dojo.event.disconnect(node, "onmousedown", node, "_menufixer_konq"); + delete node._menufixer_konq; + } + if (node.oncontextmenu){ + delete node.oncontextmenu; + } + } + } +}; + +dojo.widget.defineWidget( + "dojo.widget.MenuBar2", + dojo.widget.PopupMenu2, +{ + menuOverlap: 2, + + templateString: '<div class="dojoMenuBar2"><table class="dojoMenuBar2Client"><tr dojoAttachPoint="containerNode"></tr></table></div>', + + close: function(){ + if(this._highlighted_option){ + this._highlighted_option.onUnhover(); + } + + this.closeSubpopup(); + }, + + processKey: function(evt){ + if(evt.ctrlKey || evt.altKey){ return false; } + + var keyCode = evt.keyCode; + var rval = false; + var k = dojo.event.browser.keys; + + switch(keyCode){ + case k.KEY_DOWN_ARROW: + rval = this.moveToChildMenu(evt); + break; + case k.KEY_UP_ARROW: + rval = this.moveToParentMenu(evt); + break; + case k.KEY_RIGHT_ARROW: + rval = this.moveToNext(evt); + break; + case k.KEY_LEFT_ARROW: + rval = this.moveToPrevious(evt); + break; + default: + rval = this.inherited("processKey", evt); + break; + } + + return rval; + }, + + postCreate: function(){ + this.inherited("postCreate"); + dojo.widget.PopupManager.opened(this); + this.isShowingNow = true; + }, + + /* + * override PopupMenu2 to open the submenu below us rather than to our right + */ + openSubmenu: function(submenu, from_item){ + var fromPos = dojo.html.getAbsolutePosition(from_item.domNode, true); + var ourPos = dojo.html.getAbsolutePosition(this.domNode, true); + var our_h = dojo.html.getBorderBox(this.domNode).height; + var x = fromPos.x; + var y = ourPos.y + our_h - this.menuOverlap; + + submenu.open(x, y, this, from_item.domNode); + + this.currentSubmenuTrigger = from_item; + this.currentSubmenuTrigger.is_open = true; + } +}); + +dojo.widget.defineWidget( + "dojo.widget.MenuBarItem2", + dojo.widget.MenuItem2, +{ + templateString: + '<td class="dojoMenuBarItem2" dojoAttachEvent="onMouseOver: onHover; onMouseOut: onUnhover; onClick: _onClick;">' + +'<span><span>${this.caption}</span>${this.caption}</span>' + +'</td>', + + highlightClass: 'dojoMenuBarItem2Hover', + + setDisabled: function(value){ + this.disabled = value; + if (this.disabled){ + dojo.html.addClass(this.domNode, 'dojoMenuBarItem2Disabled'); + }else{ + dojo.html.removeClass(this.domNode, 'dojoMenuBarItem2Disabled'); + } + } +}); diff --git a/source/web/scripts/ajax/src/widget/MenuItem.js b/source/web/scripts/ajax/src/widget/MenuItem.js new file mode 100644 index 0000000000..3c26ab7ac4 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/MenuItem.js @@ -0,0 +1,47 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.provide("dojo.widget.MenuItem"); +dojo.provide("dojo.widget.DomMenuItem"); + +dojo.deprecated("dojo.widget.MenuItem, dojo.widget.DomMenuItem", "use dojo.widget.Menu2", "0.4"); + +dojo.require("dojo.string"); +dojo.require("dojo.widget.*"); + +dojo.widget.tags.addParseTreeHandler("dojo:MenuItem"); + +/* MenuItem + ***********/ + +dojo.widget.MenuItem = function(){ + dojo.widget.MenuItem.superclass.constructor.call(this); +} +dojo.inherits(dojo.widget.MenuItem, dojo.widget.Widget); + +dojo.lang.extend(dojo.widget.MenuItem, { + widgetType: "MenuItem", + isContainer: true +}); + + +/* DomMenuItem + **************/ +dojo.widget.DomMenuItem = function(){ + dojo.widget.DomMenuItem.superclass.constructor.call(this); +} +dojo.inherits(dojo.widget.DomMenuItem, dojo.widget.DomWidget); + +dojo.lang.extend(dojo.widget.DomMenuItem, { + widgetType: "MenuItem" +}); + +dojo.requireAfterIf("html", "dojo.html"); +dojo.requireAfterIf("html", "dojo.widget.html.MenuItem"); diff --git a/source/web/scripts/ajax/src/widget/MonthlyCalendar.js b/source/web/scripts/ajax/src/widget/MonthlyCalendar.js new file mode 100644 index 0000000000..f493c4ebe1 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/MonthlyCalendar.js @@ -0,0 +1,173 @@ +dojo.provide("dojo.widget.MonthlyCalendar"); +dojo.require("dojo.date"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.DatePicker"); +dojo.require("dojo.event.*"); +dojo.require("dojo.html.*"); +dojo.require("dojo.i18n.calendar.GregorianNames"); + +dojo.widget.defineWidget( + "dojo.widget.MonthlyCalendar", + dojo.widget.DatePicker, + function(){ + this.iCalendars = []; + }, +{ + dayWidth: 'wide', + + templatePath: dojo.uri.dojoUri("src/widget/templates/MonthlyCalendar.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/MonthlyCalendar.css"), + + cache: function() { + }, + + addCalendar: function(/* dojo.iCalendar */ cal) { + dojo.debug("Adding Calendar"); + this.iCalendars.push(cal); + dojo.debug("Starting init"); + this.initUI(); + dojo.debug("done init"); + }, + + createDayContents: function(node,mydate) { + dojo.html.removeChildren(node); + node.appendChild(document.createTextNode(mydate.getDate())); + for(var x=0; x<this.iCalendars.length; x++) { + var evts = this.iCalendars[x].getEvents(mydate); + if ((dojo.lang.isArray(evts)) && (evts.length>0)) { + for(var y=0;y<evts.length;y++) { + var el = document.createElement("div"); + dojo.html.addClass(el, "dojoMonthlyCalendarEvent"); + el.appendChild(document.createTextNode(evts[y].summary.value)); + el.width = dojo.html.getContentBox(node).width; + node.appendChild(el); + } + } + } + }, + + initUI: function() { + var dayLabels = dojo.i18n.calendar.GregorianNames.getNames('days', this.dayWidth, 'standAlone', this.lang); + var dayLabelNodes = this.dayLabelsRow.getElementsByTagName("td"); + for(var i=0; i<7; i++) { + dayLabelNodes.item(i).innerHTML = dayLabels[i]; + } + + this.selectedIsUsed = false; + this.currentIsUsed = false; + var currentClassName = ""; + var previousDate = new Date(); + var calendarNodes = this.calendarDatesContainerNode.getElementsByTagName("td"); + var currentCalendarNode; + // set hours of date such that there is no chance of rounding error due to + // time change in local time zones + previousDate.setHours(8); + var nextDate = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date, 8); + var lastDay = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date + 42, 8); + + if (this.iCalendars.length > 0) { + for (var x=0; x<this.iCalendars.length;x++) { + this.iCalendars[x].preComputeRecurringEvents(lastDay); + } + } + + if(this.firstSaturday.date < 7) { + // this means there are days to show from the previous month + var dayInWeek = 6; + for (var i=this.firstSaturday.date; i>0; i--) { + currentCalendarNode = calendarNodes.item(dayInWeek); + this.createDayContents(currentCalendarNode, nextDate); + + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current")); + dayInWeek--; + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, false); + } + for(var i=dayInWeek; i>-1; i--) { + currentCalendarNode = calendarNodes.item(i); + + this.createDayContents(currentCalendarNode, nextDate); + + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "previous")); + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, false); + } + } else { + nextDate.setDate(1); + for(var i=0; i<7; i++) { + currentCalendarNode = calendarNodes.item(i); + this.createDayContents(currentCalendarNode, nextDate); + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current")); + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, true); + } + } + previousDate.setDate(this.firstSaturday.date); + previousDate.setMonth(this.firstSaturday.month); + previousDate.setFullYear(this.firstSaturday.year); + nextDate = this.incrementDate(previousDate, true); + var count = 7; + currentCalendarNode = calendarNodes.item(count); + while((nextDate.getMonth() == previousDate.getMonth()) && (count<42)) { + this.createDayContents(currentCalendarNode, nextDate); + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current")); + currentCalendarNode = calendarNodes.item(++count); + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, true); + } + + while(count < 42) { + this.createDayContents(currentCalendarNode, nextDate); + dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "next")); + currentCalendarNode = calendarNodes.item(++count); + previousDate = nextDate; + nextDate = this.incrementDate(nextDate, true); + } + this.setMonthLabel(this.firstSaturday.month); + this.setYearLabels(this.firstSaturday.year); + } +}); + +dojo.widget.MonthlyCalendar.util= new function() { +// this.months = dojo.date.months; +// this.weekdays = dojo.date.days; + + this.toRfcDate = function(jsDate) { + if(!jsDate) { + jsDate = this.today; + } + var year = jsDate.getFullYear(); + var month = jsDate.getMonth() + 1; + if (month < 10) { + month = "0" + month.toString(); + } + var date = jsDate.getDate(); + if (date < 10) { + date = "0" + date.toString(); + } + // because this is a date picker and not a time picker, we treat time + // as zero + return year + "-" + month + "-" + date + "T00:00:00+00:00"; + } + + this.fromRfcDate = function(rfcDate) { + var tempDate = rfcDate.split("-"); + if(tempDate.length < 3) { + return new Date(); + } + // fullYear, month, date + return new Date(parseInt(tempDate[0]), (parseInt(tempDate[1], 10) - 1), parseInt(tempDate[2].substr(0,2), 10)); + } + +//Note: redundant with dojo.widget.DatePicker.util + this.initFirstSaturday = function(month, year) { + if(!month) { + month = this.date.getMonth(); + } + if(!year) { + year = this.date.getFullYear(); + } + var firstOfMonth = new Date(year, month, 1); + return {year: year, month: month, date: 7 - firstOfMonth.getDay()}; + } +} diff --git a/source/web/scripts/ajax/src/widget/Parse.js b/source/web/scripts/ajax/src/widget/Parse.js new file mode 100644 index 0000000000..15c1a3d55c --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Parse.js @@ -0,0 +1,355 @@ +dojo.provide("dojo.widget.Parse"); + +dojo.require("dojo.widget.Manager"); +dojo.require("dojo.dom"); +dojo.require("dojo.namespace"); + +dojo.widget.Parse = function(fragment){ + this.propertySetsList = []; + this.fragment = fragment; + + this.createComponents = function(frag, parentComp){ + var comps = []; + var built = false; + // if we have items to parse/create at this level, do it! + try{ + if((frag)&&(frag["tagName"])&&(frag!=frag["nodeRef"])){ + var djTags = dojo.widget.tags; + // we split so that you can declare multiple + // non-destructive widgets from the same ctor node + var tna = String(frag["tagName"]).split(";"); + for(var x=0; x<tna.length; x++){ + var ltn = (tna[x].replace(/^\s+|\s+$/g, "")).toLowerCase(); + var pos = ltn.indexOf(":"); + var nsName = (pos > 0) ? ltn.substring(0,pos) : null; + //if we don't already have a tag registered for this widget, try to load it's + //namespace, if it has one + if(!djTags[ltn] && dojo.getNamespace && dojo.lang.isString(ltn) && pos>0){ + var ns = dojo.getNamespace(nsName); + var tagName = ltn.substring(pos+1,ltn.length); + var domain = null; + var dojoDomain = frag[ltn]["dojoDomain"] || frag[ltn]["dojodomain"]; + if(dojoDomain){ + domain = dojoDomain[0].value; + } + if(ns){ + ns.load(tagName, domain); + } + + } + + if(!djTags[ltn]){ + dojo.deprecated('dojo.widget.Parse.createComponents', 'Widget not defined for namespace'+nsName+ + ', so searching all namespaces. Developers should specify namespaces for all non-Dojo widgets', "0.5"); + + var newNs = dojo.findNamespaceForWidget(tagName); + if(newNs){ + ltn = newNs.nsPrefix + ":" + (ltn.indexOf(":")> 0 ? ltn.substring(ltn.indexOf(":")+1) : ltn); + } + } + + if(djTags[ltn]){ + built = true; + frag.tagName = ltn; + var ret = djTags[ltn](frag, this, parentComp, frag["index"]); + comps.push(ret); + }else{ + if(dojo.lang.isString(ltn) && nsName && dojo._namespaces[nsName]){ + dojo.debug("no tag handler registered for type: ", ltn); + } + } + } + } + }catch(e){ + dojo.debug("dojo.widget.Parse: error:", e); + // throw e; + // IE is such a bitch sometimes + } + // if there's a sub-frag, build widgets from that too + if(!built){ + comps = comps.concat(this.createSubComponents(frag, parentComp)); + } + return comps; + } + + /* createSubComponents recurses over a raw JavaScript object structure, + and calls the corresponding handler for its normalized tagName if it exists + */ + this.createSubComponents = function(fragment, parentComp){ + var frag, comps = []; + for(var item in fragment){ + frag = fragment[item]; + if ((frag)&&(typeof frag == "object")&&(frag!=fragment.nodeRef)&&(frag!=fragment["tagName"])){ + comps = comps.concat(this.createComponents(frag, parentComp)); + } + } + return comps; + } + + /* parsePropertySets checks the top level of a raw JavaScript object + structure for any propertySets. It stores an array of references to + propertySets that it finds. + */ + this.parsePropertySets = function(fragment){ + return []; + var propertySets = []; + for(var item in fragment){ + if((fragment[item]["tagName"] == "dojo:propertyset")){ + propertySets.push(fragment[item]); + } + } + // FIXME: should we store these propertySets somewhere for later retrieval + this.propertySetsList.push(propertySets); + return propertySets; + } + + /* parseProperties checks a raw JavaScript object structure for + properties, and returns an array of properties that it finds. + */ + this.parseProperties = function(fragment){ + var properties = {}; + for(var item in fragment){ + // FIXME: need to check for undefined? + // case: its a tagName or nodeRef + if((fragment[item] == fragment["tagName"])|| + (fragment[item] == fragment.nodeRef)){ + // do nothing + }else{ + if((fragment[item]["tagName"])&& + (dojo.widget.tags[fragment[item].tagName.toLowerCase()])){ + // TODO: it isn't a property or property set, it's a fragment, + // so do something else + // FIXME: needs to be a better/stricter check + // TODO: handle xlink:href for external property sets + }else if((fragment[item][0])&&(fragment[item][0].value!="")&&(fragment[item][0].value!=null)){ + try{ + // FIXME: need to allow more than one provider + if(item.toLowerCase() == "dataprovider") { + var _this = this; + this.getDataProvider(_this, fragment[item][0].value); + properties.dataProvider = this.dataProvider; + } + properties[item] = fragment[item][0].value; + var nestedProperties = this.parseProperties(fragment[item]); + // FIXME: this kind of copying is expensive and inefficient! + for(var property in nestedProperties){ + properties[property] = nestedProperties[property]; + } + }catch(e){ dojo.debug(e); } + } + } + } + return properties; + } + + /* getPropertySetById returns the propertySet that matches the provided id + */ + + this.getDataProvider = function(objRef, dataUrl){ + // FIXME: this is currently sync. To make this async, we made need to move + //this step into the widget ctor, so that it is loaded when it is needed + // to populate the widget + dojo.io.bind({ + url: dataUrl, + load: function(type, evaldObj){ + if(type=="load"){ + objRef.dataProvider = evaldObj; + } + }, + mimetype: "text/javascript", + sync: true + }); + } + + + this.getPropertySetById = function(propertySetId){ + for(var x = 0; x < this.propertySetsList.length; x++){ + if(propertySetId == this.propertySetsList[x]["id"][0].value){ + return this.propertySetsList[x]; + } + } + return ""; + } + + /* getPropertySetsByType returns the propertySet(s) that match(es) the + * provided componentClass + */ + this.getPropertySetsByType = function(componentType){ + var propertySets = []; + for(var x=0; x < this.propertySetsList.length; x++){ + var cpl = this.propertySetsList[x]; + var cpcc = cpl["componentClass"]||cpl["componentType"]||null; + var propertySetId = this.propertySetsList[x]["id"][0].value; + if((cpcc)&&(propertySetId == cpcc[0].value)){ + propertySets.push(cpl); + } + } + return propertySets; + } + + /* getPropertySets returns the propertySet for a given component fragment + */ + this.getPropertySets = function(fragment){ + var ppl = "dojo:propertyproviderlist"; + var propertySets = []; + var tagname = fragment["tagName"]; + if(fragment[ppl]){ + var propertyProviderIds = fragment[ppl].value.split(" "); + // FIXME: should the propertyProviderList attribute contain # + // syntax for reference to ids or not? + // FIXME: need a better test to see if this is local or external + // FIXME: doesn't handle nested propertySets, or propertySets that + // just contain information about css documents, etc. + for(var propertySetId in propertyProviderIds){ + if((propertySetId.indexOf("..")==-1)&&(propertySetId.indexOf("://")==-1)){ + // get a reference to a propertySet within the current parsed structure + var propertySet = this.getPropertySetById(propertySetId); + if(propertySet != ""){ + propertySets.push(propertySet); + } + }else{ + // FIXME: add code to parse and return a propertySet from + // another document + // alex: is this even necessaray? Do we care? If so, why? + } + } + } + // we put the typed ones first so that the parsed ones override when + // iteration happens. + return (this.getPropertySetsByType(tagname)).concat(propertySets); + } + + /* + nodeRef is the node to be replaced... in the future, we might want to add + an alternative way to specify an insertion point + + componentName is the expected dojo widget name, i.e. Button of ContextMenu + + properties is an object of name value pairs + namespace is the namespace of the widget. Defaults to "dojo" + */ + this.createComponentFromScript = function(nodeRef, componentName, properties, namespace){ + if(!namespace){ namespace = "dojo"; } + var ltn = namespace + ":" + componentName.toLowerCase(); + + var djTags = dojo.widget.tags; + //if we don't already have a tag registered for this widget, try to load it's + //namespace, if it has one + if(!djTags[ltn] && dojo.getNamespace && dojo.lang.isString(ltn)){ + var ns = dojo.getNamespace(namespace); + if(ns){ns.load(componentName);} + } + + if(!djTags[ltn]){ + dojo.deprecated('dojo.widget.Parse.createComponentFromScript', 'Widget not defined for namespace'+ + namespace + + ', so searching all namespaces. Developers should specify namespaces for all non-Dojo widgets', "0.5"); + + var newNs = dojo.findNamespaceForWidget(componentName.toLowerCase()); + if(newNs){ + var newLtn = newNs.nsPrefix + ":"+(ltn.indexOf(":")> 0 ? ltn.substring(ltn.indexOf(":")+1) : ltn); + properties[newLtn]=properties[ltn]; + properties.namespace = newNs.nsPrefix; + ltn = newLtn; + } + } + + if(djTags[ltn]){ + properties.fastMixIn = true; + //dojo.profile.start("dojo.widget.tags - "+ltn); + //var ret = [dojo.widget.tags[ltn](properties, this, null, null, properties)]; + var ret = [dojo.widget.buildWidgetFromParseTree(ltn, properties, this, null, null, properties)]; + //dojo.profile.end("dojo.widget.tags - "+ltn); + return ret; + }else{ + dojo.debug("no tag handler registered for type: ", ltn); + } + + } +} + + +dojo.widget._parser_collection = {"dojo": new dojo.widget.Parse() }; +dojo.widget.getParser = function(name){ + if(!name){ name = "dojo"; } + if(!this._parser_collection[name]){ + this._parser_collection[name] = new dojo.widget.Parse(); + } + return this._parser_collection[name]; +} + +/** + * Creates widget. + * + * @param name The name of the widget to create with optional namespace prefix, + * e.g."ns:widget", namespace defaults to "dojo". + * @param props Key-Value pairs of properties of the widget + * @param refNode If the position argument is specified, this node is used as + * a reference for inserting this node into a DOM tree; else + * the widget becomes the domNode + * @param position The position to insert this widget's node relative to the + * refNode argument + * @return The new Widget object + */ + +dojo.widget.createWidget = function(name, props, refNode, position){ + + var isNode = false; + var isNameStr = (typeof name == "string"); + if(isNameStr){ + var pos = name.indexOf(":"); + var namespace = (pos > -1) ? name.substring(0,pos) : "dojo"; + if(pos > -1){ name = name.substring(pos+1); } + var lowerCaseName = name.toLowerCase(); + var namespacedName = namespace+":" + lowerCaseName; + isNode = ( dojo.byId(name) && (!dojo.widget.tags[namespacedName]) ); + } + + if( (arguments.length == 1) && ((isNode)||(!isNameStr)) ){ + // we got a DOM node + var xp = new dojo.xml.Parse(); + // FIXME: we should try to find the parent! + var tn = (isNode) ? dojo.byId(name) : name; + return dojo.widget.getParser().createComponents(xp.parseElement(tn, null, true))[0]; + } + + function fromScript(placeKeeperNode, name, props, namespace){ + props[namespacedName] = { + dojotype: [{value: lowerCaseName}], + nodeRef: placeKeeperNode, + fastMixIn: true + }; + props.namespace = namespace; + return dojo.widget.getParser().createComponentFromScript( + placeKeeperNode, name, props, namespace); + } + + props = props||{}; + var notRef = false; + var tn = null; + var h = dojo.render.html.capable; + if(h){ + tn = document.createElement("span"); + } + if(!refNode){ + notRef = true; + refNode = tn; + if(h){ + dojo.body().appendChild(refNode); + } + }else if(position){ + dojo.dom.insertAtPosition(tn, refNode, position); + }else{ // otherwise don't replace, but build in-place + tn = refNode; + } + var widgetArray = fromScript(tn, name.toLowerCase(), props, namespace); + if (!widgetArray || !widgetArray[0] || typeof widgetArray[0].widgetType == "undefined") { + throw new Error("createWidget: Creation of \"" + name + "\" widget failed."); + } + if (notRef) { + if (widgetArray[0].domNode.parentNode) { + widgetArray[0].domNode.parentNode.removeChild(widgetArray[0].domNode); + } + } + return widgetArray[0]; // just return the widget +} diff --git a/source/web/scripts/ajax/src/widget/PopUpButton.js b/source/web/scripts/ajax/src/widget/PopUpButton.js new file mode 100644 index 0000000000..fddbad81af --- /dev/null +++ b/source/web/scripts/ajax/src/widget/PopUpButton.js @@ -0,0 +1,192 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.provide("dojo.widget.PopUpButton"); +dojo.provide("dojo.widget.DomPopUpButton"); +dojo.provide("dojo.widget.HtmlPopUpButton"); + +dojo.deprecated("dojo.widget.PopUpButton, dojo.widget.DomPopUpButton, dojo.widget.HtmlPopUpButton", "use dojo.widget.DropDownButton", "0.4"); + +//dojo.require("dojo.widget.Button"); +//dojo.require("dojo.widget.HtmlButton"); + +dojo.require("dojo.widget.Menu"); +dojo.require("dojo.widget.MenuItem"); + +dojo.require("dojo.html"); + +dojo.widget.tags.addParseTreeHandler("dojo:PopUpButton"); + +/* PopUpButton + **************/ + +dojo.widget.PopUpButton = function () { + dojo.widget.PopUpButton.superclass.constructor.call(this); +} +dojo.inherits(dojo.widget.PopUpButton, dojo.widget.Widget); + +dojo.lang.extend(dojo.widget.PopUpButton, { + widgetType: "PopUpButton", + + label: "" +}); + + +/* DomPopUpButton + *****************/ +dojo.widget.DomPopUpButton = function(){ + dojo.widget.DomPopUpButton.superclass.constructor.call(this); +} +dojo.inherits(dojo.widget.DomPopUpButton, dojo.widget.DomWidget); + +dojo.lang.extend(dojo.widget.DomPopUpButton, { + widgetType: dojo.widget.PopUpButton.prototype.widgetType +}); + + +/* HtmlPopUpButton + ******************/ + +dojo.widget.HtmlPopUpButton = function () { + dojo.widget.HtmlPopUpButton.superclass.constructor.call(this); +} +dojo.inherits(dojo.widget.HtmlPopUpButton, dojo.widget.HtmlWidget); + +dojo.lang.extend(dojo.widget.HtmlPopUpButton, { + widgetType: dojo.widget.PopUpButton.prototype.widgetType, + templateString: null, + templateCssPath: dojo.uri.dojoUri("src/widget/templates/PopUpButton.css"), + + buildRendering: function (args, frag) { + dojo.style.insertCssFile(this.templateCssPath, null, true); + + this.domNode = document.createElement("a"); + this.domNode.className = "PopUpButton"; + dojo.event.connect(this.domNode, "onmousedown", this, "onMouseDown"); + + // draw the arrow + var arrow = document.createElement("img"); + arrow.src = dojo.uri.dojoUri("src/widget/templates/images/dropdownButtonsArrow.gif"); + dojo.html.setClass(arrow, "downArrow"); + this.domNode.appendChild(arrow); + + this.menu = dojo.widget.fromScript("Menu"); + dojo.html.addClass(this.menu.domNode, "PopUpButtonMenu"); + dojo.event.connect(this.menu, "onSelect", this, "onSelect"); + + if (frag["dojo:" + this.widgetType.toLowerCase()].nodeRef) { + var node = frag["dojo:" + this.widgetType.toLowerCase()].nodeRef; + var options = node.getElementsByTagName("option"); + for (var i = 0; i < options.length; i++) { + var properties = { + title: dojo.dom.textContent(options[i]), + value: options[i].value + } + this.addItem(dojo.widget.fromScript("MenuItem", properties)); + } + } + }, + + addItem: function (item) { + // TODO: should be dojo.widget.MenuItem + if (item instanceof dojo.widget.html.MenuItem) { + this.menu.push(item); + } else { + // TODO: create one + var menuItem = dojo.widget.fromScript("MenuItem", {title: item}); + this.menu.push(menuItem); + } + }, + + +/* Enabled utility methods + **************************/ + + _enabled: true, + + isEnabled: function() { return this._enabled; }, + + setEnabled: function(enabled, force, preventEvent) { + enabled = Boolean(enabled); + if (force || this._enabled != enabled) { + this._enabled = enabled; + if (!preventEvent) { + this._fireEvent(this._enabled ? "onEnable" : "onDisable"); + this._fireEvent("onChangeEnabled"); + } + } + + dojo.html[(this._enabled ? "add" : "remove") + + "Class"](this.domNode, "disabled"); + + return this._enabled; + }, + + enable: function(force, preventEvent) { + return this.setEnabled(true, force, preventEvent); + }, + + disable: function(force, preventEvent) { + return this.setEnabled(false, force, preventEvent); + }, + + toggleEnabled: function(force, preventEvent) { + return this.setEnabled(!this._enabled, force, preventEvent); + }, + + +/* Select utility methods + **************************/ + + onSelect: function (item, e) { + this.domNode.firstChild.nodeValue = item.title; + }, + + onMouseDown: function (e) { + if (!this._menuVisible) { + this._showMenu(e); + dojo.lang.setTimeout(dojo.event.connect, 1, document, "onmousedown", this, "_hideMenu"); + } + }, + + + _fireEvent: function(evt) { + if(typeof this[evt] == "function") { + var args = [this]; + for(var i = 1; i < arguments.length; i++) { + args.push(arguments[i]); + } + this[evt].apply(this, args); + } + }, + + + _showMenu: function (e) { + if (!this._enabled) { return; } + this._menuVisible = true; + with (dojo.html) { + var y = getAbsoluteY(this.domNode) + getInnerHeight(this.domNode); + var x = getAbsoluteX(this.domNode); + } + + document.body.appendChild(this.menu.domNode); + with (this.menu.domNode.style) { + top = y + "px"; + left = x + "px"; + } + }, + + _hideMenu: function (e) { + this.menu.domNode.parentNode.removeChild(this.menu.domNode); + dojo.event.disconnect(document, "onmousedown", this, "_hideMenu"); + this._menuVisible = false; + } + +}); diff --git a/source/web/scripts/ajax/src/widget/RemoteTabController.js b/source/web/scripts/ajax/src/widget/RemoteTabController.js new file mode 100644 index 0000000000..c95c718f62 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/RemoteTabController.js @@ -0,0 +1,171 @@ +dojo.provide("dojo.widget.RemoteTabController"); + +//Summary +//Remote Tab Controller widget. Can be located independently of a tab +//container and control the selection of its tabs +dojo.require("dojo.widget.*"); +dojo.require("dojo.event.*"); + +dojo.widget.defineWidget( + "dojo.widget.RemoteTabController", + dojo.widget.HtmlWidget, + function() { + //summary + //Initialize Remote Tab Controller + // for passing in as a parameter + this.tabContainer = ""; + + // the reference to the tab container + this._tabContainer={}; + + //hash of tabs + this.tabs = {}; + + this.selectedTab=""; + + //override these classes to change the style + this["class"]="dojoRemoteTabController"; // alt syntax: "class" is a reserved word in JS + this.labelClass="dojoRemoteTab"; + this.imageClass="dojoRemoteTabClose"; + this.imageHoverClass="dojoRemoteTabCloseHover"; + }, + { + templateCssPath: dojo.uri.dojoUri("src/widget/templates/RemoteTabControl.css"), + templateString: '<div dojoAttachPoint="domNode" wairole="tablist"></div>', + + postCreate: function() { + dojo.html.addClass(this.domNode, this["class"]); // "class" is a reserved word in JS + dojo.widget.wai.setAttr(this.domNode, "waiRole", "role", "tablist"); + + if (this.tabContainer) { + dojo.addOnLoad(dojo.lang.hitch(this, function() { + this.setTabContainer(dojo.widget.byId(this.tabContainer)); + })); + } + }, + + setTabContainer: function(/* dojo.widget.TabContainer */ tabContainer) { + //summary + //Connect this Remote Tab Controller to an existing TabContainer + this._tabContainer = tabContainer; + this.setupTabs(); + + dojo.event.connect(this._tabContainer, "addChild", this, "setupTabs"); + dojo.event.connect(this._tabContainer, "selectTab", this, "onTabSelected"); + }, + + setupTabs: function() { + //summary + //Setup tab buttons for each of the TabContainers tabs + + dojo.html.removeChildren(this.domNode); + dojo.lang.forEach(this._tabContainer.children, this.addTab, this); + }, + + onTabSelected: function(/* dojo.widget.TabPane */tab) { + //summary + //Do this when a tab gets selected + if (this.selectedTab.tab != tab.widgetId) { + dojo.html.removeClass(this.selectedTab.button, "current"); + } + + this.selectedTab = this.tabs[tab.widgetId]; + dojo.html.addClass(this.selectedTab.button,"current"); + + }, + + addTab: function(/* dojo.widget.TabPane */tab) { + //summary + //Add a new button + + // Create label + div = document.createElement("div"); + dojo.html.addClass(div, this.labelClass); + var innerDiv = document.createElement("div"); + + // need inner span so focus rectangle is drawn properly + var titleSpan = document.createElement("span"); + titleSpan.innerHTML = tab.label; + titleSpan.tabIndex="-1"; + + // set role on tab title + dojo.widget.wai.setAttr(titleSpan, "waiRole", "role", "tab"); + innerDiv.appendChild(titleSpan); + dojo.html.disableSelection(titleSpan); + + if(this._tabContainer.closeButton=="tab" || tab.tabCloseButton){ + var img = document.createElement("span"); + dojo.html.addClass(img, this.imageClass); + dojo.event.connect(img, "onclick", dojo.lang.hitch(this, function(evt){ + this._runOnCloseTab(tab); dojo.event.browser.stopEvent(evt); + })); + dojo.event.connect(img, "onmouseover", dojo.lang.hitch(this, function(){ + dojo.html.addClass(img, this.imageHoverClass); + })); + dojo.event.connect(img, "onmouseout", dojo.lang.hitch(this, function(){ + dojo.html.removeClass(img, this.imageHoverClass); + })); + innerDiv.appendChild(img); + } + + // connect to _runOnCloseTab in case the tab pane or + // another remote controller closes a tab + if(this._tabContainer.closeButton=="tab" || this._tabContainer.closeButton=="pane"){ + dojo.event.connect(this._tabContainer, "_runOnCloseTab", dojo.lang.hitch(this, function(t){ + this._runOnRemoteClose(t); + })); + } + + div.appendChild(innerDiv); + div.tabTitle=titleSpan; + this.domNode.appendChild(div); + + var tabObj = {"tab": tab, "button": div}; + + if (this._tabContainer.selectedTab == tab.widgetId || tab.selected) { + this.selectedTab = tabObj; + dojo.html.addClass(div, "current"); + } + + this.tabs[tab.widgetId] = tabObj; + + dojo.event.connect(div, "onclick", dojo.lang.hitch(this._tabContainer, function() { + this.selectTab(tab); + })); + + dojo.event.connect(div, "onkeydown", dojo.lang.hitch(this._tabContainer, function(evt) { + this.tabNavigation(evt, tab); + })); + }, + + _runOnCloseTab: function(tab){ + var tabObj = this.tabs[tab.widgetId]; + this.removeChild(tabObj); + this._tabContainer._runOnCloseTab(tab); + }, + + _runOnRemoteClose: function(tab){ + var tabObj = this.tabs[tab.widgetId]; + if(!tabObj.button){ return; } + this.removeChild(tabObj); + }, + + removeChild: function(tab){ + // remove tab event handlers + dojo.event.disconnect(tab.button, "onclick", function(){ }); + if(this._tabContainer.closeButton == "tab"){ + var img = tab.button.lastChild.lastChild; + if(img){ + dojo.html.removeClass(img, this.imageClass); + } + } + + dojo.widget.RemoteTabController.superclass.removeChild.call(this, tab); + + if(tab.domNode) { dojo.html.removeClass(tab.domNode, this.labelClass); } + this.domNode.removeChild(tab.button); + delete(tab.button); + } + }, + "html" +); diff --git a/source/web/scripts/ajax/src/widget/ResizableTextarea.js b/source/web/scripts/ajax/src/widget/ResizableTextarea.js new file mode 100644 index 0000000000..746df39e61 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/ResizableTextarea.js @@ -0,0 +1,92 @@ +dojo.provide("dojo.widget.ResizableTextarea"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.LayoutContainer"); +dojo.require("dojo.widget.ResizeHandle"); + +dojo.widget.defineWidget( + "dojo.widget.ResizableTextarea", + dojo.widget.HtmlWidget, +{ + templatePath: dojo.uri.dojoUri("src/widget/templates/ResizableTextarea.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/ResizableTextarea.css"), + isContainer: false, + textAreaNode: null, + textAreaContainer: null, + textAreaContainerNode: null, + statusBar: null, + statusBarContainerNode: null, + statusLabelNode: null, + statusLabel: null, + rootLayoutNode: null, + resizeHandleNode: null, + resizeHandle: null, + + fillInTemplate: function(args, frag){ + this.textAreaNode = this.getFragNodeRef(frag).cloneNode(true); + + // FIXME: Safari apparently needs this! + dojo.body().appendChild(this.domNode); + + this.rootLayout = dojo.widget.createWidget( + "LayoutContainer", + { + minHeight: 50, + minWidth: 100 + }, + this.rootLayoutNode + ); + + + this.textAreaContainer = dojo.widget.createWidget( + "LayoutContainer", + { layoutAlign: "client" }, + this.textAreaContainerNode + ); + this.rootLayout.addChild(this.textAreaContainer); + + this.textAreaContainer.domNode.appendChild(this.textAreaNode); + with(this.textAreaNode.style){ + width="100%"; + height="100%"; + } + + this.statusBar = dojo.widget.createWidget( + "LayoutContainer", + { + layoutAlign: "bottom", + minHeight: 28 + }, + this.statusBarContainerNode + ); + this.rootLayout.addChild(this.statusBar); + + this.statusLabel = dojo.widget.createWidget( + "LayoutContainer", + { + layoutAlign: "client", + minWidth: 50 + }, + this.statusLabelNode + ); + this.statusBar.addChild(this.statusLabel); + + this.resizeHandle = dojo.widget.createWidget( + "ResizeHandle", + { targetElmId: this.rootLayout.widgetId }, + this.resizeHandleNode + ); + this.statusBar.addChild(this.resizeHandle); + // dojo.debug(this.rootLayout.widgetId); + + // dojo.event.connect(this.resizeHandle, "beginSizing", this, "hideContent"); + // dojo.event.connect(this.resizeHandle, "endSizing", this, "showContent"); + }, + + hideContent: function(){ + this.textAreaNode.style.display = "none"; + }, + + showContent: function(){ + this.textAreaNode.style.display = ""; + } +}); diff --git a/source/web/scripts/ajax/src/widget/ResizeHandle.js b/source/web/scripts/ajax/src/widget/ResizeHandle.js new file mode 100644 index 0000000000..23764aace4 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/ResizeHandle.js @@ -0,0 +1,93 @@ +dojo.provide("dojo.widget.ResizeHandle"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.event"); + +dojo.widget.defineWidget( + "dojo.widget.ResizeHandle", + dojo.widget.HtmlWidget, +{ + isSizing: false, + startPoint: null, + startSize: null, + minSize: null, + + targetElmId: '', + + templateCssPath: dojo.uri.dojoUri("src/widget/templates/ResizeHandle.css"), + templateString: '<div class="dojoHtmlResizeHandle"><div></div></div>', + + postCreate: function(){ + dojo.event.connect(this.domNode, "onmousedown", this, "beginSizing"); + }, + + beginSizing: function(e){ + if (this.isSizing){ return false; } + + // get the target dom node to adjust. targetElmId can refer to either a widget or a simple node + this.targetWidget = dojo.widget.byId(this.targetElmId); + this.targetDomNode = this.targetWidget ? this.targetWidget.domNode : dojo.byId(this.targetElmId); + if (!this.targetDomNode){ return; } + + this.isSizing = true; + this.startPoint = {'x':e.clientX, 'y':e.clientY}; + var mb = dojo.html.getMarginBox(this.targetDomNode); + this.startSize = {'w':mb.width, 'h':mb.height}; + + dojo.event.kwConnect({ + srcObj: dojo.body(), + srcFunc: "onmousemove", + targetObj: this, + targetFunc: "changeSizing", + rate: 25 + }); + dojo.event.connect(dojo.body(), "onmouseup", this, "endSizing"); + + e.preventDefault(); + }, + + changeSizing: function(e){ + // On IE, if you move the mouse above/to the left of the object being resized, + // sometimes clientX/Y aren't set, apparently. Just ignore the event. + try{ + if(!e.clientX || !e.clientY){ return; } + }catch(e){ + // sometimes you get an exception accessing above fields... + return; + } + var dx = this.startPoint.x - e.clientX; + var dy = this.startPoint.y - e.clientY; + + var newW = this.startSize.w - dx; + var newH = this.startSize.h - dy; + + // minimum size check + if (this.minSize) { + var mb = dojo.html.getMarginBox(this.targetDomNode); + if (newW < this.minSize.w) { + newW = mb.width; + } + if (newH < this.minSize.h) { + newH = mb.height; + } + } + + if(this.targetWidget){ + this.targetWidget.resizeTo(newW, newH); + }else{ + dojo.html.setMarginBox(this.targetDomNode, { width: newW, height: newH}); + } + + e.preventDefault(); + }, + + endSizing: function(e){ + dojo.event.disconnect(dojo.body(), "onmousemove", this, "changeSizing"); + dojo.event.disconnect(dojo.body(), "onmouseup", this, "endSizing"); + + this.isSizing = false; + } + + +}); diff --git a/source/web/scripts/ajax/src/widget/RichText.js b/source/web/scripts/ajax/src/widget/RichText.js new file mode 100644 index 0000000000..924bd9a3eb --- /dev/null +++ b/source/web/scripts/ajax/src/widget/RichText.js @@ -0,0 +1,1560 @@ + /* -*- tab-width: 4 -*- */ +dojo.provide("dojo.widget.RichText"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.html.*"); +dojo.require("dojo.html.layout"); +dojo.require("dojo.event.*"); +dojo.require("dojo.string.extras"); + +// used to save content +if(dojo.hostenv.post_load_){ + (function(){ + var savetextarea = dojo.doc().createElement('textarea'); + savetextarea.id = "dojo.widget.RichText.savedContent"; + savetextarea.style = "display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"; + dojo.body().appendChild(savetextarea); + })(); +}else{ + //dojo.body() is not available before onLoad is fired + try { + dojo.doc().write('<textarea id="dojo.widget.RichText.savedContent" ' + + 'style="display:none;position:absolute;top:-100px;left:-100px;height:3px;width:3px;overflow:hidden;"></textarea>'); + }catch(e){ } +} + +dojo.widget.defineWidget( + "dojo.widget.RichText", + dojo.widget.HtmlWidget, + { + /** whether to inherit the parent's width or simply use 100% */ + inheritWidth: false, + focusOnLoad: true, + + /** + * If a save name is specified the content is saved and restored if the + * editor is not properly closed after editing has started. + */ + saveName: "", + _content: "", + + /* set height to fix the editor at a specific height, with scrolling */ + height: null, + + /** The minimum height that the editor should have */ + minHeight: "1em", + + isClosed: true, + isLoaded: false, + + /** whether to use the active-x object in IE */ + useActiveX: false, + + /*whether to use relative URLs for images - if this is enabled + images will be given absolute URLs when inside the editor but + will be changed to use relative URLs (to the current page) on save + */ + relativeImageUrls: false, + + _SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@", + + /* + defaultContentCleaner: function(content){ + if(!dojo.render.html.ie){ + return content; + } + + content = content.replace(/\x20/g, " "); + // alert(content); + return content; + }, + */ + + /* Init + *******/ + + fillInTemplate: function(){ + this.open(); + + // add the formatting functions + var funcs = ["queryCommandEnabled", "queryCommandState", + "queryCommandValue", "execCommand"]; + for(var i = 0; i < funcs.length; i++){ + dojo.event.connect("around", this, funcs[i], this, "_normalizeCommand"); + } + + // backwards compatibility, needs to be removed + dojo.event.connect(this, "onKeyPressed", this, "afterKeyPress"); + dojo.event.connect(this, "onKeyPress", this, "keyPress"); + dojo.event.connect(this, "onKeyDown", this, "keyDown"); + dojo.event.connect(this, "onKeyUp", this, "keyUp"); + + // add default some key handlers + var ctrl = this.KEY_CTRL; + var exec = function (cmd, arg) { + return arguments.length == 1 ? function () { this.execCommand(cmd); } : + function () { this.execCommand(cmd, arg); } + } + + this.addKeyHandler("b", ctrl, exec("bold")); + this.addKeyHandler("i", ctrl, exec("italic")); + this.addKeyHandler("u", ctrl, exec("underline")); + this.addKeyHandler("a", ctrl, exec("selectall")); + //this.addKeyHandler("k", ctrl, exec("createlink", "")); + //this.addKeyHandler("K", ctrl, exec("unlink")); + this.addKeyHandler("s", ctrl, function () { this.save(true); }); + + this.addKeyHandler("1", ctrl, exec("formatblock", "h1")); + this.addKeyHandler("2", ctrl, exec("formatblock", "h2")); + this.addKeyHandler("3", ctrl, exec("formatblock", "h3")); + this.addKeyHandler("4", ctrl, exec("formatblock", "h4")); + + this.addKeyHandler("\\", ctrl, exec("insertunorderedlist")); + if(!dojo.render.html.ie){ + this.addKeyHandler("Z", ctrl, exec("redo")); + } + }, + + + events: ["onBlur", "onFocus", "onKeyPress", "onKeyDown", "onKeyUp", "onClick"], + + /** + * Transforms the node referenced in this.domNode into a rich text editing + * node. This can result in the creation and replacement with an <iframe> if + * designMode is used, an <object> and active-x component if inside of IE or + * a reguler element if contentEditable is available. + */ + open: function (element) { + var h = dojo.render.html; + dojo.event.topic.publish("dojo.widget.RichText::open", this); + + if (!this.isClosed) { this.close(); } + this._content = ""; + if((arguments.length == 1)&&(element["nodeName"])){ this.domNode = element; } // else unchanged + + if( (this.domNode["nodeName"])&& + (this.domNode.nodeName.toLowerCase() == "textarea")){ + this.textarea = this.domNode; + var html = dojo.string.trim(this.textarea.value); + if(html == ""){ html = " "; } + this.domNode = document.createElement("div"); + var tmpFunc = dojo.lang.hitch(this, function(){ + with(this.textarea.style){ + display = "block"; + position = "absolute"; + width = "1px"; + height = "1px"; + border = margin = padding = "0px"; + // border = "3px solid black"; + left = top = "-50px"; + // visiblity = "hidden"; + if(h.ie){ + overflow = "hidden"; + } + } + }); + if(h.ie){ + setTimeout(tmpFunc, 10); + }else{ + tmpFunc(); + } + if(!h.safari){ + // FIXME: VERY STRANGE safari 2.0.4 behavior here caused by + // moving the textarea. Often crashed the browser!!! Seems + // fixed on webkit nightlies. + dojo.html.insertBefore(this.domNode, this.textarea); + } + // this.domNode.innerHTML = html; + + if(this.textarea.form){ + dojo.event.connect(this.textarea.form, "onsubmit", + // FIXME: should we be calling close() here instead? + dojo.lang.hitch(this, function(){ + this.textarea.value = this.getEditorContent(); + }) + ); + } + + // dojo plucks our original domNode from the document so we need + // to go back and put ourselves back in + var editor = this; + dojo.event.connect(this, "postCreate", function (){ + dojo.html.insertAfter(editor.textarea, editor.domNode); + }); + }else{ + var html = this._preFilterContent(dojo.string.trim(this.domNode.innerHTML)); + if(html == ""){ html = " "; } + } + + var content = dojo.html.getContentBox(this.domNode); + this._oldHeight = content.height; + this._oldWidth = content.width; + + this._firstChildContributingMargin = this._getContributingMargin(this.domNode, "top"); + this._lastChildContributingMargin = this._getContributingMargin(this.domNode, "bottom"); + + this.savedContent = document.createElement("div"); + while (this.domNode.hasChildNodes()) { + this.savedContent.appendChild(this.domNode.firstChild); + } + + // If we're a list item we have to put in a blank line to force the + // bullet to nicely align at the top of text + if( (this.domNode["nodeName"])&& + (this.domNode.nodeName == "LI")){ + this.domNode.innerHTML = " <br>"; + } + + if(this.saveName != ""){ + var saveTextarea = document.getElementById("dojo.widget.RichText.savedContent"); + if (saveTextarea.value != "") { + var datas = saveTextarea.value.split(this._SEPARATOR); + for (var i = 0; i < datas.length; i++) { + var data = datas[i].split(":"); + if (data[0] == this.saveName) { + html = data[1]; + datas.splice(i, 1); + break; + } + } + } + dojo.event.connect("before", window, "onunload", this, "_saveContent"); + // dojo.event.connect(window, "onunload", this, "_saveContent"); + } + + if(h.ie70 && this.useActiveX){ + dojo.debug("activeX in ie70 is not currently supported, useActiveX is ignored for now."); + this.useActiveX = false; + } + // Safari's selections go all out of whack if we do it inline, + // so for now IE is our only hero + //if (typeof document.body.contentEditable != "undefined") { + if (this.useActiveX && h.ie) { // active-x + this._drawObject(html); + // dojo.debug(this.object.document); + } else if (h.ie) { // contentEditable, easy + this.editNode = document.createElement("div"); + with (this.editNode) { + contentEditable = true; + if(h.ie70){ + if(this.height){ + style.height = this.height; + } + if(this.minHeight){ + style.minHeight = this.minHeight; + } + }else{ + style["height"] = this.height ? this.height : this.minHeight; + } + } + this.window = window; + this.document = document; + + // FIXME: setting contentEditable on switches this element to + // IE's hasLayout mode, triggering weird margin collapsing + // behavior. It's particularly bad if the element you're editing + // contains childnodes that don't have margin: defined in local + // css rules. It would be nice if it was possible to hack around + // this. Sadly _firstChildContributingMargin and + // _lastChildContributingMargin don't work on IE unless all + // elements have margins set in CSS :-( + + this.domNode.appendChild(this.editNode); + + //if the normal way fails, we try the hard way to get the list +// this.editNode.style.display = "none"; + if(!this._cacheLocalBlockFormatNames()){ + //in the array below, ul can not come directly after ol, otherwise the queryCommandValue returns Normal for it + var formats = ['p', 'pre', 'address', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'div', 'ul']; + var localhtml = ""; + for(var i in formats){ + if(formats[i].charAt(1) != 'l'){ + localhtml += "<"+formats[i]+"><span>content</span></"+formats[i]+">"; + }else{ + localhtml += "<"+formats[i]+"><li>content</li></"+formats[i]+">"; + } + } + //queryCommandValue returns empty if we hide editNode, so move it out of screen temporary + with(this.editNode.style){ + position = "absolute"; + left = "-2000px"; + top = "-2000px"; + } + this.editNode.innerHTML = localhtml; + var node = this.editNode.firstChild; + while(node){ + dojo.html.selection.selectElement(node.firstChild); + var nativename = node.tagName.toLowerCase(); + this._local2NativeFormatNames[nativename] = this.queryCommandValue("formatblock"); + dojo.debug([nativename,this._local2NativeFormatNames[nativename]]); + this._native2LocalFormatNames[this._local2NativeFormatNames[nativename]] = nativename; + node = node.nextSibling; + } + with(this.editNode.style){ + position = ""; + left = ""; + top = ""; + } + } + + this.editNode.innerHTML = html; + if(this.height){ this.editNode.style.overflowY="scroll"; } + + dojo.lang.forEach(this.events, function(e){ + dojo.event.connect(this.editNode, e.toLowerCase(), this, e); + }, this); + + this.onLoad(); + } else { // designMode in iframe + this._drawIframe(html); + } + + // TODO: this is a guess at the default line-height, kinda works + if (this.domNode.nodeName == "LI") { this.domNode.lastChild.style.marginTop = "-1.2em"; } + dojo.html.addClass(this.domNode, "RichTextEditable"); + + this.isClosed = false; + }, + + _hasCollapseableMargin: function(element, side) { + // check if an element has padding or borders on the given side + // which would prevent it from collapsing margins + if (dojo.html.getPixelValue(element, + 'border-'+side+'-width', + false)) { + return false; + } else if (dojo.html.getPixelValue(element, + 'padding-'+side, + false)) { + return false; + } else { + return true; + } + }, + + _getContributingMargin: function(element, topOrBottom) { + // calculate how much margin this element and its first or last + // child are contributing to the total margin between this element + // and the adjacent node. CSS border collapsing makes this + // necessary. + + if (topOrBottom == "top") { + var siblingAttr = "previousSibling"; + var childSiblingAttr = "nextSibling"; + var childAttr = "firstChild"; + var marginProp = "margin-top"; + var siblingMarginProp = "margin-bottom"; + } else { + var siblingAttr = "nextSibling"; + var childSiblingAttr = "previousSibling"; + var childAttr = "lastChild"; + var marginProp = "margin-bottom"; + var siblingMarginProp = "margin-top"; + } + + var elementMargin = dojo.html.getPixelValue(element, marginProp, false); + + function isSignificantNode(element) { + // see if an node is significant in the current context + // for calulating margins + return !(element.nodeType==3 && dojo.string.isBlank(element.data)) + && dojo.html.getStyle(element, "display") != "none" + && !dojo.html.isPositionAbsolute(element); + } + + // walk throuh first/last children to find total collapsed margin size + var childMargin = 0; + var child = element[childAttr]; + while (child) { + // skip over insignificant elements (whitespace, etc) + while ((!isSignificantNode(child)) && child[childSiblingAttr]) { + child = child[childSiblingAttr]; + } + + childMargin = Math.max(childMargin, dojo.html.getPixelValue(child, marginProp, false)); + // stop if we hit a bordered/padded element + if (!this._hasCollapseableMargin(child, topOrBottom)) break; + child = child[childAttr]; + } + + // if this element has a border, return full child margin immediately + // as there won't be any margin collapsing + if (!this._hasCollapseableMargin(element, topOrBottom)){ return parseInt(childMargin); } + + // find margin supplied by nearest sibling + var contextMargin = 0; + var sibling = element[siblingAttr]; + while (sibling) { + if (isSignificantNode(sibling)) { + contextMargin = dojo.html.getPixelValue(sibling, + siblingMarginProp, + false); + break; + } + sibling = sibling[siblingAttr]; + } + if (!sibling) { // no sibling, look at parent's margin instead + contextMargin = dojo.html.getPixelValue(element.parentNode, + marginProp, false); + } + + if (childMargin > elementMargin) { + return parseInt(Math.max((childMargin-elementMargin)-contextMargin, 0)); + } else { + return 0; + } + + }, + + /** Draws an iFrame using the existing one if one exists. + Used by Mozilla, Safari, and Opera */ + _drawIframe: function (html) { + + // detect firefox < 1.5, which has some iframe loading issues + var oldMoz = Boolean(dojo.render.html.moz && ( + typeof window.XML == 'undefined')) + + if (!this.iframe) { + var currentDomain = (new dojo.uri.Uri(document.location)).host; + this.iframe = document.createElement("iframe"); + with (this.iframe) { + scrolling = this.height ? "auto" : "no"; + style.border = "none"; + style.lineHeight = "0"; // squash line height + style.verticalAlign = "bottom"; + } + } + // opera likes this to be outside the with block + this.iframe.src = dojo.uri.dojoUri("src/widget/templates/richtextframe.html") + "#" + ((document.domain != currentDomain) ? document.domain : ""); + this.iframe.width = this.inheritWidth ? this._oldWidth : "100%"; + if (this.height) { + this.iframe.style.height = this.height; + } else { + var height = this._oldHeight; + if (this._hasCollapseableMargin(this.domNode, 'top')) { + height += this._firstChildContributingMargin; + } + if (this._hasCollapseableMargin(this.domNode, 'bottom')) { + height += this._lastChildContributingMargin; + } + this.iframe.height = height; + } + + var tmpContent = document.createElement('div'); + tmpContent.innerHTML = html; + + // make relative image urls absolute + if (this.relativeImageUrls) { + var imgs = tmpContent.getElementsByTagName('img'); + for (var i=0; i<imgs.length; i++) { + imgs[i].src = (new dojo.uri.Uri(window.location, imgs[i].src)).toString(); + } + html = tmpContent.innerHTML; + } + + // fix margins on tmpContent + var firstChild = dojo.html.firstElement(tmpContent); + var lastChild = dojo.html.lastElement(tmpContent); + if(firstChild){ + firstChild.style.marginTop = this._firstChildContributingMargin+"px"; + } + if(lastChild){ + lastChild.style.marginBottom = this._lastChildContributingMargin+"px"; + } + + // show existing content behind iframe for now + tmpContent.style.position = "absolute"; + this.domNode.appendChild(tmpContent); + this.domNode.appendChild(this.iframe); + + var _iframeInitialized = false; + + // now we wait for onload. Janky hack! + var ifrFunc = dojo.lang.hitch(this, function(){ + if(!_iframeInitialized){ + _iframeInitialized = true; + }else{ return; } + if(!this.editNode){ + if(this.iframe.contentWindow){ + this.window = this.iframe.contentWindow; + }else{ + // for opera + this.window = this.iframe.contentDocument.window; + } + if(dojo.render.html.moz){ + this.document = this.iframe.contentWindow.document + }else{ + this.document = this.iframe.contentDocument; + } + + // curry the getStyle function + var getStyle = (function (domNode) { return function (style) { + return dojo.html.getStyle(domNode, style); + }; })(this.domNode); + + var font = + getStyle('font-weight') + " " + + getStyle('font-size') + " " + + getStyle('font-family'); + + // line height is tricky - applying a units value will mess things up. + // if we can't get a non-units value, bail out. + var lineHeight = "1.0"; + var lineHeightStyle = dojo.html.getUnitValue(this.domNode, 'line-height'); + if (lineHeightStyle.value && lineHeightStyle.units=="") { + lineHeight = lineHeightStyle.value; + } + + dojo.html.insertCssText( + ' body,html { background: transparent; padding: 0; margin: 0; }\n' + + // TODO: left positioning will case contents to disappear out of view + // if it gets too wide for the visible area + ' body { top: 0; left: 0; right: 0;' + + (((this.height)||(dojo.render.html.opera)) ? '' : ' position: fixed; ') + + ' font: ' + font + ';\n' + + ' min-height: ' + this.minHeight + '; \n' + + ' line-height: ' + lineHeight + '} \n' + + ' p { margin: 1em 0 !important; }\n' + + ' body > *:first-child { padding-top: 0 !important; margin-top: ' + this._firstChildContributingMargin + 'px !important; }\n' + // FIXME: test firstChild nodeType + ' body > *:last-child { padding-bottom: 0 !important; margin-bottom: ' + this._lastChildContributingMargin + 'px !important; }\n' + + ' li > ul:-moz-first-node, li > ol:-moz-first-node { padding-top: 1.2em; }\n' + + ' li { min-height: 1.2em; }\n' + + //' p,ul,li { padding-top: 0; padding-bottom: 0; margin-top:0; margin-bottom: 0; }\n' + + '', this.document); + + tmpContent.parentNode.removeChild(tmpContent); + this.document.body.innerHTML = html; + if(oldMoz){ + this.document.designMode = "on"; + } + this.onLoad(); + }else{ + tmpContent.parentNode.removeChild(tmpContent); + this.editNode.innerHTML = html; + this.onDisplayChanged(); + } + }); + + if(this.editNode){ + ifrFunc(); // iframe already exists, just set content + }else if(dojo.render.html.moz){ + // FIXME: if we put this on a delay, we get a height of 20px. + // Otherwise we get the correctly specified minHeight value. + this.iframe.onload = function(){ + setTimeout(ifrFunc, 250); + } + }else{ // new mozillas, opera, safari + this.iframe.onload = ifrFunc; + } + }, + + /** Draws an active x object, used by IE */ + _drawObject: function (html) { + this.object = dojo.html.createExternalElement(dojo.doc(), "object"); + + with (this.object) { + classid = "clsid:2D360201-FFF5-11D1-8D03-00A0C959BC0A"; + width = this.inheritWidth ? this._oldWidth : "100%"; + style.height = this.height ? this.height : (this._oldHeight+"px"); + Scrollbars = this.height ? true : false; + Appearance = this._activeX.appearance.flat; + } + this.domNode.appendChild(this.object); + + this.object.attachEvent("DocumentComplete", dojo.lang.hitch(this, "onLoad")); + //DisplayChanged is fired too often even no change is made, so we ignore it + //and call onDisplayChanged manually in execCommand instead +// this.object.attachEvent("DisplayChanged", dojo.lang.hitch(this, "onDisplayChanged")); + + dojo.lang.forEach(this.events, function(e){ + this.object.attachEvent(e.toLowerCase(), dojo.lang.hitch(this, e)); + }, this); + + this.object.DocumentHTML = '<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' + + '<html><title>' + + '' + + //'' + + '
' + html + '
'; + + this._cacheLocalBlockFormatNames(); + }, + + //static cache variables shared between all instance of this class + _local2NativeFormatNames: {}, + _native2LocalFormatNames: {}, + //in IE, names for blockformat is locale dependent, so we cache the values here + //we use activeX to obtain the list, if success or the names are already cached, + //return true + _cacheLocalBlockFormatNames: function(){ + if(!this._native2LocalFormatNames['p']){ + var obj = this.object; + if(!obj){ + //create obj temporarily + try{ + obj = dojo.html.createExternalElement(dojo.doc(), "object"); + obj.classid = "clsid:2D360201-FFF5-11D1-8D03-00A0C959BC0A"; + dojo.body().appendChild(obj); + obj.DocumentHTML = ""; + }catch(e){ + return false; + } + } + var oNamesParm = new ActiveXObject("DEGetBlockFmtNamesParam.DEGetBlockFmtNamesParam"); + obj.ExecCommand(this._activeX.command['getblockformatnames'], 0, oNamesParm); + var vbNamesArray = new VBArray(oNamesParm.Names); + var localFormats = vbNamesArray.toArray(); + var nativeFormats = ['p', 'pre', 'address', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'ul', '', '', '','','div']; + for(var i=0;i0){ + this._local2NativeFormatNames[localFormats[i]] = nativeFormats[i]; + this._native2LocalFormatNames[nativeFormats[i]] = localFormats[i]; + } + } + if(!this.object){ + //delete the temporary obj + dojo.body().removeChild(obj); + } + } + return true; + }, + /* Event handlers + *****************/ + + _isResized: function(){ return false; }, + + onLoad: function(e){ + this.isLoaded = true; + if (this.object){ + this.document = this.object.DOM; + this.window = this.document.parentWindow; + this.editNode = this.document.body.firstChild; + this.domNode.style.height = this.height ? this.height : this.minHeight; + this.connect(this, "onDisplayChanged", "_updateHeight"); + }else if (this.iframe){ + this.editNode = this.document.body; + this.connect(this, "onDisplayChanged", "_updateHeight"); + + try { // sanity check for Mozilla + this.document.execCommand("useCSS", false, true); // old moz call + this.document.execCommand("styleWithCSS", false, false); // new moz call + //this.document.execCommand("insertBrOnReturn", false, false); // new moz call + }catch(e2){ } + + if (dojo.render.html.safari) { + /* + this.iframe.style.visiblity = "visible"; + this.iframe.style.border = "1px solid black"; + this.editNode.style.visiblity = "visible"; + this.editNode.style.border = "1px solid black"; + */ + // this.onDisplayChanged(); + this.connect(this.editNode, "onblur", "onBlur"); + this.connect(this.editNode, "onfocus", "onFocus"); + + this.interval = setInterval(dojo.lang.hitch(this, "onDisplayChanged"), 750); + // dojo.raise("onload"); + // dojo.debug(this.editNode.parentNode.parentNode.parentNode.nodeName); + } else if (dojo.render.html.mozilla || dojo.render.html.opera) { + + // We need to unhook the blur event listener on close as we + // can encounter a garunteed crash in FF if another event is + // also fired + var doc = this.document; + var blurfp = dojo.event.browser.addListener(this.document, "blur", dojo.lang.hitch(this, "onBlur")); + var unBlur = { unBlur: function(e){ + dojo.event.browser.removeListener(doc, "blur", blurfp); + } }; + dojo.event.connect("before", this, "close", unBlur, "unBlur"); + dojo.event.browser.addListener(this.document, "focus", dojo.lang.hitch(this, "onFocus")); + + // safari can't handle key listeners, it kills the speed + var addListener = dojo.event.browser.addListener; + addListener(this.document, "keypress", dojo.lang.hitch(this, "onKeyPress")); + addListener(this.document, "keydown", dojo.lang.hitch(this, "onKeyDown")); + addListener(this.document, "keyup", dojo.lang.hitch(this, "onKeyUp")); + addListener(this.document, "click", dojo.lang.hitch(this, "onClick")); + } + + // FIXME: when scrollbars appear/disappear this needs to be fired + }else if(dojo.render.html.ie){ + // IE contentEditable + this.editNode.style.zoom = 1.0; + } + + if(this.focusOnLoad){ + this.focus(); + } + this.onDisplayChanged(e); + }, + + /** Fired on keydown */ + onKeyDown: function(e){ + if((!e)&&(this.object)){ + e = dojo.event.browser.fixEvent(this.window.event); + } + // dojo.debug("onkeydown:", e.keyCode); + // we need this event at the moment to get the events from control keys + // such as the backspace. It might be possible to add this to Dojo, so that + // keyPress events can be emulated by the keyDown and keyUp detection. + if((dojo.render.html.ie)&&(e.keyCode == e.KEY_TAB)){ + e.preventDefault(); + e.stopPropagation(); + // FIXME: this is a poor-man's indent/outdent. It would be + // better if it added 4 " " chars in an undoable way. + // Unfortuantly pasteHTML does not prove to be undoable + this.execCommand((e.shiftKey ? "outdent" : "indent")); + }else if(dojo.render.html.ie){ + if((65 <= e.keyCode)&&(e.keyCode <= 90)){ + e.charCode = e.keyCode; + this.onKeyPress(e); + } + // dojo.debug(e.ctrlKey); + // dojo.debug(e.keyCode); + // dojo.debug(e.charCode); + // this.onKeyPress(e); + } + }, + + /** Fired on keyup */ + onKeyUp: function(e){ + return; + }, + + KEY_CTRL: 1, + + /** Fired on keypress. */ + onKeyPress: function(e){ + if((!e)&&(this.object)){ + e = dojo.event.browser.fixEvent(this.window.event); + } + // handle the various key events + + var character = e.charCode > 0 ? String.fromCharCode(e.charCode) : null; + var code = e.keyCode; + + var modifiers = e.ctrlKey ? this.KEY_CTRL : 0; + + if (this._keyHandlers[character]) { + // dojo.debug("char:", character); + var handlers = this._keyHandlers[character], i = 0, handler; + while (handler = handlers[i++]) { + if (modifiers == handler.modifiers) { + handler.handler.call(this); + e.preventDefault(); + break; + } + } + } + + /* + // define some key combos + if (e.ctrlKey || e.metaKey) { // modifier pressed + switch (character) { + case "b": this.execCommand("bold"); break; + case "i": this.execCommand("italic"); break; + case "u": this.execCommand("underline"); break; + //case "a": this.execCommand("selectall"); break; + //case "k": this.execCommand("createlink", ""); break; + //case "K": this.execCommand("unlink"); break; + case "Z": this.execCommand("redo"); break; + case "s": this.close(true); break; // saves + + case "1": this.execCommand("formatblock", "h1"); break; + case "2": this.execCommand("formatblock", "h2"); break; + case "3": this.execCommand("formatblock", "h3"); break; + case "4": this.execCommand("formatblock", "h4"); break; + + case "\\": this.execCommand("insertunorderedlist"); break; + + default: switch (code) { + case e.KEY_LEFT_ARROW: + case e.KEY_RIGHT_ARROW: + //break; // preventDefault stops the browser + // going through its history + default: + preventDefault = false; break; // didn't handle here + } + } + } else { + switch (code) { + case e.KEY_TAB: + // commenting out bcs it's crashing FF + // this.execCommand(e.shiftKey ? "unindent" : "indent"); + // break; + default: + preventDefault = false; break; // didn't handle here + } + } + + if (preventDefault) { e.preventDefault(); } + */ + + // function call after the character has been inserted + dojo.lang.setTimeout(this, this.onKeyPressed, 1, e); + }, + + addKeyHandler: function (key, modifiers, handler) { + if (!(this._keyHandlers[key] instanceof Array)) { this._keyHandlers[key] = []; } + this._keyHandlers[key].push({ + modifiers: modifiers || 0, + handler: handler + }); + }, + + + + /** + * Fired after a keypress event has occured and it's action taken. This + * is useful if action needs to be taken after text operations have + * finished + */ + onKeyPressed: function (e) { + // Mozilla adds a single

with an embedded
when you hit enter once: + //


\n

+ // when you hit enter again it adds another
inside your enter + //


\n
\n

+ // and if you hit enter again it splits the
s over 2

s + //


\n

\n


\n

+ // now this assumes that

s have double the line-height of
s to work + // and so we need to remove the

s to ensure the position of the cursor + // changes from the users perspective when they hit enter, as the second two + // html snippets render the same when margins are set to 0. + + // TODO: doesn't really work; is this really needed? + //if (dojo.render.html.moz) { + // for (var i = 0; i < this.document.getElementsByTagName("p").length; i++) { + // var p = this.document.getElementsByTagName("p")[i]; + // if (p.innerHTML.match(/^
\s$/m)) { + // while (p.hasChildNodes()) { p.parentNode.insertBefore(p.firstChild, p); } + // p.parentNode.removeChild(p); + // } + // } + //} + this.onDisplayChanged(/*e*/); // can't pass in e + }, + + onClick: function(e){ this.onDisplayChanged(e); }, + onBlur: function(e){ }, +// _initialFocus: true, + onFocus: function(e){ + //what's this for? +// if( (dojo.render.html.mozilla)&&(this._initialFocus) ){ +// this._initialFocus = false; +// if(dojo.string.trim(this.editNode.innerHTML) == " "){ +// this.execCommand("selectall"); +// this.window.getSelection().collapseToStart(); +// } +// } + }, + + blur: function () { + if(this.iframe) { this.window.blur(); } + else if(this.object) { this.document.body.blur(); } + else if(this.editNode) { this.editNode.blur(); } + }, + + focus: function () { + if(this.iframe) { this.window.focus(); } + else if(this.object) { this.document.focus(); } + // editNode may be hidden in display:none div, lets just punt in this case + else if(this.editNode && dojo.lang.isFunction(this.editNode.focus)) { this.editNode.focus(); } + }, + + /** this event will be fired everytime the display context changes and the + result needs to be reflected in the UI */ + onDisplayChanged: function (e){ }, + + + /* Formatting commands + **********************/ + + /** IE's Active X codes: see http://www.computerbytesman.com/js/activex/dhtmledit.htm */ + _activeX: { + command: { + bold: 5000, + italic: 5023, + underline: 5048, + + justifycenter: 5024, + justifyleft: 5025, + justifyright: 5026, + + cut: 5003, + copy: 5002, + paste: 5032, + "delete": 5004, + + undo: 5049, + redo: 5033, + + removeformat: 5034, + selectall: 5035, + unlink: 5050, + + indent: 5018, + outdent: 5031, + + insertorderedlist: 5030, + insertunorderedlist: 5051, + + // table commands + inserttable: 5022, + insertcell: 5019, + insertcol: 5020, + insertrow: 5021, + deletecells: 5005, + deletecols: 5006, + deleterows: 5007, + mergecells: 5029, + splitcell: 5047, + + // the command need mapping, they don't translate directly + // to the contentEditable commands + setblockformat: 5043, + getblockformat: 5011, + getblockformatnames: 5012, + setfontname: 5044, + getfontname: 5013, + setfontsize: 5045, + getfontsize: 5014, + setbackcolor: 5042, + getbackcolor: 5010, + setforecolor: 5046, + getforecolor: 5015, + + findtext: 5008, + font: 5009, + hyperlink: 5016, + image: 5017, + + lockelement: 5027, + makeabsolute: 5028, + sendbackward: 5036, + bringforward: 5037, + sendbelowtext: 5038, + bringabovetext: 5039, + sendtoback: 5040, + bringtofront: 5041, + + properties: 5052 + }, + + ui: { + "default": 0, + prompt: 1, + noprompt: 2 + }, + + status: { + notsupported: 0, + disabled: 1, + enabled: 3, + latched: 7, + ninched: 11 + }, + + appearance: { + flat: 0, + inset: 1 + }, + + state: { + unchecked: 0, + checked: 1, + gray: 2 + } + }, + + /** + * Used as the advice function by dojo.event.connect to map our + * normalized set of commands to those supported by the target + * browser + * + * @param arugments The arguments Array, containing at least one + * item, the command and an optional second item, + * an argument. + */ + _normalizeCommand: function (joinObject){ + var drh = dojo.render.html; + + var command = joinObject.args[0].toLowerCase(); + if(command == "formatblock"){ + if(drh.safari){ command = "heading"; } + if(this.object){ //IE activeX mode + joinObject.args[1] = this._native2LocalFormatNames[joinObject.args[1]]; + } + else if(drh.ie){ joinObject.args[1] = "<"+joinObject.args[1]+">"; } + }else if(command == "createlink" && this.object){ + command = "hyperlink"; + }else if(command == "hilitecolor" && !drh.mozilla) { command = "backcolor"; } + + joinObject.args[0] = command; + + if (joinObject.args.length > 1) { // a command was specified + var argument = joinObject.args[1]; + if (command == "heading") { throw new Error("unimplemented"); } + joinObject.args[1] = argument; + } + + return joinObject.proceed(); + }, + + /** + * Tests whether a command is supported by the host. Clients SHOULD check + * whether a command is supported before attempting to use it, behaviour + * for unsupported commands is undefined. + * + * @param command The command to test for + * @return true if the command is supported, false otherwise + */ + queryCommandAvailable: function (command) { + var ie = 1; + var mozilla = 1 << 1; + var safari = 1 << 2; + var opera = 1 << 3; + function isSupportedBy (browsers) { + return { + ie: Boolean(browsers & ie), + mozilla: Boolean(browsers & mozilla), + safari: Boolean(browsers & safari), + opera: Boolean(browsers & opera) + } + } + + var supportedBy = null; + + switch (command.toLowerCase()) { + case "bold": case "italic": case "underline": + case "subscript": case "superscript": + case "fontname": case "fontsize": + case "forecolor": case "hilitecolor": + case "justifycenter": case "justifyfull": case "justifyleft": + case "justifyright": case "delete": case "selectall": + supportedBy = isSupportedBy(mozilla | ie | safari | opera); + break; + + case "createlink": case "unlink": case "removeformat": + case "inserthorizontalrule": case "insertimage": + case "insertorderedlist": case "insertunorderedlist": + case "indent": case "outdent": case "formatblock": + case "inserthtml": case "undo": case "redo": + supportedBy = isSupportedBy(mozilla | ie | opera); + break; + + case "strikethrough": + supportedBy = isSupportedBy(mozilla | opera | (this.object ? 0 : ie)); + break; + + case "blockdirltr": case "blockdirrtl": + case "dirltr": case "dirrtl": + case "inlinedirltr": case "inlinedirrtl": + supportedBy = isSupportedBy(ie); + break; + case "cut": case "copy": case "paste": + supportedBy = isSupportedBy(ie|mozilla); + break; + + case "inserttable": + supportedBy = isSupportedBy(mozilla | (this.object ? ie : 0)); + break; + + case "insertcell": case "insertcol": case "insertrow": + case "deletecells": case "deletecols": case "deleterows": + case "mergecells": case "splitcell": + supportedBy = isSupportedBy(this.object ? ie : 0); + break; + + default: return false; + } + + return (dojo.render.html.ie && supportedBy.ie) || + (dojo.render.html.mozilla && supportedBy.mozilla) || + (dojo.render.html.safari && supportedBy.safari) || + (dojo.render.html.opera && supportedBy.opera); + }, + + /** + * Executes a command in the Rich Text area + * + * @param command The command to execute + * @param argument An optional argument to the command + */ + execCommand: function (command, argument){ + var returnValue; + + //focus() is required for IE (none-activeX mode) to work + //In addition, focus() makes sure after the execution of + //the command, the editor receives the focus as expected + this.focus(); + + if(this.object){ + switch (command) { + case "forecolor": + case "backcolor": + case "fontsize": + case "fontname": + command = "set" + command; + break; + case "formatblock": + command = "setblockformat"; + } + + //if (typeof this._activeX.command[command] == "undefined") { return null; } + + if(command == "inserttable"){ + var tableInfo = this.constructor._tableInfo; + if(!tableInfo){ + tableInfo = document.createElement("object"); + tableInfo.classid = "clsid:47B0DFC7-B7A3-11D1-ADC5-006008A5848C"; + dojo.body().appendChild(tableInfo); + this.constructor._table = tableInfo; + } + + tableInfo.NumRows = argument["rows"]; + tableInfo.NumCols = argument["cols"]; + tableInfo.TableAttrs = argument["TableAttrs"]; + tableInfo.CellAttrs = argument["CellAttrs"]; + tableInfo.Caption = argument["Caption"]; + } + + if(command == "inserthtml"){ + var insertRange = this.document.selection.createRange(); + insertRange.select(); + insertRange.pasteHTML(argument); + insertRange.collapse(true); + returnValue = true; + }else if(arguments.length == 1){ + returnValue = this.object.ExecCommand(this._activeX.command[command], + this._activeX.ui.noprompt); + }else{ + returnValue = this.object.ExecCommand(this._activeX.command[command], + this._activeX.ui.noprompt, argument); + } + + /* */ + }else if(command == "inserthtml"){ + // on IE, we can use the pasteHTML method of the textRange object + // to get an undo-able innerHTML modification + if(dojo.render.html.ie){ + dojo.debug("inserthtml breaks the undo stack when not using the ActiveX version of the control!"); + var insertRange = this.document.selection.createRange(); + insertRange.select(); + insertRange.pasteHTML(argument); + insertRange.collapse(true); + return true; + }else{ + return this.document.execCommand(command, false, argument); + } + /* */ + // fix up unlink in Mozilla to unlink the link and not just the selection + }else if((command == "unlink")&& + (this.queryCommandEnabled("unlink"))&& + (dojo.render.html.mozilla)){ + // grab selection + // Mozilla gets upset if we just store the range so we have to + // get the basic properties and recreate to save the selection + var selection = this.window.getSelection(); + var selectionRange = selection.getRangeAt(0); + var selectionStartContainer = selectionRange.startContainer; + var selectionStartOffset = selectionRange.startOffset; + var selectionEndContainer = selectionRange.endContainer; + var selectionEndOffset = selectionRange.endOffset; + + // select our link and unlink + var a = dojo.withGlobal(this.window, "getAncestorElement", dojo.html.selection, 'a'); + dojo.withGlobal(this.window, "selectElement", dojo.html.selection, a); + + returnValue = this.document.execCommand("unlink", false, null); + + // restore original selection + var selectionRange = document.createRange(); + selectionRange.setStart(selectionStartContainer, selectionStartOffset); + selectionRange.setEnd(selectionEndContainer, selectionEndOffset); + selection.removeAllRanges(); + selection.addRange(selectionRange); + + return returnValue; + }else if((command == "inserttable")&&(dojo.render.html.mozilla)){ + + var cols = ""; + for (var i = 0; i < argument.cols; i++) { cols += ""; } + cols += ""; + + var table = ""; + for (var i = 0; i < argument.rows; i++) { table += cols; } + table += "
"; + returnValue = this.document.execCommand("inserthtml", false, table); + + }else if((command == "hilitecolor")&&(dojo.render.html.mozilla)){ + // mozilla doesn't support hilitecolor properly when useCSS is + // set to false (bugzilla #279330) + + this.document.execCommand("useCSS", false, false); + returnValue = this.document.execCommand(command, false, argument); + this.document.execCommand("useCSS", false, true); + + }else if((dojo.render.html.ie)&&( (command == "backcolor")||(command == "forecolor") )){ + // Tested under IE 6 XP2, no problem here, comment out + // IE weirdly collapses ranges when we exec these commands, so prevent it +// var tr = this.document.selection.createRange(); + argument = arguments.length > 1 ? argument : null; + returnValue = this.document.execCommand(command, false, argument); + + // timeout is workaround for weird IE behavior were the text + // selection gets correctly re-created, but subsequent input + // apparently isn't bound to it +// setTimeout(function(){tr.select();}, 1); + }else{ + // dojo.debug("command:", command, "arg:", argument); + + argument = arguments.length > 1 ? argument : null; + if(dojo.render.html.moz){ + this.document = this.iframe.contentWindow.document + } + + if(argument || command!="createlink") { + returnValue = this.document.execCommand(command, false, argument); + } + } + + this.onDisplayChanged(); + return returnValue; + }, + + queryCommandEnabled: function(command, argument){ + if(this.object){ + switch (command) { + case "forecolor": + case "backcolor": + case "fontsize": + case "fontname": + command = "set" + command; + break; + case "formatblock": + command = "setblockformat"; + } + + if(typeof this._activeX.command[command] == "undefined"){ return false; } + var status = this.object.QueryStatus(this._activeX.command[command]); + return ((status != this._activeX.status.notsupported)&& + (status != this._activeX.status.disabled)); + }else{ + // mozilla returns true always + if(command == "unlink" && dojo.render.html.mozilla){ + return dojo.withGlobal(this.window, "hasAncestorElement", dojo.html.selection, 'a'); + } else if (command == "inserttable" && dojo.render.html.mozilla) { + return true; + } + + // return this.document.queryCommandEnabled(command); + var elem = (dojo.render.html.ie) ? this.document.selection.createRange() : this.document; + return elem.queryCommandEnabled(command); + } + }, + + queryCommandState: function(command, argument){ + if(this.object){ + if(command == "forecolor"){ + command = "setforecolor"; + }else if(command == "backcolor"){ + command = "setbackcolor"; + } + + if(typeof this._activeX.command[command] == "undefined"){ return null; } + var status = this.object.QueryStatus(this._activeX.command[command]); + return ((status == this._activeX.status.latched)|| + (status == this._activeX.status.ninched)); + }else{ + return this.document.queryCommandState(command); + } + }, + + queryCommandValue: function (command, argument) { + if (this.object) { + switch (command) { + case "forecolor": + case "backcolor": + case "fontsize": + case "fontname": + command = "get" + command; + return this.object.execCommand( + this._activeX.command[command], + this._activeX.ui.noprompt); + case "formatblock": + var retvalue = this.object.execCommand( + this._activeX.command["getblockformat"], + this._activeX.ui.noprompt); + if(retvalue){ + return this._local2NativeFormatNames[retvalue]; + } + } + } else { + if(dojo.render.html.ie && command == "formatblock"){ + return this._local2NativeFormatNames[this.document.queryCommandValue(command)] || this.document.queryCommandValue(command); + } + return this.document.queryCommandValue(command); + } + }, + + + /* Misc. + ********/ + + placeCursorAtStart: function(){ + this.focus(); + dojo.withGlobal(this.window, "selectElementChildren", dojo.html.selection, this.editNode); + dojo.withGlobal(this.window, "collapse", dojo.html.selection, true); + }, + + placeCursorAtEnd: function(){ + this.focus(); + //In mozilla, if last child is not a text node, we have to use selectElementChildren on this.editNode.lastChild + //otherwise the cursor would be placed at the end of the closing tag of this.editNode.lastChild + if(dojo.render.html.moz && this.editNode.lastChild && + this.editNode.lastChild.nodeType != dojo.dom.TEXT_NODE){ + dojo.withGlobal(this.window, "selectElementChildren", dojo.html.selection, this.editNode.lastChild); + }else{ + dojo.withGlobal(this.window, "selectElementChildren", dojo.html.selection, this.editNode); + } + dojo.withGlobal(this.window, "collapse", dojo.html.selection, false); + }, + + //this function set the content while trying to maintain the undo stack + replaceEditorContent: function(html){ + html = this._preFilterContent(html); + if(this.isClosed){ + this.domNode.innerHTML = html; + }else if(this.window.getSelection && !dojo.render.html.moz){ // Safari + // look ma! it's a totally f'd browser! + this.editNode.innerHTML = html; + }else if(this.window.getSelection || this.document.selection){ // Moz/IE + this.execCommand("selectall"); + this.execCommand("inserthtml", html); + } + }, + + _preFilterContent: function(html){ + var ec = html; + dojo.lang.forEach(this.contentPreFilters, function(ef){ + ec = ef(ec); + }); + return ec; + }, + + _lastHeight: 0, + + /** Updates the height of the editor area to fit the contents. */ + _updateHeight: function(){ + if(!this.isLoaded){ return; } + if(this.height){ return; } + if(this.iframe){ + /* + if(!this.dojo.body()["offsetHeight"]){ + return; + } + */ + // The height includes the padding, borders and margins so these + // need to be added on + var heights = ["margin-top", "margin-bottom", + "padding-bottom", "padding-top", + "border-width-bottom", "border-width-top"]; + for(var i = 0, chromeheight = 0; i < heights.length; i++){ + var height = dojo.html.getStyle(this.iframe, heights[i]); + // Safari doesn't have all the heights so we have to test + if(height){ + chromeheight += Number(height.replace(/[^0-9]/g, "")); + } + } + + if(this.document.body["offsetHeight"]){ + this._lastHeight = Math.max(this.document.body.scrollHeight, this.document.body.offsetHeight) + chromeheight; + this.iframe.height = this._lastHeight + "px"; + this.window.scrollTo(0, 0); + } + // dojo.debug(this.iframe.height); + }else if(this.object){ + var height = dojo.html.getBorderBox(this.editNode).height; + //height maybe zero in some cases even though the content is not empty, + //we try the height of body instead + if(!height){ + height = dojo.html.getBorderBox(this.document.body).height; + } + if(height == 0){ + dojo.debug("Can not figure out the height of the editing area!"); + return; //prevent setting height to 0 + } + this.object.style.height = height+"px"; + } + }, + + /** + * Saves the content in an onunload event if the editor has not been closed + */ + _saveContent: function(e){ + var saveTextarea = document.getElementById("dojo.widget.RichText.savedContent"); + saveTextarea.value += this._SEPARATOR + this.saveName + ":" + this.getEditorContent(); + }, + + getEditorContent: function(){ + var ec = ""; + try{ + ec = (this._content.length > 0) ? this._content : this.editNode.innerHTML; + if(dojo.string.trim(ec) == " "){ ec = ""; } + }catch(e){ /* squelch */ } + + dojo.lang.forEach(this.contentPostFilters, function(ef){ + ec = ef(ec); + }); + + if (this.relativeImageUrls) { + // why use a regexp instead of dom? because IE is stupid + // and won't let us set img.src to a relative URL + // this comes after contentPostFilters because once content + // gets innerHTML'd img urls will be fully qualified + var siteBase = window.location.protocol + "//" + window.location.host; + var pathBase = window.location.pathname; + if (pathBase.match(/\/$/)) { + // ends with slash, match full path + } else { + // match parent path to find siblings + var pathParts = pathBase.split("/"); + if (pathParts.length) { + pathParts.pop(); + } + pathBase = pathParts.join("/") + "/"; + + } + + var sameSite = new RegExp("(]*\ src=[\"'])("+siteBase+"("+pathBase+")?)", "ig"); + ec = ec.replace(sameSite, "$1"); + } + return ec; + }, + + /** + * Kills the editor and optionally writes back the modified contents to the + * element from which it originated. + * + * @param save Whether or not to save the changes. If false, the changes are + * discarded. + * @return true if the contents has been modified, false otherwise + */ + close: function(save, force){ + if(this.isClosed){return false; } + + if (arguments.length == 0) { save = true; } + this._content = this.editNode.innerHTML; + var changed = (this.savedContent.innerHTML != this._content); + + // line height is squashed for iframes + // FIXME: why was this here? if (this.iframe){ this.domNode.style.lineHeight = null; } + + if(this.interval){ clearInterval(this.interval); } + + if(dojo.render.html.ie && !this.object){ + dojo.event.browser.clean(this.editNode); + } + + if (this.iframe) { + // FIXME: should keep iframe around for later re-use + delete this.iframe; + } + this.domNode.innerHTML = ""; + + if(save){ + // kill listeners on the saved content + dojo.event.browser.clean(this.savedContent); + if(dojo.render.html.moz){ + var nc = document.createElement("span"); + this.domNode.appendChild(nc); + nc.innerHTML = this.editNode.innerHTML; + }else{ + this.domNode.innerHTML = this._content; + } + } else { + while (this.savedContent.hasChildNodes()) { + this.domNode.appendChild(this.savedContent.firstChild); + } + } + delete this.savedContent; + + dojo.html.removeClass(this.domNode, "RichTextEditable"); + this.isClosed = true; + this.isLoaded = false; + // FIXME: is this always the right thing to do? + delete this.editNode; + + return changed; + }, + + destroyRendering: function(){}, // stub! + + destroy: function (){ + this.destroyRendering(); + if(!this.isClosed){ this.close(false); } + + // disconnect those listeners. + while(this._connected.length){ + this.disconnect(this._connected[0], + this._connected[1], this._connected[2]); + } + this.window = null; + this.document = null; + this.editNode = null; + this.object = null; + dojo.widget.RichText.superclass.destroy.call(this); + }, + + _connected: [], + connect: function (targetObj, targetFunc, thisFunc) { + dojo.event.connect(targetObj, targetFunc, this, thisFunc); + // this._connected.push([targetObj, targetFunc, thisFunc]); + }, + + // FIXME: below two functions do not work with the above line commented out + disconnect: function (targetObj, targetFunc, thisFunc) { + for (var i = 0; i < this._connected.length; i++) { + if (this._connected[0] == targetObj && + this._connected[1] == targetFunc && + this._connected[2] == thisFunc) { + dojo.event.disconnect(targetObj, targetFunc, this, thisFunc); + this._connected.splice(i, 1); + break; + } + } + }, + + disconnectAllWithRoot: function (targetObj) { + for (var i = 0; i < this._connected.length; i++) { + if (this._connected[0] == targetObj) { + dojo.event.disconnect(targetObj, + this._connected[1], this, this._connected[2]); + this._connected.splice(i, 1); + } + } + }, + + fixContentForMoz: function(html){ + //Moz can not handle strong/em tags correctly, so we change them here + html = html.replace(/])/gi, '/gi, '<\/b>' ); + html = html.replace(/])/gi, '/gi, '<\/i>' ); + return html; + } + }, + "html", + function(){ + this.contentPreFilters = []; + this.contentPostFilters = []; + if(dojo.render.html.moz){ + this.contentPreFilters.push(this.fixContentForMoz); + } + + this._keyHandlers = {}; + } +); diff --git a/source/web/scripts/ajax/src/widget/Rounded.js b/source/web/scripts/ajax/src/widget/Rounded.js new file mode 100644 index 0000000000..fac9fde11a --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Rounded.js @@ -0,0 +1,700 @@ +dojo.provide("dojo.widget.Rounded"); +dojo.widget.tags.addParseTreeHandler("dojo:rounded"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.ContentPane"); +dojo.require("dojo.html.style"); +dojo.require("dojo.html.display"); + +/* + * The following script is derived (with permission) from curvyCorners, + * written by Cameron Cooke (CLA on file) and was adapted to Dojo by Brian + * Lucas (CLA on file) + */ + +dojo.widget.defineWidget( + "dojo.widget.Rounded", + dojo.widget.ContentPane, +{ + isSafari: dojo.render.html.safari, + + boxMargin: "50px", // margin outside rounded corner box + radius: 14, // radius of corners + domNode: "", + corners: "TR,TL,BR,BL", // corner string to render + antiAlias: true, // false to disable anti-aliasing + + fillInTemplate: function(args, frag) { + dojo.widget.Rounded.superclass.fillInTemplate.call(this, args, frag); + + dojo.html.insertCssFile(this.templateCssPath); + + // Magic to automatically calculate the box height/width if not supplied + if (this.domNode.style.height<=0) { + var minHeight = (this.radius*1)+this.domNode.clientHeight; + this.domNode.style.height = minHeight+"px"; + } + + if (this.domNode.style.width<=0) { + var minWidth = (this.radius*1)+this.domNode.clientWidth; + this.domNode.style.width = minWidth+"px"; + } + + var cornersAvailable = ["TR", "TL", "BR", "BL"]; + var cornersPassed = this.corners.split(","); + + this.settings = { + antiAlias: this.antiAlias + }; + + var setCorner = function(currentCorner) { + var val = currentCorner.toLowerCase(); + if(dojo.lang.inArray(cornersPassed, currentCorner)) { + this.settings[val] = { radius: this.radius, enabled: true }; + } else { + this.settings[val] = { radius: 0 } + } + } + dojo.lang.forEach(cornersAvailable, setCorner, this); + + this.domNode.style.margin = this.boxMargin; + this.curvyCorners(this.settings); + this.applyCorners(); + }, + + // ------------- curvyCorners OBJECT + + curvyCorners: function(settings){ + + // Setup Globals + this.box = this.domNode; + this.topContainer = null; + this.bottomContainer = null; + this.masterCorners = []; + + // Get box formatting details + var boxHeight = dojo.html.getStyle(this.box, "height"); + if(boxHeight=="") boxHeight="0px"; + var boxWidth = dojo.html.getStyle(this.box, "width"); + var borderWidth = dojo.html.getStyle(this.box, "borderTopWidth"); + if(borderWidth=="") borderWidth="0px"; + //alert(borderWidth); + + var borderColour = dojo.html.getStyle(this.box, "borderTopColor"); + // Set to true if we have a border + if(borderWidth>0) this.antiAlias=true; + + var boxColour = dojo.html.getStyle(this.box, "backgroundColor"); + var backgroundImage = dojo.html.getStyle(this.box, "backgroundImage"); + var boxPosition = dojo.html.getStyle(this.box, "position"); + + // Set formatting propertes + this.boxHeight = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight)); + this.boxWidth = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth)); + this.borderWidth = parseInt(((borderWidth != "" && borderWidth.indexOf("px") !== -1)? borderWidth.slice(0, borderWidth.indexOf("px")) : 0)); + + // DEBUG ME? + + //dojo.debug(this.rgb2Hex(boxColour)); + var test = new dojo.graphics.color.Color(boxColour); + //dojo.debug(test.toHex()); + + this.boxColour = ((boxColour != "" && boxColour != "transparent")? ((boxColour.substr(0, 3) == "rgb")? this.rgb2Hex(boxColour) : boxColour) : "#ffffff"); + this.borderColour = ((borderColour != "" && borderColour != "transparent" && this.borderWidth > 0)? ((borderColour.substr(0, 3) == "rgb")? this.rgb2Hex(borderColour) : borderColour) : this.boxColour); + this.borderString = this.borderWidth + "px" + " solid " + this.borderColour; + this.backgroundImage = ((backgroundImage != "none")? backgroundImage : ""); + + // Make box relative if not already absolute + if(boxPosition != "absolute") this.box.style.position = "relative"; + + //This method creates the corners and + //applies them to the div element. + + this.applyCorners = function() { + // Create top and bottom containers. + // These will be used as a parent for the corners and bars. + for(var t = 0; t < 2; t++) { + switch(t) { + // Top + case 0: + // Only build top bar if a top corner is to be draw + if(this.settings.tl.enabled || this.settings.tr.enabled ) { + var newMainContainer = document.createElement("DIV"); + + with(newMainContainer.style){ + width = "100%"; + fontSize = "1px"; + overflow = "hidden"; + position = "absolute"; + //backgroundColor = "#FFFFC4"; + paddingLeft = this.borderWidth + "px"; + paddingRight = this.borderWidth + "px"; + var topMaxRadius = Math.max(this.settings.tl ? this.settings.tl.radius : 0, this.settings.tr ? this.settings.tr.radius : 0); + height = topMaxRadius + "px"; + top = 0 - topMaxRadius + "px"; + left = 0 - this.borderWidth + "px"; + } + + this.topContainer = this.box.appendChild(newMainContainer); + } + break; + + // Bottom + case 1: + // Only build bottom bar if a top corner is to be draw + if(this.settings.bl.enabled || this.settings.br.enabled) { + var newMainContainer = document.createElement("DIV"); + with(newMainContainer.style){ + width = "100%"; + fontSize = "1px"; + overflow = "hidden"; + position = "absolute"; + //backgroundColor = "#FFFFC4"; + paddingLeft = this.borderWidth + "px"; + paddingRight = this.borderWidth + "px"; + var botMaxRadius = Math.max(this.settings.bl ? this.settings.bl.radius : 0, this.settings.br ? this.settings.br.radius : 0); + height = botMaxRadius + "px"; + bottom = 0 - botMaxRadius + "px"; + left = 0 - this.borderWidth + "px"; + } + this.bottomContainer = this.box.appendChild(newMainContainer); + } + break; + } + } + + // Turn off current borders + if(this.topContainer) this.box.style.borderTopWidth = "0px"; + if(this.bottomContainer) this.box.style.borderBottomWidth = "0px"; + + // Create array of available corners + var corners = ["tr", "tl", "br", "bl"]; + + //Loop for each corner + + for(var i in corners) { + // Get current corner type from array + var cc = corners[i]; + + // Has the user requested the currentCorner be round? + if(!this.settings[cc]) { + // No + if(((cc == "tr" || cc == "tl") && this.topContainer != null) || ((cc == "br" || cc == "bl") && this.bottomContainer != null)) { + // We need to create a filler div to fill the space upto the next horzontal corner. + var newCorner = document.createElement("DIV"); + + // Setup corners properties + newCorner.style.position = "relative"; + newCorner.style.fontSize = "1px"; + newCorner.style.overflow = "hidden"; + + // Add background image? + if(this.backgroundImage == "") { + newCorner.style.backgroundColor = this.boxColour; + } else { + newCorner.style.backgroundImage = this.backgroundImage; + } + + switch(cc) { + case "tl": + with(newCorner.style){ + height = topMaxRadius - this.borderWidth + "px"; + marginRight = this.settings.tr.radius - (this.borderWidth*2) + "px"; + borderLeft = this.borderString; + borderTop = this.borderString; + left = -this.borderWidth + "px"; + } + break; + + case "tr": + with(newCorner.style){ + height = topMaxRadius - this.borderWidth + "px"; + marginLeft = this.settings.tl.radius - (this.borderWidth*2) + "px"; + borderRight = this.borderString; + borderTop = this.borderString; + backgroundPosition = "-" + this.boxWidth + "px 0px"; + left = this.borderWidth + "px"; + } + break; + + case "bl": + with(newCorner.style){ + height = botMaxRadius - this.borderWidth + "px"; + marginRight = this.settings.br.radius - (this.borderWidth*2) + "px"; + borderLeft = this.borderString; + borderBottom = this.borderString; + left = -this.borderWidth + "px"; + } + break; + + case "br": + with(newCorner.style){ + height = botMaxRadius - this.borderWidth + "px"; + marginLeft = this.settings.bl.radius - (this.borderWidth*2) + "px"; + borderRight = this.borderString; + borderBottom = this.borderString; + left = this.borderWidth + "px" + } + break; + } + } + } else { + /* + PERFORMANCE NOTE: + + If more than one corner is requested and a corner has been already + created for the same radius then that corner will be used as a master and cloned. + The pixel bars will then be repositioned to form the new corner type. + All new corners start as a bottom right corner. + */ + if(this.masterCorners[this.settings[cc].radius]) { + // Create clone of the master corner + var newCorner = this.masterCorners[this.settings[cc].radius].cloneNode(true); + } else { + // Yes, we need to create a new corner + var newCorner = document.createElement("DIV"); + with(newCorner.style){ + height = this.settings[cc].radius + "px"; + width = this.settings[cc].radius + "px"; + position = "absolute"; + fontSize = "1px"; + overflow = "hidden"; + } + // THE FOLLOWING BLOCK OF CODE CREATES A ROUNDED CORNER + // ---------------------------------------------------- TOP + + // Get border radius + var borderRadius = parseInt(this.settings[cc].radius - this.borderWidth); + + // Cycle the x-axis + for(var intx = 0, j = this.settings[cc].radius; intx < j; intx++) { + // Calculate the value of y1 which identifies the pixels inside the border + if((intx +1) >= borderRadius) { + var y1 = -1; + } else { + var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1); + } + + // Only calculate y2 and y3 if there is a border defined + if(borderRadius != j) { + if((intx) >= borderRadius) { + var y2 = -1; + } else { + var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2))); + } + + if((intx+1) >= j) { + var y3 = -1; + } else { + var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1); + } + } + + // Calculate y4 + if((intx) >= j) { + var y4 = -1; + } else { + var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2))); + } + + // Draw bar on inside of the border with foreground colour + if(y1 > -1) this.drawPixel(intx, 0, this.boxColour, 100, (y1+1), newCorner, -1, this.settings[cc].radius); + + // Only draw border/foreground antialiased pixels and border if there is a border defined + if(borderRadius != j) { + // Draw aa pixels? + if(this.antiAlias) { + // Cycle the y-axis + for(var inty = (y1 + 1); inty < y2; inty++) { + // For each of the pixels that need anti aliasing between the foreground and border colour draw single pixel divs + if(this.backgroundImage != "") { + var borderFract = (this.pixelFraction(intx, inty, borderRadius) * 100); + + if (borderFract < 30) { + this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, 0, this.settings[cc].radius); + } else { + this.drawPixel(intx, inty, this.borderColour, 100, 1, newCorner, -1, this.settings[cc].radius); + } + } else { + var pixelcolour = dojo.graphics.color.blend(this.boxColour, this.borderColour, this.pixelFraction(intx, inty, borderRadius)); + this.drawPixel(intx, inty, pixelcolour, 100, 1, newCorner, 0, this.settings[cc].radius); + } + } + } + + // Draw bar for the border + if(y3 >= y2) { + if (y1 == -1) { + y1 = 0; + } + this.drawPixel(intx, y2, this.borderColour, 100, (y3 - y2 + 1), newCorner, 0, this.settings[cc].radius); + } + // Set the colour for the outside curve + var outsideColour = this.borderColour; + } else { + // Set the coour for the outside curve + var outsideColour = this.boxColour; + var y3 = y1; + } + + // Draw aa pixels? + if(this.antiAlias) { + // Cycle the y-axis and draw the anti aliased pixels on the + // outside of the curve + for(var inty = (y3 + 1); inty < y4; inty++) { + // For each of the pixels that need anti aliasing between + //the foreground/border colour & background draw single pixel divs + this.drawPixel(intx, inty, outsideColour, (this.pixelFraction(intx, inty , j) * 100), 1, newCorner, ((this.borderWidth > 0)? 0 : -1), this.settings[cc].radius); + } + } + } + + // END OF CORNER CREATION + // ---------------------------------------------------- END + + // We now need to store the current corner in the masterConers array + this.masterCorners[this.settings[cc].radius] = newCorner.cloneNode(true); + } + + //Now we have a new corner we need to reposition all the pixels unless + //the current corner is the bottom right. + if(cc != "br") { + // Loop through all children (pixel bars) + for(var t = 0, k = newCorner.childNodes.length; t < k; t++) { + // Get current pixel bar + var pixelBar = newCorner.childNodes[t]; + + // Get current top and left properties + var pixelBarTop = parseInt(pixelBar.style.top.substring(0, pixelBar.style.top.indexOf("px"))); + var pixelBarLeft = parseInt(pixelBar.style.left.substring(0, pixelBar.style.left.indexOf("px"))); + var pixelBarHeight = parseInt(pixelBar.style.height.substring(0, pixelBar.style.height.indexOf("px"))); + + // Reposition pixels + if(cc == "tl" || cc == "bl") { + pixelBar.style.left = this.settings[cc].radius -pixelBarLeft -1 + "px"; // Left + } + if(cc == "tr" || cc == "tl") { + pixelBar.style.top = this.settings[cc].radius -pixelBarHeight -pixelBarTop + "px"; // Top + } + var value; + + switch(cc) { + case "tr": + value = (-1 *( Math.abs((this.boxWidth - this.settings[cc].radius + this.borderWidth) + pixelBarLeft) - (Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth)))); + pixelBar.style.backgroundPosition = value + "px"; + + break; + + case "tl": + value = (-1 *( Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) - (Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth)))); + pixelBar.style.backgroundPosition = value + "px"; + + break; + + case "bl": + value = (-1 *( Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) - (Math.abs((this.boxHeight + this.settings[cc].radius + pixelBarTop) -this.borderWidth)))); + pixelBar.style.backgroundPosition = value + "px"; + + break; + } + } + } + } + if(newCorner) { + // Position the container + switch(cc) { + case "tl": + if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; + if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; + if(this.topContainer) this.topContainer.appendChild(newCorner); + break; + + case "tr": + if(newCorner.style.position == "absolute") newCorner.style.top = "0px"; + if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; + if(this.topContainer) this.topContainer.appendChild(newCorner); + break; + + case "bl": + if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; + if(newCorner.style.position == "absolute") newCorner.style.left = "0px"; + if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); + break; + + case "br": + if(newCorner.style.position == "absolute") newCorner.style.bottom = "0px"; + if(newCorner.style.position == "absolute") newCorner.style.right = "0px"; + if(this.bottomContainer) this.bottomContainer.appendChild(newCorner); + break; + } + } + } + //The last thing to do is draw the rest of the filler DIVs. + //We only need to create a filler DIVs when two corners have + //diffrent radiuses in either the top or bottom container. + + // Find out which corner has the biiger radius and get the difference amount + var radiusDiff = []; + radiusDiff["t"] = this.settings.tl.enabled && this.settings.tr.enabled ? Math.abs(this.settings.tl.radius - this.settings.tr.radius) : 0; + radiusDiff["b"] = this.settings.bl.enabled && this.settings.br.enabled ? Math.abs(this.settings.bl.radius - this.settings.br.radius) : 0; + + for(var z in radiusDiff) { + if(radiusDiff[z]) { + // Get the type of corner that is the smaller one + var smallerCornerType = ((this.settings[z + "l"].radius < this.settings[z + "r"].radius)? z +"l" : z +"r"); + + // First we need to create a DIV for the space under the smaller corner + var newFiller = document.createElement("DIV"); + with(newFiller.style) { + height = radiusDiff[z] + "px"; + width = this.settings[smallerCornerType].radius+ "px" + position = "absolute"; + fontSize = "1px"; + overflow = "hidden"; + backgroundColor = this.boxColour; + } + + // Position filler + switch(smallerCornerType) { + case "tl": + with(newFiller.style) { + bottom = "0px"; + left = "0px"; + borderLeft = this.borderString; + } + this.topContainer.appendChild(newFiller); + break; + + case "tr": + with(newFiller.style) { + bottom = "0px"; + right = "0px"; + borderRight = this.borderString; + } + this.topContainer.appendChild(newFiller); + break; + + case "bl": + with(newFiller.style) { + top = "0px"; + left = "0px"; + borderLeft = this.borderString; + } + this.bottomContainer.appendChild(newFiller); + break; + + case "br": + with(newFiller.style) { + top = "0px"; + right = "0px"; + borderRight = this.borderString; + } + this.bottomContainer.appendChild(newFiller); + break; + } + } + + // Create the bar to fill the gap between each corner horizontally + var newFillerBar = document.createElement("DIV"); + with(newFillerBar.style) { + position = "relative"; + fontSize = "1px"; + overflow = "hidden"; + backgroundColor = this.boxColour; + } + + switch(z) { + case "t": + // Top Bar + if(this.topContainer) { + with(newFillerBar.style) { + height = topMaxRadius - this.borderWidth + "px"; + marginLeft = this.settings.tl.radius - this.borderWidth + "px"; + marginRight = this.settings.tr.radius - this.borderWidth + "px"; + borderTop = this.borderString; + } + this.topContainer.appendChild(newFillerBar); + } + break; + + case "b": + if(this.bottomContainer) { + // Bottom Bar + with(newFillerBar.style) { + height = botMaxRadius - this.borderWidth + "px"; + marginLeft = this.settings.bl.radius - this.borderWidth + "px"; + marginRight = this.settings.br.radius - this.borderWidth + "px"; + borderBottom = this.borderString; + } + this.bottomContainer.appendChild(newFillerBar); + } + break; + } + } + } + + // This function draws the pixles + this.drawPixel = function(intx, inty, colour, transAmount, height, newCorner, image, cornerRadius) { + // Create pixel + var pixel = document.createElement("DIV"); + + + // Section doesn't like with (pixel.style) { DEBUG? + pixel.style.height = height + "px"; + pixel.style.width = "1px"; + pixel.style.position = "absolute"; + pixel.style.fontSize = "1px"; + pixel.style.overflow = "hidden"; + + // Dont apply background image to border pixels + if(image == -1 && this.backgroundImage != "") { + pixel.style.backgroundImage = this.backgroundImage; + pixel.style.backgroundPosition = "-" + (this.boxWidth - (cornerRadius - intx) + this.borderWidth) + "px -" + ((this.boxHeight + cornerRadius + inty) -this.borderWidth) + "px"; + } else { + pixel.style.backgroundColor = colour; + } + + // Set opacity if the transparency is anything other than 100 + if (transAmount != 100) { + dojo.html.setOpacity(pixel, transAmount); + } + // Set the pixels position + pixel.style.top = inty + "px"; + pixel.style.left = intx + "px"; + + newCorner.appendChild(pixel); + } + }, + + //For a pixel cut by the line determines the fraction of the pixel on the 'inside' of the + //line. Returns a number between 0 and 1 + pixelFraction: function(x, y, r) { + var pixelfraction = 0; + + //determine the co-ordinates of the two points on the perimeter of the pixel that the + //circle crosses + + var xvalues = []; + var yvalues = []; + var point = 0; + var whatsides = ""; + + // x + 0 = Left + var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2))); + + if ((intersect >= y) && (intersect < (y+1))) { + whatsides = "Left"; + xvalues[point] = 0; + yvalues[point] = intersect - y; + point = point + 1; + } + + // y + 1 = Top + var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2))); + + if ((intersect >= x) && (intersect < (x+1))) { + whatsides = whatsides + "Top"; + xvalues[point] = intersect - x; + yvalues[point] = 1; + point = point + 1; + } + // x + 1 = Right + var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2))); + + if ((intersect >= y) && (intersect < (y+1))) { + whatsides = whatsides + "Right"; + xvalues[point] = 1; + yvalues[point] = intersect - y; + point = point + 1; + } + // y + 0 = Bottom + var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2))); + + if ((intersect >= x) && (intersect < (x+1))) { + whatsides = whatsides + "Bottom"; + xvalues[point] = intersect - x; + yvalues[point] = 0; + } + + //depending on which sides of the perimeter of the pixel the circle crosses calculate the + //fraction of the pixel inside the circle + + switch (whatsides) { + case "LeftRight": + pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2); + break; + + case "TopRight": + pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2); + break; + + case "TopBottom": + pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2); + break; + + case "LeftBottom": + pixelfraction = (yvalues[0]*xvalues[1])/2; + break; + + default: + pixelfraction = 1; + } + return pixelfraction; + }, + + // This function converts CSS rgb(x, x, x) to hexadecimal + rgb2Hex: function (rgbColour) { + try{ + // Get array of RGB values + var rgbArray = this.rgb2Array(rgbColour); + + // Get RGB values + var red = parseInt(rgbArray[0]); + var green = parseInt(rgbArray[1]); + var blue = parseInt(rgbArray[2]); + + // Build hex colour code + var hexColour = "#" + this.intToHex(red) + this.intToHex(green) + this.intToHex(blue); + } + catch(e){ alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex"); + } + return hexColour; + }, + + //Converts a number to hexadecimal format + + intToHex: function (strNum) { + var base = strNum / 16; + var rem = strNum % 16; + var base = base - (rem / 16); + var baseS = this.makeHex(base); + var remS = this.makeHex(rem); + return baseS + '' + remS; + }, + //gets the hex bits of a number + + makeHex: function(x) { + if((x >= 0) && (x <= 9)) { + return x; + } else { + switch(x) { + case 10: return "A"; + case 11: return "B"; + case 12: return "C"; + case 13: return "D"; + case 14: return "E"; + case 15: return "F"; + } + } + }, + + // Returns an array of rbg values + rgb2Array: function(rgbColour) { + // Remove rgb() + var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")")); + + // Split RGB into array + var rgbArray = rgbValues.split(", "); + return rgbArray; + } +}); // end function diff --git a/source/web/scripts/ajax/src/widget/Select.js b/source/web/scripts/ajax/src/widget/Select.js new file mode 100644 index 0000000000..b13ab21c68 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Select.js @@ -0,0 +1,66 @@ +dojo.provide("dojo.widget.Select"); + +dojo.require("dojo.widget.ComboBox"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.html.stabile"); + +/* + * The Select widget is an enhanced version of HTML's ', + + fillInTemplate: function(args, frag) { + dojo.widget.ToolbarSelect.superclass.fillInTemplate.call(this, args, frag, true); + var keys = args.values; + var i = 0; + for(var val in keys) { + var opt = document.createElement("option"); + opt.setAttribute("value", keys[val]); + opt.innerHTML = val; + this.selectBox.appendChild(opt); + } + }, + + changed: function(e) { + this._fireEvent("onSetValue", this.selectBox.value); + }, + + setEnabled: function(is, force, preventEvent) { + var ret = dojo.widget.ToolbarSelect.superclass.setEnabled.call(this, is, force, preventEvent); + this.selectBox.disabled = !this._enabled; + return ret; + }, + + // don't want events! + _onmouseover: null, + _onmouseout: null, + _onclick: null, + _onmousedown: null, + _onmouseup: null +}); + +/* Icon + *********/ +// arguments can be IMG nodes, Image() instances or URLs -- enabled is the only one required +dojo.widget.Icon = function(enabled, disabled, hover, selected){ + if(!arguments.length){ + // FIXME: should this be dojo.raise? + throw new Error("Icon must have at least an enabled state"); + } + var states = ["enabled", "disabled", "hover", "selected"]; + var currentState = "enabled"; + var domNode = document.createElement("img"); + + this.getState = function(){ return currentState; } + this.setState = function(value){ + if(dojo.lang.inArray(value, states)){ + if(this[value]){ + currentState = value; + domNode.setAttribute("src", this[currentState].src); + } + }else{ + throw new Error("Invalid state set on Icon (state: " + value + ")"); + } + } + + this.setSrc = function(state, value){ + if(/^img$/i.test(value.tagName)){ + this[state] = value; + }else if(typeof value == "string" || value instanceof String + || value instanceof dojo.uri.Uri){ + this[state] = new Image(); + this[state].src = value.toString(); + } + return this[state]; + } + + this.setIcon = function(icon){ + for(var i = 0; i < states.length; i++){ + if(icon[states[i]]){ + this.setSrc(states[i], icon[states[i]]); + } + } + this.update(); + } + + this.enable = function(){ this.setState("enabled"); } + this.disable = function(){ this.setState("disabled"); } + this.hover = function(){ this.setState("hover"); } + this.select = function(){ this.setState("selected"); } + + this.getSize = function(){ + return { + width: domNode.width||domNode.offsetWidth, + height: domNode.height||domNode.offsetHeight + }; + } + + this.setSize = function(w, h){ + domNode.width = w; + domNode.height = h; + return { width: w, height: h }; + } + + this.getNode = function(){ + return domNode; + } + + this.getSrc = function(state){ + if(state){ return this[state].src; } + return domNode.src||""; + } + + this.update = function(){ + this.setState(currentState); + } + + for(var i = 0; i < states.length; i++){ + var arg = arguments[i]; + var state = states[i]; + this[state] = null; + if(!arg){ continue; } + this.setSrc(state, arg); + } + + this.enable(); +} + +dojo.widget.Icon.make = function(a,b,c,d){ + for(var i = 0; i < arguments.length; i++){ + if(arguments[i] instanceof dojo.widget.Icon){ + return arguments[i]; + } + } + + return new dojo.widget.Icon(a,b,c,d); +} diff --git a/source/web/scripts/ajax/src/widget/Tooltip.js b/source/web/scripts/ajax/src/widget/Tooltip.js new file mode 100644 index 0000000000..4dda698192 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Tooltip.js @@ -0,0 +1,121 @@ +dojo.provide("dojo.widget.Tooltip"); +dojo.require("dojo.widget.ContentPane"); +dojo.require("dojo.widget.Menu2"); +dojo.require("dojo.uri.Uri"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.event"); +dojo.require("dojo.html.style"); +dojo.require("dojo.html.util"); +dojo.require("dojo.html.iframe"); + +dojo.widget.defineWidget( + "dojo.widget.Tooltip", + [dojo.widget.ContentPane, dojo.widget.PopupContainerBase], + { + isContainer: true, + + // Constructor arguments + caption: "", + showDelay: 500, + hideDelay: 100, + connectId: "", + + templateCssPath: dojo.uri.dojoUri("src/widget/templates/TooltipTemplate.css"), + + connectNode: null, + + fillInTemplate: function(args, frag){ + if(this.caption != ""){ + this.domNode.appendChild(document.createTextNode(this.caption)); + } + this.connectNode = dojo.byId(this.connectId); + dojo.widget.Tooltip.superclass.fillInTemplate.call(this, args, frag); + + this.addOnLoad(this, "_LoadedContent"); + dojo.html.addClass(this.domNode, "dojoTooltip"); + + //copy style from input node to output node + var source = this.getFragNodeRef(frag); + dojo.html.copyStyle(this.domNode, source); + + //apply the necessary css rules to the node so that it can popup + this.applyPopupBasicStyle(); + }, + + postCreate: function(args, frag){ + dojo.event.connect(this.connectNode, "onmouseover", this, "onMouseOver"); + dojo.widget.Tooltip.superclass.postCreate.call(this, args, frag); + }, + + onMouseOver: function(e) { + this.mouse = {x: e.pageX, y: e.pageY}; + + if(!this.showTimer){ + this.showTimer = setTimeout(dojo.lang.hitch(this, "open"), this.showDelay); + dojo.event.connect(document.documentElement, "onmousemove", this, "onMouseMove"); + } + }, + + onMouseMove: function(e) { + this.mouse = {x: e.pageX, y: e.pageY}; + + if(dojo.html.overElement(this.connectNode, e) || dojo.html.overElement(this.domNode, e)){ + // If the tooltip has been scheduled to be erased, cancel that timer + // since we are hovering over element/tooltip again + if(this.hideTimer) { + clearTimeout(this.hideTimer); + delete this.hideTimer; + } + } else { + // mouse has been moved off the element/tooltip + // note: can't use onMouseOut to detect this because the "explode" effect causes + // spurious onMouseOut/onMouseOver events (due to interference from outline) + if(this.showTimer){ + clearTimeout(this.showTimer); + delete this.showTimer; + } + if(this.isShowingNow && !this.hideTimer){ + this.hideTimer = setTimeout(dojo.lang.hitch(this, "close"), this.hideDelay); + } + } + }, + + open: function() { + if (this.isShowingNow) { return; } + + dojo.widget.PopupContainerBase.prototype.open.call(this, this.mouse.x, this.mouse.y, null, [this.mouse.x, this.mouse.y], "TL,TR,BL,BR", [10,15]); + }, + + close: function() { + if (this.isShowingNow) { + if ( this.showTimer ) { + clearTimeout(this.showTimer); + delete this.showTimer; + } + if ( this.hideTimer ) { + clearTimeout(this.hideTimer); + delete this.hideTimer; + } + dojo.event.disconnect(document.documentElement, "onmousemove", this, "onMouseMove"); + dojo.widget.PopupContainerBase.prototype.close.call(this); + } + }, + + position: function(){ + this.move(this.mouse.x, this.mouse.y, [10,15], "TL,TR,BL,BR"); + }, + + _LoadedContent: function(){ + if(this.isShowingNow){ + // the tooltip has changed size due to downloaded contents, so reposition it + this.position(); + } + }, + + checkSize: function() { + // checkSize() is called when the user has resized the browser window, + // but that doesn't affect this widget (or this widget's children) + // so it can be safely ignored + } + } +); diff --git a/source/web/scripts/ajax/src/widget/Tree.js b/source/web/scripts/ajax/src/widget/Tree.js new file mode 100644 index 0000000000..d642d21b4b --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Tree.js @@ -0,0 +1,562 @@ +/** + * Tree model does all the drawing, visual node management etc. + * Throws events about clicks on it, so someone may catch them and process + * Tree knows nothing about DnD stuff, covered in TreeDragAndDrop and (if enabled) attached by controller +*/ + +/** + * TODO: use domNode.cloneNode instead of createElement for grid + * Should be faster (lyxsus) + */ +dojo.provide("dojo.widget.Tree"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.event.*"); +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.TreeNode"); +dojo.require("dojo.html.common"); +dojo.require("dojo.html.selection"); + + + +// make it a tag +dojo.widget.tags.addParseTreeHandler("dojo:Tree"); + + +dojo.widget.Tree = function() { + dojo.widget.HtmlWidget.call(this); + + this.eventNames = {}; + + this.tree = this; + this.DNDAcceptTypes = []; + this.actionsDisabled = []; + +} +dojo.inherits(dojo.widget.Tree, dojo.widget.HtmlWidget); + +dojo.lang.extend(dojo.widget.Tree, { + widgetType: "Tree", + + eventNamesDefault: { + // new child does not get domNode filled in (only template draft) + // until addChild->createDOMNode is called(program way) OR createDOMNode (html-way) + // hook events to operate on new DOMNode, create dropTargets etc + createDOMNode: "createDOMNode", + // tree created.. Perform tree-wide actions if needed + treeCreate: "treeCreate", + treeDestroy: "treeDestroy", + // expand icon clicked + treeClick: "treeClick", + // node icon clicked + iconClick: "iconClick", + // node title clicked + titleClick: "titleClick", + + moveFrom: "moveFrom", + moveTo: "moveTo", + addChild: "addChild", + removeNode: "removeNode", + expand: "expand", + collapse: "collapse" + }, + + isContainer: true, + + DNDMode: "off", + + lockLevel: 0, // lock ++ unlock --, so nested locking works fine + + strictFolders: true, + + DNDModes: { + BETWEEN: 1, + ONTO: 2 + }, + + DNDAcceptTypes: "", + + templateCssPath: dojo.uri.dojoUri("src/widget/templates/images/Tree/Tree.css"), + + templateString: '

', + + isExpanded: true, // consider this "root node" to be always expanded + + isTree: true, + + objectId: "", + + // autoCreate if not "off" + // used to get the autocreated controller ONLY. + // generally, tree DOES NOT KNOW about its CONTROLLER, it just doesn't care + // controller gets messages via dojo.event + controller: "", + + // autoCreate if not "off" + // used to get the autocreated selector ONLY. + // generally, tree DOES NOT KNOW its SELECTOR + // binding is made with dojo.event + selector: "", + + // used ONLY at initialization time + menu: "", // autobind menu if menu's widgetId is set here + + expandLevel: "", // expand to level automatically + + // + // these icons control the grid and expando buttons for the whole tree + // + + blankIconSrc: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_blank.gif"), + + gridIconSrcT: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_t.gif"), // for non-last child grid + gridIconSrcL: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_l.gif"), // for last child grid + gridIconSrcV: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_v.gif"), // vertical line + gridIconSrcP: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_p.gif"), // for under parent item child icons + gridIconSrcC: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_c.gif"), // for under child item child icons + gridIconSrcX: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_x.gif"), // grid for sole root item + gridIconSrcY: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_y.gif"), // grid for last rrot item + gridIconSrcZ: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_z.gif"), // for under root parent item child icon + + expandIconSrcPlus: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_expand_plus.gif"), + expandIconSrcMinus: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_expand_minus.gif"), + expandIconSrcLoading: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_loading.gif"), + + + iconWidth: 18, + iconHeight: 18, + + + // + // tree options + // + + showGrid: true, + showRootGrid: true, + + actionIsDisabled: function(action) { + var _this = this; + return dojo.lang.inArray(_this.actionsDisabled, action) + }, + + + actions: { + ADDCHILD: "ADDCHILD" + }, + + + getInfo: function() { + var info = { + widgetId: this.widgetId, + objectId: this.objectId + } + + return info; + }, + + initializeController: function() { + if (this.controller != "off") { + if (this.controller) { + this.controller = dojo.widget.byId(this.controller); + } + else { + // create default controller here + dojo.require("dojo.widget.TreeBasicController"); + this.controller = dojo.widget.createWidget("TreeBasicController", + { DNDController: (this.DNDMode ? "create" : ""), dieWithTree: true } + ); + + } + this.controller.listenTree(this); // controller listens to my events + + } else { + this.controller = null; + } + }, + + initializeSelector: function() { + + if (this.selector != "off") { + if (this.selector) { + this.selector = dojo.widget.byId(this.selector); + } + else { + // create default controller here + dojo.require("dojo.widget.TreeSelector"); + this.selector = dojo.widget.createWidget("TreeSelector", {dieWithTree: true}); + } + + this.selector.listenTree(this); + + } else { + this.selector = null; + } + }, + + initialize: function(args, frag){ + + var _this = this; + + for(name in this.eventNamesDefault) { + if (dojo.lang.isUndefined(this.eventNames[name])) { + this.eventNames[name] = this.widgetId+"/"+this.eventNamesDefault[name]; + } + } + + for(var i=0; i no createDOMNode => no createDOMNode event + domNodeInitialized: child.domNodeInitialized + } + + this.doAddChild.apply(this, arguments); + + dojo.event.topic.publish(this.tree.eventNames.addChild, message); + }, + + + // not called for initial tree building. See createDOMNode instead. + // builds child html node if needed + // index is "last node" by default + /** + * FIXME: Is it possible that removeNode from the tree will cause leaks cause of attached events ? + * if yes, then only attach events in addChild and detach in remove.. Seems all ok yet. + */ + doAddChild: function(child, index){ + + if (dojo.lang.isUndefined(index)) { + index = this.children.length; + } + + if (!child.isTreeNode){ + dojo.raise("You can only add TreeNode widgets to a "+this.widgetType+" widget!"); + return; + } + + // usually it is impossible to change "isFolder" state, but if anyone wants to add a child to leaf, + // it is possible program-way. + if (this.isTreeNode){ + if (!this.isFolder) { // just became a folder. + //dojo.debug("becoming folder "+this); + this.setFolder(); + } + } + + // adjust tree + var _this = this; + dojo.lang.forEach(child.getDescendants(), function(elem) { elem.tree = _this.tree; }); + + // fix parent + child.parent = this; + + + // no dynamic loading for those who become parents + if (this.isTreeNode) { + this.state = this.loadStates.LOADED; + } + + // add new child into DOM after it was added into children + if (index < this.children.length) { // children[] already has child + //dojo.debug("Inserting before "+this.children[index].title); + dojo.html.insertBefore(child.domNode, this.children[index].domNode); + } else { + this.containerNode.appendChild(child.domNode); + if (this.isExpanded && this.isTreeNode) { + /* When I add children to hidden containerNode => show container w/ them */ + this.showChildren(); + } + } + + + this.children.splice(index, 0, child); + + //dojo.debugShallow(this.children); + + + // if node exists - adjust its depth, otherwise build it + if (child.domNodeInitialized) { + var d = this.isTreeNode ? this.depth : -1; + child.adjustDepth( d - child.depth + 1 ); + + + // update icons to link generated dom with Tree => updateParentGrid + // if I moved child from LastNode inside the tree => need to link it up'n'down => + // updateExpandGridColumn + // if I change depth => need to update all grid.. + child.updateIconTree(); + } else { + //dojo.debug("Create domnode "); + child.depth = this.isTreeNode ? this.depth+1 : 0; + child.createDOMNode(child.tree, child.depth); + } + + + + // Use-case: + // When previous sibling was created => it was last, no children after it + // so it did not create link down => let's add it for all descendants + // Use-case: + // a child was moved down under the last node so last node should be updated + var prevSibling = child.getPreviousSibling(); + if (child.isLastNode() && prevSibling) { + prevSibling.updateExpandGridColumn(); + } + + + //dojo.debug("Added child "+child); + + + + }, + + + + + makeBlankImg: function() { + var img = document.createElement('img'); + + img.style.width = this.iconWidth + 'px'; + img.style.height = this.iconHeight + 'px'; + img.src = this.blankIconSrc; + img.style.verticalAlign = 'middle'; + + return img; + }, + + + updateIconTree: function(){ + + //dojo.debug("Update icons for "+this) + if (!this.isTree) { + this.updateIcons(); + } + + for(var i=0; i0) { + children[index-1].updateExpandGridColumn(); + } + // if it WAS first node in WHOLE TREE - + // update link up of its former lower neighbour(if exists still) + if (parent instanceof dojo.widget.Tree && index == 0 && children.length>0) { + children[0].updateExpandGrid(); + } + + //parent.updateIconTree(); + + + child.parent = child.tree = null; + + return child; + }, + + markLoading: function() { + // no way to mark tree loading + }, + + unMarkLoading: function() { + // no way to show that tree finished loading + }, + + + lock: function() { + !this.lockLevel && this.markLoading(); + this.lockLevel++; + }, + unlock: function() { + if (!this.lockLevel) { + dojo.raise("unlock: not locked"); + } + this.lockLevel--; + !this.lockLevel && this.unMarkLoading(); + }, + + isLocked: function() { + var node = this; + while (true) { + if (node.lockLevel) { + return true; + } + if (node instanceof dojo.widget.Tree) { + break; + } + node = node.parent; + } + + return false; + }, + + flushLock: function() { + this.lockLevel = 0; + this.unMarkLoading(); + } +}); + + diff --git a/source/web/scripts/ajax/src/widget/TreeBasicController.js b/source/web/scripts/ajax/src/widget/TreeBasicController.js new file mode 100644 index 0000000000..b223b4a2f8 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeBasicController.js @@ -0,0 +1,284 @@ + +dojo.provide("dojo.widget.TreeBasicController"); + +dojo.require("dojo.event.*"); +dojo.require("dojo.json") +dojo.require("dojo.io.*"); + + +dojo.widget.tags.addParseTreeHandler("dojo:TreeBasicController"); + + +dojo.widget.TreeBasicController = function() { + dojo.widget.HtmlWidget.call(this); +} + +dojo.inherits(dojo.widget.TreeBasicController, dojo.widget.HtmlWidget); + + +dojo.lang.extend(dojo.widget.TreeBasicController, { + widgetType: "TreeBasicController", + + DNDController: "", + + dieWithTree: false, + + initialize: function(args, frag){ + + /* no DND by default for compatibility */ + if (this.DNDController == "create") { + dojo.require("dojo.dnd.TreeDragAndDrop"); + this.DNDController = new dojo.dnd.TreeDNDController(this); + } + + + + }, + + + /** + * Binds controller to all tree events + */ + listenTree: function(tree) { + //dojo.debug("Event "+tree.eventNames.treeClick); + dojo.event.topic.subscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode"); + dojo.event.topic.subscribe(tree.eventNames.treeClick, this, "onTreeClick"); + dojo.event.topic.subscribe(tree.eventNames.treeCreate, this, "onTreeCreate"); + dojo.event.topic.subscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy"); + + if (this.DNDController) { + this.DNDController.listenTree(tree); + } + }, + + unlistenTree: function(tree) { + dojo.event.topic.unsubscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode"); + dojo.event.topic.unsubscribe(tree.eventNames.treeClick, this, "onTreeClick"); + dojo.event.topic.unsubscribe(tree.eventNames.treeCreate, this, "onTreeCreate"); + dojo.event.topic.unsubscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy"); + }, + + onTreeDestroy: function(message) { + var tree = message.source; + + this.unlistenTree(tree); + + if (this.dieWithTree) { + //alert("Killing myself "+this.widgetId); + this.destroy(); + //dojo.debug("done"); + } + }, + + onCreateDOMNode: function(message) { + + var node = message.source; + + + if (node.expandLevel > 0) { + this.expandToLevel(node, node.expandLevel); + } + }, + + // perform actions-initializers for tree + onTreeCreate: function(message) { + var tree = message.source; + var _this = this; + if (tree.expandLevel) { + dojo.lang.forEach(tree.children, + function(child) { + _this.expandToLevel(child, tree.expandLevel-1) + } + ); + } + }, + + expandToLevel: function(node, level) { + if (level == 0) return; + + var children = node.children; + var _this = this; + + var handler = function(node, expandLevel) { + this.node = node; + this.expandLevel = expandLevel; + // recursively expand opened node + this.process = function() { + //dojo.debug("Process "+node+" level "+level); + for(var i=0; i 0) { + this.expandToLevel(message.node, message.node.expandLevel); + } + if (message.node.loadLevel > 0) { + this.loadToLevel(message.node, message.node.loadLevel); + } + + //dojo.profile.end("onTreeChange"); + return; + } + + if (!message.newTree || !this.listenedTrees[message.newTree.widgetId]) { + // I got this message because node leaves me (oldtree) + /** + * clean all folders that I listen. I don't listen to non-folders. + */ + this.processDescendants(message.node, this.listenNodeFilter, this.unlistenNode); + } + + if (!message.oldTree || !this.listenedTrees[message.oldTree.widgetId]) { + // we have new node + this.processDescendants(message.node, this.listenNodeFilter, this.listenNode); + } + + //dojo.profile.end("onTreeChange"); + }, + + + // perform actions-initializers for tree + onAfterTreeCreate: function(message) { + var tree = message.source; + if (tree.expandLevel) { + this.expandToLevel(tree, tree.expandLevel) + } + if (tree.loadLevel) { + this.loadToLevel(tree, tree.loadLevel); + } + }, + + /** + * time between expand calls for batch operations + * @see expandToLevel + */ + batchExpandTimeout: 20, + + + expandAll: function(nodeOrTree) { + return this.expandToLevel(nodeOrTree, Number.POSITIVE_INFINITY); + + }, + + + collapseAll: function(nodeOrTree) { + var _this = this; + + var filter = function(elem) { + return (elem instanceof dojo.widget.Widget) && elem.isFolder && elem.isExpanded; + } + + if (nodeOrTree.isTreeNode) { + this.processDescendants(nodeOrTree, filter, this.collapse); + } else if (nodeOrTree.isTree) { + dojo.lang.forEach(nodeOrTree.children,function(c) { _this.processDescendants(c, filter, _this.collapse) }); + } + }, + + /** + * walk a node in time, forward order, with pauses between expansions + */ + expandToLevel: function(nodeOrTree, level) { + dojo.require("dojo.widget.TreeTimeoutIterator"); + + var filterFunc = function(elem) { + var res = elem.isFolder || elem.children && elem.children.length; + //dojo.debug("Filter "+elem+ " result:"+res); + return res; + }; + var callFunc = function(node, iterator) { + this.expand(node, true); + iterator.forward(); + } + + var iterator = new dojo.widget.TreeTimeoutIterator(nodeOrTree, callFunc, this); + iterator.setFilter(filterFunc); + + + iterator.timeout = this.batchExpandTimeout; + + //dojo.debug("here "+nodeOrTree+" level "+level); + + iterator.setMaxLevel(nodeOrTree.isTreeNode ? level-1 : level); + + + return iterator.start(nodeOrTree.isTreeNode); + }, + + + + getWidgetByNode: function(node) { + var widgetId; + while (! (widgetId = node.widgetId) ) { + node = node.parentNode; + } + return dojo.widget.byId(widgetId); + }, + + onExpandClick: function(e){ + //dojo.debugShallow(e) + var node = this.getWidgetByNode(e.target); + + if (node.isExpanded){ + this.collapse(node); + } else { + this.expand(node); + } + }, + + /** + * callout activated even if node is expanded already + */ + expand: function(node) { + //dojo.debug("Expand "+node); + + if (node.isFolder) { + node.expand(); // skip trees or non-folders + } + + }, + + collapse: function(node) { + node.collapse(); + }, + + + // -------------------------- TODO: Inline edit node --------------------- + canEditLabel: function(node) { + if (node.actionIsDisabledNow(node.actions.EDIT)) return false; + + return true; + }, + + + editLabelStart: function(node) { + if (!this.canEditLabel(node)) { + return false; + } + + if (!this.editor.isClosed()) { + //dojo.debug("editLabelStart editor open"); + this.editLabelFinish(this.editor.saveOnBlur); + } + + this.doEditLabelStart(node); + + + }, + + + editLabelFinish: function(save) { + this.doEditLabelFinish(save); + }, + + + doEditLabelStart: function(node) { + if (!this.editor) { + dojo.raise(this.widgetType+": no editor specified"); + } + + //dojo.debug("editLabelStart editor open "+node); + + this.editor.open(node); + }, + + doEditLabelFinish: function(save) { + if (!this.editor) { + dojo.raise(this.widgetType+": no editor specified"); + } + var node = this.editor.node; + + this.editor.close(save); + //dojo.debug("Finish edit "+node+" "+node.isPhantom+" save:"+save); + + if (node.isPhantom) { + if (save) { + node.isPhantom = false; + } else { + node.destroy(); + } + } + }, + + + + + /** + * check that something is possible + * run maker to do it + * run exposer to expose result to visitor immediatelly + * exposer does not affect result + */ + runStages: function(check, prepare, make, finalize, expose, args) { + + if (check && !check.apply(this, args)) { + return false; + } + + if (prepare && !prepare.apply(this, args)) { + return false; + } + + var result = make.apply(this, args); + + + if (finalize) { + finalize.apply(this,args); + } + + if (!result) { + return result; + } + + + if (expose) { + expose.apply(this, args); + } + + return result; + } +}); + + +// create and edit +dojo.lang.extend(dojo.widget.TreeBasicControllerV3, { + + createAndEdit: function(parent, index) { + var data = {title:parent.tree.defaultChildTitle}; + + if (!this.canCreateChild(parent, index, data)) { + return false; + } + + var child = this.doCreateChild(parent, index, data); + if (!child) return false; + this.exposeCreateChild(parent, index, data); + + child.isPhantom = true; + + if (!this.editor.isClosed()) { + //dojo.debug("editLabelStart editor open"); + this.editLabelFinish(this.editor.saveOnBlur); + } + + + + this.doEditLabelStart(child); + + } + +}); + + +// =============================== clone ============================ +dojo.lang.extend(dojo.widget.TreeBasicControllerV3, { + + canClone: function(child, newParent, index, deep){ + return true; + }, + + + clone: function(child, newParent, index, deep) { + return this.runStages( + this.canClone, this.prepareClone, this.doClone, this.finalizeClone, this.exposeClone, arguments + ); + }, + + exposeClone: function(child, newParent) { + if (newParent.isTreeNode) { + this.expand(newParent); + } + }, + + doClone: function(child, newParent, index, deep) { + //dojo.debug("Clone "+child); + var cloned = child.clone(deep); + newParent.addChild(cloned, index); + + return cloned; + } + + +}); + +// =============================== detach ============================ + +dojo.lang.extend(dojo.widget.TreeBasicControllerV3, { + canDetach: function(child) { + if (child.actionIsDisabledNow(child.actions.DETACH)) { + return false; + } + + return true; + }, + + + detach: function(node) { + return this.runStages( + this.canDetach, this.prepareDetach, this.doDetach, this.finalizeDetach, this.exposeDetach, arguments + ); + }, + + + doDetach: function(node, callObj, callFunc) { + node.detach(); + } + +}); + + +// =============================== destroy ============================ +dojo.lang.extend(dojo.widget.TreeBasicControllerV3, { + + canDestroy: function(child) { + + if (child.parent && !this.canDetach(child)) { + return false; + } + return true; + }, + + + destroy: function(node) { + return this.runStages( + this.canDestroy, this.prepareDestroy, this.doDestroy, this.finalizeDestroy, this.exposeDestroy, arguments + ); + }, + + + doDestroy: function(node) { + node.destroy(); + } + +}); + + + +// =============================== move ============================ + +dojo.lang.extend(dojo.widget.TreeBasicControllerV3, { + + /** + * check for non-treenodes + */ + canMoveNotANode: function(child, parent) { + if (child.treeCanMove) { + return child.treeCanMove(parent); + } + + return true; + }, + + /** + * Checks whether it is ok to change parent of child to newParent + * May incur type checks etc + * + * It should check only hierarchical possibility w/o index, etc + * because in onDragOver event for Between Dnd mode we can't calculate index at once on onDragOVer. + * index changes as client moves mouse up-down over the node + */ + canMove: function(child, newParent){ + if (!child.isTreeNode) { + return this.canMoveNotANode(child, newParent); + } + + if (child.actionIsDisabledNow(child.actions.MOVE)) { + return false; + } + + // if we move under same parent then no matter if ADDCHILD disabled for him + // but if we move to NEW parent then check if action is disabled for him + // also covers case for newParent being a non-folder in strict mode etc + if (child.parent !== newParent && newParent.actionIsDisabledNow(newParent.actions.ADDCHILD)) { + return false; + } + + // Can't move parent under child. check whether new parent is child of "child". + var node = newParent; + while(node.isTreeNode) { + //dojo.debugShallow(node.title) + if (node === child) { + // parent of newParent is child + return false; + } + node = node.parent; + } + + return true; + }, + + + move: function(child, newParent, index/*,...*/) { + return this.runStages(this.canMove, this.prepareMove, this.doMove, this.finalizeMove, this.exposeMove, arguments); + }, + + doMove: function(child, newParent, index) { + //dojo.debug("MOVE "+child); + child.tree.move(child, newParent, index); + + return true; + }, + + exposeMove: function(child, newParent) { + if (newParent.isTreeNode) { + this.expand(newParent); + } + } + + +}); + +dojo.lang.extend(dojo.widget.TreeBasicControllerV3, { + + // ----------------------------------------------------------------------------- + // Create node stuff + // ----------------------------------------------------------------------------- + + + canCreateChild: function(parent, index, data) { + if (parent.actionIsDisabledNow(parent.actions.ADDCHILD)) { + return false; + } + + return true; + }, + + + /* send data to server and add child from server */ + /* data may contain an almost ready child, or anything else, suggested to server */ + /*in Rpc controllers server responds with child data to be inserted */ + createChild: function(parent, index, data) { + return this.runStages(this.canCreateChild, this.prepareCreateChild, this.doCreateChild, this.finalizeCreateChild, this.exposeCreateChild, arguments); + }, + + + doCreateChild: function(parent, index, data) { + + var newChild = parent.tree.createNode(data); + //var newChild = dojo.widget.createWidget(widgetType, data); + + parent.addChild(newChild, index); + + return newChild; + }, + + exposeCreateChild: function(parent) { + return this.expand(parent); + } + + +}); diff --git a/source/web/scripts/ajax/src/widget/TreeCommon.js b/source/web/scripts/ajax/src/widget/TreeCommon.js new file mode 100644 index 0000000000..471bd43acb --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeCommon.js @@ -0,0 +1,121 @@ + +dojo.provide("dojo.widget.TreeCommon"); +dojo.require("dojo.widget.*"); // for dojo.widget.manager + +dojo.widget.TreeCommon = function() { +} + +dojo.lang.extend(dojo.widget.TreeCommon, { + + + listenTreeEvents: [], + listenedTrees: {}, + + /** + * evaluates to false => skip unlistening nodes + * provided => use it + */ + listenNodeFilter: null, + + listenTree: function(tree) { + + //dojo.debug("listenTree in "+this+" tree "+tree); + + var _this = this; + + if (this.listenedTrees[tree.widgetId]) { + return; // already listening + } + + dojo.lang.forEach(this.listenTreeEvents, function(event) { + var eventHandler = "on" + event.charAt(0).toUpperCase() + event.substr(1); + //dojo.debug("subscribe: event "+tree.eventNames[event]+" widget "+_this+" handler "+eventHandler); + dojo.event.topic.subscribe(tree.eventNames[event], _this, eventHandler); + }); + + + var filter; + + if (this.listenNodeFilter) { + this.processDescendants(tree, this.listenNodeFilter, this.listenNode, true); + } + + /** + * remember that I listen to this tree. No unbinding/binding/deselection + * needed when transfer between listened trees + */ + this.listenedTrees[tree.widgetId] = true; + + }, + + // interface functions + listenNode: function() {}, + unlistenNode: function() {}, + + unlistenTree: function(tree, nodeFilter) { + + var _this = this; + + if (!this.listenedTrees[tree.widgetId]) { + return; + } + + dojo.lang.forEach(this.listenTreeEvents, function(event) { + var eventHandler = "on" + event.charAt(0).toUpperCase() + event.substr(1); + dojo.event.topic.unsubscribe(tree.eventNames[event], _this, eventHandler); + }); + + + if (this.listenNodeFilter) { + this.processDescendants(tree, this.listenNodeFilter, this.unlistenNode, true); + } + + delete this.listenedTrees[tree.widgetId]; + + }, + + + domElement2TreeNode: function(domElement) { + while (domElement && !domElement.widgetId) { + domElement = domElement.parentNode; + } + + if (!domElement) { + dojo.raise("domElement2TreeNode couldnt detect widget"); + } + + return dojo.widget.byId(domElement.widgetId); + }, + + /** + * it is here, not in Widget, because mostly tree needs it + */ + processDescendants: function(elem, filter, func, skipFirst) { + + var _this = this; + + if (!skipFirst) { + if (!filter.call(_this,elem)) { + return; + } + func.call(_this,elem); + } + + + var stack = [elem] + while (elem = stack.pop()) { + dojo.lang.forEach(elem.children, function(elem) { + if (filter.call(_this, elem)) { + func.call(_this, elem); + stack.push(elem); + } + }); + } + } + + + + + + +}); \ No newline at end of file diff --git a/source/web/scripts/ajax/src/widget/TreeContextMenu.js b/source/web/scripts/ajax/src/widget/TreeContextMenu.js new file mode 100644 index 0000000000..72b36224c9 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeContextMenu.js @@ -0,0 +1,204 @@ + + +dojo.provide("dojo.widget.TreeContextMenu"); +dojo.provide("dojo.widget.TreeMenuItem"); + +dojo.require("dojo.event.*"); +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.Menu2"); + + +dojo.widget.tags.addParseTreeHandler("dojo:TreeContextMenu"); +dojo.widget.tags.addParseTreeHandler("dojo:TreeMenuItem"); + + + +dojo.widget.TreeContextMenu = function() { + dojo.widget.PopupMenu2.call(this); + + this.listenedTrees = []; + +} + + +dojo.inherits(dojo.widget.TreeContextMenu, dojo.widget.PopupMenu2); + +dojo.lang.extend(dojo.widget.TreeContextMenu, { + + widgetType: "TreeContextMenu", + + open: function(x, y, parentMenu, explodeSrc){ + + var result = dojo.widget.PopupMenu2.prototype.open.apply(this, arguments); + + /* publish many events here about structural changes */ + dojo.event.topic.publish(this.eventNames.open, { menu:this }); + + return result; + }, + + listenTree: function(tree) { + /* add context menu to all nodes that exist already */ + var nodes = tree.getDescendants(); + + for(var i=0; i I did not process onNodeDomCreate + this.bindTreeNode(message.child); + } + } + + +}); + + + + + + +dojo.widget.TreeMenuItem = function() { + dojo.widget.MenuItem2.call(this); + +} + + +dojo.inherits(dojo.widget.TreeMenuItem, dojo.widget.MenuItem2); + + +dojo.lang.extend(dojo.widget.TreeMenuItem, { + + widgetType: "TreeMenuItem", + + // treeActions menu item performs following actions (to be checked for permissions) + treeActions: "", + + initialize: function(args, frag) { + + this.treeActions = this.treeActions.split(","); + for(var i=0; i= this.randomChildrenMaxCount) { + break; + } + } + + var i=1; + var _this = this; + dojo.lang.forEach(children, function(child) { + var t = title+(_this.randomChildrenDepth==1 ? '' : '.')+i; + i++; + + // first node is always folder for tests + if (_this.randomChildrenCount<_this.randomChildrenMaxCount && ( + _this.randomChildrenDepth==1 && child === children[0] || _this.randomChildrenDepth<5 && Math.random()>0.3 ) + ) { + //dojo.debug("add child "+t); + child.children = _this.makeRandomChildren(t); + } + }); + + + //dojo.debug("out "+nodeOrTree); + + this.randomChildrenDepth--; + + return children; + }, + + bindDemoMenu: function(controller) { + var _t = this; + + dojo.event.topic.subscribe('treeContextMenuDestroy/engage', + function (menuItem) { + var node = menuItem.getTreeNode(); + //if (confirm("Delete node with descendants: "+node.title.replace(/(<([^>]+)>)/ig," ") +" ?")) { + _t.reportIfDefered(controller.destroy(node)); + + } + ); + + + dojo.event.topic.subscribe('treeContextMenuCreate/engage', + function (menuItem) { + var node = menuItem.getTreeNode(); + var d = controller.createAndEdit(node, 0); + _t.reportIfDefered(d); + } + ); + + + dojo.event.topic.subscribe('treeContextMenuUp/engage', + function (menuItem) { + var node = menuItem.getTreeNode(); + if (node.isFirstNode()) return; + _t.reportIfDefered(controller.move(node, node.parent, node.getParentIndex()-1)); + } + ); + + + dojo.event.topic.subscribe('treeContextMenuDown/engage', + function (menuItem) { + var node = menuItem.getTreeNode(); + if (node.isLastNode()) return; + _t.reportIfDefered(controller.move(node, node.parent, node.getParentIndex()+1)); + } + ); + + dojo.event.topic.subscribe('treeContextMenuEdit/engage', + function (menuItem) { + var node = menuItem.getTreeNode(); + _t.reportIfDefered(controller.editLabelStart(node)); + } + ); + + } + + + +} diff --git a/source/web/scripts/ajax/src/widget/TreeDeselectOnDblselect.js b/source/web/scripts/ajax/src/widget/TreeDeselectOnDblselect.js new file mode 100644 index 0000000000..e755076be4 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeDeselectOnDblselect.js @@ -0,0 +1,40 @@ + +dojo.provide("dojo.widget.TreeDeselectOnDblselect"); + +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.TreeSelectorV3"); + +// selector extension to emphase node + + +dojo.widget.tags.addParseTreeHandler("dojo:TreeDeselectOnDblselect"); + + +dojo.widget.TreeDeselectOnDblselect = function() { + //dojo.debug("TEST"); + dojo.widget.HtmlWidget.call(this); +} + +dojo.inherits(dojo.widget.TreeDeselectOnDblselect, dojo.widget.HtmlWidget); + + +dojo.lang.extend(dojo.widget.TreeDeselectOnDblselect, { + widgetType: "TreeDeselectOnDblselect", + + selector: "", + + initialize: function() { + this.selector = dojo.widget.byId(this.selector); + //dojo.debug("OK "+this.selector); + dojo.event.topic.subscribe(this.selector.eventNames.dblselect, this, "onDblselect"); + }, + + + onDblselect: function(message) { + //dojo.debug("happen "+this.selector); + //dojo.debug(message.node); + this.selector.deselect(message.node); + } + + +}); diff --git a/source/web/scripts/ajax/src/widget/TreeDndControllerV3.js b/source/web/scripts/ajax/src/widget/TreeDndControllerV3.js new file mode 100644 index 0000000000..8b8fcbad1c --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeDndControllerV3.js @@ -0,0 +1,142 @@ + +dojo.provide("dojo.widget.TreeDndControllerV3"); + + +dojo.require("dojo.dnd.TreeDragAndDropV3"); + +dojo.widget.tags.addParseTreeHandler("dojo:TreeDndControllerV3"); + + +dojo.widget.TreeDndControllerV3 = function() { + this.dragSources = {}; + + this.dropTargets = {}; + + this.listenedTrees = {}; +} + +dojo.inherits(dojo.widget.TreeDndControllerV3, dojo.widget.HtmlWidget); + + +dojo.lang.extend(dojo.widget.TreeDndControllerV3, dojo.widget.TreeCommon.prototype); + +dojo.lang.extend(dojo.widget.TreeDndControllerV3, { + widgetType: "TreeDndControllerV3", + + listenTreeEvents: ["afterChangeTree","beforeTreeDestroy", "afterAddChild"], + listenNodeFilter: function(elem) { return elem instanceof dojo.widget.Widget}, + + initialize: function(args) { + this.treeController = dojo.lang.isString(args.controller) ? dojo.widget.byId(args.controller) : args.controller; + + if (!this.treeController) { + dojo.raise("treeController must be declared"); + } + + }, + + onBeforeTreeDestroy: function(message) { + this.unlistenTree(message.source); + }, + + // first Dnd registration happens in addChild + // because I have information about parent on this stage and can use it + // to check locking or other things + onAfterAddChild: function(message) { + //dojo.debug("Dnd addChild "+message.child); + this.listenNode(message.child); + }, + + + onAfterChangeTree: function(message) { + /* catch new nodes on afterAddChild, because I need parent */ + if (!message.oldTree) return; + + //dojo.debug("HERE"); + + if (!message.newTree || !this.listenedTrees[message.newTree.widgetId]) { + this.processDescendants(message.node, this.listenNodeFilter, this.unlistenNode); + } + + if (!this.listenedTrees[message.oldTree.widgetId]) { + // we have new node + this.processDescendants(message.node, this.listenNodeFilter, this.listenNode); + } + //dojo.profile.end("onTreeChange"); + }, + + + /** + * Controller(node model) creates DndNodes because it passes itself to node for synchroneous drops processing + * I can't process DnD with events cause an event can't return result success/false + */ + listenNode: function(node) { + + //dojo.debug("listen dnd "+node); + //dojo.debug((new Error()).stack) + //dojo.profile.start("Dnd listenNode "+node); + if (!node.tree.DndMode) return; + if (this.dragSources[node.widgetId] || this.dropTargets[node.widgetId]) return; + + + /* I drag label, not domNode, because large domNodes are very slow to copy and large to drag */ + + var source = null; + var target = null; + + + if (!node.actionIsDisabled(node.actions.MOVE)) { + //dojo.debug("reg source") + + //dojo.profile.start("Dnd source "+node); + var source = this.makeDragSource(node); + //dojo.profile.end("Dnd source "+node); + + this.dragSources[node.widgetId] = source; + } + + //dojo.profile.start("Dnd target "+node); + //dojo.debug("reg target"); + var target = this.makeDropTarget(node); + //dojo.profile.end("Dnd target "+node); + + this.dropTargets[node.widgetId] = target; + + //dojo.profile.end("Dnd listenNode "+node); + + + }, + + /** + * Factory method, override it to create special source + */ + makeDragSource: function(node) { + return new dojo.dnd.TreeDragSourceV3(node.contentNode, this, node.tree.widgetId, node); + }, + + + /** + * Factory method, override it to create special target + */ + makeDropTarget: function(node) { + return new dojo.dnd.TreeDropTargetV3(node.contentNode, this.treeController, node.tree.DndAcceptTypes, node); + }, + + unlistenNode: function(node) { + + if (this.dragSources[node.widgetId]) { + dojo.dnd.dragManager.unregisterDragSource(this.dragSources[node.widgetId]); + delete this.dragSources[node.widgetId]; + } + + if (this.dropTargets[node.widgetId]) { + dojo.dnd.dragManager.unregisterDropTarget(this.dropTargets[node.widgetId]); + delete this.dropTargets[node.widgetId]; + } + } + + + + + +}); diff --git a/source/web/scripts/ajax/src/widget/TreeDocIconExtension.js b/source/web/scripts/ajax/src/widget/TreeDocIconExtension.js new file mode 100644 index 0000000000..f828912f64 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeDocIconExtension.js @@ -0,0 +1,112 @@ + +dojo.provide("dojo.widget.TreeDocIconExtension"); + +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.TreeExtension"); + +// selector extension to emphase node + + +dojo.widget.tags.addParseTreeHandler("dojo:TreeDocIconExtension"); + + +dojo.widget.TreeDocIconExtension = function() { + dojo.widget.TreeExtension.call(this); +} + +dojo.inherits(dojo.widget.TreeDocIconExtension, dojo.widget.TreeExtension); + +/** + * can't unlisten + */ +dojo.lang.extend(dojo.widget.TreeDocIconExtension, { + widgetType: "TreeDocIconExtension", + + templateCssPath: dojo.uri.dojoUri("src/widget/templates/TreeDocIcon.css"), + + templateString: '
', + + + listenTreeEvents: ["afterChangeTree","afterSetFolder","afterUnsetFolder"], + + listenNodeFilter: function(elem) { return elem instanceof dojo.widget.Widget }, + + + setNodeTypeClass: function(node) { + //dojo.debug("setNodeTypeClass in "+node+" type "+node.getNodeType()); + //dojo.debug(node.iconNode) + + var reg = new RegExp("(^|\\s)"+node.tree.classPrefix+"Icon\\w+",'g'); + + var clazz = dojo.html.getClass(node.iconNode).replace(reg,'') + ' ' + node.tree.classPrefix+'Icon'+node.getNodeType(); + dojo.html.setClass(node.iconNode, clazz); + }, + + + onAfterSetFolder: function(message) { + //dojo.debug("FOLDER"); + if (message.source.iconNode) { + // on node-initialize time when folder is set there is no iconNode + // this case will be processed in treeChange anyway + this.setNodeTypeClass(message.source); + } + }, + + + onAfterUnsetFolder: function(message) { + this.setNodeTypeClass(message.source); + }, + + + listenNode: function(node) { + /** + * add node with document type icon to node template and Tree.iconNodeTemplate + * it will be set to TreeNode.iconNode on node creation + * we do not assign document type yet, its node specific + */ + //dojo.debug("listenNode in "+node); + + node.contentIconNode = document.createElement("div"); + var clazz = node.tree.classPrefix+"IconContent"; + if (dojo.render.html.ie) { + clazz = clazz+' '+ node.tree.classPrefix+"IEIconContent"; + } + dojo.html.setClass(node.contentIconNode, clazz); + + node.contentNode.parentNode.replaceChild(node.contentIconNode, node.expandNode); + + node.iconNode = document.createElement("div"); + dojo.html.setClass(node.iconNode, node.tree.classPrefix+"Icon"+' '+node.tree.classPrefix+'Icon'+node.getNodeType()); + + node.contentIconNode.appendChild(node.expandNode); + node.contentIconNode.appendChild(node.iconNode); + + dojo.dom.removeNode(node.contentNode) + node.contentIconNode.appendChild(node.contentNode); + + + + //dojo.html.insertAfter(node.iconNode, node.expandNode); + + //dojo.debug("listenNode out "+node); + + }, + + + onAfterChangeTree: function(message) { + var _this = this; + + //dojo.debug(message.node) + + if (!message.oldTree || !this.listenedTrees[message.oldTree.widgetId]) { + // moving from old tree to our tree + this.processDescendants(message.node, + this.listenNodeFilter, + this.listenNode + ); + } + + } + + +}); diff --git a/source/web/scripts/ajax/src/widget/TreeEditor.js b/source/web/scripts/ajax/src/widget/TreeEditor.js new file mode 100644 index 0000000000..9d72f6c8f5 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeEditor.js @@ -0,0 +1,103 @@ +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.RichText"); + +dojo.provide("dojo.widget.TreeEditor"); + +dojo.widget.defineWidget( + "dojo.widget.TreeEditor", + dojo.widget.HtmlWidget, +{ + + + singleLineMode: false, // enter saves + saveOnBlur: true, // blur or new edit saves current + sync: false, // finish editing in sync/async mode + + controller: null, + + node: null, + + richTextParams: {}, + + + + getContents: function() { + return this.richText.getEditorContent(); + }, + + open: function(node) { + + if (!this.richText) { + this.richText = dojo.widget.createWidget("RichText", this.richTextParams, node.labelNode); + + dojo.event.connect("around", this.richText, "onKeyDown", this, "richText_onKeyDown" ); + dojo.event.connect(this.richText, "onBlur", this, "richText_onBlur" ); + } else { + this.richText.open(node.labelNode); + } + + this.node = node; + }, + + close: function(save) { + if (save) { + var title = this.getContents(); + } + + this.richText.close(save); + + if (save) { + this.node.setTitle(title); // to make sure everything updated and event sent + } + + this.node = null; + }, + + isClosed: function() { + return !this.richText || this.richText.isClosed; + }, + + execCommand: function() { + this.richText.execCommand.apply(this.richText, arguments); + }, + + richText_onKeyDown: function(invocation) { + var e = invocation.args[0]; + if((!e)&&(this.object)) { + e = dojo.event.browser.fixEvent(this.editor.window.event); + } + + switch (e.keyCode) { + case e.KEY_ESCAPE: + this.finish(false); + dojo.event.browser.stopEvent(e); + break; + case e.KEY_ENTER: + if( e.ctrlKey && !this.singleLineMode ) { + this.execCommand( "inserthtml", "
" ); + + } + else { + this.finish(true); + //dojo.debug("finish"); + } + dojo.event.browser.stopEvent(e); + break; + default: + return invocation.proceed(); + } + }, + + richText_onBlur: function() { + this.finish(this.saveOnBlur); + }, + + + finish: function(save) { + return this.controller.editLabelFinish(save, this.sync); + } + + + +}); diff --git a/source/web/scripts/ajax/src/widget/TreeEmphaseOnSelect.js b/source/web/scripts/ajax/src/widget/TreeEmphaseOnSelect.js new file mode 100644 index 0000000000..00bc794e05 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeEmphaseOnSelect.js @@ -0,0 +1,55 @@ + +dojo.provide("dojo.widget.TreeEmphaseOnSelect"); + +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.TreeSelectorV3"); + +// selector extension to emphase node + + +dojo.widget.tags.addParseTreeHandler("dojo:TreeEmphaseOnSelect"); + + +dojo.widget.TreeEmphaseOnSelect = function() { + dojo.widget.HtmlWidget.call(this); + this.saveSelected = {} +} + +dojo.inherits(dojo.widget.TreeEmphaseOnSelect, dojo.widget.HtmlWidget); + + +dojo.lang.extend(dojo.widget.TreeEmphaseOnSelect, { + widgetType: "TreeEmphaseOnSelect", + + selector: "", + + initialize: function() { + this.selector = dojo.widget.byId(this.selector); + + dojo.event.topic.subscribe(this.selector.eventNames.select, this, "onSelect"); + //dojo.event.topic.subscribe(this.selector.eventNames.listenNode, this, "onDeselect"); + //dojo.event.topic.subscribe(this.selector.eventNames.unlistenNode, this, "onDeselect"); + dojo.event.topic.subscribe(this.selector.eventNames.deselect, this, "onDeselect"); + }, + + + onSelect: function(message) { + var domNode = message.node.labelNode; + + dojo.html.clearSelection(domNode); + + + dojo.html.addClass(domNode, message.node.tree.classPrefix+'NodeEmphased'); + //dojo.debug("after select "+dojo.html.getClass(message.node.labelNode)) + + }, + + onDeselect: function(message) { + var domNode = message.node.labelNode; + + //dojo.debug(dojo.html.getClass(message.node.labelNode)) + dojo.html.removeClass(domNode, message.node.tree.classPrefix+'NodeEmphased'); + } + + +}); diff --git a/source/web/scripts/ajax/src/widget/TreeExtension.js b/source/web/scripts/ajax/src/widget/TreeExtension.js new file mode 100644 index 0000000000..f6243460c5 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeExtension.js @@ -0,0 +1,20 @@ + +dojo.provide("dojo.widget.TreeExtension"); + +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.TreeCommon"); + + + +// abstract class +dojo.widget.TreeExtension = function() { + dojo.widget.HtmlWidget.call(this); + + this.listenedTrees = {} +} + +dojo.inherits(dojo.widget.TreeExtension, dojo.widget.HtmlWidget); + + +dojo.lang.extend(dojo.widget.TreeExtension, dojo.widget.TreeCommon.prototype); + diff --git a/source/web/scripts/ajax/src/widget/TreeLinkExtension.js b/source/web/scripts/ajax/src/widget/TreeLinkExtension.js new file mode 100644 index 0000000000..9a67996cec --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeLinkExtension.js @@ -0,0 +1,72 @@ + +dojo.provide("dojo.widget.TreeLinkExtension"); + +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.TreeExtension"); + +dojo.widget.tags.addParseTreeHandler("dojo:TreeLinkExtension"); + + +dojo.widget.TreeLinkExtension = function() { + dojo.widget.TreeExtension.call(this); + + this.params = {} +} + +dojo.inherits(dojo.widget.TreeLinkExtension, dojo.widget.TreeExtension); + + +/** + * can only listen, no unlisten + */ +dojo.lang.extend(dojo.widget.TreeLinkExtension, { + widgetType: "TreeLinkExtension", + + listenTreeEvents: ["afterChangeTree"], + + + listenTree: function(tree) { + + dojo.widget.TreeCommon.prototype.listenTree.call(this,tree); + + var labelNode = tree.labelNodeTemplate; + var newLabel = this.makeALabel(); + dojo.html.setClass(newLabel, dojo.html.getClass(labelNode)); + labelNode.parentNode.replaceChild(newLabel, labelNode); + }, + + + + makeALabel: function() { + var newLabel = document.createElement("a"); + + for(key in this.params) { + if (key in {}) continue; + newLabel.setAttribute(key, this.params[key]); + } + + return newLabel; + }, + + + onAfterChangeTree: function(message) { + var _this = this; + + + // only for new nodes + if (!message.oldTree) { + this.listenNode(message.node); + } + + }, + + listenNode: function(node) { + for(key in node.object) { + if (key in {}) continue; + node.labelNode.setAttribute(key, node.object[key]); + } + } + + + +}); diff --git a/source/web/scripts/ajax/src/widget/TreeLoadingController.js b/source/web/scripts/ajax/src/widget/TreeLoadingController.js new file mode 100644 index 0000000000..608d81ddb6 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeLoadingController.js @@ -0,0 +1,207 @@ + +dojo.provide("dojo.widget.TreeLoadingController"); + +dojo.require("dojo.widget.TreeBasicController"); +dojo.require("dojo.event.*"); +dojo.require("dojo.json") +dojo.require("dojo.io.*"); + + +dojo.widget.tags.addParseTreeHandler("dojo:TreeLoadingController"); + + +dojo.widget.TreeLoadingController = function() { + dojo.widget.TreeBasicController.call(this); +} + +dojo.inherits(dojo.widget.TreeLoadingController, dojo.widget.TreeBasicController); + + +dojo.lang.extend(dojo.widget.TreeLoadingController, { + widgetType: "TreeLoadingController", + + RPCUrl: "", + + RPCActionParam: "action", // used for GET for RPCUrl + + + /** + * Common RPC error handler (dies) + */ + RPCErrorHandler: function(type, obj, evt) { + alert( "RPC Error: " + (obj.message||"no message")); + }, + + preventCache: true, + + getRPCUrl: function(action) { + + // RPCUrl=local meant SOLELY for DEMO and LOCAL TESTS. + // May lead to widgetId collisions + if (this.RPCUrl == "local") { + var dir = document.location.href.substr(0, document.location.href.lastIndexOf('/')); + var localUrl = dir+"/"+action; + //dojo.debug(localUrl); + return localUrl; + } + + if (!this.RPCUrl) { + dojo.raise("Empty RPCUrl: can't load"); + } + + return this.RPCUrl + ( this.RPCUrl.indexOf("?") > -1 ? "&" : "?") + this.RPCActionParam+"="+action; + }, + + + /** + * Add all loaded nodes from array obj as node children and expand it + */ + loadProcessResponse: function(node, result, callObj, callFunc) { + + if (!dojo.lang.isUndefined(result.error)) { + this.RPCErrorHandler("server", result.error); + return false; + } + + //dojo.debugShallow(result); + + var newChildren = result; + + if (!dojo.lang.isArray(newChildren)) { + dojo.raise('loadProcessResponse: Not array loaded: '+newChildren); + } + + for(var i=0; i -1 ? "&" : "?") + this.RpcActionParam+"="+action; + }, + + + /** + * Add all loaded nodes from array obj as node children and expand it + */ + loadProcessResponse: function(node, result) { + //dojo.debug("Process response "+node); + + if (!dojo.lang.isArray(result)) { + throw new dojo.FormatError('loadProcessResponse: Not array loaded: '+result); + } + + node.setChildren(result); + + }, + + /** + * kw = { url, sync, params } + */ + runRpc: function(kw) { + var _this = this; + + var deferred = new dojo.Deferred(); + + dojo.io.bind({ + url: kw.url, + handle: this.getDeferredBindHandler(deferred), + mimetype: "text/json", + preventCache: this.preventCache, + sync: kw.sync, + content: { data: dojo.json.serialize(kw.params) } + }); + + return deferred; + + }, + + + + /** + * Load children of the node from server + * Synchroneous loading doesn't break control flow + * I need sync mode for DnD + */ + loadRemote: function(node, sync){ + var _this = this; + + var params = { + node: this.getInfo(node), + tree: this.getInfo(node.tree) + }; + + + var deferred = this.runRpc({ + url: this.getRpcUrl('getChildren'), + sync: sync, + params: params + }); + + deferred.addCallback(function(res) { return _this.loadProcessResponse(node,res) }); + + + + return deferred; + + }, + + batchExpandTimeout: 0, + + recurseToLevel: function(widget, level, callFunc, callObj, skipFirst, sync) { + if (level == 0) return; + + + + if (!skipFirst) { + var deferred = callFunc.call(callObj, widget, sync); + } else { + var deferred = dojo.Deferred.prototype.makeCalled(); + } + + //dojo.debug("expand deferred saved "+node+" sync "+sync); + + + var _this = this; + + var recurseOnExpand = function() { + var children = widget.children; + var deferreds = []; + for(var i=0; i ' + + ' ' + + ' ${this.title} ' + + ' ' + + '${this.afterLabel} ' + + '
' + + '
').replace(/(>|<)\s+/g, '$1'), // strip whitespaces between nodes + + + childIconSrc: "", + childIconFolderSrc: dojo.uri.dojoUri("src/widget/templates/images/Tree/closed.gif"), // for under root parent item child icon, + childIconDocumentSrc: dojo.uri.dojoUri("src/widget/templates/images/Tree/document.gif"), // for under root parent item child icon, + + childIcon: null, + isTreeNode: true, + + objectId: "", // the widget represents an object + + afterLabel: "", + afterLabelNode: null, // node to the left of labelNode + + // an icon left from childIcon: imgs[-2]. + // if +/- for folders, blank for leaves + expandIcon: null, + + title: "", + object: "", // node may have object attached, settable from HTML + isFolder: false, + + labelNode: null, // the item label + titleNode: null, // the item title + imgs: null, // an array of icons imgs + + expandLevel: "", // expand to level + + tree: null, + + depth: 0, + + isExpanded: false, + + state: null, // after creation will change to loadStates: "loaded/loading/unchecked" + domNodeInitialized: false, // domnode is initialized with icons etc + + + isFirstNode: function() { + return this.getParentIndex() == 0 ? true: false; + }, + + isLastNode: function() { + return this.getParentIndex() == this.parent.children.length-1 ? true : false; + }, + + lock: function(){ return this.tree.lock.apply(this, arguments) }, + unlock: function(){ return this.tree.unlock.apply(this, arguments) }, + isLocked: function(){ return this.tree.isLocked.apply(this, arguments) }, + cleanLock: function(){ return this.tree.cleanLock.apply(this, arguments) }, + + actionIsDisabled: function(action) { + var _this = this; + + var disabled = false; + + if (this.tree.strictFolders && action == this.actions.ADDCHILD && !this.isFolder) { + disabled = true; + } + + if (dojo.lang.inArray(_this.actionsDisabled, action)) { + disabled = true; + } + + if (this.isLocked()) { + disabled = true; + } + + return disabled; + }, + + getInfo: function() { + // No title here (title may be widget) + var info = { + widgetId: this.widgetId, + objectId: this.objectId, + index: this.getParentIndex(), + isFolder: this.isFolder + } + + return info; + }, + + initialize: function(args, frag){ + + //dojo.debug(this.title) + + this.state = this.loadStates.UNCHECKED; + + for(var i=0; i move right, negative => move left + */ + adjustDepth: function(depthDiff) { + + for(var i=0; i0) { + for(var i=0; i= this.imgs.length-2) return; + this.imgs[idx].style.backgroundImage = 'url(' + src + ')'; + }, + + + updateIconTree: function(){ + this.tree.updateIconTree.call(this); + }, + + + + + expand: function(){ + if (this.isExpanded) return; + + if (this.children.length) { + this.showChildren(); + } + + this.isExpanded = true; + + this.updateExpandIcon(); + + dojo.event.topic.publish(this.tree.eventNames.expand, {source: this} ); + }, + + collapse: function(){ + if (!this.isExpanded) return; + + this.hideChildren(); + this.isExpanded = false; + + this.updateExpandIcon(); + + dojo.event.topic.publish(this.tree.eventNames.collapse, {source: this} ); + }, + + hideChildren: function(){ + this.tree.toggleObj.hide( + this.containerNode, this.toggleDuration, this.explodeSrc, dojo.lang.hitch(this, "onHide") + ); + + /* if dnd is in action, recalculate changed coordinates */ + if(dojo.exists(dojo, 'dnd.dragManager.dragObjects') && dojo.dnd.dragManager.dragObjects.length) { + dojo.dnd.dragManager.cacheTargetLocations(); + } + }, + + showChildren: function(){ + this.tree.toggleObj.show( + this.containerNode, this.toggleDuration, this.explodeSrc, dojo.lang.hitch(this, "onShow") + ); + + /* if dnd is in action, recalculate changed coordinates */ + if(dojo.exists(dojo, 'dnd.dragManager.dragObjects') && dojo.dnd.dragManager.dragObjects.length) { + dojo.dnd.dragManager.cacheTargetLocations(); + } + }, + + addChild: function(){ + return this.tree.addChild.apply(this, arguments); + }, + + doAddChild: function(){ + return this.tree.doAddChild.apply(this, arguments); + }, + + + + /* Edit current node : change properties and update contents */ + edit: function(props) { + dojo.lang.mixin(this, props); + if (props.title) { + this.titleNode.innerHTML = this.title; + } + + if (props.afterLabel) { + this.afterLabelNode.innerHTML = this.afterLabel; + } + + if (props.childIconSrc) { + this.buildChildIcon(); + } + + + }, + + + removeNode: function(){ return this.tree.removeNode.apply(this, arguments) }, + doRemoveNode: function(){ return this.tree.doRemoveNode.apply(this, arguments) }, + + + toString: function() { + return "["+this.widgetType+" Tree:"+this.tree+" ID:"+this.widgetId+" Title:"+this.title+"]"; + + } + +}); + + + + diff --git a/source/web/scripts/ajax/src/widget/TreeNodeV3.js b/source/web/scripts/ajax/src/widget/TreeNodeV3.js new file mode 100644 index 0000000000..a52398fef8 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeNodeV3.js @@ -0,0 +1,707 @@ +dojo.provide("dojo.widget.TreeNodeV3"); + +dojo.require("dojo.html.*"); +dojo.require("dojo.event.*"); +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.TreeWithNode"); + + +// make it a tag +dojo.widget.tags.addParseTreeHandler("dojo:TreeNodeV3"); + + +// # ////////// + +dojo.widget.TreeNodeV3 = function() { + + dojo.widget.HtmlWidget.call(this); + + this.actionsDisabled = []; +} + +dojo.inherits(dojo.widget.TreeNodeV3, dojo.widget.HtmlWidget); + +dojo.lang.extend(dojo.widget.TreeNodeV3, dojo.widget.TreeWithNode); + + +dojo.lang.extend(dojo.widget.TreeNodeV3, { + widgetType: "TreeNodeV3", + + + tryLazyInit: true, + + + + expandNode: null, + labelNode: null, + + + nodeType: "Document", + + getNodeType: function() { + if (this.isFolder) return "Folder"; + return this.nodeType; + }, + + cloneProperties: ["actionsDisabled","tryLazyInit","nodeType","objectId","object", + "title","isFolder","isExpanded","state"], + + + /** + * copy cloneProperties with recursion into them + * contains "copy constructor" + */ + clone: function(deep) { + var ret = new this.constructor(); + + //dojo.debug("start cloning props "+this); + + for(var i=0; i registerChild or from postInitialize->registerChild + * not called in batch procession + * HTML & widget.createWidget only + * Layout MUST be removed when node is detached + * + */ + addedTo: function(parent, index, dontPublishEvent) { + //dojo.profile.start("addedTo"); + //dojo.debug(this + " addedTo "+parent+" index "+index); + //dojo.debug(parent.children); + //dojo.debug(parent.containerNode.innerHTML); + + //dojo.debug((new Error()).stack); + + + if (this.tree !== parent.tree) { + this.updateTree(parent.tree); + } + + if (parent.isTreeNode) { + if (!parent.isFolder) { + //dojo.debug("folderize parent "+parent); + parent.setFolder(); + parent.state = parent.loadStates.LOADED; + } + } + + + var siblingsCount = parent.children.length; + + // setFolder works BEFORE insertNode + this.insertNode(parent, index); + + + this.viewAddLayout(); + + + //dojo.debug("siblings "+parent.children); + + if (siblingsCount > 1) { + if (index == 0 && parent.children[1] instanceof dojo.widget.Widget) { + parent.children[1].viewUpdateLayout(); + } + if (index == siblingsCount-1 && parent.children[siblingsCount-2] instanceof dojo.widget.Widget) { + parent.children[siblingsCount-2].viewUpdateLayout(); + } + } else if (parent.isTreeNode) { + // added as the first child + //dojo.debug("added as first"); + parent.viewSetHasChildren(); + } + + if (!dontPublishEvent) { + + var message = { + child: this, + index: index, + parent: parent + } + + dojo.event.topic.publish(this.tree.eventNames.afterAddChild, message); + } + + //dojo.profile.end("addedTo"); + + + }, + + /** + * Fast program-only hacky creation of widget + * + */ + createSimple: function(args, parent) { + // I pass no args and ignore default controller + //dojo.profile.start(this.widgetType+" createSimple"); + //dojo.profile.start(this.widgetType+" createSimple constructor"); + if (args.tree) { + var tree = args.tree; + } else if (parent) { + var tree = parent.tree; + } else { + dojo.raise("createSimple: can't evaluate tree"); + } + tree = dojo.widget.byId(tree); + + //dojo.debug(tree); + + var treeNode = new tree.defaultChildWidget(); + //dojo.profile.end(this.widgetType+" createSimple constructor"); + + //dojo.profile.start(this.widgetType+" createSimple mixin"); + for(var x in args){ // fastMixIn + treeNode[x] = args[x]; + } + + + //dojo.profile.end(this.widgetType+" createSimple mixin"); + + + // HtmlWidget.postMixIn + treeNode.toggleObj = dojo.lfx.toggle[treeNode.toggle.toLowerCase()] || dojo.lfx.toggle.plain; + + //dojo.profile.start(this.widgetType + " manager"); + dojo.widget.manager.add(treeNode); + //dojo.profile.end(this.widgetType + " manager"); + + //dojo.profile.start(this.widgetType + " buildRendering"); + treeNode.buildRendering(args, {}, parent); + //dojo.profile.end(this.widgetType + " buildRendering"); + + treeNode.initialize(args, {}, parent); + + //dojo.profile.end(this.widgetType+"createSimple"); + if (treeNode.parent) { + delete dojo.widget.manager.topWidgets[treeNode.widgetId]; + } + + return treeNode; + }, + + + + // can override e.g for case of div with +- text inside + viewUpdateLayout: function() { + //dojo.profile.start("viewUpdateLayout"); + //dojo.debug("UpdateLayout in "+this); + + this.viewRemoveLayout(); + this.viewAddLayout(); + //dojo.profile.end("viewUpdateLayout"); + }, + + + viewAddContainer: function() { + // make controller only if children exist + this.containerNode = this.tree.containerNodeTemplate.cloneNode(true); + this.domNode.appendChild(this.containerNode); + }, + /* + viewRemoveContainer: function() { + // make controller only if children exist + this.domNode.removeChild(this.containerNode); + this.containerNode = null; + }, + */ + + viewAddLayout: function() { + //dojo.profile.start("viewAddLayout"); + //dojo.debug("viewAddLayout in "+this); + + if (this.parent["isTree"]) { + //dojo.debug("Parent isTree => add isTreeRoot"); + + // use setClass, not addClass for speed + dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode) + ' '+this.tree.classPrefix+'IsRoot') + } + //dojo.debug(this.parent.children.length); + //dojo.debug(this.parent.children[this.parent.children.length-1]); + if (this.isLastNode()) { + //dojo.debug("Checked last node for "+this); + //dojo.debug("Parent last is "+this.parent.children[this.parent.children.length-1]); + //dojo.debug("last node => add isTreeLast for "+this); + dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode) + ' '+this.tree.classPrefix+'IsLast') + } + //dojo.profile.end("viewAddLayout"); + //dojo.debug("viewAddLayout out"); + + }, + + + viewRemoveLayout: function() { + //dojo.debug("viewRemoveLayout in "+this); + //dojo.profile.start("viewRemoveLayout"); + //dojo.debug((new Error()).stack); + dojo.html.removeClass(this.domNode, this.tree.classPrefix+"IsRoot"); + dojo.html.removeClass(this.domNode, this.tree.classPrefix+"IsLast"); + //dojo.profile.end("viewRemoveLayout"); + }, + + viewGetExpandClass: function() { + if (this.isFolder) { + return this.isExpanded ? "ExpandOpen" : "ExpandClosed"; + } else { + return "ExpandLeaf"; + } + }, + + viewSetExpand: function() { + //dojo.profile.start("viewSetExpand"); + + //dojo.debug("viewSetExpand in "+this); + + var expand = this.tree.classPrefix+this.viewGetExpandClass(); + var reg = new RegExp("(^|\\s)"+this.tree.classPrefix+"Expand\\w+",'g'); + + dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode).replace(reg,'') + ' '+expand); + + //dojo.debug(dojo.html.getClass(this.domNode)) + //dojo.profile.end("viewSetExpand"); + this.viewSetHasChildrenAndExpand(); + }, + + viewGetChildrenClass: function() { + return 'Children'+(this.children.length ? 'Yes' : 'No'); + }, + + viewSetHasChildren: function() { + //dojo.debug(this+' '+this.children.length) + + var clazz = this.tree.classPrefix+this.viewGetChildrenClass(); + + var reg = new RegExp("(^|\\s)"+this.tree.classPrefix+"Children\\w+",'g'); + + dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode).replace(reg,'') + ' '+clazz); + + this.viewSetHasChildrenAndExpand(); + }, + + /** + * set TreeStateChildrenYes-ExpandClosed pair + * needed for IE, because IE reads only last class from .TreeChildrenYes.TreeExpandClosed pair + */ + viewSetHasChildrenAndExpand: function() { + var clazz = this.tree.classPrefix+'State'+this.viewGetChildrenClass()+'-'+this.viewGetExpandClass(); + + var reg = new RegExp("(^|\\s)"+this.tree.classPrefix+"State[\\w-]+",'g'); + + dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode).replace(reg,'') + ' '+clazz); + }, + + + +// ================================ detach from parent =================================== + + detach: function() { + if (!this.parent) return; + + var oldParent = this.parent; + + this.doDetach.apply(this, arguments); + + dojo.event.topic.publish(this.tree.eventNames.afterDetach, + { child: this, tree: this.tree, parent: oldParent } + ); + + }, + + + /* node does not leave tree */ + doDetach: function() { + //dojo.debug("doDetach in "+this+" parent "+this.parent+" class "+dojo.html.getClass(this.domNode)); + + var parent = this.parent; + + //dojo.debug(parent.containerNode.style.display) + + if (!parent) return; + + var index = this.getParentIndex(); + + + this.viewRemoveLayout(); + + dojo.widget.DomWidget.prototype.removeChild.call(parent, this); + + var siblingsCount = parent.children.length; + + //dojo.debug("siblingsCount "+siblingsCount); + + if (siblingsCount > 0) { + if (index == 0) { // deleted first node => update new first + parent.children[0].viewUpdateLayout(); + } + if (index == siblingsCount) { // deleted last node + parent.children[siblingsCount-1].viewUpdateLayout(); + } + } else { + if (parent.isTreeNode) { + parent.viewSetHasChildren(); + } + } + + if (this.tree.unsetFolderOnEmpty && !parent.children.length && parent.isTreeNode) { + parent.unsetFolder(); + } + + //dojo.debug(parent.containerNode.style.display) + + this.parent = null; + }, + + + /** + * publish destruction event so that controller may unregister/unlisten + */ + destroy: function() { + + dojo.event.topic.publish(this.tree.eventNames.beforeDestroy, { source: this } ); + + this.detach(); + + return dojo.widget.HtmlWidget.prototype.destroy.apply(this, arguments); + }, + + + expand: function(){ + if (this.isExpanded) return; + + + //dojo.profile.start("expand "+this); + + //dojo.debug("expand in "+this); + + //dojo.profile.start("expand - lazy init "+this); + if (this.tryLazyInit) { + this.setChildren(); + this.tryLazyInit = false; + } + + //dojo.profile.end("expand - lazy init "+this); + + + this.isExpanded = true; + + this.viewSetExpand(); + + //dojo.profile.start("expand - showChildren "+this); + + /** + * no matter if I have children or not. need to show/hide container anyway. + * use case: empty folder is expanded => then child is added, container already shown all fine + */ + this.showChildren(); + + //dojo.profile.end("expand - showChildren "+this); + + + //dojo.profile.start("expand - viewSetExpand "+this); + + //dojo.profile.end("expand - viewSetExpand "+this); + + //dojo.profile.start("expand - event "+this); + + + //dojo.profile.end("expand - event "+this); + + //dojo.profile.end("expand "+this); + }, + + + collapse: function(){ + + + if (!this.isExpanded) return; + + + this.isExpanded = false; + + this.hideChildren(); + + + }, + + + hideChildren: function(){ + this.tree.toggleObj.hide( + this.containerNode, this.toggleDuration, this.explodeSrc, dojo.lang.hitch(this, "onHideChildren") + ); + }, + + + showChildren: function(){ + this.tree.toggleObj.show( + this.containerNode, this.toggleDuration, this.explodeSrc, dojo.lang.hitch(this, "onShowChildren") + ); + }, + + onShowChildren: function() { + this.onShow(); + dojo.event.topic.publish(this.tree.eventNames.afterExpand, {source: this} ); + }, + + onHideChildren: function() { + + this.viewSetExpand(); + this.onHide(); + dojo.event.topic.publish(this.tree.eventNames.afterCollapse, {source: this} ); + }, + + /* Edit current node : change properties and update contents */ + setTitle: function(title) { + this.labelNode.innerHTML = this.title = title; + + dojo.event.topic.publish(this.tree.eventNames.afterSetTitle, { source: this, title:title }); + + }, + + toString: function() { + return '['+this.widgetType+', '+this.title+']'; + } + + + +}); diff --git a/source/web/scripts/ajax/src/widget/TreeRPCController.js b/source/web/scripts/ajax/src/widget/TreeRPCController.js new file mode 100644 index 0000000000..23bff5b97b --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeRPCController.js @@ -0,0 +1,161 @@ + +dojo.provide("dojo.widget.TreeRPCController"); + +dojo.require("dojo.event.*"); +dojo.require("dojo.json") +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.TreeLoadingController"); + +dojo.widget.tags.addParseTreeHandler("dojo:TreeRPCController"); + +dojo.widget.TreeRPCController = function(){ + dojo.widget.TreeLoadingController.call(this); +} + +dojo.inherits(dojo.widget.TreeRPCController, dojo.widget.TreeLoadingController); + +dojo.lang.extend(dojo.widget.TreeRPCController, { + widgetType: "TreeRPCController", + + /** + * Make request to server about moving children. + * + * Request returns "true" if move succeeded, + * object with error field if failed + * + * I can't leave DragObject floating until async request returns, need to return false/true + * so making it sync way... + * + * Also, "loading" icon is not shown until function finishes execution, so no indication for remote request. + */ + doMove: function(child, newParent, index){ + + //if (newParent.isTreeNode) newParent.markLoading(); + + var params = { + // where from + child: this.getInfo(child), + childTree: this.getInfo(child.tree), + // where to + newParent: this.getInfo(newParent), + newParentTree: this.getInfo(newParent.tree), + newIndex: index + }; + + var success; + + this.runRPC({ + url: this.getRPCUrl('move'), + /* I hitch to get this.loadOkHandler */ + load: function(response){ + success = this.doMoveProcessResponse(response, child, newParent, index) ; + }, + sync: true, + lock: [child, newParent], + params: params + }); + + + return success; + }, + + doMoveProcessResponse: function(response, child, newParent, index){ + + if(!dojo.lang.isUndefined(response.error)){ + this.RPCErrorHandler("server", response.error); + return false; + } + + var args = [child, newParent, index]; + return dojo.widget.TreeLoadingController.prototype.doMove.apply(this, args); + + }, + + + doRemoveNode: function(node, callObj, callFunc){ + + var params = { + node: this.getInfo(node), + tree: this.getInfo(node.tree) + } + + this.runRPC({ + url: this.getRPCUrl('removeNode'), + /* I hitch to get this.loadOkHandler */ + load: function(response){ + this.doRemoveNodeProcessResponse(response, node, callObj, callFunc) + }, + params: params, + lock: [node] + }); + + }, + + + doRemoveNodeProcessResponse: function(response, node, callObj, callFunc){ + if(!dojo.lang.isUndefined(response.error)){ + this.RPCErrorHandler("server", response.error); + return false; + } + + if(!response){ return false; } + + if(response == true){ + /* change parent succeeded */ + var args = [ node, callObj, callFunc ]; + dojo.widget.TreeLoadingController.prototype.doRemoveNode.apply(this, args); + + return; + }else if(dojo.lang.isObject(response)){ + dojo.raise(response.error); + }else{ + dojo.raise("Invalid response "+response) + } + + + }, + + + + // ----------------------------------------------------------------------------- + // Create node stuff + // ----------------------------------------------------------------------------- + + + doCreateChild: function(parent, index, output, callObj, callFunc){ + + var params = { + tree: this.getInfo(parent.tree), + parent: this.getInfo(parent), + index: index, + data: output + } + + this.runRPC({ + url: this.getRPCUrl('createChild'), + load: function(response) { + // suggested data is dead, fresh data from server is used + this.doCreateChildProcessResponse( response, parent, index, callObj, callFunc) + }, + params: params, + lock: [parent] + }); + + }, + + doCreateChildProcessResponse: function(response, parent, index, callObj, callFunc){ + + if(!dojo.lang.isUndefined(response.error)){ + this.RPCErrorHandler("server",response.error); + return false; + } + + if(!dojo.lang.isObject(response)){ + dojo.raise("Invalid result "+response) + } + + var args = [parent, index, response, callObj, callFunc]; + + dojo.widget.TreeLoadingController.prototype.doCreateChild.apply(this, args); + } +}); diff --git a/source/web/scripts/ajax/src/widget/TreeRpcControllerV3.js b/source/web/scripts/ajax/src/widget/TreeRpcControllerV3.js new file mode 100644 index 0000000000..cd4162a83c --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeRpcControllerV3.js @@ -0,0 +1,411 @@ + +dojo.provide("dojo.widget.TreeRpcControllerV3"); + + + +dojo.require("dojo.event.*"); +dojo.require("dojo.json") +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.TreeLoadingControllerV3"); + +dojo.widget.tags.addParseTreeHandler("dojo:TreeRpcControllerV3"); + +dojo.widget.TreeRpcControllerV3 = function(){ + dojo.widget.TreeLoadingControllerV3.call(this); +} + +dojo.inherits(dojo.widget.TreeRpcControllerV3, dojo.widget.TreeLoadingControllerV3); + + +// TODO: do something with addChild / setChild, so that RpcController become able +// to hook on this and report to server +dojo.lang.extend(dojo.widget.TreeRpcControllerV3, { + widgetType: "TreeRpcControllerV3", + + extraRpcOnEdit: true, + + /** + * Make request to server about moving children. + * + * Request returns "true" if move succeeded, + * object with error field if failed + * + * I can't leave DragObject floating until async request returns, need to return false/true + * so making it sync way... + * + * Also, "loading" icon is not shown until function finishes execution, so no indication for remote request. + */ + doMove: function(child, newParent, index, sync){ + + //if (newParent.isTreeNode) newParent.markLoading(); + + + var params = { + // where from + child: this.getInfo(child), + childTree: this.getInfo(child.tree), + // where to + newParent: this.getInfo(newParent), + newParentTree: this.getInfo(newParent.tree), + newIndex: index + }; + + + var deferred = this.runRpc({ + url: this.getRpcUrl('move'), + sync: sync, + params: params + }); + + var _this = this; + var args = arguments; + + //deferred.addCallback(function(res) { dojo.debug("doMove fired "+res); return res}); + + deferred.addCallback(function() { + dojo.widget.TreeBasicControllerV3.prototype.doMove.apply(_this,args); + }); + + + return deferred; + } +}); + + +// -------------- detach +dojo.lang.extend(dojo.widget.TreeRpcControllerV3, { + + prepareDetach: function(node, sync) { + var deferred = this.startProcessing(node); + return deferred; + }, + + finalizeDetach: function(node) { + this.finishProcessing(node); + }, + + + doDetach: function(node, sync){ + + + var params = { + node: this.getInfo(node), + tree: this.getInfo(node.tree) + } + + var deferred = this.runRpc({ + url: this.getRpcUrl('detach'), + sync: sync, + params: params + }); + + + var _this = this; + var args = arguments; + + deferred.addCallback(function() { + dojo.widget.TreeBasicControllerV3.prototype.doDetach.apply(_this,args); + }); + + + return deferred; + + } +}); + + +// -------------------------- Inline edit node --------------------- +dojo.lang.extend(dojo.widget.TreeRpcControllerV3, { + /** + * send edit start request if needed + * useful for server-side locking + */ + requestEditConfirmation: function(node, action, sync) { + if (!this.extraRpcOnEdit) { + return dojo.Deferred.prototype.makeCalled(); + } + + //dojo.debug("requestEditConfirmation "+node+" "+action); + + var _this = this; + + var deferred = this.startProcessing(node); + + //dojo.debug("startProcessing "+node); + + var params = { + node: this.getInfo(node), + tree: this.getInfo(node.tree) + } + + deferred.addCallback(function() { + //dojo.debug("add action on requestEditConfirmation "+action); + return _this.runRpc({ + url: _this.getRpcUrl(action), + sync: sync, + params: params + }); + }); + + + deferred.addBoth(function(r) { + //dojo.debug("finish rpc with "+r); + _this.finishProcessing(node); + return r; + }); + + return deferred; + }, + + editLabelSave: function(node, newContent, sync) { + var deferred = this.startProcessing(node); + + var _this = this; + + var params = { + node: this.getInfo(node), + tree: this.getInfo(node.tree), + newContent: newContent + } + + + deferred.addCallback(function() { + return _this.runRpc({ + url: _this.getRpcUrl('editLabelSave'), + sync: sync, + params: params + }); + }); + + + deferred.addBoth(function(r) { + _this.finishProcessing(node); + return r; + }); + + return deferred; + }, + + + editLabelStart: function(node, sync) { + if (!this.canEditLabel(node)) { + return false; + } + + if (!this.editor.isClosed()) { + //dojo.debug("editLabelStart editor open"); + var deferred = this.editLabelFinish(this.editor.saveOnBlur, sync); + deferred.addCallback(function() { + return _this.editLabelStart(node, sync); + }); + return deferred; + } + + var _this = this; + //dojo.debug("editLabelStart closed, request"); + var deferred = this.requestEditConfirmation(node, 'editLabelStart', sync); + + deferred.addCallback(function() { + //dojo.debug("start edit"); + _this.doEditLabelStart(node); + }); + + + return deferred; + + }, + + + editLabelFinish: function(save, sync) { + var _this = this; + + var node = this.editor.node; + + var deferred = dojo.Deferred.prototype.makeCalled(); + + if (!save && !node.isPhantom) { + deferred = this.requestEditConfirmation(this.editor.node,'editLabelFinishCancel', sync); + } + + if (save) { + deferred = this.editLabelSave(this.editor.node, this.editor.getContents(), sync); + } + + deferred.addCallback(function() { + _this.doEditLabelFinish(save); + }); + + deferred.addErrback(function(r) { + _this.doEditLabelFinish(false); + return false; + }); + + return deferred; + }, + + createAndEdit: function(parent, index, sync) { + var data = {title:parent.tree.defaultChildTitle}; + + if (!this.canCreateChild(parent, index, data)) { + return false; + } + + /* close editor first */ + if (!this.editor.isClosed()) { + //dojo.debug("editLabelStart editor open"); + var deferred = this.editLabelFinish(this.editor.saveOnBlur, sync); + deferred.addCallback(function() { + return _this.createAndEdit(parent, index, sync); + }); + return deferred; + } + + var _this = this; + + /* load parent and create child*/ + var deferred = this.prepareCreateChild(parent, index, data, sync); + + + deferred.addCallback(function() { + var child = dojo.widget.TreeBasicControllerV3.prototype.doCreateChild.call(_this,parent,index,data); + child.isPhantom = true; + return child; + }); + + + deferred.addBoth(function(r) { + _this.finalizeCreateChild(parent, index, data, sync); + return r; + }); + + /* expand parent */ + deferred.addCallback(function(child) { + var d = _this.exposeCreateChild(parent, index, data, sync); + d.addCallback(function() { return child }); + return d; + }); + + + deferred.addCallback(function(child) { + //dojo.debug("start edit"); + _this.doEditLabelStart(child); + return child; + }); + + + + return deferred; + + } + + + +}); + +dojo.lang.extend(dojo.widget.TreeRpcControllerV3, { + + prepareDestroy: function(node, sync) { + //dojo.debug(node); + var deferred = this.startProcessing(node); + return deferred; + }, + + finalizeDestroy: function(node) { + this.finishProcessing(node); + }, + + + doDestroy: function(node, sync){ + + + var params = { + node: this.getInfo(node), + tree: this.getInfo(node.tree) + } + + var deferred = this.runRpc({ + url: this.getRpcUrl('destroy'), + sync: sync, + params: params + }); + + + var _this = this; + var args = arguments; + + deferred.addCallback(function() { + dojo.widget.TreeBasicControllerV3.prototype.doDestroy.apply(_this,args); + }); + + + return deferred; + + } +}); + + +dojo.lang.extend(dojo.widget.TreeRpcControllerV3, { + + // ----------------------------------------------------------------------------- + // Create node stuff + // ----------------------------------------------------------------------------- + + + doCreateChild: function(parent, index, data, sync){ + + var params = { + tree: this.getInfo(parent.tree), + parent: this.getInfo(parent), + index: index, + data: data + } + + var deferred = this.runRpc({ + url: this.getRpcUrl('createChild'), + sync: sync, + params: params + }); + + var _this = this; + var args = arguments; + + + deferred.addCallback(function() { + return dojo.widget.TreeBasicControllerV3.prototype.doCreateChild.apply(_this,args); + }); + + + return deferred; + }, + + + doClone: function(child, newParent, index, deep, sync) { + + var params = { + child: this.getInfo(child), + newParent: this.getInfo(newParent), + index: index, + deep: deep ? true : false, // undefined -> false + tree: this.getInfo(child.tree) + } + + + var deferred = this.runRpc({ + url: this.getRpcUrl('clone'), + sync: sync, + params: params + }); + + var _this = this; + var args = arguments; + + deferred.addCallback(function() { + dojo.widget.TreeBasicControllerV3.prototype.doClone.apply(_this,args); + }); + + + return deferred; + } + + +}); diff --git a/source/web/scripts/ajax/src/widget/TreeSelector.js b/source/web/scripts/ajax/src/widget/TreeSelector.js new file mode 100644 index 0000000000..4a22082c44 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeSelector.js @@ -0,0 +1,179 @@ + +dojo.provide("dojo.widget.TreeSelector"); + +dojo.require("dojo.widget.HtmlWidget"); + + +dojo.widget.tags.addParseTreeHandler("dojo:TreeSelector"); + + +dojo.widget.TreeSelector = function() { + dojo.widget.HtmlWidget.call(this); + + + this.eventNames = {}; + + this.listenedTrees = []; + +} + +dojo.inherits(dojo.widget.TreeSelector, dojo.widget.HtmlWidget); + + +dojo.lang.extend(dojo.widget.TreeSelector, { + widgetType: "TreeSelector", + selectedNode: null, + + dieWithTree: false, + + eventNamesDefault: { + select : "select", + destroy : "destroy", + deselect : "deselect", + dblselect: "dblselect" // select already selected node.. Edit or whatever + }, + + initialize: function() { + + for(name in this.eventNamesDefault) { + if (dojo.lang.isUndefined(this.eventNames[name])) { + this.eventNames[name] = this.widgetId+"/"+this.eventNamesDefault[name]; + } + } + + }, + + + destroy: function() { + dojo.event.topic.publish(this.eventNames.destroy, { source: this } ); + + return dojo.widget.HtmlWidget.prototype.destroy.apply(this, arguments); + }, + + + listenTree: function(tree) { + dojo.event.topic.subscribe(tree.eventNames.titleClick, this, "select"); + dojo.event.topic.subscribe(tree.eventNames.iconClick, this, "select"); + dojo.event.topic.subscribe(tree.eventNames.collapse, this, "onCollapse"); + dojo.event.topic.subscribe(tree.eventNames.moveFrom, this, "onMoveFrom"); + dojo.event.topic.subscribe(tree.eventNames.removeNode, this, "onRemoveNode"); + dojo.event.topic.subscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy"); + + /* remember all my trees to deselect when element is movedFrom them */ + this.listenedTrees.push(tree); + }, + + + unlistenTree: function(tree) { + + dojo.event.topic.unsubscribe(tree.eventNames.titleClick, this, "select"); + dojo.event.topic.unsubscribe(tree.eventNames.iconClick, this, "select"); + dojo.event.topic.unsubscribe(tree.eventNames.collapse, this, "onCollapse"); + dojo.event.topic.unsubscribe(tree.eventNames.moveFrom, this, "onMoveFrom"); + dojo.event.topic.unsubscribe(tree.eventNames.removeNode, this, "onRemoveNode"); + dojo.event.topic.unsubscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy"); + + + for(var i=0; i dblselect. Need to transform to select->select + onLabelDblClick: function(event) { + this.onLabelClick(event); + }, + + checkSpecialEvent: function(event) { + return event.shiftKey || event.ctrlKey; + }, + + /** + * press on selected with ctrl => deselect it + * press on selected w/o ctrl => dblselect it and deselect all other + * + * press on unselected with ctrl => add it to selection + */ + onLabelClick: function(event) { + var node = this.domElement2TreeNode(event.target); + + //dojo.debug("click "+node+ "special "+this.checkSpecialEvent(event)); + //dojo.html.setClass(event.target, "TreeLabel TreeNodeEmphased"); + + if (dojo.lang.inArray(this.selectedNodes, node)) { + if(this.checkSpecialEvent(event)){ + // If the node is currently selected, and they select it again while holding + // down a meta key, it deselects it + this.deselect(node); + return; + } + + var _this = this; + dojo.lang.forEach(this.selectedNodes, function(selectedNode) { + if (selectedNode !== node) { + _this.deselect(selectedNode); + } + }); + dojo.event.topic.publish(this.eventNames.dblselect, { node: node }); + return; + } + + // if unselected node.. + + + // deselect all if no meta key or disallowed + if (!this.checkSpecialEvent(event) || !this.allowedMulti) { + //dojo.debug("deselect All"); + this.deselectAll(); + } + + //dojo.debug("select"); + + this.select(node); + + }, + + + deselectIfAncestorMatch: function(ancestor) { + /* deselect all nodes with this ancestor */ + var _this = this; + dojo.lang.forEach(this.selectedNodes, function(node) { + var selectedNode = node; + while (node && node.isTreeNode) { + if (node === ancestor) { + _this.deselect(selectedNode); + return; + } + node = node.parent; + } + }); + }, + + + onAfterChangeTree: function(message) { + + + if (!message.newTree || !this.listenedTrees[message.newTree.widgetId]) { + // moving from our trfee to new one + + if (this.selectedNode && message.node.children) { + this.deselectIfAncestorMatch(message.node); + } + + this.processDescendants(message.node, this.listenNodeFilter, this.unlistenNode); + + } + if (!message.oldTree || !this.listenedTrees[message.oldTree.widgetId]) { + //dojo.debugShallow("listen! "+this.listenedTrees); + // moving from old tree to our tree + this.processDescendants(message.node, this.listenNodeFilter, this.listenNode); + } + + + }, + + + onAfterDetach: function(message) { + this.deselectIfAncestorMatch(message.child); + }, + + + select: function(node) { + + var index = dojo.lang.find(this.selectedNodes, node, true); + + if (index >=0 ) { + return; // already selected + } + + //dojo.debug("select "+node); + this.selectedNodes.push(node); + + dojo.event.topic.publish(this.eventNames.select, {node: node} ); + }, + + + deselect: function(node){ + var index = dojo.lang.find(this.selectedNodes, node, true); + if (index < 0) { + //dojo.debug("not selected"); + return; // not selected + } + + //dojo.debug("deselect "+node); + + + this.selectedNodes.splice(index, 1); + dojo.event.topic.publish(this.eventNames.deselect, {node: node} ); + //dojo.debug("deselect"); + + }, + + deselectAll: function() { + //dojo.debug("deselect all "+this.selectedNodes); + while (this.selectedNodes.length) { + this.deselect(this.selectedNodes[0]); + } + } + +}); + + + diff --git a/source/web/scripts/ajax/src/widget/TreeTimeoutIterator.js b/source/web/scripts/ajax/src/widget/TreeTimeoutIterator.js new file mode 100644 index 0000000000..9518af5add --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeTimeoutIterator.js @@ -0,0 +1,161 @@ + +dojo.provide("dojo.widget.TreeTimeoutIterator"); + + +dojo.require("dojo.event.*"); +dojo.require("dojo.json") +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.TreeCommon"); + + +/** + * Iterates the tree processNext + * filterFunc/filterObj called to determine if I need to pass the node + * + * callFunc/callObj called to process the node + * callObj.callFunc(elem, iterator) should call iterator.forward() to go on + * callFunc may change elem to another object (e.g create widget from it), + * keeping its parent and parent position are untouched * + * + * finishFunc/finishObj called at the end + * + * TODO: it should work only sync-way to solve CPU-hungry tasks + */ +dojo.widget.TreeTimeoutIterator = function(elem, callFunc, callObj) { + var _this = this; + + this.currentParent = elem; + + this.callFunc = callFunc; + this.callObj = callObj ? callObj: this; + this.stack = []; + +} + + +dojo.lang.extend(dojo.widget.TreeTimeoutIterator, { + + // public + maxStackDepth: Number.POSITIVE_INFINITY, + + stack: null, + currentParent: null, + + currentIndex: 0, + + filterFunc: function() { return true }, + + finishFunc: function() { return true }, + + + setFilter: function(func, obj) { + this.filterFunc = func; + this.filterObj = obj; + }, + + + setMaxLevel: function(level) { + if (level<1) { + dojo.raise("setMaxLevel: level="+level+" should be >=1"); + } + + this.maxStackDepth = level-2; + }, + + forward: function(timeout) { + var _this = this; + + if (this.timeout) { + // tid will be assigned at the end of outer func execution + var tid = setTimeout(function() {_this.processNext(); clearTimeout(tid); }, _this.timeout); + } else { + return this.processNext(); + } + }, + + start: function(processFirst) { + if (processFirst) { + return this.callFunc.call(this.callObj, this.currentParent, this); + } + + return this.processNext(); + }, + + /** + * @private + * find next node, move current parent to it if possible & process + */ + processNext: function() { + + //dojo.debug("processNext with currentParent "+this.currentParent+" index "+this.currentIndex); + var handler; + + var _this = this; + + var found; + + var next; + + + while (true) { + var children = this.currentParent.children; + + if (children && children.length) { + + // look for a node that can be the next target + do { + next = children[this.currentIndex]; + //dojo.debug("check "+next); + } while (this.currentIndex++ < children.length && !(found = this.filterFunc.call(this.filterObj,next))); + + + if (found) { + //dojo.debug("found "+next); + // move to next node as new parent if depth is fine + // I can't check current children to decide whether to move it or not, + // because expand may populate children + if (next.isFolder && this.stack.length <= this.maxStackDepth) { + this.moveParent(next,0); + } + //dojo.debug("Run callFunc on "+next); + return this.callFunc.call(this.callObj, next, this); + } + } + + if (this.stack.length) { + this.popParent(); + continue; + } + + break; + } + + /** + * couldn't find next node to process, finish here + */ + return this.finishFunc.call(this.finishObj); + + }, + + setFinish: function(func, obj) { + this.finishFunc = func; + this.finishObj = obj; + }, + + popParent: function() { + var p = this.stack.pop(); + //dojo.debug("Pop "+p[0]+":"+p[1]); + this.currentParent = p[0]; + this.currentIndex = p[1]; + }, + + moveParent: function(nextParent, nextIndex) { + //dojo.debug("Move from "+this.currentParent+":"+this.currentIndex+" to "+nextParent+":"+nextIndex); + this.stack.push([this.currentParent, this.currentIndex]); + this.currentParent = nextParent; + this.currentIndex = nextIndex; + } + + + +}); \ No newline at end of file diff --git a/source/web/scripts/ajax/src/widget/TreeV3.js b/source/web/scripts/ajax/src/widget/TreeV3.js new file mode 100644 index 0000000000..7527a70a50 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeV3.js @@ -0,0 +1,362 @@ +/** + * Tree model does all the drawing, visual node management etc. + * Throws events about clicks on it, so someone may catch them and process + * Tree knows nothing about DnD stuff, covered in TreeDragAndDrop and (if enabled) attached by controller +*/ + +/** + * TODO: use domNode.cloneNode instead of createElement for grid + * Should be faster (lyxsus) + */ +dojo.provide("dojo.widget.TreeV3"); + + +dojo.require("dojo.widget.TreeWithNode"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.event.*"); +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.TreeNodeV3"); + + + +// make it a tag +dojo.widget.tags.addParseTreeHandler("dojo:TreeV3"); + + +dojo.widget.TreeV3 = function() { + dojo.widget.HtmlWidget.call(this); + + this.eventNames = {}; + + this.DndAcceptTypes = []; + this.actionsDisabled = []; + + this.listeners = []; + + this.tree = this; + +} +dojo.inherits(dojo.widget.TreeV3, dojo.widget.HtmlWidget); + +dojo.lang.extend(dojo.widget.TreeV3, dojo.widget.TreeWithNode); + +dojo.lang.extend(dojo.widget.TreeV3, { + widgetType: "TreeV3", + + DndMode: "", + + /** + * factory to generate default widgets + */ + defaultChildWidget: null, + + defaultChildTitle: "New Node", // for editing + + + eagerWidgetInstantiation: false, + + eventNamesDefault: { + + // tree created.. Perform tree-wide actions if needed + afterTreeCreate: "afterTreeCreate", + beforeTreeDestroy: "beforeTreeDestroy", + beforeDestroy: "beforeDestroy", + afterChangeTree: "afterChangeTree", + + afterSetFolder: "afterSetFolder", + afterUnsetFolder: "afterUnsetFolder", + beforeMoveFrom: "beforeMoveFrom", + beforeMoveTo: "beforeMoveTo", + afterMoveFrom: "afterMoveFrom", + afterMoveTo: "afterMoveTo", + afterAddChild: "afterAddChild", + afterDetach: "afterDetach", + afterExpand: "afterExpand", + afterSetTitle: "afterSetTitle", + afterCollapse: "afterCollapse" + }, + + classPrefix: "Tree", + + /** + * is it possible to add a new child to leaf ? + */ + allowAddChildToLeaf: true, + + /** + * when last children is removed from node should it stop being a "folder" ? + */ + unsetFolderOnEmpty: true, + + + DndModes: { + BETWEEN: 1, + ONTO: 2 + }, + + DndAcceptTypes: "", + + // will have cssRoot before it + templateCssPath: dojo.uri.dojoUri("src/widget/templates/TreeV3.css"), + + templateString: '
\n
', + + isExpanded: true, // consider this "root node" to be always expanded + + isTree: true, + + objectId: "", + + createNode: function(data) { + + data.tree = this.widgetId; + + if (data.widgetName) { + // TODO: check if such widget has createSimple + return dojo.widget.createWidget(data.widgetName, data); + } else if (this.defaultChildWidget.prototype.createSimple) { + return this.defaultChildWidget.prototype.createSimple(data); + } else { + var ns = this.defaultChildWidget.prototype.namespace; + var wt = this.defaultChildWidget.prototype.widgetType; + + return dojo.widget.createWidget(ns + ":" + wt, data); + } + + }, + + + // expandNode has +- CSS background. Not img.src for performance, background src string resides in single place. + // selection in KHTML/Mozilla disabled treewide, IE requires unselectable for every node + // you can add unselectable if you want both in postCreate of tree and in this template + + // create new template and put into prototype + makeNodeTemplate: function() { + + var domNode = document.createElement("div"); + dojo.html.setClass(domNode, this.classPrefix+"Node "+this.classPrefix+"ExpandLeaf "+this.classPrefix+"ChildrenNo"); + this.nodeTemplate = domNode; + + var expandNode = document.createElement("div"); + var clazz = this.classPrefix+"Expand"; + if (dojo.render.html.ie) { + clazz = clazz + ' ' + this.classPrefix+"IEExpand"; + } + dojo.html.setClass(expandNode, clazz); + + this.expandNodeTemplate = expandNode; + + // need inside
+ // div for multiline support, span for styling exactly the text, not whole line + var labelNode = document.createElement("span"); + dojo.html.setClass(labelNode, this.classPrefix+"Label"); + this.labelNodeTemplate = labelNode; + + var contentNode = document.createElement("div"); + var clazz = this.classPrefix+"Content"; + + /** + * IE does not support min-height properly so I have to rely + * on this hack + * FIXME: do it in CSS only, remove iconHeight from code + */ + if (dojo.render.html.ie) { + clazz = clazz + ' ' + this.classPrefix+"IEContent"; + } + + + dojo.html.setClass(contentNode, clazz); + + this.contentNodeTemplate = contentNode; + + domNode.appendChild(expandNode); + domNode.appendChild(contentNode); + contentNode.appendChild(labelNode); + + + }, + + makeContainerNodeTemplate: function() { + + var div = document.createElement('div'); + div.style.display = 'none'; + dojo.html.setClass(div, this.classPrefix+"Container"); + + this.containerNodeTemplate = div; + + }, + + + // + // these icons control the grid and expando buttons for the whole tree + // + + icons: { + expandLoading: dojo.uri.dojoUri("src/widget/templates/images/TreeV3/loading.gif") + }, + + + + actions: { + ADDCHILD: "ADDCHILD" + }, + + + getInfo: function() { + var info = { + widgetId: this.widgetId, + objectId: this.objectId + } + + return info; + }, + + adjustEventNames: function() { + + for(var name in this.eventNamesDefault) { + if (dojo.lang.isUndefined(this.eventNames[name])) { + this.eventNames[name] = this.widgetId+"/"+this.eventNamesDefault[name]; + } + } + }, + + + adjustDndMode: function() { + var _this = this; + + + var DndMode = 0; + dojo.lang.forEach(this.DndMode.split(';'), + function(elem) { + var mode = _this.DndModes[dojo.string.trim(elem).toUpperCase()]; + if (mode) DndMode = DndMode | mode; + } + ); + + + this.DndMode = DndMode; + + }, + + /** + * publish destruction event so that any listeners should stop listening + */ + destroy: function() { + dojo.event.topic.publish(this.tree.eventNames.beforeTreeDestroy, { source: this } ); + + return dojo.widget.HtmlWidget.prototype.destroy.apply(this, arguments); + }, + + initialize: function(args){ + + //dojo.debug(args.defaultChildWidget ? true : false) + + if (!args.defaultChildWidget) { + this.defaultChildWidget = dojo.widget.TreeNodeV3; + } else { + this.defaultChildWidget = dojo.lang.getObjPathValue(args.defaultChildWidget); + } + + this.adjustEventNames(); + this.adjustDndMode(); + + this.makeNodeTemplate(); + this.makeContainerNodeTemplate(); + + //this.initializeSelector(); + //this.initializeController(); + //this.initializeMenu(); + + this.containerNode = this.domNode; + + dojo.html.setClass(this.domNode, this.classPrefix+"Container"); + + var _this = this; + + //dojo.debug(this.listeners[1]); + + + dojo.lang.forEach(this.listeners, + function(elem) { + var t = dojo.widget.manager.getWidgetById(elem); + if (! (t instanceof dojo.widget.Widget)) { + dojo.raise("No listener found by widgetId for "+elem); + } + //dojo.debug(t) + t.listenTree(_this) + + } + ); + + + + }, + + + postCreate: function() { + dojo.event.topic.publish(this.eventNames.afterTreeCreate, { source: this } ); + }, + + + /** + * Move child to newParent as last child + * redraw tree and update icons. + * + * Called by target, saves source in event. + * events are published for BOTH trees AFTER update. + */ + move: function(child, newParent, index) { + + if (!child.parent) { + dojo.raise(this.widgetType+": child can be moved only while it's attached"); + } + + var oldParent = child.parent; + var oldTree = child.tree; + var oldIndex = child.getParentIndex(); + var newTree = newParent.tree; + var newParent = newParent; + var newIndex = index; + + var message = { + oldParent: oldParent, oldTree: oldTree, oldIndex: oldIndex, + newParent: newParent, newTree: newTree, newIndex: newIndex, + child: child + }; + + dojo.event.topic.publish(oldTree.eventNames.beforeMoveFrom, message); + dojo.event.topic.publish(newTree.eventNames.beforeMoveTo, message); + + this.doMove.apply(this, arguments); + + + /* publish events here about structural changes for both source and target trees */ + dojo.event.topic.publish(oldTree.eventNames.afterMoveFrom, message); + dojo.event.topic.publish(newTree.eventNames.afterMoveTo, message); + + }, + + + /* do actual parent change here. Write remove child first */ + doMove: function(child, newParent, index) { + //dojo.debug("MOVE "+child+" to "+newParent+" at "+index); + + //var parent = child.parent; + child.doDetach(); + + //dojo.debug("addChild "+child+" to "+newParent+" at "+index); + + newParent.doAddChild(child, index); + }, + + toString: function() { + return "["+this.widgetType+" ID:"+this.widgetId +"]" + } + + + +}); + + + diff --git a/source/web/scripts/ajax/src/widget/TreeWithNode.js b/source/web/scripts/ajax/src/widget/TreeWithNode.js new file mode 100755 index 0000000000..336a880f92 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/TreeWithNode.js @@ -0,0 +1,257 @@ + +dojo.provide("dojo.widget.TreeWithNode"); + + +dojo.widget.TreeWithNode = { + + /* + * dynamic loading-related stuff. + * When an empty folder node appears, it is "UNCHECKED" first, + * then after Rpc call it becomes LOADING and, finally LOADED + * + * tree may be dynamically loaded also + */ + loadStates: { + UNCHECKED: "UNCHECKED", + LOADING: "LOADING", + LOADED: "LOADED" + }, + + state: "UNCHECKED", // after creation will change to loadStates: "loaded/loading/unchecked" + + + /* + * Basic actions one can perform on nodes and, some(addchild) on trees + */ + actions: { + MOVE: "MOVE", + DETACH: "DETACH", + EDIT: "EDIT", + ADDCHILD: "ADDCHILD" + }, + + + // I need this to parse children + isContainer: true, + + lockLevel: 0, // lock ++ unlock --, so nested locking works fine + + lock: function() { + this.lockLevel++; + }, + unlock: function() { + if (!this.lockLevel) { + //dojo.debug((new Error()).stack); + dojo.raise(this.widgetType+" unlock: not locked"); + } + this.lockLevel--; + }, + + + expandLevel: "", // expand to level automatically + loadLevel: "", // load to level automatically + + hasLock: function() { + return this.lockLevel>0; + }, + + isLocked: function() { + var node = this; + while (true) { + if (node.lockLevel) { + return true; + } + if (!node.parent || node.isTree) { + break; + } + + node = node.parent; + + } + + return false; + }, + + + flushLock: function() { + this.lockLevel = 0; + //this.unMarkLoading(); + }, + + + actionIsDisabled: function(action) { + var disabled = false; + + if (dojo.lang.inArray(this.actionsDisabled, action)) { + disabled = true; + } + + + //dojo.debug("Check "+this+" "+disabled) + + + if (this.isTreeNode) { + if (!this.tree.allowAddChildToLeaf && action == this.actions.ADDCHILD && !this.isFolder) { + disabled = true; + } + } + return disabled; + }, + + actionIsDisabledNow: function(action) { + return this.actionIsDisabled(action) || this.isLocked(); + }, + + + /** + * childrenArray is array of Widgets or array of Objects + * widgets may be both attached and detached + * + * Use Cases + * 1) lots of widgets are packed and passed in. + * - widgets are created + * - widgets have no parent (detached or not attached yet) + * + * 2) array of widgets and data objects passed in with flag makeWidgetsFromChildren + * - some widgets are not created + * - all objects have no parent + * + * 3) expand is called with makeWidgetsFromChildren=true + * - some objects need to be turned into widgets + * - some widgets have parent (e.g markup), some widgets and objects do not + * + * Will folderize a node as side-effect. + */ + setChildren: function(childrenArray) { + //dojo.profile.start("setChildren "+this); + //dojo.debug("setChildren in "+this); + + + if (this.isTreeNode && !this.isFolder) { + //dojo.debug("folder parent "+parent+ " isfolder "+parent.isFolder); + this.setFolder(); + } else if (this.isTreeNode) { + this.state = this.loadStates.LOADED; + } + + var hadChildren = this.children.length > 0; + + if (childrenArray) { + this.children = childrenArray; + } + + + + var hasChildren = this.children.length > 0; + if (this.isTreeNode && hasChildren != hadChildren) { + // call only when hasChildren state changes + this.viewSetHasChildren(); + } + + + + for(var i=0; i")); + + if (!child.isTreeNode){ + dojo.raise("You can only add TreeNode widgets to a "+this.widgetType+" widget!"); + return; + } + + this.children.splice(index, 0, child); + child.parent = this; + + child.addedTo(this, index, dontPublishEvent); + + // taken from DomWidget.registerChild + // delete from widget list that are notified on resize etc (no parent) + delete dojo.widget.manager.topWidgets[child.widgetId]; + + } + + + + +} diff --git a/source/web/scripts/ajax/src/widget/Widget.js b/source/web/scripts/ajax/src/widget/Widget.js new file mode 100644 index 0000000000..a9582f62dd --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Widget.js @@ -0,0 +1,624 @@ +dojo.provide("dojo.widget.Widget"); +dojo.provide("dojo.widget.tags"); + +dojo.require("dojo.lang.func"); +dojo.require("dojo.lang.array"); +dojo.require("dojo.lang.extras"); +dojo.require("dojo.lang.declare"); +dojo.require("dojo.widget.Manager"); +dojo.require("dojo.event.*"); + +dojo.declare("dojo.widget.Widget", null, + function() { + //dojo.debug("NEW "+this.widgetType); + // these properties aren't primitives and need to be created on a per-item + // basis. + this.children = []; + // this.selection = new dojo.widget.Selection(); + // FIXME: need to replace this with context menu stuff + this.extraArgs = {}; + }, +{ + // FIXME: need to be able to disambiguate what our rendering context is + // here! + // + // needs to be a string with the end classname. Every subclass MUST + // over-ride. + // + // base widget properties + parent: null, + // obviously, top-level and modal widgets should set these appropriately + isTopLevel: false, + isModal: false, + + isEnabled: true, + isHidden: false, + isContainer: false, // can we contain other widgets? + widgetId: "", + widgetType: "Widget", // used for building generic widgets + + namespace: "dojo", //defaults to 'dojo' + + toString: function() { + return '[Widget ' + this.widgetType + ', ' + (this.widgetId || 'NO ID') + ']'; // string + }, + + repr: function(){ + return this.toString(); + }, + + enable: function(){ + // should be over-ridden + this.isEnabled = true; + }, + + disable: function(){ + // should be over-ridden + this.isEnabled = false; + }, + + hide: function(){ + // should be over-ridden + this.isHidden = true; + }, + + show: function(){ + // should be over-ridden + this.isHidden = false; + }, + + onResized: function(){ + // Clients should override this function to do special processing, + // then call this.notifyChildrenOfResize() to notify children of resize + this.notifyChildrenOfResize(); + }, + + notifyChildrenOfResize: function(){ + for(var i=0; i mixInProperties"); + //dojo.profile.start(this.widgetType + " mixInProperties"); + this.mixInProperties(args, fragment, parentComp); + //dojo.profile.end(this.widgetType + " mixInProperties"); + // dojo.debug(this.widgetType, "-> postMixInProperties"); + //dojo.profile.start(this.widgetType + " postMixInProperties"); + this.postMixInProperties(args, fragment, parentComp); + //dojo.profile.end(this.widgetType + " postMixInProperties"); + // dojo.debug(this.widgetType, "-> dojo.widget.manager.add"); + dojo.widget.manager.add(this); + // dojo.debug(this.widgetType, "-> buildRendering"); + //dojo.profile.start(this.widgetType + " buildRendering"); + this.buildRendering(args, fragment, parentComp); + //dojo.profile.end(this.widgetType + " buildRendering"); + // dojo.debug(this.widgetType, "-> initialize"); + //dojo.profile.start(this.widgetType + " initialize"); + this.initialize(args, fragment, parentComp); + //dojo.profile.end(this.widgetType + " initialize"); + // dojo.debug(this.widgetType, "-> postInitialize"); + // postinitialize includes subcomponent creation + // profile is put directly to function + this.postInitialize(args, fragment, parentComp); + // dojo.debug(this.widgetType, "-> postCreate"); + //dojo.profile.start(this.widgetType + " postCreate"); + this.postCreate(args, fragment, parentComp); + //dojo.profile.end(this.widgetType + " postCreate"); + // dojo.debug(this.widgetType, "done!"); + + //dojo.profile.end(this.widgetType + " create"); + + return this; + }, + + // Destroy this widget and it's descendants + destroy: function(finalize){ + // FIXME: this is woefully incomplete + this.destroyChildren(); + this.uninitialize(); + this.destroyRendering(finalize); + dojo.widget.manager.removeById(this.widgetId); + }, + + // Destroy the children of this widget, and their descendents + destroyChildren: function(){ + while(this.children.length > 0){ + var tc = this.children[0]; + this.removeChild(tc); + tc.destroy(); + } + }, + + getChildrenOfType: function(type, recurse){ + var ret = []; + var isFunc = dojo.lang.isFunction(type); + if(!isFunc){ + type = type.toLowerCase(); + } + for(var x=0; xsi)){ + this[x][pairs[y].substr(0, si).replace(/^\s+|\s+$/g, "")] = pairs[y].substr(si+1); + } + } + } + }else{ + // the default is straight-up string assignment. When would + // we ever hit this? + this[x] = args[x]; + } + } + }else{ + // collect any extra 'non mixed in' args + this.extraArgs[x.toLowerCase()] = args[x]; + } + } + // dojo.profile.end("mixInProperties"); + }, + + postMixInProperties: function(){ + }, + + initialize: function(args, frag){ + // dojo.unimplemented("dojo.widget.Widget.initialize"); + return false; + }, + + postInitialize: function(args, frag){ + return false; + }, + + postCreate: function(args, frag){ + return false; + }, + + uninitialize: function(){ + // dojo.unimplemented("dojo.widget.Widget.uninitialize"); + return false; + }, + + buildRendering: function(){ + // SUBCLASSES MUST IMPLEMENT + dojo.unimplemented("dojo.widget.Widget.buildRendering, on "+this.toString()+", "); + return false; + }, + + destroyRendering: function(){ + // SUBCLASSES MUST IMPLEMENT + dojo.unimplemented("dojo.widget.Widget.destroyRendering"); + return false; + }, + + cleanUp: function(){ + // SUBCLASSES MUST IMPLEMENT + dojo.unimplemented("dojo.widget.Widget.cleanUp"); + return false; + }, + + addedTo: function(parent){ + // this is just a signal that can be caught + }, + + addChild: function(child){ + // SUBCLASSES MUST IMPLEMENT + dojo.unimplemented("dojo.widget.Widget.addChild"); + return false; + }, + + // Detach the given child widget from me, but don't destroy it + removeChild: function(widget){ + for(var x=0; x +dojo.widget.tags["dojo:connect"] = function(fragment, widgetParser, parentComp){ + var properties = widgetParser.parseProperties(fragment["dojo:connect"]); +} + +// FIXME: if we know the insertion point (to a reasonable location), why then do we: +// - create a template node +// - clone the template node +// - render the clone and set properties +// - remove the clone from the render tree +// - place the clone +// this is quite dumb +dojo.widget.buildWidgetFromParseTree = function(type, frag, + parser, parentComp, + insertionIndex, localProps){ + + //dojo.profile.start("buildWidgetFromParseTree"); + + var stype = type.split(":"); + stype = (stype.length == 2) ? stype[1] : type; + // FIXME: we don't seem to be doing anything with this! + // var propertySets = parser.getPropertySets(frag); + var localProperties = localProps || parser.parseProperties(frag[frag.namespace+":"+stype]); + // var tic = new Date(); + var twidget = dojo.widget.manager.getImplementation(stype,null,null,frag.namespace); + if(!twidget){ + throw new Error("cannot find \"" + stype + "\" widget"); + }else if (!twidget.create){ + throw new Error("\"" + stype + "\" widget object does not appear to implement *Widget"); + } + localProperties["dojoinsertionindex"] = insertionIndex; + // FIXME: we lose no less than 5ms in construction! + + + var ret = twidget.create(localProperties, frag, parentComp, frag.namespace); + // dojo.debug(new Date() - tic); + + //dojo.profile.end("buildWidgetFromParseTree"); + + return ret; +} + +/* + * Create a widget constructor function (aka widgetClass) + */ +dojo.widget.defineWidget = function(widgetClass /*string*/, renderer /*string*/, superclasses /*function||array*/, init /*function*/, props /*object*/){ + // This meta-function does parameter juggling for backward compat and overloading + // if 4th argument is a string, we are using the old syntax + // old sig: widgetClass, superclasses, props (object), renderer (string), init (function) + if(dojo.lang.isString(arguments[3])){ + dojo.widget._defineWidget(arguments[0], arguments[3], arguments[1], arguments[4], arguments[2]); + }else{ + // widgetClass + var args = [ arguments[0] ], p = 3; + if(dojo.lang.isString(arguments[1])){ + // renderer, superclass + args.push(arguments[1], arguments[2]); + }else{ + // superclass + args.push('', arguments[1]); + p = 2; + } + if(dojo.lang.isFunction(arguments[p])){ + // init (function), props (object) + args.push(arguments[p], arguments[p+1]); + }else{ + // props (object) + args.push(null, arguments[p]); + } + dojo.widget._defineWidget.apply(this, args); + } +} + +dojo.widget.defineWidget.renderers = "html|svg|vml"; + +dojo.widget._defineWidget = function(widgetClass /*string*/, renderer /*string*/, superclasses /*function||array*/, init /*function*/, props /*object*/){ + // FIXME: uncomment next line to test parameter juggling ... remove when confidence improves + //dojo.debug('(c:)' + widgetClass + '\n\n(r:)' + renderer + '\n\n(i:)' + init + '\n\n(p:)' + props); + // widgetClass takes the form foo.bar.baz<.renderer>.WidgetName (e.g. foo.bar.baz.WidgetName or foo.bar.baz.html.WidgetName) + var namespace = widgetClass.split("."); + var type = namespace.pop(); // type <= WidgetName, namespace <= foo.bar.baz<.renderer> + var regx = "\\.(" + (renderer ? renderer + '|' : '') + dojo.widget.defineWidget.renderers + ")\\."; + var r = widgetClass.search(new RegExp(regx)); + namespace = (r < 0 ? namespace.join(".") : widgetClass.substr(0, r)); + + dojo.widget.manager.registerWidgetPackage(namespace); + + var pos = namespace.indexOf("."); + var nsName = (pos > -1) ? namespace.substring(0,pos) : namespace; + dojo.widget.tags.addParseTreeHandler(nsName+":"+type.toLowerCase()); + if(nsName != "dojo"){ + // 2006/06/26 Providing a duplicate dojo handler is a deprecation + // and should eventually be removed from code + dojo.widget.tags.addParseTreeHandler("dojo:"+type.toLowerCase()); + } + + props=(props)||{}; + props.widgetType = type; + if((!init)&&(props["classConstructor"])){ + init = props.classConstructor; + delete props.classConstructor; + } + dojo.declare(widgetClass, superclasses, init, props); +} diff --git a/source/web/scripts/ajax/src/widget/Wizard.js b/source/web/scripts/ajax/src/widget/Wizard.js new file mode 100644 index 0000000000..067015ec9a --- /dev/null +++ b/source/web/scripts/ajax/src/widget/Wizard.js @@ -0,0 +1,187 @@ +dojo.provide("dojo.widget.Wizard"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.LayoutContainer"); +dojo.require("dojo.widget.ContentPane"); +dojo.require("dojo.event.*"); +dojo.require("dojo.html.style"); + +////////////////////////////////////////// +// WizardContainer -- a set of panels +////////////////////////////////////////// +dojo.widget.defineWidget( + "dojo.widget.WizardContainer", + dojo.widget.LayoutContainer, +{ + labelPosition: "top", + + templatePath: dojo.uri.dojoUri("src/widget/templates/Wizard.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/templates/Wizard.css"), + + selected: null, // currently selected panel + wizardNode: null, // the outer wizard node + wizardPanelContainerNode: null, // the container for the panels + wizardControlContainerNode: null, // the container for the wizard controls + previousButton: null, // the previous button + nextButton: null, // the next button + cancelButton: null, // the cancel button + doneButton: null, // the done button + nextButtonLabel: "next", + previousButtonLabel: "previous", + cancelButtonLabel: "cancel", + doneButtonLabel: "done", + cancelFunction : "", + + hideDisabledButtons: false, + + fillInTemplate: function(args, frag){ + dojo.event.connect(this.nextButton, "onclick", this, "nextPanel"); + dojo.event.connect(this.previousButton, "onclick", this, "previousPanel"); + if (this.cancelFunction){ + dojo.event.connect(this.cancelButton, "onclick", this.cancelFunction); + }else{ + this.cancelButton.style.display = "none"; + } + dojo.event.connect(this.doneButton, "onclick", this, "done"); + this.nextButton.value = this.nextButtonLabel; + this.previousButton.value = this.previousButtonLabel; + this.cancelButton.value = this.cancelButtonLabel; + this.doneButton.value = this.doneButtonLabel; + }, + + checkButtons: function(){ + var lastStep = !this.hasNextPanel(); + this.nextButton.disabled = lastStep; + this.setButtonClass(this.nextButton); + if(this.selected.doneFunction){ + this.doneButton.style.display = ""; + // hide the next button if this is the last one and we have a done function + if(lastStep){ + this.nextButton.style.display = "none"; + } + }else{ + this.doneButton.style.display = "none"; + } + this.previousButton.disabled = ((!this.hasPreviousPanel()) || (!this.selected.canGoBack)); + this.setButtonClass(this.previousButton); + }, + + setButtonClass: function(button){ + if(!this.hideDisabledButtons){ + button.style.display = ""; + dojo.html.setClass(button, button.disabled ? "WizardButtonDisabled" : "WizardButton"); + }else{ + button.style.display = button.disabled ? "none" : ""; + } + }, + + registerChild: function(panel, insertionIndex){ + dojo.widget.WizardContainer.superclass.registerChild.call(this, panel, insertionIndex); + this.wizardPanelContainerNode.appendChild(panel.domNode); + panel.hide(); + + if(!this.selected){ + this.onSelected(panel); + } + this.checkButtons(); + }, + + onSelected: function(panel){ + // Deselect old panel and select new one + if(this.selected ){ + if (this.selected.checkPass()) { + this.selected.hide(); + } else { + return; + } + } + panel.show(); + this.selected = panel; + }, + + getPanels: function() { + return this.getChildrenOfType("WizardPane", false); + }, + + selectedIndex: function() { + if (this.selected) { + return dojo.lang.indexOf(this.getPanels(), this.selected); + } + return -1; + }, + + nextPanel: function() { + var selectedIndex = this.selectedIndex(); + if ( selectedIndex > -1 ) { + var childPanels = this.getPanels(); + if (childPanels[selectedIndex + 1]) { + this.onSelected(childPanels[selectedIndex + 1]); + } + } + this.checkButtons(); + }, + + previousPanel: function() { + var selectedIndex = this.selectedIndex(); + if ( selectedIndex > -1 ) { + var childPanels = this.getPanels(); + if (childPanels[selectedIndex - 1]) { + this.onSelected(childPanels[selectedIndex - 1]); + } + } + this.checkButtons(); + }, + + hasNextPanel: function() { + var selectedIndex = this.selectedIndex(); + return (selectedIndex < (this.getPanels().length - 1)); + }, + + hasPreviousPanel: function() { + var selectedIndex = this.selectedIndex(); + return (selectedIndex > 0); + }, + + done: function() { + this.selected.done(); + } +}); + +////////////////////////////////////////// +// WizardPane -- a panel in a wizard +////////////////////////////////////////// +dojo.widget.defineWidget( + "dojo.widget.WizardPane", + dojo.widget.ContentPane, +{ + canGoBack: true, + + passFunction: "", + doneFunction: "", + + fillInTemplate: function(args, frag) { + if (this.passFunction) { + this.passFunction = dj_global[this.passFunction]; + } + if (this.doneFunction) { + this.doneFunction = dj_global[this.doneFunction]; + } + }, + + checkPass: function() { + if (this.passFunction && dojo.lang.isFunction(this.passFunction)) { + var failMessage = this.passFunction(); + if (failMessage) { + alert(failMessage); + return false; + } + } + return true; + }, + + done: function() { + if (this.doneFunction && dojo.lang.isFunction(this.doneFunction)) { + this.doneFunction(); + } + } +}); diff --git a/source/web/scripts/ajax/src/widget/YahooMap.js b/source/web/scripts/ajax/src/widget/YahooMap.js new file mode 100644 index 0000000000..cbee7762ad --- /dev/null +++ b/source/web/scripts/ajax/src/widget/YahooMap.js @@ -0,0 +1,159 @@ +dojo.provide("dojo.widget.YahooMap"); +dojo.require("dojo.event.*"); +dojo.require("dojo.math"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); + +(function(){ + var yappid = djConfig["yAppId"]||djConfig["yahooAppId"]||"dojotoolkit"; + if(!dojo.hostenv.post_load_){ + if(yappid == "dojotoolkit"){ + dojo.debug("please provide a unique Yahoo App ID in djConfig.yahooAppId when using the map widget"); + } + var tag = ""; + if(!dj_global["YMap"]){ + document.write(tag); + } + }else{ + dojo.debug("cannot initialize map system after the page has been loaded! Please either manually include the script block provided by Yahoo in your page or require() the YahooMap widget before onload has fired"); + } +})(); + +dojo.widget.defineWidget( + "dojo.widget.YahooMap", + dojo.widget.HtmlWidget, + function(){ + // parameters + this.map=null; + this.datasrc=""; + this.data=[]; + this.width=0; + this.height=0; + this.controls=["zoomlong","maptype","pan"]; + }, +{ + isContainer: false, + templatePath:null, + templateCssPath:null, + + findCenter:function(aPts){ + var start=new YGeoPoint(37,-90); + if(aPts.length==0) return start; + var minLat,maxLat, minLon, maxLon, cLat, cLon; + minLat=maxLat=aPts[0].Lat; + minLon=maxLon=aPts[0].Lon; + for(var i=0; i"+d[i].description+"
"); + } + this.map.addOverlay(m); + } + var c=this.findCenter(pts); + var z=this.map.getZoomLevel(pts); + this.map.drawZoomAndCenter(c,z); + }, + + initialize:function(args, frag){ + if(!YMap || !YGeoPoint){ + dojo.raise("dojo.widget.YahooMap: The Yahoo Map script must be included in order to use this widget."); + } + if(this.datasrc){ + this.parse(dojo.byId(this.datasrc)); + } + else if(this.domNode.getElementsByTagName("table")[0]){ + this.parse(this.domNode.getElementsByTagName("table")[0]); + } + }, + postCreate:function(){ + // clean the domNode before creating the map. + while(this.domNode.childNodes.length>0){ + this.domNode.removeChild(this.domNode.childNodes[0]); + } + + if(this.width>0&&this.height>0){ + this.map=new YMap(this.domNode, YAHOO_MAP_REG, new YSize(this.width, this.height)); + }else{ + this.map=new YMap(this.domNode); + } + this.setControls(); + this.render(); + } +}); diff --git a/source/web/scripts/ajax/src/widget/__package__.js b/source/web/scripts/ajax/src/widget/__package__.js new file mode 100644 index 0000000000..fb551fa844 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/__package__.js @@ -0,0 +1,14 @@ +dojo.kwCompoundRequire({ + common: ["dojo.xml.Parse", + "dojo.widget.Widget", + "dojo.widget.Parse", + "dojo.widget.Manager", + "dojo.namespaces.dojo"], + browser: ["dojo.widget.DomWidget", + "dojo.widget.HtmlWidget"], + dashboard: ["dojo.widget.DomWidget", + "dojo.widget.HtmlWidget"], + svg: ["dojo.widget.SvgWidget"], + rhino: ["dojo.widget.SwtWidget"] +}); +dojo.provide("dojo.widget.*"); diff --git a/source/web/scripts/ajax/src/widget/demoEngine/DemoContainer.js b/source/web/scripts/ajax/src/widget/demoEngine/DemoContainer.js new file mode 100644 index 0000000000..1543aa94bd --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/DemoContainer.js @@ -0,0 +1,97 @@ +dojo.provide("dojo.widget.demoEngine.DemoContainer"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.demoEngine.DemoPane"); +dojo.require("dojo.widget.demoEngine.SourcePane"); +dojo.require("dojo.widget.TabContainer"); + +dojo.widget.defineWidget("my.widget.demoEngine.DemoContainer", + dojo.widget.HtmlWidget, + { + templatePath: dojo.uri.dojoUri("src/widget/demoEngine/templates/DemoContainer.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/demoEngine/templates/DemoContainer.css"), + postCreate: function() { + dojo.html.addClass(this.domNode,this.domNodeClass); + dojo.html.addClass(this.tabNode, this.tabClass); + dojo.html.addClass(this.returnImageNode, this.returnClass); + this.returnImageNode.src=this.returnImage; + + this.tabContainer = dojo.widget.createWidget("TabContainer",{},this.tabNode); + + this.demoTab = dojo.widget.createWidget("DemoPane",{}); + this.tabContainer.addChild(this.demoTab); + + this.sourceTab= dojo.widget.createWidget("SourcePane",{}); + this.tabContainer.addChild(this.sourceTab); + + dojo.html.setOpacity(this.domNode,0); + dojo.html.hide(this.domNode); + }, + + loadDemo: function(url) { + this.demoTab.setHref(url); + this.sourceTab.setHref(url); + this.showDemo(); + }, + + setName: function(name) { + dojo.html.removeChildren(this.demoNameNode); + this.demoNameNode.appendChild(document.createTextNode(name)); + }, + + setSummary: function(summary) { + dojo.html.removeChildren(this.summaryNode); + this.summaryNode.appendChild(document.createTextNode(summary)); + }, + + showSource: function() { + dojo.html.removeClass(this.demoButtonNode,this.selectedButtonClass); + dojo.html.addClass(this.sourceButtonNode,this.selectedButtonClass); + this.tabContainer.selectTab(this.sourceTab); + }, + + showDemo: function() { + dojo.html.removeClass(this.sourceButtonNode,this.selectedButtonClass); + dojo.html.addClass(this.demoButtonNode,this.selectedButtonClass); + this.tabContainer.selectTab(this.demoTab); + }, + + returnToDemos: function() { + dojo.debug("Return To Demos"); + }, + + show: function() { + dojo.html.setOpacity(this.domNode,1); + dojo.html.show(this.domNode); + this.tabContainer.checkSize(); + } + }, + "", + function() { + dojo.debug("DemoPane Init"); + this.domNodeClass="demoContainer"; + + this.tabContainer=""; + this.sourceTab=""; + this.demoTab=""; + + this.headerNode=""; + this.returnNode=""; + + this.returnImageNode=""; + this.returnImage="images/dojoDemos.gif"; + this.returnClass="return"; + + this.summaryNode=""; + this.demoNameNode=""; + this.tabControlNode=""; + + this.tabNode=""; + this.tabClass = "demoContainerTabs"; + + this.sourceButtonNode=""; + this.demoButtonNode=""; + + this.selectedButtonClass="selected"; + } +); diff --git a/source/web/scripts/ajax/src/widget/demoEngine/DemoItem.js b/source/web/scripts/ajax/src/widget/demoEngine/DemoItem.js new file mode 100644 index 0000000000..338a5a320f --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/DemoItem.js @@ -0,0 +1,61 @@ +dojo.provide("dojo.widget.demoEngine.DemoItem"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); + +dojo.widget.defineWidget("my.widget.demoEngine.DemoItem", + dojo.widget.HtmlWidget, + { + templatePath: dojo.uri.dojoUri("src/widget/demoEngine/templates/DemoItem.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/demoEngine/templates/DemoItem.css"), + postCreate: function() { + dojo.html.addClass(this.domNode,this.domNodeClass); + dojo.html.addClass(this.summaryBoxNode, this.summaryBoxClass); + dojo.html.addClass(this.screenshotTdNode, this.screenshotTdClass); + dojo.html.addClass(this.summaryContainerNode, this.summaryContainerClass); + dojo.html.addClass(this.summaryNode, this.summaryClass); + dojo.html.addClass(this.viewDemoLinkNode, this.viewDemoLinkClass); + + this.nameNode.appendChild(document.createTextNode(this.name)); + this.descriptionNode.appendChild(document.createTextNode(this.description)); + this.thumbnailImageNode.src = this.thumbnail; + this.thumbnailImageNode.name=this.name; + this.viewDemoImageNode.src = this.viewDemoImage; + this.viewDemoImageNode.name=this.name; + }, + onSelectDemo: function() { + //Attach to this to do something when a demo is selected + } + }, + "", + function() { + this.demo = ""; + + this.domNodeClass="demoItemWrapper"; + + this.summaryBoxNode=""; + this.summaryBoxClass="demoItemSummaryBox"; + + this.nameNode=""; + this.thumbnailImageNode=""; + this.viewDemoImageNode=""; + + this.screenshotTdNode=""; + this.screenshotTdClass="demoItemScreenshot"; + + this.summaryContainerNode=""; + this.summaryContainerClass="demoItemSummaryContainer"; + + this.summaryNode=""; + this.summaryClass="demoItemSummary"; + + this.viewDemoLinkNode=""; + this.viewDemoLinkClass="demoItemView"; + + this.descriptionNode=""; + + this.name="Some Demo"; + this.description="This is the description of this demo."; + this.thumbnail="images/test_thumb.gif"; + this.viewDemoImage="images/viewDemo.png"; + } +); diff --git a/source/web/scripts/ajax/src/widget/demoEngine/DemoNavigator.js b/source/web/scripts/ajax/src/widget/demoEngine/DemoNavigator.js new file mode 100644 index 0000000000..0543495134 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/DemoNavigator.js @@ -0,0 +1,178 @@ +dojo.provide("dojo.widget.demoEngine.DemoNavigator"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.Button"); +dojo.require("dojo.widget.demoEngine.DemoItem"); +dojo.require("dojo.io.*"); +dojo.require("dojo.lfx.*"); +dojo.require("dojo.lang.Common"); + +dojo.widget.defineWidget("my.widget.demoEngine.DemoNavigator", + dojo.widget.HtmlWidget, + { + templatePath: dojo.uri.dojoUri("src/widget/demoEngine/templates/DemoNavigator.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/demoEngine/templates/DemoNavigator.css"), + postCreate: function() { + dojo.html.addClass(this.domNode,this.domNodeClass); + dojo.html.addClass(this.demoListWrapperNode,this.demoListWrapperClass); + dojo.html.addClass(this.demoListContainerNode,this.demoListContainerClass); + + if (dojo.render.html.ie) { + dojo.debug("render ie"); + dojo.html.hide(this.demoListWrapperNode); + } else { + dojo.debug("render non-ie"); + dojo.lfx.html.fadeHide(this.demoListWrapperNode, 0).play(); + } + + this.getRegistry(this.demoRegistryUrl); + + this.demoContainer = dojo.widget.createWidget("DemoContainer",{returnImage: this.returnImage},this.demoNode); + dojo.event.connect(this.demoContainer,"returnToDemos", this, "returnToDemos"); + this.demoContainer.hide(); + }, + + returnToDemos: function() { + this.demoContainer.hide(); + if (dojo.render.html.ie) { + dojo.debug("render ie"); + dojo.html.show(this.navigationContainer) ; + } else { + dojo.debug("render non-ie"); + dojo.lfx.html.fadeShow(this.navigationContainer,250).play(); + } + + //if (dojo.render.html.ie) { + // dojo.html.setOpacity(this.navigationContainer); + //} + + dojo.lang.forEach(this.categoriesChildren, dojo.lang.hitch(this, function(child){ + child.checkSize(); + })); + + dojo.lang.forEach(this.demoListChildren, dojo.lang.hitch(this, function(child){ + child.checkSize(); + })); + }, + + show: function() { + //dojo.widget.demoEngine.DemoNavigator.superclass.show.call(this); + dojo.html.show(this.domNode); + dojo.html.setOpacity(this.domNode,1); + //dojo.html.setOpacity(this.navigationContainer); + //dojo.html.show(this.navigationContainer); + dojo.html.setOpacity(this.navigationContainer,1); + + dojo.lang.forEach(this.categoriesChildren, dojo.lang.hitch(this, function(child){ + child.checkSize(); + })); + + dojo.lang.forEach(this.demoListChildren, dojo.lang.hitch(this, function(child){ + child.checkSize(); + })); + }, + getRegistry: function(url) { + dojo.io.bind({ + url: url, + load: dojo.lang.hitch(this,this.processRegistry), + mimetype: "text/json" + }); + }, + + processRegistry: function(type,registry,e) { + dojo.debug("Processing Registry"); + this.registry = registry; + dojo.lang.forEach(this.registry.navigation, dojo.lang.hitch(this,this.addCategory)); + }, + + addCategory: function(category) { + var newCat = dojo.widget.createWidget("Button",{caption: category.name}); + + if(!dojo.lang.isObject(this.registry.categories)) { + this.registry.categories=function(){}; + } + + this.registry.categories[category.name] = category; + this.categoriesChildren.push(newCat); + this.categoriesButtonsNode.appendChild(newCat.domNode); + newCat.domNode.categoryName = category.name; + dojo.event.connect(newCat,"onClick", this, "onSelectCategory"); + }, + + addDemo: function(demoName) { + var demo = this.registry.definitions[demoName]; + + if (dojo.render.html.ie) { + dojo.html.show(this.demoListWrapperNode) + } else { + dojo.lfx.html.fadeShow(this.demoListWrapperNode, 250).play(); + } + + var newDemo = dojo.widget.createWidget("DemoItem",{viewDemoImage: this.viewDemoImage, name: demoName, description: demo.description, thumbnail: demo.thumbnail}); + this.demoListChildren.push(newDemo); + this.demoListContainerNode.appendChild(newDemo.domNode); + dojo.event.connect(newDemo,"onSelectDemo",this,"onSelectDemo"); + }, + + onSelectCategory: function(e) { + catName = e.currentTarget.categoryName; + dojo.debug("Selected Category: " + catName); + //Remove current list of demos + dojo.lang.forEach(this.demoListChildren, function(child) { + child.destroy(); + }); + this.demoListChildren=[]; + + //add demos from this cat + dojo.lang.forEach(this.registry.categories[catName].demos, dojo.lang.hitch(this,function(demoName){ + this.addDemo(demoName); + })); + }, + + onSelectDemo: function(e) { + //Attach to this to do something when a demo is selected + dojo.debug("Demo Selected: " + e.target.name); + + if (dojo.render.html.ie) { + dojo.debug("render ie"); + dojo.html.hide(this.navigationContainer) ; + this.demoContainer.show(); + this.demoContainer.showDemo(); + } else { + dojo.debug("render non-ie"); + dojo.lfx.html.fadeHide(this.navigationContainer,250,null,dojo.lang.hitch(this, function() { + this.demoContainer.show(); + this.demoContainer.showDemo(); + })).play(); + } + + this.demoContainer.loadDemo(this.registry.definitions[e.target.name].url); + this.demoContainer.setName(e.target.name); + this.demoContainer.setSummary(this.registry.definitions[e.target.name].description); + } + + }, + "", + function() { + this.demoRegistryUrl="demoRegistry.json"; + this.registry=function(){}; + + this.categoriesNode=""; + this.categoriesButtonsNode=""; + this.navigationContainer=""; + + this.domNodeClass="demoNavigator"; + + this.demoNode=""; + this.demoContainer=""; + + this.demoListWrapperNode=""; + this.demoListWrapperClass="demoNavigatorListWrapper"; + this.demoListContainerClass="demoNavigatorListContainer"; + + this.returnImage="images/dojoDemos.gif"; + this.viewDemoImage="images/viewDemo.png"; + this.demoListChildren = []; + this.categoriesChildren = []; + } +); diff --git a/source/web/scripts/ajax/src/widget/demoEngine/DemoPane.js b/source/web/scripts/ajax/src/widget/demoEngine/DemoPane.js new file mode 100644 index 0000000000..6738d9d367 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/DemoPane.js @@ -0,0 +1,34 @@ +dojo.provide("dojo.widget.demoEngine.DemoPane"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); + +dojo.widget.defineWidget("my.widget.demoEngine.DemoPane", + dojo.widget.HtmlWidget, + { + templatePath: dojo.uri.dojoUri("src/widget/demoEngine/templates/DemoPane.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/demoEngine/templates/DemoPane.css"), + postCreate: function() { + dojo.html.addClass(this.domNode,this.domNodeClass); + dojo.debug("PostCreate"); + this._launchDemo(); + }, + + _launchDemo: function() { + dojo.debug("Launching Demo"); + dojo.debug(this.demoNode); + this.demoNode.src=this.href; + }, + + setHref: function(url) { + this.href = url; + this._launchDemo(); + } + }, + "", + function() { + dojo.debug("DemoPane Init"); + this.domNodeClass="demoPane"; + this.demoNode = ""; + this.href = ""; + } +); diff --git a/source/web/scripts/ajax/src/widget/demoEngine/SourcePane.js b/source/web/scripts/ajax/src/widget/demoEngine/SourcePane.js new file mode 100644 index 0000000000..16b5b68d48 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/SourcePane.js @@ -0,0 +1,42 @@ +dojo.provide("dojo.widget.demoEngine.SourcePane"); +dojo.require("dojo.widget.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.io.*"); + +dojo.widget.defineWidget("my.widget.demoEngine.SourcePane", + dojo.widget.HtmlWidget, + { + templatePath: dojo.uri.dojoUri("src/widget/demoEngine/templates/SourcePane.html"), + templateCssPath: dojo.uri.dojoUri("src/widget/demoEngine/templates/SourcePane.css"), + postCreate: function() { + dojo.html.addClass(this.domNode,this.domNodeClass); + dojo.debug("PostCreate"); + }, + + getSource: function() { + if (this.href) { + dojo.io.bind({ + url: this.href, + load: dojo.lang.hitch(this, "fillInSource"), + mimetype: "text/plain" + }); + } + }, + + fillInSource: function(type, source, e) { + this.sourceNode.value=source; + }, + + setHref: function(url) { + this.href = url; + this.getSource(); + } + }, + "", + function() { + dojo.debug("SourcePane Init"); + this.domNodeClass="sourcePane"; + this.sourceNode = ""; + this.href = ""; + } +); diff --git a/source/web/scripts/ajax/src/widget/demoEngine/__package__.js b/source/web/scripts/ajax/src/widget/demoEngine/__package__.js new file mode 100644 index 0000000000..588e1f01d6 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/__package__.js @@ -0,0 +1,10 @@ +dojo.kwCompoundRequire({ + browser: [ + "dojo.widget.demoEngine.DemoItem", + "dojo.widget.demoEngine.DemoNavigator", + "dojo.widget.demoEngine.DemoPane", + "dojo.widget.demoEngine.SourcePane", + "dojo.widget.demoEngine.DemoContainer" + ] +}); +dojo.provide("dojo.widget.demoEngine.*"); diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoContainer.css b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoContainer.css new file mode 100644 index 0000000000..07d97c0e11 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoContainer.css @@ -0,0 +1,39 @@ +.demoContainer{ + width: 100%; + height: 100%; + padding: 0px; + margin: 0px; +} + +.demoContainer .return { + cursor: pointer; +} + +.demoContainer span { + margin-right: 10px; + cursor: pointer; +} + +.demoContainer .selected { + border-bottom: 5px solid #95bfff; +} + +.demoContainer table { + background: #f5f5f5; + width: 100%; + height: 100%; +} + +.demoContainerTabs { + width: 100%; + height: 400px; +} + +.demoContainerTabs .dojoTabLabels-top { + display: none; +} + +.demoContainerTabs .dojoTabPaneWrapper { + border: 0px; +} + diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoContainer.html b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoContainer.html new file mode 100644 index 0000000000..08ee83ac9a --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoContainer.html @@ -0,0 +1,25 @@ +
+ + + + + + + + + + + +
+ + +

+

+
+ source + demo +
+
+
+
+
diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoItem.css b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoItem.css new file mode 100644 index 0000000000..6e99158499 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoItem.css @@ -0,0 +1,58 @@ +.demoItemSummaryBox { + background: #efefef; + border:1px solid #dae3ee; +} + +.demoItemScreenshot { + padding:0.65em; + width:175px; + border-right:1px solid #fafafa; + text-align:center; + cursor: pointer; +} + +.demoItemWrapper{ + margin-bottom:1em; +} + +.demoItemWrapper a:link, .demoItemWrapper a:visited { + color:#a6238f; + text-decoration:none; +} + +.demoItemSummaryContainer { + border-left:1px solid #ddd; +} + +.demoItemSummaryContainer h1 { + background-color:#e8e8e8; + border-bottom: 1px solid #e6e6e6; + color:#738fb9; + margin:1px; + padding:0.5em; + font-family:"Lucida Grande", "Tahoma", serif; + font-size:1.25em; + font-weight:normal; +} + +.demoItemSummaryContainer h1 .packageSummary { + display:block; + color:#000; + font-size:10px; + margin-top:2px; +} + +.demoItemSummaryContainer .demoItemSummary{ + padding:1em; +} + +.demoItemSummaryContainer .demoItemSummary p { + font-size:0.85em; + padding:0; + margin:0; +} + +.demoItemView { + text-align:right; + cursor: pointer; +} diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoItem.html b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoItem.html new file mode 100644 index 0000000000..db037912b8 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoItem.html @@ -0,0 +1,21 @@ +
+
+ + + + + + + +
+ + +

+

+
+

+
+
+
+
+
diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoNavigator.css b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoNavigator.css new file mode 100644 index 0000000000..612aaf33f6 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoNavigator.css @@ -0,0 +1,28 @@ +.demoNavigatorListWrapper { + border:1px solid #dcdbdb; + background-color:#f8f8f8; + padding:2px; +} + +.demoNavigatorListContainer { + border:1px solid #f0f0f0; + background-color:#fff; + padding:1em; +} + +.demoNavigator h1 { + margin-top: 0px; + margin-bottom: 10px; + font-size: 1.2em; + border-bottom:1px dotted #a9ccf5; +} + +.demoNavigator .dojoButton { + margin-bottom: 5px; +} + +.demoNavigator .dojoButton .dojoButtonContents { + font-size: 1.1em; + width: 100px; + color: black; +} diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoNavigator.html b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoNavigator.html new file mode 100644 index 0000000000..dd044061b9 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoNavigator.html @@ -0,0 +1,24 @@ +
+ + + + + + + + + + + +
+

Categories

+
+
+
+
+
+
+
+
+
+
diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoPane.css b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoPane.css new file mode 100644 index 0000000000..dd6836543b --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoPane.css @@ -0,0 +1,18 @@ +.demoPane { + width: 100%; + height: 100%; + padding: 0px; + margin: 0px; + overflow: hidden; +} + +.demoPane iframe { + width: 100%; + height: 100%; + border: 0px; + border: none; + overflow: auto; + padding: 0px; + margin:0px; + background: #ffffff; +} diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoPane.html b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoPane.html new file mode 100644 index 0000000000..482f3d1d54 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/DemoPane.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/SourcePane.css b/source/web/scripts/ajax/src/widget/demoEngine/templates/SourcePane.css new file mode 100644 index 0000000000..8e78c80d4a --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/SourcePane.css @@ -0,0 +1,20 @@ +.sourcePane { + width: 100%; + height: 100%; + padding: 0px; + margin: 0px; + overflow: hidden; +} + +.sourcePane textarea{ + width: 100%; + height: 100%; + border: 0px; + overflow: auto; + padding: 0px; + margin:0px; +} + +* html .sourcePane { + overflow: auto; +} diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/SourcePane.html b/source/web/scripts/ajax/src/widget/demoEngine/templates/SourcePane.html new file mode 100644 index 0000000000..b55b5404a6 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/SourcePane.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/general.css b/source/web/scripts/ajax/src/widget/demoEngine/templates/general.css new file mode 100644 index 0000000000..45dae861a3 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/demoEngine/templates/general.css @@ -0,0 +1,73 @@ +.demoListWrapper { + border:1px solid #dcdbdb; + background-color:#f8f8f8; + padding:2px; +} + +.demoListContainer { + border:1px solid #f0f0f0; + background-color:#fff; + padding:1em; +} + +.demoSummaryBox { + background: #efefef; + border:1px solid #dae3ee; +} + +.screenshot { + padding:0.65em; + width:175px; + border-right:1px solid #fafafa; + text-align:center; +} + +.demoSummary { + margin-bottom:1em; +} + +.demoSummary a:link, .demoSummary a:visited { + color:#a6238f; + text-decoration:none; +} + +.summaryContainer { + border-left:1px solid #ddd; +} + +.summaryContainer h1 { + background-color:#e8e8e8; + border-bottom: 1px solid #e6e6e6; + color:#738fb9; + margin:1px; + padding:0.5em; + font-family:"Lucida Grande", "Tahoma", serif; + font-size:1.25em; + font-weight:normal; +} + +.summaryContainer h1 .packageSummary { + display:block; + color:#000; + font-size:10px; + margin-top:2px; +} + +.summaryContainer .summary { + padding:1em; +} + +.summaryContainer .summary p { + font-size:0.85em; + padding:0; + margin:0; +} + +.reflection { + background: url("images/demoBoxReflection.gif") repeat-x top left; + height:25px; +} + +.view { + text-align:right; +} diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/images/test_thumb.gif b/source/web/scripts/ajax/src/widget/demoEngine/templates/images/test_thumb.gif new file mode 100644 index 0000000000000000000000000000000000000000..8bf85e9efcd8725cce3a740fb0ff75b39b0cdff6 GIT binary patch literal 3198 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 literal 0 HcmV?d00001 diff --git a/source/web/scripts/ajax/src/widget/demoEngine/templates/images/viewDemo.png b/source/web/scripts/ajax/src/widget/demoEngine/templates/images/viewDemo.png new file mode 100644 index 0000000000000000000000000000000000000000..07c6ff9ef74d2447a0eadfcff45c45a01714edaf GIT binary patch literal 859 zcmV-h1El8beb;sOhQs0W9OW7SvplI*t1tWg{)-&v-?Ayv z#~J*#wf6`0dVQzU>D+F&+wV;ZC=?1l9zEzpWUD-vCI~cMVPAcbraD7n7~XI|P=0`P#Iwf}ksl*JV7oy1Z?1_iMMK*#iqwWS4~nCK z4+P*tQI0JjCkAzsy$c|_DvCA#EE{Y|t20sXR}J_CT8Dl#v=2EVCt?Gdw{8adF`OZl z#5dnCdTkz@fS0QXkSW(tIV4XR(&f7;Y}nCS9ti*0axq&z7QBmi1pOl3 zk9bOa%kLsq|0hQmJ`d-?&3~1blXo1sCpk*PAB_x~nP(YZSLT4T%Q#1u4e)jbpDpN3 z=sENZdcn_gPM^}onxo~P7`jbzUH4uFpKH+TDftBUH^V;TW#kkmi0PFFoJBAgH~5Z$ lVE!Tn#EU#u)8&r<0|3>C2!LGRs}ukL002ovPDHLkV1k}&gm3@= literal 0 HcmV?d00001 diff --git a/source/web/scripts/ajax/src/widget/html/AccordionPane.js b/source/web/scripts/ajax/src/widget/html/AccordionPane.js new file mode 100644 index 0000000000..07db076c75 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/html/AccordionPane.js @@ -0,0 +1,98 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.provide("dojo.widget.html.AccordionPane"); +dojo.require("dojo.widget.TitlePane"); + +dojo.widget.html.AccordionPane = function(){ + + dojo.widget.html.TitlePane.call(this); + this.widgetType = "AccordionPane"; + + this.open=false; + this.allowCollapse=true; + this.label=""; + this.open=false; + + this.labelNodeClass=""; + this.containerNodeClass=""; +} + +dojo.inherits(dojo.widget.html.AccordionPane, dojo.widget.html.TitlePane); + +dojo.lang.extend(dojo.widget.html.AccordionPane, { + postCreate: function() { + dojo.widget.html.AccordionPane.superclass.postCreate.call(this); + this.domNode.widgetType=this.widgetType; + this.setSizes(); + dojo.html.addClass(this.labelNode, this.labelNodeClass); + dojo.html.disableSelection(this.labelNode); + dojo.html.addClass(this.containerNode, this.containerNodeClass); + }, + + collapse: function() { + //dojo.fx.html.wipeOut(this.containerNode,250); + //var anim = dojo.fx.html.wipe(this.containerNode, 1000, this.containerNode.offsetHeight, 0, null, true); + this.containerNode.style.display="none"; + this.open=false; + }, + + expand: function() { + //dojo.fx.html.wipeIn(this.containerNode,250); + this.containerNode.style.display="block"; + //var anim = dojo.fx.html.wipe(this.containerNode, 1000, 0, this.containerNode.scrollHeight, null, true); + this.open=true; + }, + + getCollapsedHeight: function() { + return dojo.style.getOuterHeight(this.labelNode)+1; + }, + + setSizes: function() { + var siblings = this.domNode.parentNode.childNodes; + var height=dojo.style.getInnerHeight(this.domNode.parentNode)-this.getCollapsedHeight(); + + this.siblingWidgets = []; + + for (var x=0; x node.offsetTop){ + parent.scrollTop -= (parent.scrollTop - node.offsetTop); + } + } + }, + + // does the actual highlight + focusOptionNode: function(node){ + if(this._highlighted_option != node){ + this.blurOptionNode(); + this._highlighted_option = node; + dojo.html.addClass(this._highlighted_option, "dojoComboBoxItemHighlight"); + } + }, + + // removes highlight on highlighted + blurOptionNode: function(){ + if(this._highlighted_option){ + dojo.html.removeClass(this._highlighted_option, "dojoComboBoxItemHighlight"); + this._highlighted_option = null; + } + }, + + highlightNextOption: function(){ + if((!this._highlighted_option) || !this._highlighted_option.parentNode){ + this.focusOptionNode(this.optionsListNode.firstChild); + }else if(this._highlighted_option.nextSibling){ + this.focusOptionNode(this._highlighted_option.nextSibling); + } + this.scrollIntoView(); + }, + + highlightPrevOption: function(){ + if(this._highlighted_option && this._highlighted_option.previousSibling){ + this.focusOptionNode(this._highlighted_option.previousSibling); + }else{ + this._highlighted_option = null; + this.hideResultList(); + return; + } + this.scrollIntoView(); + }, + + itemMouseOver: function(evt){ + this.focusOptionNode(evt.target); + dojo.html.addClass(this._highlighted_option, "dojoComboBoxItemHighlight"); + }, + + itemMouseOut: function(evt){ + this.blurOptionNode(); + }, + + fillInTemplate: function(args, frag){ + // FIXME: need to get/assign DOM node names for form participation here. + this.comboBoxValue.name = this.name; + this.comboBoxSelectionValue.name = this.name+"_selected"; + + var source = this.getFragNodeRef(frag); + dojo.html.copyStyle(this.domNode, source); + + var dpClass; + if(this.mode == "remote"){ + dpClass = dojo.widget.incrementalComboBoxDataProvider; + }else if(typeof this.dataProviderClass == "string"){ + dpClass = dojo.evalObjPath(this.dataProviderClass) + }else{ + dpClass = this.dataProviderClass; + } + this.dataProvider = new dpClass(); + this.dataProvider.init(this, this.getFragNodeRef(frag)); + + // Prevent IE bleed-through problem + this.optionsIframe = new dojo.html.BackgroundIframe(this.optionsListWrapper); + this.optionsIframe.size([0,0,0,0]); + }, + + + focus: function(){ + // summary + // set focus to input node from code + this.tryFocus(); + }, + + openResultList: function(results){ + this.clearResultList(); + if(!results.length){ + this.hideResultList(); + } + + if( (this.autoComplete)&& + (results.length)&& + (!this._prev_key_backspace)&& + (this.textInputNode.value.length > 0)){ + var cpos = this.getCaretPos(this.textInputNode); + // only try to extend if we added the last character at the end of the input + if((cpos+1) > this.textInputNode.value.length){ + // only add to input node as we would overwrite Capitalisation of chars + this.textInputNode.value += results[0][0].substr(cpos); + // build a new range that has the distance from the earlier + // caret position to the end of the first string selected + this.setSelectedRange(this.textInputNode, cpos, this.textInputNode.value.length); + } + } + + var even = true; + while(results.length){ + var tr = results.shift(); + if(tr){ + var td = document.createElement("div"); + td.appendChild(document.createTextNode(tr[0])); + td.setAttribute("resultName", tr[0]); + td.setAttribute("resultValue", tr[1]); + td.className = "dojoComboBoxItem "+((even) ? "dojoComboBoxItemEven" : "dojoComboBoxItemOdd"); + even = (!even); + this.optionsListNode.appendChild(td); + dojo.event.connect(td, "onmouseover", this, "itemMouseOver"); + dojo.event.connect(td, "onmouseout", this, "itemMouseOut"); + } + } + + // show our list (only if we have content, else nothing) + this.showResultList(); + }, + + onFocusInput: function(){ + this._hasFocus = true; + }, + + onBlurInput: function(){ + this._hasFocus = false; + this._handleBlurTimer(true, 500); + }, + + // collect all blur timers issues here + _handleBlurTimer: function(/*Boolean*/clear, /*Number*/ millisec){ + if(this.blurTimer && (clear || millisec)){ + clearTimeout(this.blurTimer); + } + if(millisec){ // we ignore that zero is false and never sets as that never happens in this widget + this.blurTimer = dojo.lang.setTimeout(this, "checkBlurred", millisec); + } + }, + + // these 2 are needed in IE and Safari as inputTextNode loses focus when scrolling optionslist + _onMouseOver: function(evt){ + if(!this._mouseover_list){ + this._handleBlurTimer(true, 0); + this._mouseover_list = true; + } + }, + + _onMouseOut:function(evt){ + var relTarget = evt.relatedTarget; + if(!relTarget || relTarget.parentNode!=this.optionsListNode){ + this._mouseover_list = false; + this._handleBlurTimer(true, 100); + this.tryFocus(); + } + }, + + _isInputEqualToResult: function(result){ + input = this.textInputNode.value; + if(!this.dataProvider.caseSensitive){ + input = input.toLowerCase(); + result = result.toLowerCase(); + } + return (input == result); + }, + + _isValidOption: function(){ + tgt = dojo.dom.firstElement(this.optionsListNode); + isValidOption = false; + while(!isValidOption && tgt){ + if(this._isInputEqualToResult(tgt.getAttribute("resultName"))){ + isValidOption = true; + }else{ + tgt = dojo.dom.nextElement(tgt); + } + } + return isValidOption; + }, + + checkBlurred: function(){ + if(!this._hasFocus && !this._mouseover_list){ + this.hideResultList(); + // clear the list if the user empties field and moves away. + if(!this.textInputNode.value.length){ + this.setAllValues("", ""); + return; + } + + isValidOption = this._isValidOption(); + // enforce selection from option list + if(this.forceValidOption && !isValidOption){ + this.setAllValues("", ""); + return; + } + if(!isValidOption){// clear + this.setSelectedValue(""); + } + } + }, + + sizeBackgroundIframe: function(){ + var w = dojo.style.getOuterWidth(this.optionsListNode); + var h = dojo.style.getOuterHeight(this.optionsListNode); + if( w==0 || h==0 ){ + // need more time to calculate size + dojo.lang.setTimeout(this, "sizeBackgroundIframe", 100); + return; + } + if(this._result_list_open){ + this.optionsIframe.size([0,0,w,h]); + } + }, + + selectOption: function(evt){ + var tgt = null; + if(!evt){ + evt = { target: this._highlighted_option }; + } + + if(!dojo.dom.isDescendantOf(evt.target, this.optionsListNode)){ + // handle autocompletion where the the user has hit ENTER or TAB + + // if the input is empty do nothing + if(!this.textInputNode.value.length){ + return; + } + tgt = dojo.dom.firstElement(this.optionsListNode); + + // user has input value not in option list + if(!tgt || !this._isInputEqualToResult(tgt.getAttribute("resultName"))){ + return; + } + // otherwise the user has accepted the autocompleted value + }else{ + tgt = evt.target; + } + + while((tgt.nodeType!=1)||(!tgt.getAttribute("resultName"))){ + tgt = tgt.parentNode; + if(tgt === document.body){ + return false; + } + } + + this.textInputNode.value = tgt.getAttribute("resultName"); + this.selectedResult = [tgt.getAttribute("resultName"), tgt.getAttribute("resultValue")]; + this.setAllValues(tgt.getAttribute("resultName"), tgt.getAttribute("resultValue")); + if(!evt.noHide){ + this.hideResultList(); + this.setSelectedRange(this.textInputNode, 0, null); + } + this.tryFocus(); + }, + + clearResultList: function(){ + var oln = this.optionsListNode; + while(oln.firstChild){ + dojo.event.disconnect(oln.firstChild, "onmouseover", this, "itemMouseOver"); + dojo.event.disconnect(oln.firstChild, "onmouseout", this, "itemMouseOut"); + oln.removeChild(oln.firstChild); + } + }, + + hideResultList: function(){ + if(this._result_list_open){ + this._result_list_open = false; + this.optionsIframe.size([0,0,0,0]); + dojo.lfx.fadeHide(this.optionsListNode, this.fadeTime).play(); + } + }, + + showResultList: function(){ + // Our dear friend IE doesnt take max-height so we need to calculate that on our own every time + var childs = this.optionsListNode.childNodes; + if(childs.length){ + var visibleCount = this.maxListLength; + if(childs.length < visibleCount){ + visibleCount = childs.length; + } + + with(this.optionsListNode.style){ + display = ""; + height = ((visibleCount) ? (dojo.style.getOuterHeight(childs[0]) * visibleCount) : 0)+"px"; + width = dojo.html.getOuterWidth(this.cbTableNode)-2+"px"; + } + // only fadein once (flicker) + if(!this._result_list_open){ + dojo.html.setOpacity(this.optionsListNode, 0); + dojo.lfx.fadeIn(this.optionsListNode, this.fadeTime).play(); + } + + // prevent IE bleed through + this._iframeTimer = dojo.lang.setTimeout(this, "sizeBackgroundIframe", 200); + this._result_list_open = true; + }else{ + this.hideResultList(); + } + }, + + handleArrowClick: function(){ + this._handleBlurTimer(true, 0); + this.tryFocus(); + if(this._result_list_open){ + this.hideResultList(); + }else{ + this.startSearchFromInput(); + } + }, + + tryFocus: function(){ + try { + this.textInputNode.focus(); + } catch (e) { + // element isn't focusable if disabled, or not visible etc - not easy to test for. + }; + }, + + startSearchFromInput: function(){ + this.startSearch(this.textInputNode.value); + }, + + postCreate: function(){ + dojo.event.connect(this, "startSearch", this.dataProvider, "startSearch"); + dojo.event.connect(this.dataProvider, "provideSearchResults", this, "openResultList"); + dojo.event.connect(this.textInputNode, "onblur", this, "onBlurInput"); + dojo.event.connect(this.textInputNode, "onfocus", this, "onFocusInput"); + + var s = dojo.widget.html.stabile.getState(this.widgetId); + if (s) { + this.setState(s); + } + } + } +); diff --git a/source/web/scripts/ajax/src/widget/html/ContentPane.js b/source/web/scripts/ajax/src/widget/html/ContentPane.js new file mode 100644 index 0000000000..8c045da3e3 --- /dev/null +++ b/source/web/scripts/ajax/src/widget/html/ContentPane.js @@ -0,0 +1,566 @@ +/* + Copyright (c) 2004-2006, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +dojo.provide("dojo.widget.html.ContentPane"); + +dojo.require("dojo.widget.*"); +dojo.require("dojo.io.*"); +dojo.require("dojo.widget.HtmlWidget"); +dojo.require("dojo.widget.ContentPane"); +dojo.require("dojo.string"); +dojo.require("dojo.string.extras"); +dojo.require("dojo.style"); + +dojo.widget.html.ContentPane = function(){ + this._onLoadStack = []; + this._onUnLoadStack = []; + dojo.widget.HtmlWidget.call(this); +} +dojo.inherits(dojo.widget.html.ContentPane, dojo.widget.HtmlWidget); + +dojo.lang.extend(dojo.widget.html.ContentPane, { + widgetType: "ContentPane", + isContainer: true, + + // remote loading options + adjustPaths: true, + href: "", + extractContent: true, + parseContent: true, + cacheContent: true, + preload: false, // force load of data even if pane is hidden + refreshOnShow: false, + handler: "", // generate pane content from a java function + executeScripts: false, // if true scripts in content will be evaled after content is set and parsed + scriptScope: null, // scopeContainer for downloaded scripts + + // If the user want a global in the remote script he/she just omitts the var + // examples: + //-------------------------- + // these gets collected by scriptScope and is reached by dojo.widget.byId('..').scriptScope.myCustomproperty + // this.myString = "dojo is a great javascript toolkit!"; + // + // this.alertMyString = function(){ + // alert(myString); + // } + // ------------------------- + // these go into the global namespace (window) notice lack of var, equiv to window.myString + // myString = "dojo is a javascript toolkit!"; + // + // alertMyString = function(){ + // alert(myString); + // } + + + // private + _remoteStyles: null, // array of stylenodes inserted to document head + // by remote content, used when we clean up for new content + + _callOnUnLoad: false, // used by setContent and _handleDefults, makes sure onUnLoad is only called once + + postCreate: function(args, frag, parentComp){ + if ( this.handler != "" ){ + this.setHandler(this.handler); + } + if(this.isShowing()||this.preload){ this.loadContents(); } + }, + + show: function(){ + // if refreshOnShow is true, reload the contents every time; otherwise, load only the first time + if(this.refreshOnShow){ + this.refresh(); + }else{ + this.loadContents(); + } + dojo.widget.html.ContentPane.superclass.show.call(this); + }, + + refresh: function(){ + this.isLoaded=false; + this.loadContents(); + }, + + loadContents: function() { + if ( this.isLoaded ){ + return; + } + this.isLoaded=true; + if ( dojo.lang.isFunction(this.handler)) { + this._runHandler(); + } else if ( this.href != "" ) { + this._downloadExternalContent(this.href, this.cacheContent); + } + }, + + + setUrl: function(/*String*/ url) { + // summary: + // Reset the (external defined) content of this pane and replace with new url + this.href = url; + this.isLoaded = false; + if ( this.preload || this.isShowing() ){ + this.loadContents(); + } + }, + + _downloadExternalContent: function(url, useCache) { + this._handleDefaults("Loading...", "onDownloadStart"); + var self = this; + dojo.io.bind({ + url: url, + useCache: useCache, + preventCache: !useCache, + mimetype: "text/html", + handler: function(type, data, e) { + if(type == "load") { + self.onDownloadEnd.call(self, url, data); + } else { + // works best when from a live server instead of from file system + self._handleDefaults.call(self, "Error loading '" + url + "' (" + e.status + " "+ e.statusText + ")", "onDownloadError"); + self.onLoad(); + } + } + }); + }, + + // called when setContent is finished + onLoad: function(e){ + this._runStack("_onLoadStack"); + }, + + // called before old content is cleared + onUnLoad: function(e){ + this._runStack("_onUnLoadStack"); + this.scriptScope = null; + }, + + _runStack: function(stName){ + var st = this[stName]; var err = ""; + for(var i = 0;i < st.length; i++){ + try{ + st[i].call(this.scriptScope); + }catch(e){ + err += "\n"+st[i]+" failed: "+e.description; + } + } + this[stName] = []; + + if(err.length){ + var name = (stName== "_onLoadStack") ? "addOnLoad" : "addOnUnLoad"; + this._handleDefaults(name+" failure\n "+err, "onExecError", true); + } + }, + + addOnLoad: function(obj, func){ + // summary + // same as to dojo.addOnLoad but does not take "function_name" as a string + this._pushOnStack(this._onLoadStack, obj, func); + }, + + addOnUnLoad: function(obj, func){ + // summary + // same as to dojo.addUnOnLoad but does not take "function_name" as a string + this._pushOnStack(this._onUnLoadStack, obj, func); + }, + + _pushOnStack: function(stack, obj, func){ + if(typeof func == 'undefined') { + stack.push(obj); + }else{ + stack.push(function(){ obj[func](); }); + } + }, + + destroy: function(){ + // make sure we call onUnLoad + this.onUnLoad(); + dojo.widget.html.ContentPane.superclass.destroy.call(this); + }, + + // called when content script eval error or Java error occurs, preventDefault-able + onExecError: function(e){ /*stub*/ }, + + // called on DOM faults, require fault etc in content, preventDefault-able + onContentError: function(e){ /*stub*/ }, + + // called when download error occurs, preventDefault-able + onDownloadError: function(e){ /*stub*/ }, + + // called before download starts, preventDefault-able + onDownloadStart: function(e){ /*stub*/ }, + + // called when download is finished + onDownloadEnd: function(url, data){ + data = this.splitAndFixPaths(data, url); + this.setContent(data); + }, + + // usefull if user wants to prevent default behaviour ie: _setContent("Error...") + _handleDefaults: function(e, handler, useAlert){ + if(!handler){ handler = "onContentError"; } + if(dojo.lang.isString(e)){ + e = { + "text": e, + "toString": function(){ return this.text; } + } + } + if(typeof e.returnValue != "boolean"){ + e.returnValue = true; + } + if(typeof e.preventDefault != "function"){ + e.preventDefault = function(){ + this.returnValue = false; + } + } + // call our handler + this[handler](e); + if(e.returnValue){ + if(useAlert){ + alert(e.toString()); + }else{ + if(this._callOnUnLoad){ + this.onUnLoad(); // makes sure scripts can clean up after themselves, before we setContent + } + this._callOnUnLoad = false; // makes sure we dont try to call onUnLoad again on this event, + // ie onUnLoad before 'Loading...' but not before clearing 'Loading...' + this._setContent(e.toString()); + } + } + }, + + + splitAndFixPaths: function(/*String*/s, /*dojo.uri.Uri?*/url){ + // summary: + // fixes all remote paths in (hopefully) all cases for example images, remote scripts, links etc. + // splits up content in different pieces, scripts, title, style, link and whats left becomes .xml + + if(!url) { url = "./"; } // point to this page if not set + if(!s) { return ""; } + + // fix up paths in data + var titles = []; var scripts = []; var linkStyles = []; + var styles = []; var remoteScripts = []; var requires = []; + + // khtml is much more picky about dom faults, you can't for example attach a style node under body of document + // must go into head, as does a title node, so we need to cut out those tags + // cut out title tags + var match = []; + while(match){ + match = s.match(/]*>([\s\S]*?)<\/title>/i); // can't match with dot as that + if(!match){ break;} //doesnt match newline in js + titles.push(match[1]); + s = s.replace(/]*>[\s\S]*?<\/title>/i, ""); + } + + // cut out , as that bails out in khtml + var match = []; + while(match){ + match = s.match(/]*>([\s\S]*?)<\/style>/i); + if(!match){ break; } + styles.push(dojo.style.fixPathsInCssText(match[1], url)); + s = s.replace(/]*?>[\s\S]*?<\/style>/i, ""); + } + + // attributepaths one tag can have multiple paths example: + // or
+ // strip out the tag and run fix on that. + // this guarantees that we won't run replace another tag's attribute + it was easier do + var pos = 0; var pos2 = 0; var stop = 0 ;var str = ""; var fixedPath = ""; + var attr = []; var fix = ""; var tagFix = ""; var tag = ""; var regex = ""; + while(pos>-1){ + pos = s.search(/<[a-z][a-z0-9]*[^>]*\s(?:(?:src|href|style)=[^>])+[^>]*>/i); + if(pos==-1){ break; } + str += s.substring(0, pos); + s = s.substring(pos, s.length); + tag = s.match(/^<[a-z][a-z0-9]*[^>]*>/i)[0]; + s = s.substring(tag.length, s.length); + + // loop through attributes + pos2 = 0; tagFix = ""; fix = ""; regex = ""; var regexlen = 0; + while(pos2!=-1){ + // slices up before next attribute check, values from previous loop + tagFix += tag.substring(0, pos2) + fix; + tag = tag.substring(pos2+regexlen, tag.length); + + // fix next attribute or bail out when done + // hopefully this charclass covers most urls + attr = tag.match(/ (src|href|style)=(['"]?)([\w()\[\]\/.,\\'"-:;#=&?\s@]+?)\2/i); + if(!attr){ break; } + + switch(attr[1].toLowerCase()){ + case "src":// falltrough + case "href": + // this hopefully covers most common protocols + if(attr[3].search(/^(?:[#]|(?:(?:https?|ftps?|file|javascript|mailto|news):))/)==-1){ + fixedPath = (new dojo.uri.Uri(url, attr[3]).toString()); + } else { + pos2 = pos2 + attr[3].length; + continue; + } + break; + case "style":// style + fixedPath = dojo.style.fixPathsInCssText(attr[3], url); + break; + default: + pos2 = pos2 + attr[3].length; + continue; + } + + regex = " " + attr[1] + "=" + attr[2] + attr[3] + attr[2]; + regexlen = regex.length; + fix = " " + attr[1] + "=" + attr[2] + fixedPath + attr[2]; + pos2 = tag.search(new RegExp(dojo.string.escapeRegExp(regex))); + } + str += tagFix + tag; + pos = 0; // reset for next mainloop + } + s = str+s; + + // cut out all script tags, push them into scripts array + match = []; var tmp = []; + while(match){ + match = s.match(/]*)>([\s\S]*?)<\/script>/i); + if(!match){ break; } + if(match[1]){ + attr = match[1].match(/src=(['"]?)([^"']*)\1/i); + if(attr){ + // remove a dojo.js or dojo.js.uncompressed.js from remoteScripts + // we declare all files with dojo.js as bad, regardless of folder + var tmp = attr[2].search(/.*(\bdojo\b(?:\.uncompressed)?\.js)$/); + if(tmp > -1){ + dojo.debug("Security note! inhibit:"+attr[2]+" from beeing loaded again."); + }else{ + remoteScripts.push(attr[2]); + } + } + } + if(match[2]){ + // strip out all djConfig variables from script tags nodeValue + // this is ABSOLUTLY needed as reinitialize djConfig after dojo is initialised + // makes a dissaster greater than Titanic, update remove writeIncludes() to + var sc = match[2].replace(/(?:var )?\bdjConfig\b(?:[\s]*=[\s]*\{[^}]+\}|\.[\w]*[\s]*=[\s]*[^;\n]*)?;?|dojo\.hostenv\.writeIncludes\(\s*\);?/g, ""); + if(!sc){ continue; } + + // cut out all dojo.require (...) calls, if we have execute + // scripts false widgets dont get there require calls + // does suck out possible widgetpackage registration as well + tmp = []; + while(tmp && requires.length<100){ + tmp = sc.match(/dojo\.(?:(?:require(?:After)?(?:If)?)|(?:widget\.(?:manager\.)?registerWidgetPackage)|(?:(?:hostenv\.)?setModulePrefix))\((['"]).*?\1\)\s*;?/); + if(!tmp){ break;} + requires.push(tmp[0]); + sc = sc.replace(tmp[0], ""); + } + scripts.push(sc); + } + s = s.replace(/]*>[\s\S]*?<\/script>/i, ""); + } + + // scan for scriptScope in html eventHandlers and replace with link to this pane + if(this.executeScripts){ + var regex = /(<[a-zA-Z][a-zA-Z0-9]*\s[^>]*\S=(['"])[^>]*[^\.\]])scriptScope([^>]*>)/; + var pos = 0;var str = "";match = [];var cit = ""; + while(pos > -1){ + pos = s.search(regex); + if(pos > -1){ + cit = ((RegExp.$2=="'") ? '"': "'"); + str += s.substring(0, pos); + s = s.substr(pos).replace(regex, "$1dojo.widget.byId("+ cit + this.widgetId + cit + ").scriptScope$3"); + } + } + s = str + s; + } + + // cut out all + match = []; + while(match){ + match = s.match(/]*rel=['"]?stylesheet['"]?[^>]*)>/i); + if(!match){ break; } + attr = match[1].match(/href=(['"]?)([^'">]*)\1/i); + if(attr){ + linkStyles.push(attr[2]); + } + s = s.replace(new RegExp(match[0]), ""); + } + + return {"xml": s, // Object + "styles": styles, + "linkStyles": linkStyles, + "titles": titles, + "requires": requires, + "scripts": scripts, + "remoteScripts": remoteScripts, + "url": url}; + }, + + + _setContent: function(/*String*/ xml){ + // summary: + // private internal function without path regExpCheck and no onLoad calls aftervards + + // remove old children from current content + this.destroyChildren(); + + // remove old stylenodes from HEAD + if(this._remoteStyles){ + for(var i = 0; i < this._remoteStyles.length; i++){ + if(this._remoteStyles[i] && this._remoteStyles.parentNode){ + this._remoteStyles[i].parentNode.removeChild(this._remoteStyles[i]); + } + } + this._remoteStyles = null; + } + + var node = this.containerNode || this.domNode; + try{ + if(typeof xml != "string"){ + node.innerHTML = ""; + node.appendChild(xml); + }else{ + node.innerHTML = xml; + } + } catch(e){ + e = "Could'nt load content:"+e; + this._handleDefaults(e, "onContentError"); + } + }, + + setContent: function(/*String*/ data){ + // summary: + // Destroys old content and setting new content, and possibly initialize any widgets within 'data' + + if(this._callOnUnLoad){ // this tells a remote script clean up after itself + this.onUnLoad(); + } + this._callOnUnLoad = true; + + if(!data || dojo.dom.isNode(data)){ + // if we do a clean using setContent(""); or setContent(#node) bypass all parseing, extractContent etc + this._setContent(data); + this.onResized(); + this.onLoad(); + }else{ + // need to run splitAndFixPaths? ie. manually setting content + if((!data.xml)&&(this.adjustPaths)){ + data = this.splitAndFixPaths(data); + } + if(this.extractContent) { + var matches = data.xml.match(/]*>\s*([\s\S]+)\s*<\/body>/im); + if(matches) { data.xml = matches[1]; } + } + // insert styleNodes, from